tizen 2.4 release accepted/tizen_2.4_mobile tizen_2.4 accepted/tizen/2.4/mobile/20151029.033411 submit/tizen_2.4/20151028.064055 tizen_2.4_mobile_release
authorjk7744.park <jk7744.park@samsung.com>
Sat, 24 Oct 2015 07:38:46 +0000 (16:38 +0900)
committerjk7744.park <jk7744.park@samsung.com>
Sat, 24 Oct 2015 07:38:46 +0000 (16:38 +0900)
685 files changed:
Android.mk
CONTRIBUTIONS
COPYING
COPYING.mbsd [new file with mode: 0644]
README
configurations/tizen.config
configurations/tizen_hostapd.config
doc/Makefile
doc/code_structure.doxygen
doc/ctrl_iface.doxygen
doc/dbus.doxygen
doc/directories.doxygen
doc/doxygen.conf
doc/driver_wrapper.doxygen
doc/eap.doxygen
doc/eap_server.doxygen
doc/hostapd.fig
doc/hostapd_ctrl_iface.doxygen
doc/mainpage.doxygen
doc/p2p.doxygen
doc/porting.doxygen
doc/testing_tools.doxygen
doc/wpa_supplicant.fig
eap_example/eap_example.c
eap_example/eap_example_peer.c
etc/rc.d/init.d/wpa_supplicant [deleted file]
files/wpa_supp.sh
hostapd/Android.mk
hostapd/ChangeLog
hostapd/Makefile
hostapd/README
hostapd/README-WPS
hostapd/android.config
hostapd/config_file.c
hostapd/ctrl_iface.c
hostapd/defconfig
hostapd/eap_register.c
hostapd/hapd_module_tests.c [new file with mode: 0644]
hostapd/hlr_auc_gw.c
hostapd/hostapd.8
hostapd/hostapd.conf
hostapd/hostapd.eap_user
hostapd/hostapd.eap_user_sqlite
hostapd/hostapd_cli.c
hostapd/main.c
hostapd/wps-ap-nfc.py
hs20/client/Android.mk [new file with mode: 0644]
hs20/client/Makefile [new file with mode: 0644]
hs20/client/devdetail.xml [new file with mode: 0644]
hs20/client/devinfo.xml [new file with mode: 0644]
hs20/client/est.c [new file with mode: 0644]
hs20/client/oma_dm_client.c [new file with mode: 0644]
hs20/client/osu_client.c [new file with mode: 0644]
hs20/client/osu_client.h [new file with mode: 0644]
hs20/client/spp_client.c [new file with mode: 0644]
mac80211_hwsim/tools/Makefile
mac80211_hwsim/tools/hwsim_test.c
packaging/wpa_supplicant.manifest
packaging/wpasupplicant.spec
radius_example/Makefile
radius_example/radius_example.c
src/Makefile
src/ap/Makefile
src/ap/accounting.c
src/ap/acs.c [new file with mode: 0644]
src/ap/acs.h [new file with mode: 0644]
src/ap/ap_config.c
src/ap/ap_config.h
src/ap/ap_drv_ops.c
src/ap/ap_drv_ops.h
src/ap/ap_list.c
src/ap/ap_list.h
src/ap/ap_mlme.c
src/ap/authsrv.c
src/ap/beacon.c
src/ap/beacon.h
src/ap/bss_load.c [new file with mode: 0644]
src/ap/bss_load.h [new file with mode: 0644]
src/ap/ctrl_iface_ap.c
src/ap/ctrl_iface_ap.h
src/ap/dfs.c [new file with mode: 0644]
src/ap/dfs.h [new file with mode: 0644]
src/ap/dhcp_snoop.c [new file with mode: 0644]
src/ap/dhcp_snoop.h [new file with mode: 0644]
src/ap/drv_callbacks.c
src/ap/eap_user_db.c
src/ap/gas_serv.c
src/ap/gas_serv.h
src/ap/hostapd.c
src/ap/hostapd.h
src/ap/hs20.c
src/ap/hs20.h
src/ap/hw_features.c
src/ap/hw_features.h
src/ap/iapp.c
src/ap/ieee802_11.c
src/ap/ieee802_11.h
src/ap/ieee802_11_auth.c
src/ap/ieee802_11_ht.c
src/ap/ieee802_11_shared.c
src/ap/ieee802_11_vht.c
src/ap/ieee802_1x.c
src/ap/ieee802_1x.h
src/ap/ndisc_snoop.c [new file with mode: 0644]
src/ap/ndisc_snoop.h [new file with mode: 0644]
src/ap/p2p_hostapd.c
src/ap/peerkey_auth.c
src/ap/pmksa_cache_auth.c
src/ap/pmksa_cache_auth.h
src/ap/sta_info.c
src/ap/sta_info.h
src/ap/tkip_countermeasures.c
src/ap/vlan_init.c
src/ap/vlan_init.h
src/ap/wmm.c
src/ap/wmm.h
src/ap/wnm_ap.c
src/ap/wnm_ap.h
src/ap/wpa_auth.c [changed mode: 0644->0755]
src/ap/wpa_auth.h
src/ap/wpa_auth_ft.c
src/ap/wpa_auth_glue.c
src/ap/wpa_auth_i.h
src/ap/wpa_auth_ie.c
src/ap/wpa_auth_ie.h
src/ap/wps_hostapd.c
src/ap/wps_hostapd.h
src/ap/x_snoop.c [new file with mode: 0755]
src/ap/x_snoop.h [new file with mode: 0755]
src/common/Makefile
src/common/common_module_tests.c [new file with mode: 0644]
src/common/defs.h
src/common/eapol_common.h
src/common/hw_features_common.c [new file with mode: 0755]
src/common/hw_features_common.h [new file with mode: 0755]
src/common/ieee802_11_common.c
src/common/ieee802_11_common.h
src/common/ieee802_11_defs.h
src/common/ieee802_1x_defs.h [new file with mode: 0644]
src/common/privsep_commands.h
src/common/qca-vendor-attr.h [new file with mode: 0644]
src/common/qca-vendor.h [new file with mode: 0644]
src/common/sae.c
src/common/sae.h
src/common/tnc.h [new file with mode: 0644]
src/common/version.h
src/common/wpa_common.c
src/common/wpa_common.h
src/common/wpa_ctrl.c
src/common/wpa_ctrl.h
src/common/wpa_helpers.c [new file with mode: 0644]
src/common/wpa_helpers.h [new file with mode: 0644]
src/crypto/Makefile
src/crypto/aes-ccm.c
src/crypto/aes-eax.c
src/crypto/aes-gcm.c
src/crypto/aes-omac1.c
src/crypto/aes-siv.c [new file with mode: 0755]
src/crypto/aes-unwrap.c
src/crypto/aes-wrap.c
src/crypto/aes_wrap.h
src/crypto/crypto.h
src/crypto/crypto_internal-rsa.c
src/crypto/crypto_nss.c [deleted file]
src/crypto/crypto_openssl.c
src/crypto/dh_groups.c
src/crypto/md5.c
src/crypto/milenage.c
src/crypto/ms_funcs.c
src/crypto/random.c
src/crypto/sha1-internal.c
src/crypto/sha1-prf.c
src/crypto/sha1.c
src/crypto/sha256-kdf.c [new file with mode: 0755]
src/crypto/sha256-prf.c
src/crypto/sha256.h
src/crypto/sha384.h [new file with mode: 0755]
src/crypto/tls.h
src/crypto/tls_gnutls.c [changed mode: 0644->0755]
src/crypto/tls_internal.c
src/crypto/tls_none.c
src/crypto/tls_nss.c [deleted file]
src/crypto/tls_openssl.c [changed mode: 0644->0755]
src/crypto/tls_schannel.c
src/drivers/Makefile
src/drivers/android_drv.h
src/drivers/driver.h [changed mode: 0644->0755]
src/drivers/driver_atheros.c [changed mode: 0644->0755]
src/drivers/driver_bsd.c
src/drivers/driver_common.c
src/drivers/driver_hostap.c
src/drivers/driver_macsec_qca.c [new file with mode: 0644]
src/drivers/driver_madwifi.c
src/drivers/driver_ndis.c
src/drivers/driver_nl80211.c [changed mode: 0644->0755]
src/drivers/driver_nl80211.h [new file with mode: 0755]
src/drivers/driver_nl80211_android.c [new file with mode: 0755]
src/drivers/driver_nl80211_capa.c [new file with mode: 0755]
src/drivers/driver_nl80211_event.c [new file with mode: 0755]
src/drivers/driver_nl80211_monitor.c [new file with mode: 0644]
src/drivers/driver_nl80211_scan.c [new file with mode: 0644]
src/drivers/driver_none.c
src/drivers/driver_privsep.c
src/drivers/driver_roboswitch.c
src/drivers/driver_test.c
src/drivers/driver_wext.c [changed mode: 0644->0755]
src/drivers/driver_wext.h
src/drivers/driver_wired.c
src/drivers/drivers.c
src/drivers/drivers.mak
src/drivers/drivers.mk
src/drivers/linux_defines.h [new file with mode: 0644]
src/drivers/linux_ioctl.c
src/drivers/linux_wext.h
src/drivers/netlink.c
src/drivers/nl80211_copy.h
src/drivers/priv_netlink.h
src/eap_common/Makefile
src/eap_common/eap_common.c
src/eap_common/eap_common.h
src/eap_common/eap_defs.h
src/eap_common/eap_eke_common.c [new file with mode: 0644]
src/eap_common/eap_eke_common.h [new file with mode: 0644]
src/eap_common/eap_fast_common.c
src/eap_common/eap_fast_common.h
src/eap_common/eap_gpsk_common.c
src/eap_common/eap_ikev2_common.c
src/eap_common/eap_ikev2_common.h
src/eap_common/eap_pax_common.c
src/eap_common/eap_pax_common.h
src/eap_common/eap_pwd_common.c
src/eap_common/eap_pwd_common.h
src/eap_common/eap_sim_common.c
src/eap_common/eap_sim_common.h
src/eap_common/ikev2_common.c
src/eap_common/ikev2_common.h
src/eap_peer/Makefile
src/eap_peer/eap.c
src/eap_peer/eap.h
src/eap_peer/eap_aka.c
src/eap_peer/eap_config.h
src/eap_peer/eap_eke.c [new file with mode: 0644]
src/eap_peer/eap_fast.c
src/eap_peer/eap_fast_pac.c
src/eap_peer/eap_gpsk.c
src/eap_peer/eap_i.h
src/eap_peer/eap_ikev2.c
src/eap_peer/eap_leap.c
src/eap_peer/eap_methods.c
src/eap_peer/eap_methods.h
src/eap_peer/eap_mschapv2.c
src/eap_peer/eap_pax.c
src/eap_peer/eap_peap.c
src/eap_peer/eap_proxy.h
src/eap_peer/eap_proxy_dummy.c
src/eap_peer/eap_psk.c
src/eap_peer/eap_pwd.c
src/eap_peer/eap_sake.c
src/eap_peer/eap_sim.c
src/eap_peer/eap_tls.c
src/eap_peer/eap_tls_common.c
src/eap_peer/eap_tls_common.h
src/eap_peer/eap_tnc.c
src/eap_peer/eap_ttls.c
src/eap_peer/eap_vendor_test.c
src/eap_peer/eap_wsc.c
src/eap_peer/ikev2.c
src/eap_peer/mschapv2.c
src/eap_peer/tncc.c
src/eap_server/Makefile
src/eap_server/eap.h
src/eap_server/eap_i.h
src/eap_server/eap_methods.h
src/eap_server/eap_server.c
src/eap_server/eap_server_aka.c
src/eap_server/eap_server_eke.c [new file with mode: 0644]
src/eap_server/eap_server_fast.c
src/eap_server/eap_server_gpsk.c
src/eap_server/eap_server_gtc.c
src/eap_server/eap_server_identity.c
src/eap_server/eap_server_ikev2.c
src/eap_server/eap_server_md5.c
src/eap_server/eap_server_methods.c
src/eap_server/eap_server_mschapv2.c
src/eap_server/eap_server_pax.c
src/eap_server/eap_server_peap.c
src/eap_server/eap_server_psk.c
src/eap_server/eap_server_pwd.c
src/eap_server/eap_server_sake.c
src/eap_server/eap_server_sim.c
src/eap_server/eap_server_tls.c
src/eap_server/eap_server_tls_common.c
src/eap_server/eap_server_tnc.c
src/eap_server/eap_server_ttls.c
src/eap_server/eap_server_wsc.c
src/eap_server/eap_sim_db.c
src/eap_server/eap_tls_common.h
src/eap_server/ikev2.c
src/eap_server/tncs.c
src/eapol_auth/Makefile
src/eapol_auth/eapol_auth_dump.c
src/eapol_auth/eapol_auth_sm.c
src/eapol_auth/eapol_auth_sm.h
src/eapol_auth/eapol_auth_sm_i.h
src/eapol_supp/Makefile
src/eapol_supp/eapol_supp_sm.c
src/eapol_supp/eapol_supp_sm.h
src/l2_packet/Makefile
src/l2_packet/l2_packet.h
src/l2_packet/l2_packet_freebsd.c
src/l2_packet/l2_packet_linux.c
src/l2_packet/l2_packet_ndis.c
src/l2_packet/l2_packet_none.c
src/l2_packet/l2_packet_pcap.c
src/l2_packet/l2_packet_privsep.c
src/l2_packet/l2_packet_winpcap.c
src/lib.rules
src/p2p/Makefile
src/p2p/p2p.c [changed mode: 0644->0755]
src/p2p/p2p.h [changed mode: 0644->0755]
src/p2p/p2p_build.c [changed mode: 0644->0755]
src/p2p/p2p_dev_disc.c [changed mode: 0644->0755]
src/p2p/p2p_go_neg.c [changed mode: 0644->0755]
src/p2p/p2p_group.c [changed mode: 0644->0755]
src/p2p/p2p_i.h [changed mode: 0644->0755]
src/p2p/p2p_invitation.c [changed mode: 0644->0755]
src/p2p/p2p_parse.c [changed mode: 0644->0755]
src/p2p/p2p_pd.c [changed mode: 0644->0755]
src/p2p/p2p_sd.c [changed mode: 0644->0755]
src/p2p/p2p_utils.c
src/pae/Makefile [new file with mode: 0644]
src/pae/ieee802_1x_cp.c [new file with mode: 0644]
src/pae/ieee802_1x_cp.h [new file with mode: 0644]
src/pae/ieee802_1x_kay.c [new file with mode: 0644]
src/pae/ieee802_1x_kay.h [new file with mode: 0644]
src/pae/ieee802_1x_kay_i.h [new file with mode: 0644]
src/pae/ieee802_1x_key.c [new file with mode: 0644]
src/pae/ieee802_1x_key.h [new file with mode: 0644]
src/pae/ieee802_1x_secy_ops.c [new file with mode: 0644]
src/pae/ieee802_1x_secy_ops.h [new file with mode: 0644]
src/radius/Makefile
src/radius/radius.c
src/radius/radius.h
src/radius/radius_client.c
src/radius/radius_das.c
src/radius/radius_das.h
src/radius/radius_server.c
src/radius/radius_server.h
src/rsn_supp/Makefile
src/rsn_supp/peerkey.c [changed mode: 0644->0755]
src/rsn_supp/peerkey.h [changed mode: 0644->0755]
src/rsn_supp/pmksa_cache.c [changed mode: 0644->0755]
src/rsn_supp/pmksa_cache.h [changed mode: 0644->0755]
src/rsn_supp/preauth.c [changed mode: 0644->0755]
src/rsn_supp/preauth.h [changed mode: 0644->0755]
src/rsn_supp/tdls.c [changed mode: 0644->0755]
src/rsn_supp/wpa.c [changed mode: 0644->0755]
src/rsn_supp/wpa.h [changed mode: 0644->0755]
src/rsn_supp/wpa_ft.c [changed mode: 0644->0755]
src/rsn_supp/wpa_i.h [changed mode: 0644->0755]
src/rsn_supp/wpa_ie.c [changed mode: 0644->0755]
src/rsn_supp/wpa_ie.h
src/tls/asn1.c
src/tls/asn1.h
src/tls/pkcs1.c
src/tls/pkcs1.h
src/tls/rsa.c
src/tls/rsa.h
src/tls/tlsv1_client.c
src/tls/tlsv1_client_read.c [changed mode: 0644->0755]
src/tls/tlsv1_client_write.c
src/tls/tlsv1_common.c
src/tls/tlsv1_common.h
src/tls/tlsv1_record.c
src/tls/tlsv1_server.c
src/tls/tlsv1_server.h
src/tls/tlsv1_server_i.h
src/tls/tlsv1_server_read.c
src/tls/tlsv1_server_write.c
src/tls/x509v3.c
src/utils/Makefile
src/utils/base64.c
src/utils/bitfield.c
src/utils/browser-android.c [new file with mode: 0644]
src/utils/browser-system.c [new file with mode: 0644]
src/utils/browser-wpadebug.c [new file with mode: 0644]
src/utils/browser.c [new file with mode: 0644]
src/utils/browser.h [new file with mode: 0644]
src/utils/build_config.h
src/utils/common.c
src/utils/common.h
src/utils/edit.c
src/utils/edit_simple.c
src/utils/eloop.c
src/utils/eloop.h
src/utils/eloop_win.c
src/utils/ext_password_test.c
src/utils/http-utils.h [new file with mode: 0644]
src/utils/http_curl.c [new file with mode: 0644]
src/utils/ip_addr.c
src/utils/ip_addr.h
src/utils/list.h
src/utils/os.h
src/utils/os_internal.c
src/utils/os_none.c
src/utils/os_unix.c
src/utils/os_win32.c
src/utils/pcsc_funcs.c
src/utils/pcsc_funcs.h
src/utils/platform.h [new file with mode: 0644]
src/utils/radiotap.c
src/utils/radiotap.h
src/utils/radiotap_iter.h
src/utils/trace.c
src/utils/trace.h
src/utils/utils_module_tests.c [new file with mode: 0644]
src/utils/uuid.c
src/utils/wpa_debug.c
src/utils/wpa_debug.h
src/utils/wpabuf.c
src/utils/wpabuf.h
src/utils/xml-utils.c [new file with mode: 0644]
src/utils/xml-utils.h [new file with mode: 0644]
src/utils/xml_libxml2.c [new file with mode: 0644]
src/wps/Makefile
src/wps/http_server.c
src/wps/httpread.c
src/wps/ndef.c
src/wps/wps.c
src/wps/wps.h
src/wps/wps_attr_build.c
src/wps/wps_attr_parse.c
src/wps/wps_attr_parse.h
src/wps/wps_attr_process.c
src/wps/wps_common.c
src/wps/wps_defs.h
src/wps/wps_dev_attr.c
src/wps/wps_dev_attr.h
src/wps/wps_enrollee.c
src/wps/wps_er.c
src/wps/wps_i.h
src/wps/wps_module_tests.c [new file with mode: 0644]
src/wps/wps_registrar.c
src/wps/wps_upnp.c
src/wps/wps_upnp_ap.c
src/wps/wps_upnp_i.h
src/wps/wps_upnp_ssdp.c
src/wps/wps_upnp_web.c
src/wps/wps_validate.c
tests/Makefile
tests/hwsim/auth_serv/as.conf [new file with mode: 0644]
tests/hwsim/auth_serv/as2.conf [new file with mode: 0644]
tests/hwsim/auth_serv/ca-incorrect.pem [new file with mode: 0644]
tests/hwsim/auth_serv/ca.der [new file with mode: 0644]
tests/hwsim/auth_serv/ca.pem [new file with mode: 0644]
tests/hwsim/auth_serv/dh.conf [new file with mode: 0644]
tests/hwsim/auth_serv/eap_user.conf [new file with mode: 0644]
tests/hwsim/auth_serv/ec-ca-openssl.cnf [new file with mode: 0644]
tests/hwsim/auth_serv/ec-ca.pem [new file with mode: 0644]
tests/hwsim/auth_serv/ec-generate.sh [new file with mode: 0644]
tests/hwsim/auth_serv/ec-server.key [new file with mode: 0644]
tests/hwsim/auth_serv/ec-server.pem [new file with mode: 0644]
tests/hwsim/auth_serv/ec-user.key [new file with mode: 0644]
tests/hwsim/auth_serv/ec-user.pem [new file with mode: 0644]
tests/hwsim/auth_serv/ec2-ca.pem [new file with mode: 0644]
tests/hwsim/auth_serv/ec2-generate.sh [new file with mode: 0644]
tests/hwsim/auth_serv/ec2-server.key [new file with mode: 0644]
tests/hwsim/auth_serv/ec2-server.pem [new file with mode: 0644]
tests/hwsim/auth_serv/ec2-user.key [new file with mode: 0644]
tests/hwsim/auth_serv/ec2-user.pem [new file with mode: 0644]
tests/hwsim/auth_serv/hlr_auc_gw.gsm [new file with mode: 0644]
tests/hwsim/auth_serv/hlr_auc_gw.milenage_db [new file with mode: 0644]
tests/hwsim/auth_serv/index-revoked.txt [new file with mode: 0644]
tests/hwsim/auth_serv/index-unknown.txt [new file with mode: 0644]
tests/hwsim/auth_serv/index.txt [new file with mode: 0644]
tests/hwsim/auth_serv/ocsp-req.der [new file with mode: 0644]
tests/hwsim/auth_serv/ocsp-responder.key [new file with mode: 0644]
tests/hwsim/auth_serv/ocsp-responder.pem [new file with mode: 0644]
tests/hwsim/auth_serv/ocsp-server-cache.der [new file with mode: 0644]
tests/hwsim/auth_serv/ocsp-server-cache.der-invalid [new file with mode: 0644]
tests/hwsim/auth_serv/radius_clients.conf [new file with mode: 0644]
tests/hwsim/auth_serv/radius_clients_ipv6.conf [new file with mode: 0644]
tests/hwsim/auth_serv/server-eku-client-server.key [new file with mode: 0644]
tests/hwsim/auth_serv/server-eku-client-server.pem [new file with mode: 0644]
tests/hwsim/auth_serv/server-eku-client.key [new file with mode: 0644]
tests/hwsim/auth_serv/server-eku-client.pem [new file with mode: 0644]
tests/hwsim/auth_serv/server-expired.key [new file with mode: 0644]
tests/hwsim/auth_serv/server-expired.pem [new file with mode: 0644]
tests/hwsim/auth_serv/server-no-dnsname.key [new file with mode: 0644]
tests/hwsim/auth_serv/server-no-dnsname.pem [new file with mode: 0644]
tests/hwsim/auth_serv/server.key [new file with mode: 0644]
tests/hwsim/auth_serv/server.pem [new file with mode: 0644]
tests/hwsim/auth_serv/server.pkcs12 [new file with mode: 0644]
tests/hwsim/auth_serv/user.key [new file with mode: 0644]
tests/hwsim/auth_serv/user.pem [new file with mode: 0644]
tests/hwsim/auth_serv/user.pkcs12 [new file with mode: 0644]
tests/hwsim/auth_serv/user.rsa-key [new file with mode: 0644]
tests/hwsim/hostapd.py
tests/hwsim/hwsim_utils.py
tests/hwsim/run-all.sh [changed mode: 0755->0644]
tests/hwsim/run-tests.py [changed mode: 0755->0644]
tests/hwsim/start.sh [changed mode: 0755->0644]
tests/hwsim/test_ap_ft.py
tests/hwsim/test_ap_hs20.py
tests/hwsim/test_ap_pmf.py
tests/hwsim/test_ap_roam.py
tests/hwsim/test_ap_tdls.py
tests/hwsim/test_ap_wps.py
tests/hwsim/test_nfc_wps.py
tests/hwsim/test_p2p_autogo.py
tests/hwsim/test_p2p_discovery.py
tests/hwsim/test_p2p_grpform.py
tests/hwsim/tnc/.gitignore [new file with mode: 0644]
tests/hwsim/tnc/Makefile [new file with mode: 0644]
tests/hwsim/tnc/hostap2_imc.c [new file with mode: 0644]
tests/hwsim/tnc/hostap2_imv.c [new file with mode: 0644]
tests/hwsim/tnc/hostap_imc.c [new file with mode: 0644]
tests/hwsim/tnc/hostap_imv.c [new file with mode: 0644]
tests/hwsim/tnc/tnc_config [new file with mode: 0644]
tests/hwsim/vm/.gitignore [new file with mode: 0644]
tests/hwsim/vm/README [new file with mode: 0644]
tests/hwsim/vm/build-codecov.sh [new file with mode: 0644]
tests/hwsim/vm/combine-codecov.sh [new file with mode: 0644]
tests/hwsim/vm/dbus.conf [new file with mode: 0644]
tests/hwsim/vm/inside.sh [new file with mode: 0644]
tests/hwsim/vm/kernel-config [new file with mode: 0644]
tests/hwsim/vm/parallel-vm.py [new file with mode: 0644]
tests/hwsim/vm/parallel-vm.sh [new file with mode: 0644]
tests/hwsim/vm/process-codecov.sh [new file with mode: 0644]
tests/hwsim/vm/uevent.sh [new file with mode: 0644]
tests/hwsim/vm/vm-run.sh [new file with mode: 0644]
tests/hwsim/wlantest.py
tests/hwsim/wpasupplicant.py
tests/test-aes.c
tests/test-asn1.c
tests/test-https.c
tests/test-md4.c
tests/test-md5.c
tests/test-milenage.c
tests/test-rc4.c
tests/test-sha1.c
tests/test-sha256.c
tests/test-x509.c
tests/test-x509v3.c
tests/test_x509v3_nist.sh [changed mode: 0755->0644]
tests/test_x509v3_nist2.sh [changed mode: 0755->0644]
wlantest/Makefile
wlantest/bip.c
wlantest/bss.c
wlantest/ctrl.c
wlantest/gcmp.c
wlantest/inject.c
wlantest/process.c
wlantest/rx_data.c
wlantest/rx_eapol.c
wlantest/rx_mgmt.c
wlantest/sta.c
wlantest/test_vectors.c
wlantest/wlantest.c
wlantest/wlantest.h
wlantest/wlantest_cli.c
wlantest/wlantest_ctrl.h
wlantest/writepcap.c
wpa_supplicant/Android.mk
wpa_supplicant/ChangeLog
wpa_supplicant/Makefile
wpa_supplicant/README
wpa_supplicant/README-HS20
wpa_supplicant/README-P2P
wpa_supplicant/README-WPS
wpa_supplicant/android.config
wpa_supplicant/ap.c
wpa_supplicant/ap.h
wpa_supplicant/bgscan.c
wpa_supplicant/bgscan.h
wpa_supplicant/bgscan_learn.c
wpa_supplicant/bgscan_simple.c
wpa_supplicant/bss.c [changed mode: 0644->0755]
wpa_supplicant/bss.h
wpa_supplicant/config.c
wpa_supplicant/config.h
wpa_supplicant/config_file.c
wpa_supplicant/config_ssid.h
wpa_supplicant/config_winreg.c
wpa_supplicant/ctrl_iface.c [changed mode: 0644->0755]
wpa_supplicant/ctrl_iface.h
wpa_supplicant/ctrl_iface_udp.c
wpa_supplicant/ctrl_iface_unix.c
wpa_supplicant/dbus/Makefile
wpa_supplicant/dbus/dbus_common.c
wpa_supplicant/dbus/dbus_dict_helpers.c
wpa_supplicant/dbus/dbus_dict_helpers.h
wpa_supplicant/dbus/dbus_new.c [changed mode: 0644->0755]
wpa_supplicant/dbus/dbus_new.h [changed mode: 0644->0755]
wpa_supplicant/dbus/dbus_new_handlers.c [changed mode: 0644->0755]
wpa_supplicant/dbus/dbus_new_handlers.h [changed mode: 0644->0755]
wpa_supplicant/dbus/dbus_new_handlers_p2p.c [changed mode: 0644->0755]
wpa_supplicant/dbus/dbus_new_handlers_p2p.h [changed mode: 0644->0755]
wpa_supplicant/dbus/dbus_new_handlers_wps.c [changed mode: 0644->0755]
wpa_supplicant/dbus/dbus_new_helpers.c [changed mode: 0644->0755]
wpa_supplicant/dbus/dbus_new_helpers.h [changed mode: 0644->0755]
wpa_supplicant/dbus/dbus_new_introspect.c [changed mode: 0644->0755]
wpa_supplicant/dbus/dbus_old.c [changed mode: 0644->0755]
wpa_supplicant/dbus/dbus_old.h [changed mode: 0644->0755]
wpa_supplicant/dbus/dbus_old_handlers.c [changed mode: 0644->0755]
wpa_supplicant/dbus/dbus_old_handlers.h [changed mode: 0644->0755]
wpa_supplicant/dbus/dbus_old_handlers_wps.c [changed mode: 0644->0755]
wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service.in [changed mode: 0644->0755]
wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service.in [changed mode: 0644->0755]
wpa_supplicant/defconfig
wpa_supplicant/doc/docbook/Makefile
wpa_supplicant/doc/docbook/eapol_test.sgml [new file with mode: 0644]
wpa_supplicant/doc/docbook/wpa_background.sgml
wpa_supplicant/doc/docbook/wpa_cli.sgml
wpa_supplicant/doc/docbook/wpa_gui.sgml
wpa_supplicant/doc/docbook/wpa_passphrase.sgml
wpa_supplicant/doc/docbook/wpa_priv.sgml
wpa_supplicant/doc/docbook/wpa_supplicant.sgml
wpa_supplicant/driver_i.h
wpa_supplicant/eap_proxy_dummy.mak [new file with mode: 0644]
wpa_supplicant/eap_register.c
wpa_supplicant/eapol_test.c
wpa_supplicant/events.c [changed mode: 0644->0755]
wpa_supplicant/examples/p2p-action.sh
wpa_supplicant/examples/p2p-nfc.py [new file with mode: 0755]
wpa_supplicant/examples/wps-ap-cli
wpa_supplicant/examples/wps-nfc.py
wpa_supplicant/gas_query.c [changed mode: 0644->0755]
wpa_supplicant/gas_query.h [changed mode: 0644->0755]
wpa_supplicant/hs20_supplicant.c [changed mode: 0644->0755]
wpa_supplicant/hs20_supplicant.h [changed mode: 0644->0755]
wpa_supplicant/ibss_rsn.c [changed mode: 0644->0755]
wpa_supplicant/ibss_rsn.h [changed mode: 0644->0755]
wpa_supplicant/interworking.c [changed mode: 0644->0755]
wpa_supplicant/interworking.h [changed mode: 0644->0755]
wpa_supplicant/main.c [changed mode: 0644->0755]
wpa_supplicant/main_none.c [changed mode: 0644->0755]
wpa_supplicant/main_winmain.c [changed mode: 0644->0755]
wpa_supplicant/main_winsvc.c [changed mode: 0644->0755]
wpa_supplicant/mesh.c [new file with mode: 0755]
wpa_supplicant/mesh.h [new file with mode: 0755]
wpa_supplicant/mesh_mpm.c [new file with mode: 0755]
wpa_supplicant/mesh_mpm.h [new file with mode: 0755]
wpa_supplicant/mesh_rsn.c [new file with mode: 0755]
wpa_supplicant/mesh_rsn.h [new file with mode: 0755]
wpa_supplicant/nfc_pw_token.c [changed mode: 0644->0755]
wpa_supplicant/nmake.mak [changed mode: 0644->0755]
wpa_supplicant/notify.c [changed mode: 0644->0755]
wpa_supplicant/notify.h [changed mode: 0644->0755]
wpa_supplicant/offchannel.c [changed mode: 0644->0755]
wpa_supplicant/offchannel.h [changed mode: 0644->0755]
wpa_supplicant/p2p_supplicant.c [changed mode: 0644->0755]
wpa_supplicant/p2p_supplicant.h [changed mode: 0644->0755]
wpa_supplicant/preauth_test.c [changed mode: 0644->0755]
wpa_supplicant/scan.c [changed mode: 0644->0755]
wpa_supplicant/scan.h [changed mode: 0644->0755]
wpa_supplicant/sme.c [changed mode: 0644->0755]
wpa_supplicant/sme.h [changed mode: 0644->0755]
wpa_supplicant/tests/test_wpa.c
wpa_supplicant/todo.txt [changed mode: 0644->0755]
wpa_supplicant/wifi_display.c [changed mode: 0644->0755]
wpa_supplicant/wifi_display.h [changed mode: 0644->0755]
wpa_supplicant/win_if_list.c [changed mode: 0644->0755]
wpa_supplicant/wmm_ac.c [new file with mode: 0755]
wpa_supplicant/wmm_ac.h [new file with mode: 0755]
wpa_supplicant/wnm_sta.c [changed mode: 0644->0755]
wpa_supplicant/wnm_sta.h [changed mode: 0644->0755]
wpa_supplicant/wpa_cli.c [changed mode: 0644->0755]
wpa_supplicant/wpa_gui-qt4/scanresults.cpp
wpa_supplicant/wpa_gui-qt4/wpa_gui.pro
wpa_supplicant/wpa_gui-qt4/wpagui.cpp [changed mode: 0644->0755]
wpa_supplicant/wpa_gui-qt4/wpagui.h
wpa_supplicant/wpa_passphrase.c [changed mode: 0644->0755]
wpa_supplicant/wpa_priv.c [changed mode: 0644->0755]
wpa_supplicant/wpa_supplicant.c [changed mode: 0644->0755]
wpa_supplicant/wpa_supplicant.conf
wpa_supplicant/wpa_supplicant_i.h
wpa_supplicant/wpa_supplicant_template.conf
wpa_supplicant/wpas_glue.c
wpa_supplicant/wpas_kay.c [new file with mode: 0644]
wpa_supplicant/wpas_kay.h [new file with mode: 0644]
wpa_supplicant/wpas_module_tests.c [new file with mode: 0644]
wpa_supplicant/wps_supplicant.c
wpa_supplicant/wps_supplicant.h
wpaspy/wpaspy.py

index c2cc68b..bd7a409 100644 (file)
@@ -1,3 +1,10 @@
-ifeq ($(WPA_SUPPLICANT_VERSION),VER_2_1_DEVEL)
-    include $(call all-subdir-makefiles)
+LOCAL_PATH:= $(call my-dir)
+
+ifneq ($(filter VER_0_8_X VER_2_1_DEVEL,$(WPA_SUPPLICANT_VERSION)),)
+# The order of the 2 Android.mks does matter!
+# TODO: Clean up the Android.mks, reset all the temporary variables at the
+# end of each Android.mk, so that one Android.mk doesn't depend on variables
+# set up in the other Android.mk.
+include $(LOCAL_PATH)/hostapd/Android.mk \
+        $(LOCAL_PATH)/wpa_supplicant/Android.mk
 endif
index 5ac7868..ca09bae 100644 (file)
@@ -8,10 +8,29 @@ clear record of contributions and terms under which they are licensed.
 To help with this, following procedure is used to allow acceptance and
 recording of the terms.
 
-These terms are similar to the process used in Linux kernel development.
-The items (a) through (d) are identical to the Developer's Certificate
-of Origin 1.1. To enable cleaner licensing option to be provided in the
-future, an additional item (e) is included.
+All contributions are expected to be licensed under the modified BSD
+license (see below). Acknowledgment of the terms is tracked through
+inclusion of Signed-off-by tag in the contributions at the end of the
+commit log message. This tag indicates that the contributor agrees with
+the Developer Certificate of Origin (DCO) version 1.1 terms (see below;
+also available from http://developercertificate.org/).
+
+
+The current requirements for contributions to hostap.git
+--------------------------------------------------------
+
+To indicate your acceptance of Developer's Certificate of Origin 1.1
+terms, please add the following line to the end of the commit message
+for each contribution you make to the project:
+
+Signed-off-by: Your Name <your@email.example.org>
+
+using your real name. Pseudonyms or anonymous contributions cannot
+unfortunately be accepted.
+
+
+History of license and contributions terms
+------------------------------------------
 
 Until February 11, 2012, in case of most files in hostap.git, "under the
 open source license indicated in the file" means that the contribution
@@ -27,15 +46,39 @@ licensed until GPL v2. In case of most files in hostap.git, "under the
 open source license indicated in the file" means that the contribution
 is licensed under the modified BSD license (see below).
 
+Until February 13, 2014, the project used an extended version of the DCO
+that included the identical items (a) through (d) from DCO 1.1 and an
+additional item (e):
+
+(e) The contribution can be licensed under the modified BSD license
+    as shown below even in case of files that are currently licensed
+    under other terms.
+
+This was used during the period when some of the files included the old
+license terms. Acceptance of this extended DCO version was indicated
+with a Signed-hostap tag in the commit message. This additional item (e)
+was used to collect explicit approval to license the contribution with
+only the modified BSD license (see below), i.e., without the GPL v2
+option. This was done to allow simpler licensing terms to be used in the
+future. It should be noted that the modified BSD license is compatible
+with GNU GPL and as such, this possible move to simpler licensing option
+does not prevent use of this software in GPL projects.
+
 
-The additional item (e) is used to collect explicit approval to license
-the contribution with only the modified BSD license (see below), i.e.,
-without the GPL v2 option. This was done to allow simpler licensing
-terms to be used in the future. It should be noted that the modified BSD
-license is compatible with GNU GPL and as such, this possible move to
-simpler licensing option does not prevent use of this software in
-GPL projects.
+===[ start quote from http://developercertificate.org/ ]=======================
 
+Developer Certificate of Origin
+Version 1.1
+
+Copyright (C) 2004, 2006 The Linux Foundation and its contributors.
+660 York Street, Suite 102,
+San Francisco, CA 94110 USA
+
+Everyone is permitted to copy and distribute verbatim copies of this
+license document, but changing it is not allowed.
+
+
+Developer's Certificate of Origin 1.1
 
 By making a contribution to this project, I certify that:
 
@@ -61,26 +104,15 @@ By making a contribution to this project, I certify that:
     maintained indefinitely and may be redistributed consistent with
     this project or the open source license(s) involved.
 
-Additionally, I certify that:
-
-(e) The contribution can be licensed under the modified BSD license
-    as shown below even in case of files that are currently licensed
-    under other terms.
-
-
-To indicate your acceptance of these terms, please add the following
-line to each contribution you make to the project:
-
-Signed-hostap: Your Name <your@email.example.org>
-
-using your real name. Pseudonyms or anonymous contributions cannot
-unfortunately be accepted.
+===[ end quote from http://developercertificate.org/ ]=========================
 
 
+The license terms used for hostap.git files
+-------------------------------------------
 
 Modified BSD license (no advertisement clause):
 
-Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/COPYING b/COPYING
index 8a98582..5962e2f 100644 (file)
--- a/COPYING
+++ b/COPYING
@@ -1,7 +1,7 @@
 wpa_supplicant and hostapd
 --------------------------
 
-Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 
diff --git a/COPYING.mbsd b/COPYING.mbsd
new file mode 100644 (file)
index 0000000..8a70427
--- /dev/null
@@ -0,0 +1,26 @@
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright
+       notice, this list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above
+       copyright notice, this list of conditions and the following
+       disclaimer in the documentation and/or other materials provided
+       with the distribution.
+
+    3. The name of the author may not be used to endorse or promote
+       products derived from this software without specific prior
+       written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
diff --git a/README b/README
index b0f2e6e..07d1d25 100644 (file)
--- a/README
+++ b/README
@@ -1,7 +1,7 @@
 wpa_supplicant and hostapd
 --------------------------
 
-Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 These programs are licensed under the BSD license (the one with
index 7590798..0ccc4cf 100644 (file)
 # used to fix build issues on such systems (krb5.h not found).
 #CFLAGS += -I/usr/include/kerberos
 
-# Example configuration for various cross-compilation platforms
-
-#### sveasoft (e.g., for Linksys WRT54G) ######################################
-#CC=mipsel-uclibc-gcc
-#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc
-#CFLAGS += -Os
-#CPPFLAGS += -I../src/include -I../../src/router/openssl/include
-#LIBS += -L/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib -lssl
-###############################################################################
-
-#### openwrt (e.g., for Linksys WRT54G) #######################################
-#CC=mipsel-uclibc-gcc
-#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc
-#CFLAGS += -Os
-#CPPFLAGS=-I../src/include -I../openssl-0.9.7d/include \
-#      -I../WRT54GS/release/src/include
-#LIBS = -lssl
-###############################################################################
-
-
-# Driver interface for Host AP driver
-#CONFIG_DRIVER_HOSTAP=y
-
-# Driver interface for Agere driver
-#CONFIG_DRIVER_HERMES=y
-# Change include directories to match with the local setup
-#CFLAGS += -I../../hcf -I../../include -I../../include/hcf
-#CFLAGS += -I../../include/wireless
-
-# Driver interface for madwifi driver
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_MADWIFI=y
-# Set include directory to the madwifi source tree
-#CFLAGS += -I../../madwifi
-
-# Driver interface for ndiswrapper
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_NDISWRAPPER=y
-
-# Driver interface for Atmel driver
-#CONFIG_DRIVER_ATMEL=y
-
-# Driver interface for old Broadcom driver
-# Please note that the newer Broadcom driver ("hybrid Linux driver") supports
-# Linux wireless extensions and does not need (or even work) with the old
-# driver wrapper. Use CONFIG_DRIVER_WEXT=y with that driver.
-#CONFIG_DRIVER_BROADCOM=y
-# Example path for wlioctl.h; change to match your configuration
-#CFLAGS += -I/opt/WRT54GS/release/src/include
-
-# Driver interface for Intel ipw2100/2200 driver
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_IPW=y
-
-# Driver interface for Ralink driver
-#CONFIG_DRIVER_RALINK=y
-
 # Driver interface for generic Linux wireless extensions
 # Note: WEXT is deprecated in the current Linux kernel version and no new
 # functionality is added to it. nl80211-based interface is the new
@@ -88,6 +31,19 @@ CONFIG_DRIVER_WEXT=y
 # Driver interface for Linux drivers using the nl80211 kernel interface
 CONFIG_DRIVER_NL80211=y
 
+# driver_nl80211.c requires libnl. If you are compiling it yourself
+# you may need to point hostapd to your version of libnl.
+#
+#CFLAGS += -I$<path to libnl include files>
+#LIBS += -L$<path to libnl library files>
+
+# Use libnl v2.0 (or 3.0) libraries.
+CONFIG_LIBNL20=y
+
+# Use libnl 3.2 libraries (if this is selected, CONFIG_LIBNL20 is ignored)
+#CONFIG_LIBNL32=y
+
+
 # Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
 #CONFIG_DRIVER_BSD=y
 #CFLAGS += -I/usr/local/include
@@ -111,9 +67,6 @@ CONFIG_DRIVER_NL80211=y
 # wpa_supplicant.
 # CONFIG_USE_NDISUIO=y
 
-# Driver interface for development testing
-#CONFIG_DRIVER_TEST=y
-
 # Driver interface for wired Ethernet drivers
 #CONFIG_DRIVER_WIRED=y
 
@@ -147,11 +100,10 @@ CONFIG_EAP_PEAP=y
 CONFIG_EAP_TTLS=y
 
 # EAP-FAST
-# Note: Default OpenSSL package does not include support for all the
-# functionality needed for EAP-FAST. If EAP-FAST is enabled with OpenSSL,
-# the OpenSSL library must be patched (openssl-0.9.8d-tls-extensions.patch)
-# to add the needed functions.
-#CONFIG_EAP_FAST=y
+# Note: If OpenSSL is used as the TLS library, OpenSSL 1.0 or newer is needed
+# for EAP-FAST support. Older OpenSSL releases would need to be patched, e.g.,
+# with openssl-0.9.8x-tls-extensions.patch, to add the needed functions.
+CONFIG_EAP_FAST=y
 
 # EAP-GTC
 CONFIG_EAP_GTC=y
@@ -160,11 +112,14 @@ CONFIG_EAP_GTC=y
 CONFIG_EAP_OTP=y
 
 # EAP-SIM (enable CONFIG_PCSC, if EAP-SIM is used)
-#CONFIG_EAP_SIM=y
+CONFIG_EAP_SIM=y
 
 # EAP-PSK (experimental; this is _not_ needed for WPA-PSK)
 #CONFIG_EAP_PSK=y
 
+# EAP-pwd (secure authentication using only a password)
+CONFIG_EAP_PWD=y
+
 # EAP-PAX
 #CONFIG_EAP_PAX=y
 
@@ -172,7 +127,7 @@ CONFIG_EAP_OTP=y
 CONFIG_EAP_LEAP=y
 
 # EAP-AKA (enable CONFIG_PCSC, if EAP-AKA is used)
-#CONFIG_EAP_AKA=y
+CONFIG_EAP_AKA=y
 
 # EAP-AKA' (enable CONFIG_PCSC, if EAP-AKA' is used).
 # This requires CONFIG_EAP_AKA to be enabled, too.
@@ -194,17 +149,20 @@ CONFIG_EAP_LEAP=y
 
 # Wi-Fi Protected Setup (WPS)
 CONFIG_WPS=y
-# Enable WSC 2.0 support
-CONFIG_WPS2=y
 # Enable WPS external registrar functionality
 #CONFIG_WPS_ER=y
 # Disable credentials for an open network by default when acting as a WPS
 # registrar.
 #CONFIG_WPS_REG_DISABLE_OPEN=y
+# Enable WPS support with NFC config method
+CONFIG_WPS_NFC=y
 
 # EAP-IKEv2
 #CONFIG_EAP_IKEV2=y
 
+# EAP-EKE
+CONFIG_EAP_EKE=y
+
 # PKCS#12 (PFX) support (used to read private key and certificate file from
 # a file that usually has extension .p12 or .pfx)
 CONFIG_PKCS12=y
@@ -217,13 +175,22 @@ CONFIG_SMARTCARD=y
 # Enable this if EAP-SIM or EAP-AKA is included
 #CONFIG_PCSC=y
 
+# Support HT overrides (disable HT/HT40, mask MCS rates, etc.)
+#CONFIG_HT_OVERRIDES=y
+
+# Support VHT overrides (disable VHT, mask MCS rates, etc.)
+#CONFIG_VHT_OVERRIDES=y
+
 # Development testing
 #CONFIG_EAPOL_TEST=y
 
 # Select control interface backend for external programs, e.g, wpa_cli:
 # unix = UNIX domain sockets (default for Linux/*BSD)
 # udp = UDP sockets using localhost (127.0.0.1)
+# udp6 = UDP IPv6 sockets using localhost (::1)
 # named_pipe = Windows Named Pipe (default for Windows)
+# udp-remote = UDP sockets with remote access (only for tests systems/purpose)
+# udp6-remote = UDP IPv6 sockets with remote access (only for tests purpose)
 # y = use default (backwards compatibility)
 # If this option is commented out, control interface is not included in the
 # build.
@@ -249,11 +216,6 @@ CONFIG_CTRL_IFACE=unix
 # 35-50 kB in code size.
 #CONFIG_NO_WPA=y
 
-# Remove WPA2 support. This allows WPA to be used, but removes WPA2 code to
-# save about 1 kB in code size when building only WPA-Personal (no EAP support)
-# or 6 kB if building for WPA-Enterprise.
-#CONFIG_NO_WPA2=y
-
 # Remove IEEE 802.11i/WPA-Personal ASCII passphrase support
 # This option can be used to reduce code size by removing support for
 # converting ASCII passphrases into PSK. If this functionality is removed, the
@@ -288,7 +250,7 @@ CONFIG_BACKEND=file
 # main_none = Very basic example (development use only)
 CONFIG_MAIN=main
 
-# Select wrapper for operatins system and C library specific functions
+# Select wrapper for operating system and C library specific functions
 # unix = UNIX/POSIX like systems (default)
 # win32 = Windows systems
 # none = Empty template
@@ -297,9 +259,14 @@ CONFIG_OS=unix
 # Select event loop implementation
 # eloop = select() loop (default)
 # eloop_win = Windows events and WaitForMultipleObject() loop
-# eloop_none = Empty template
 CONFIG_ELOOP=eloop
 
+# Should we use poll instead of select? Select is used by default.
+#CONFIG_ELOOP_POLL=y
+
+# Should we use epoll instead of select? Select is used by default.
+#CONFIG_ELOOP_EPOLL=y
+
 # Select layer 2 packet implementation
 # linux = Linux packet socket (default)
 # pcap = libpcap/libdnet/WinPcap
@@ -312,9 +279,7 @@ CONFIG_L2_PACKET=linux
 # PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
 CONFIG_PEERKEY=y
 
-# IEEE 802.11w (management frame protection)
-# This version is an experimental implementation based on IEEE 802.11w/D1.0
-# draft and is subject to change since the standard has not yet been finalized.
+# IEEE 802.11w (management frame protection), also known as PMF
 # Driver support is also needed for IEEE 802.11w.
 #CONFIG_IEEE80211W=y
 
@@ -323,7 +288,7 @@ CONFIG_PEERKEY=y
 # gnutls = GnuTLS
 # internal = Internal TLSv1 implementation (experimental)
 # none = Empty template
-CONFIG_TLS=openssl
+#CONFIG_TLS=openssl
 
 # TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.1)
 # can be enabled to get a stronger construction of messages when block ciphers
@@ -404,6 +369,16 @@ CONFIG_DEBUG_FILE=y
 # Set syslog facility for debug messages
 #CONFIG_DEBUG_SYSLOG_FACILITY=LOG_DAEMON
 
+# Add support for sending all debug messages (regardless of debug verbosity)
+# to the Linux kernel tracing facility. This helps debug the entire stack by
+# making it easy to record everything happening from the driver up into the
+# same file, e.g., using trace-cmd.
+#CONFIG_DEBUG_LINUX_TRACING=y
+
+# Add support for writing debug log to Android logcat instead of standard
+# output
+#CONFIG_ANDROID_LOG=y
+
 # Enable privilege separation (see README 'Privilege separation' for details)
 #CONFIG_PRIVSEP=y
 
@@ -415,7 +390,7 @@ CONFIG_DEBUG_FILE=y
 # This tracks use of memory allocations and other registrations and reports
 # incorrect use with a backtrace of call (or allocation) location.
 #CONFIG_WPA_TRACE=y
-# For BSD, comment out these.
+# For BSD, uncomment these.
 #LIBS += -lexecinfo
 #LIBS_p += -lexecinfo
 #LIBS_c += -lexecinfo
@@ -424,7 +399,7 @@ CONFIG_DEBUG_FILE=y
 # This enables use of libbfd to get more detailed symbols for the backtraces
 # generated by CONFIG_WPA_TRACE=y.
 #CONFIG_WPA_TRACE_BFD=y
-# For BSD, comment out these.
+# For BSD, uncomment these.
 #LIBS += -lbfd -liberty -lz
 #LIBS_p += -lbfd -liberty -lz
 #LIBS_c += -lbfd -liberty -lz
@@ -463,13 +438,60 @@ CONFIG_DEBUG_FILE=y
 # IEEE 802.11n (High Throughput) support (mainly for AP mode)
 CONFIG_IEEE80211N=y
 
+# IEEE 802.11ac (Very High Throughput) support (mainly for AP mode)
+# (depends on CONFIG_IEEE80211N)
+CONFIG_IEEE80211AC=y
+
+# Wireless Network Management (IEEE Std 802.11v-2011)
+# Note: This is experimental and not complete implementation.
+#CONFIG_WNM=y
+
 # Interworking (IEEE 802.11u)
 # This can be used to enable functionality to improve interworking with
 # external networks (GAS/ANQP to learn more about the networks and network
 # selection based on available credentials).
 #CONFIG_INTERWORKING=y
 
-#Additional flags
-CONFIG_LIBNL20=y
-#CONFIG_P2P=y
+# Hotspot 2.0
+CONFIG_HS20=y
+
+# Disable roaming in wpa_supplicant
+#CONFIG_NO_ROAMING=y
+
+# AP mode operations with wpa_supplicant
+# This can be used for controlling AP mode operations with wpa_supplicant. It
+# should be noted that this is mainly aimed at simple cases like
+# WPA2-Personal while more complex configurations like WPA2-Enterprise with an
+# external RADIUS server can be supported with hostapd.
 CONFIG_AP=y
+
+# P2P (Wi-Fi Direct)
+# This can be used to enable P2P support in wpa_supplicant. See README-P2P for
+# more information on P2P operations.
+CONFIG_P2P=y
+
+# Enable TDLS support
+CONFIG_TDLS=y
+
+# Wi-Fi Direct
+# This can be used to enable Wi-Fi Direct extensions for P2P using an external
+# program to control the additional information exchanges in the messages.
+CONFIG_WIFI_DISPLAY=y
+
+# Autoscan
+# This can be used to enable automatic scan support in wpa_supplicant.
+# See wpa_supplicant.conf for more information on autoscan usage.
+#
+# Enabling directly a module will enable autoscan support.
+# For exponential module:
+#CONFIG_AUTOSCAN_EXPONENTIAL=y
+# For periodic module:
+#CONFIG_AUTOSCAN_PERIODIC=y
+
+# Password (and passphrase, etc.) backend for external storage
+# These optional mechanisms can be used to add support for storing passwords
+# and other secrets in external (to wpa_supplicant) location. This allows, for
+# example, operating system specific key storage to be used
+#
+# External password backend for testing purposes (developer use)
+#CONFIG_EXT_PASSWORD_TEST=y
index cc597d3..d09787c 100644 (file)
@@ -1,4 +1,4 @@
-# Example wpa_supplicant build time configuration
+# Example hostapd build time configuration
 #
 # This file lists the configuration options that are used when building the
 # hostapd binary. All lines starting with # are ignored. Configuration option
@@ -6,88 +6,30 @@
 # just setting VARIABLE=n is not disabling that variable.
 #
 # This file is included in Makefile, so variables like CFLAGS and LIBS can also
-# be modified from here. In most cases, these lines should use += in order not
+# be modified from here. In most cass, these lines should use += in order not
 # to override previous values of the variables.
 
+# Driver interface for Host AP driver
+#CONFIG_DRIVER_HOSTAP=y
 
-# Uncomment following two lines and fix the paths if you have installed OpenSSL
-# or GnuTLS in non-default location
-#CFLAGS += -I/usr/local/openssl/include
-#LIBS += -L/usr/local/openssl/lib
-
-# Some Red Hat versions seem to include kerberos header files from OpenSSL, but
-# the kerberos files are not in the default include path. Following line can be
-# used to fix build issues on such systems (krb5.h not found).
-#CFLAGS += -I/usr/include/kerberos
-
-# Example configuration for various cross-compilation platforms
+# Driver interface for wired authenticator
+#CONFIG_DRIVER_WIRED=y
 
-#### sveasoft (e.g., for Linksys WRT54G) ######################################
-#CC=mipsel-uclibc-gcc
-#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc
-#CFLAGS += -Os
-#CPPFLAGS += -I../src/include -I../../src/router/openssl/include
-#LIBS += -L/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib -lssl
-###############################################################################
+# Driver interface for drivers using the nl80211 kernel interface
+CONFIG_DRIVER_NL80211=y
 
-#### openwrt (e.g., for Linksys WRT54G) #######################################
-#CC=mipsel-uclibc-gcc
-#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc
-#CFLAGS += -Os
-#CPPFLAGS=-I../src/include -I../openssl-0.9.7d/include \
-#      -I../WRT54GS/release/src/include
-#LIBS = -lssl
-###############################################################################
+# driver_nl80211.c requires libnl. If you are compiling it yourself
+# you may need to point hostapd to your version of libnl.
+#
+#CFLAGS += -I$<path to libnl include files>
+#LIBS += -L$<path to libnl library files>
 
+# Use libnl v2.0 (or 3.0) libraries.
+CONFIG_LIBNL20=y
 
-# Driver interface for Host AP driver
-#CONFIG_DRIVER_HOSTAP=y
+# Use libnl 3.2 libraries (if this is selected, CONFIG_LIBNL20 is ignored)
+#CONFIG_LIBNL32=y
 
-# Driver interface for Agere driver
-#CONFIG_DRIVER_HERMES=y
-# Change include directories to match with the local setup
-#CFLAGS += -I../../hcf -I../../include -I../../include/hcf
-#CFLAGS += -I../../include/wireless
-
-# Driver interface for madwifi driver
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_MADWIFI=y
-# Set include directory to the madwifi source tree
-#CFLAGS += -I../../madwifi
-
-# Driver interface for ndiswrapper
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_NDISWRAPPER=y
-
-# Driver interface for Atmel driver
-#CONFIG_DRIVER_ATMEL=y
-
-# Driver interface for old Broadcom driver
-# Please note that the newer Broadcom driver ("hybrid Linux driver") supports
-# Linux wireless extensions and does not need (or even work) with the old
-# driver wrapper. Use CONFIG_DRIVER_WEXT=y with that driver.
-#CONFIG_DRIVER_BROADCOM=y
-# Example path for wlioctl.h; change to match your configuration
-#CFLAGS += -I/opt/WRT54GS/release/src/include
-
-# Driver interface for Intel ipw2100/2200 driver
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_IPW=y
-
-# Driver interface for Ralink driver
-#CONFIG_DRIVER_RALINK=y
-
-# Driver interface for generic Linux wireless extensions
-# Note: WEXT is deprecated in the current Linux kernel version and no new
-# functionality is added to it. nl80211-based interface is the new
-# replacement for WEXT and its use allows wpa_supplicant to properly control
-# the driver to improve existing functionality like roaming and to support new
-# functionality.
-#CONFIG_DRIVER_WEXT=y
-
-# Driver interface for Linux drivers using the nl80211 kernel interface
-CONFIG_DRIVER_NL80211=y
-CONFIG_LIBNL20=y
 
 # Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
 #CONFIG_DRIVER_BSD=y
@@ -96,228 +38,207 @@ CONFIG_LIBNL20=y
 #LIBS_p += -L/usr/local/lib
 #LIBS_c += -L/usr/local/lib
 
-# Driver interface for Windows NDIS
-#CONFIG_DRIVER_NDIS=y
-#CFLAGS += -I/usr/include/w32api/ddk
-#LIBS += -L/usr/local/lib
-# For native build using mingw
-#CONFIG_NATIVE_WINDOWS=y
-# Additional directories for cross-compilation on Linux host for mingw target
-#CFLAGS += -I/opt/mingw/mingw32/include/ddk
-#LIBS += -L/opt/mingw/mingw32/lib
-#CC=mingw32-gcc
-# By default, driver_ndis uses WinPcap for low-level operations. This can be
-# replaced with the following option which replaces WinPcap calls with NDISUIO.
-# However, this requires that WZC is disabled (net stop wzcsvc) before starting
-# wpa_supplicant.
-# CONFIG_USE_NDISUIO=y
-
-# Driver interface for development testing
-#CONFIG_DRIVER_TEST=y
-
-# Driver interface for wired Ethernet drivers
-#CONFIG_DRIVER_WIRED=y
+# Driver interface for no driver (e.g., RADIUS server only)
+#CONFIG_DRIVER_NONE=y
 
-# Driver interface for the Broadcom RoboSwitch family
-#CONFIG_DRIVER_ROBOSWITCH=y
+# IEEE 802.11F/IAPP
+#CONFIG_IAPP=y
 
-# Driver interface for no driver (e.g., WPS ER only)
-#CONFIG_DRIVER_NONE=y
+# WPA2/IEEE 802.11i RSN pre-authentication
+#CONFIG_RSN_PREAUTH=y
 
-# Solaris libraries
-#LIBS += -lsocket -ldlpi -lnsl
-#LIBS_c += -lsocket
+# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
+CONFIG_PEERKEY=y
 
-# Enable IEEE 802.1X Supplicant (automatically included if any EAP method is
-# included)
-#CONFIG_IEEE8021X_EAPOL=y
+# IEEE 802.11w (management frame protection)
+#CONFIG_IEEE80211W=y
 
-# EAP-MD5
-#CONFIG_EAP_MD5=y
+# Integrated EAP server
+CONFIG_EAP=y
 
-# EAP-MSCHAPv2
-#CONFIG_EAP_MSCHAPV2=y
+# EAP Re-authentication Protocol (ERP) in integrated EAP server
+CONFIG_ERP=y
 
-# EAP-TLS
-#CONFIG_EAP_TLS=y
+# EAP-MD5 for the integrated EAP server
+#CONFIG_EAP_MD5=y
 
-# EAL-PEAP
-#CONFIG_EAP_PEAP=y
+# EAP-TLS for the integrated EAP server
+#CONFIG_EAP_TLS=y
 
-# EAP-TTLS
-#CONFIG_EAP_TTLS=y
+# EAP-MSCHAPv2 for the integrated EAP server
+#CONFIG_EAP_MSCHAPV2=y
 
-# EAP-FAST
-# Note: Default OpenSSL package does not include support for all the
-# functionality needed for EAP-FAST. If EAP-FAST is enabled with OpenSSL,
-# the OpenSSL library must be patched (openssl-0.9.8d-tls-extensions.patch)
-# to add the needed functions.
-#CONFIG_EAP_FAST=y
+# EAP-PEAP for the integrated EAP server
+#CONFIG_EAP_PEAP=y
 
-# EAP-GTC
+# EAP-GTC for the integrated EAP server
 #CONFIG_EAP_GTC=y
 
-# EAP-OTP
-#CONFIG_EAP_OTP=y
+# EAP-TTLS for the integrated EAP server
+#CONFIG_EAP_TTLS=y
 
-# EAP-SIM (enable CONFIG_PCSC, if EAP-SIM is used)
+# EAP-SIM for the integrated EAP server
 #CONFIG_EAP_SIM=y
 
-# EAP-PSK (experimental; this is _not_ needed for WPA-PSK)
-#CONFIG_EAP_PSK=y
-
-# EAP-PAX
-#CONFIG_EAP_PAX=y
-
-# LEAP
-#CONFIG_EAP_LEAP=y
-
-# EAP-AKA (enable CONFIG_PCSC, if EAP-AKA is used)
+# EAP-AKA for the integrated EAP server
 #CONFIG_EAP_AKA=y
 
-# EAP-AKA' (enable CONFIG_PCSC, if EAP-AKA' is used).
+# EAP-AKA' for the integrated EAP server
 # This requires CONFIG_EAP_AKA to be enabled, too.
 #CONFIG_EAP_AKA_PRIME=y
 
-# Enable USIM simulator (Milenage) for EAP-AKA
-#CONFIG_USIM_SIMULATOR=y
+# EAP-PAX for the integrated EAP server
+#CONFIG_EAP_PAX=y
+
+# EAP-PSK for the integrated EAP server (this is _not_ needed for WPA-PSK)
+#CONFIG_EAP_PSK=y
+
+# EAP-pwd for the integrated EAP server (secure authentication with a password)
+#CONFIG_EAP_PWD=y
 
-# EAP-SAKE
+# EAP-SAKE for the integrated EAP server
 #CONFIG_EAP_SAKE=y
 
-# EAP-GPSK
+# EAP-GPSK for the integrated EAP server
 #CONFIG_EAP_GPSK=y
 # Include support for optional SHA256 cipher suite in EAP-GPSK
 #CONFIG_EAP_GPSK_SHA256=y
 
-# EAP-TNC and related Trusted Network Connect support (experimental)
-#CONFIG_EAP_TNC=y
+# EAP-FAST for the integrated EAP server
+# Note: If OpenSSL is used as the TLS library, OpenSSL 1.0 or newer is needed
+# for EAP-FAST support. Older OpenSSL releases would need to be patched, e.g.,
+# with openssl-0.9.8x-tls-extensions.patch, to add the needed functions.
+#CONFIG_EAP_FAST=y
 
 # Wi-Fi Protected Setup (WPS)
 CONFIG_WPS=y
-# Enable WSC 2.0 support
-CONFIG_WPS2=y
-# Enable WPS external registrar functionality
-#CONFIG_WPS_ER=y
-# Disable credentials for an open network by default when acting as a WPS
-# registrar.
-#CONFIG_WPS_REG_DISABLE_OPEN=y
+# Enable UPnP support for external WPS Registrars
+#CONFIG_WPS_UPNP=y
+# Enable WPS support with NFC config method
+#CONFIG_WPS_NFC=y
 
 # EAP-IKEv2
 #CONFIG_EAP_IKEV2=y
 
+# Trusted Network Connect (EAP-TNC)
+#CONFIG_EAP_TNC=y
+
+# EAP-EKE for the integrated EAP server
+#CONFIG_EAP_EKE=y
+
 # PKCS#12 (PFX) support (used to read private key and certificate file from
 # a file that usually has extension .p12 or .pfx)
 #CONFIG_PKCS12=y
 
-# Smartcard support (i.e., private key on a smartcard), e.g., with openssl
-# engine.
-#CONFIG_SMARTCARD=y
-
-# PC/SC interface for smartcards (USIM, GSM SIM)
-# Enable this if EAP-SIM or EAP-AKA is included
-#CONFIG_PCSC=y
+# RADIUS authentication server. This provides access to the integrated EAP
+# server from external hosts using RADIUS.
+#CONFIG_RADIUS_SERVER=y
 
-# Development testing
-#CONFIG_EAPOL_TEST=y
+# Build IPv6 support for RADIUS operations
+#CONFIG_IPV6=y
 
-# Select control interface backend for external programs, e.g, wpa_cli:
-# unix = UNIX domain sockets (default for Linux/*BSD)
-# udp = UDP sockets using localhost (127.0.0.1)
-# named_pipe = Windows Named Pipe (default for Windows)
-# y = use default (backwards compatibility)
-# If this option is commented out, control interface is not included in the
-# build.
-CONFIG_CTRL_IFACE=unix
+# IEEE Std 802.11r-2008 (Fast BSS Transition)
+#CONFIG_IEEE80211R=y
 
-# Include support for GNU Readline and History Libraries in wpa_cli.
-# When building a wpa_cli binary for distribution, please note that these
-# libraries are licensed under GPL and as such, BSD license may not apply for
-# the resulting binary.
-#CONFIG_READLINE=y
+# Use the hostapd's IEEE 802.11 authentication (ACL), but without
+# the IEEE 802.11 Management capability (e.g., madwifi or FreeBSD/net80211)
+#CONFIG_DRIVER_RADIUS_ACL=y
 
-# Include internal line edit mode in wpa_cli. This can be used as a replacement
-# for GNU Readline to provide limited command line editing and history support.
-#CONFIG_WPA_CLI_EDIT=y
+# IEEE 802.11n (High Throughput) support
+CONFIG_IEEE80211N=y
 
-# Remove debugging code that is printing out debug message to stdout.
-# This can be used to reduce the size of the wpa_supplicant considerably
-# if debugging code is not needed. The size reduction can be around 35%
-# (e.g., 90 kB).
-#CONFIG_NO_STDOUT_DEBUG=y
+# Wireless Network Management (IEEE Std 802.11v-2011)
+# Note: This is experimental and not complete implementation.
+#CONFIG_WNM=y
 
-# Remove WPA support, e.g., for wired-only IEEE 802.1X supplicant, to save
-# 35-50 kB in code size.
-#CONFIG_NO_WPA=y
+# IEEE 802.11ac (Very High Throughput) support
+#CONFIG_IEEE80211AC=y
 
-# Remove WPA2 support. This allows WPA to be used, but removes WPA2 code to
-# save about 1 kB in code size when building only WPA-Personal (no EAP support)
-# or 6 kB if building for WPA-Enterprise.
-#CONFIG_NO_WPA2=y
+# Remove debugging code that is printing out debug messages to stdout.
+# This can be used to reduce the size of the hostapd considerably if debugging
+# code is not needed.
+#CONFIG_NO_STDOUT_DEBUG=y
 
-# Remove IEEE 802.11i/WPA-Personal ASCII passphrase support
-# This option can be used to reduce code size by removing support for
-# converting ASCII passphrases into PSK. If this functionality is removed, the
-# PSK can only be configured as the 64-octet hexstring (e.g., from
-# wpa_passphrase). This saves about 0.5 kB in code size.
-#CONFIG_NO_WPA_PASSPHRASE=y
+# Add support for writing debug log to a file: -f /tmp/hostapd.log
+# Disabled by default.
+CONFIG_DEBUG_FILE=y
 
-# Disable scan result processing (ap_mode=1) to save code size by about 1 kB.
-# This can be used if ap_scan=1 mode is never enabled.
-#CONFIG_NO_SCAN_PROCESSING=y
+# Add support for sending all debug messages (regardless of debug verbosity)
+# to the Linux kernel tracing facility. This helps debug the entire stack by
+# making it easy to record everything happening from the driver up into the
+# same file, e.g., using trace-cmd.
+#CONFIG_DEBUG_LINUX_TRACING=y
 
-# Select configuration backend:
-# file = text file (e.g., wpa_supplicant.conf; note: the configuration file
-#      path is given on command line, not here; this option is just used to
-#      select the backend that allows configuration files to be used)
-# winreg = Windows registry (see win_example.reg for an example)
-CONFIG_BACKEND=file
+# Remove support for RADIUS accounting
+#CONFIG_NO_ACCOUNTING=y
 
-# Remove configuration write functionality (i.e., to allow the configuration
-# file to be updated based on runtime configuration changes). The runtime
-# configuration can still be changed, the changes are just not going to be
-# persistent over restarts. This option can be used to reduce code size by
-# about 3.5 kB.
-#CONFIG_NO_CONFIG_WRITE=y
+# Remove support for RADIUS
+CONFIG_NO_RADIUS=y
 
-# Remove support for configuration blobs to reduce code size by about 1.5 kB.
-#CONFIG_NO_CONFIG_BLOBS=y
+# Remove support for VLANs
+#CONFIG_NO_VLAN=y
 
-# Select program entry point implementation:
-# main = UNIX/POSIX like main() function (default)
-# main_winsvc = Windows service (read parameters from registry)
-# main_none = Very basic example (development use only)
-CONFIG_MAIN=main
+# Enable support for fully dynamic VLANs. This enables hostapd to
+# automatically create bridge and VLAN interfaces if necessary.
+#CONFIG_FULL_DYNAMIC_VLAN=y
 
-# Select wrapper for operatins system and C library specific functions
-# unix = UNIX/POSIX like systems (default)
-# win32 = Windows systems
-# none = Empty template
-CONFIG_OS=unix
+# Use netlink-based kernel API for VLAN operations instead of ioctl()
+# Note: This requires libnl 3.1 or newer.
+#CONFIG_VLAN_NETLINK=y
 
-# Select event loop implementation
-# eloop = select() loop (default)
-# eloop_win = Windows events and WaitForMultipleObject() loop
-# eloop_none = Empty template
-CONFIG_ELOOP=eloop
+# Remove support for dumping internal state through control interface commands
+# This can be used to reduce binary size at the cost of disabling a debugging
+# option.
+#CONFIG_NO_DUMP_STATE=y
 
-# Select layer 2 packet implementation
-# linux = Linux packet socket (default)
-# pcap = libpcap/libdnet/WinPcap
-# freebsd = FreeBSD libpcap
-# winpcap = WinPcap with receive thread
-# ndis = Windows NDISUIO (note: requires CONFIG_USE_NDISUIO=y)
-# none = Empty template
-CONFIG_L2_PACKET=linux
+# Enable tracing code for developer debugging
+# This tracks use of memory allocations and other registrations and reports
+# incorrect use with a backtrace of call (or allocation) location.
+#CONFIG_WPA_TRACE=y
+# For BSD, comment out these.
+#LIBS += -lexecinfo
+#LIBS_p += -lexecinfo
+#LIBS_c += -lexecinfo
 
-# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS)
-CONFIG_PEERKEY=y
+# Use libbfd to get more details for developer debugging
+# This enables use of libbfd to get more detailed symbols for the backtraces
+# generated by CONFIG_WPA_TRACE=y.
+#CONFIG_WPA_TRACE_BFD=y
+# For BSD, comment out these.
+#LIBS += -lbfd -liberty -lz
+#LIBS_p += -lbfd -liberty -lz
+#LIBS_c += -lbfd -liberty -lz
 
-# IEEE 802.11w (management frame protection)
-# This version is an experimental implementation based on IEEE 802.11w/D1.0
-# draft and is subject to change since the standard has not yet been finalized.
-# Driver support is also needed for IEEE 802.11w.
-#CONFIG_IEEE80211W=y
+# hostapd depends on strong random number generation being available from the
+# operating system. os_get_random() function is used to fetch random data when
+# needed, e.g., for key generation. On Linux and BSD systems, this works by
+# reading /dev/urandom. It should be noted that the OS entropy pool needs to be
+# properly initialized before hostapd is started. This is important especially
+# on embedded devices that do not have a hardware random number generator and
+# may by default start up with minimal entropy available for random number
+# generation.
+#
+# As a safety net, hostapd is by default trying to internally collect
+# additional entropy for generating random data to mix in with the data
+# fetched from the OS. This by itself is not considered to be very strong, but
+# it may help in cases where the system pool is not initialized properly.
+# However, it is very strongly recommended that the system pool is initialized
+# with enough entropy either by using hardware assisted random number
+# generator or by storing state over device reboots.
+#
+# hostapd can be configured to maintain its own entropy store over restarts to
+# enhance random number generation. This is not perfect, but it is much more
+# secure than using the same sequence of random numbers after every reboot.
+# This can be enabled with -e<entropy file> command line option. The specified
+# file needs to be readable and writable by hostapd.
+#
+# If the os_get_random() is known to provide strong random data (e.g., on
+# Linux/BSD, the board in question is known to have reliable source of random
+# data from /dev/urandom), the internal hostapd random pool can be disabled.
+# This will save some in binary size and CPU use. However, this should only be
+# considered for builds that are known to be used on devices that meet the
+# requirements described above.
+#CONFIG_NO_RANDOM_POOL=y
 
 # Select TLS implementation
 # openssl = OpenSSL (default)
@@ -328,16 +249,11 @@ CONFIG_TLS=openssl
 
 # TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.1)
 # can be enabled to get a stronger construction of messages when block ciphers
-# are used. It should be noted that some existing TLS v1.0 -based
-# implementation may not be compatible with TLS v1.1 message (ClientHello is
-# sent prior to negotiating which version will be used)
+# are used.
 #CONFIG_TLSV11=y
 
 # TLS-based EAP methods require at least TLS v1.0. Newer version of TLS (v1.2)
-# can be enabled to enable use of stronger crypto algorithms. It should be
-# noted that some existing TLS v1.0 -based implementation may not be compatible
-# with TLS v1.2 message (ClientHello is sent prior to negotiating which version
-# will be used)
+# can be enabled to enable use of stronger crypto algorithms.
 #CONFIG_TLSV12=y
 
 # If CONFIG_TLS=internal is used, additional library and include paths are
@@ -356,120 +272,94 @@ CONFIG_TLS=openssl
 # speed up DH and RSA calculation considerably
 #CONFIG_INTERNAL_LIBTOMMATH_FAST=y
 
-# Include NDIS event processing through WMI into wpa_supplicant/wpasvc.
-# This is only for Windows builds and requires WMI-related header files and
-# WbemUuid.Lib from Platform SDK even when building with MinGW.
-#CONFIG_NDIS_EVENTS_INTEGRATED=y
-#PLATFORMSDKLIB="/opt/Program Files/Microsoft Platform SDK/Lib"
-
-# Add support for old DBus control interface
-# (fi.epitest.hostap.WPASupplicant)
-#CONFIG_CTRL_IFACE_DBUS=y
-
-# Add support for new DBus control interface
-# (fi.w1.hostap.wpa_supplicant1)
-#CONFIG_CTRL_IFACE_DBUS_NEW=y
-
-# Add introspection support for new DBus control interface
-#CONFIG_CTRL_IFACE_DBUS_INTRO=y
-
-# Add support for loading EAP methods dynamically as shared libraries.
-# When this option is enabled, each EAP method can be either included
-# statically (CONFIG_EAP_<method>=y) or dynamically (CONFIG_EAP_<method>=dyn).
-# Dynamic EAP methods are build as shared objects (eap_*.so) and they need to
-# be loaded in the beginning of the wpa_supplicant configuration file
-# (see load_dynamic_eap parameter in the example file) before being used in
-# the network blocks.
-#
-# Note that some shared parts of EAP methods are included in the main program
-# and in order to be able to use dynamic EAP methods using these parts, the
-# main program must have been build with the EAP method enabled (=y or =dyn).
-# This means that EAP-TLS/PEAP/TTLS/FAST cannot be added as dynamic libraries
-# unless at least one of them was included in the main build to force inclusion
-# of the shared code. Similarly, at least one of EAP-SIM/AKA must be included
-# in the main build to be able to load these methods dynamically.
+# Interworking (IEEE 802.11u)
+# This can be used to enable functionality to improve interworking with
+# external networks.
+#CONFIG_INTERWORKING=y
+
+# Hotspot 2.0
+#CONFIG_HS20=y
+
+# Enable SQLite database support in hlr_auc_gw, EAP-SIM DB, and eap_user_file
+#CONFIG_SQLITE=y
+
+# Testing options
+# This can be used to enable some testing options (see also the example
+# configuration file) that are really useful only for testing clients that
+# connect to this hostapd. These options allow, for example, to drop a
+# certain percentage of probe requests or auth/(re)assoc frames.
 #
-# Please also note that using dynamic libraries will increase the total binary
-# size. Thus, it may not be the best option for targets that have limited
-# amount of memory/flash.
-#CONFIG_DYNAMIC_EAP_METHODS=y
+#CONFIG_TESTING_OPTIONS=y
 
-# IEEE Std 802.11r-2008 (Fast BSS Transition)
-#CONFIG_IEEE80211R=y
+# Automatic Channel Selection
+# This will allow hostapd to pick the channel automatically when channel is set
+# to "acs_survey" or "0". Eventually, other ACS algorithms can be added in
+# similar way.
+#
+# Automatic selection is currently only done through initialization, later on
+# we hope to do background checks to keep us moving to more ideal channels as
+# time goes by. ACS is currently only supported through the nl80211 driver and
+# your driver must have survey dump capability that is filled by the driver
+# during scanning.
+#
+# You can customize the ACS survey algorithm with the hostapd.conf variable
+# acs_num_scans.
+#
+# Supported ACS drivers:
+# * ath9k
+# * ath5k
+# * ath10k
+#
+# For more details refer to:
+# http://wireless.kernel.org/en/users/Documentation/acs
+#
+#CONFIG_ACS=y
 
-# Add support for writing debug log to a file (/tmp/wpa_supplicant-log-#.txt)
-CONFIG_DEBUG_FILE=y
 
-# Send debug messages to syslog instead of stdout
-#CONFIG_DEBUG_SYSLOG=y
-# Set syslog facility for debug messages
-#CONFIG_DEBUG_SYSLOG_FACILITY=LOG_DAEMON
+# Select control interface backend for external programs, e.g, wpa_cli:
+# unix = UNIX domain sockets (default for Linux/*BSD)
+# udp = UDP sockets using localhost (127.0.0.1)
+# named_pipe = Windows Named Pipe (default for Windows)
+# y = use default (backwards compatibility)
+# If this option is commented out, control interface is not included in the
+# build.
+CONFIG_CTRL_IFACE=unix
 
-# Enable privilege separation (see README 'Privilege separation' for details)
-#CONFIG_PRIVSEP=y
+# Select configuration backend:
+# file = text file (e.g., wpa_supplicant.conf; note: the configuration file
+#      path is given on command line, not here; this option is just used to
+#      select the backend that allows configuration files to be used)
+# winreg = Windows registry (see win_example.reg for an example)
+CONFIG_BACKEND=file
 
-# Enable mitigation against certain attacks against TKIP by delaying Michael
-# MIC error reports by a random amount of time between 0 and 60 seconds
-#CONFIG_DELAYED_MIC_ERROR_REPORT=y
 
-# Enable tracing code for developer debugging
-# This tracks use of memory allocations and other registrations and reports
-# incorrect use with a backtrace of call (or allocation) location.
-#CONFIG_WPA_TRACE=y
-# For BSD, comment out these.
-#LIBS += -lexecinfo
-#LIBS_p += -lexecinfo
-#LIBS_c += -lexecinfo
+# Select program entry point implementation:
+# main = UNIX/POSIX like main() function (default)
+# main_winsvc = Windows service (read parameters from registry)
+# main_none = Very basic example (development use only)
+CONFIG_MAIN=main
 
-# Use libbfd to get more details for developer debugging
-# This enables use of libbfd to get more detailed symbols for the backtraces
-# generated by CONFIG_WPA_TRACE=y.
-#CONFIG_WPA_TRACE_BFD=y
-# For BSD, comment out these.
-#LIBS += -lbfd -liberty -lz
-#LIBS_p += -lbfd -liberty -lz
-#LIBS_c += -lbfd -liberty -lz
 
-# wpa_supplicant depends on strong random number generation being available
-# from the operating system. os_get_random() function is used to fetch random
-# data when needed, e.g., for key generation. On Linux and BSD systems, this
-# works by reading /dev/urandom. It should be noted that the OS entropy pool
-# needs to be properly initialized before wpa_supplicant is started. This is
-# important especially on embedded devices that do not have a hardware random
-# number generator and may by default start up with minimal entropy available
-# for random number generation.
-#
-# As a safety net, wpa_supplicant is by default trying to internally collect
-# additional entropy for generating random data to mix in with the data fetched
-# from the OS. This by itself is not considered to be very strong, but it may
-# help in cases where the system pool is not initialized properly. However, it
-# is very strongly recommended that the system pool is initialized with enough
-# entropy either by using hardware assisted random number generator or by
-# storing state over device reboots.
-#
-# wpa_supplicant can be configured to maintain its own entropy store over
-# restarts to enhance random number generation. This is not perfect, but it is
-# much more secure than using the same sequence of random numbers after every
-# reboot. This can be enabled with -e<entropy file> command line option. The
-# specified file needs to be readable and writable by wpa_supplicant.
-#
-# If the os_get_random() is known to provide strong random data (e.g., on
-# Linux/BSD, the board in question is known to have reliable source of random
-# data from /dev/urandom), the internal wpa_supplicant random pool can be
-# disabled. This will save some in binary size and CPU use. However, this
-# should only be considered for builds that are known to be used on devices
-# that meet the requirements described above.
-#CONFIG_NO_RANDOM_POOL=y
+# Select wrapper for operatins system and C library specific functions
+# unix = UNIX/POSIX like systems (default)
+# win32 = Windows systems
+# none = Empty template
+CONFIG_OS=unix
 
-# IEEE 802.11n (High Throughput) support (mainly for AP mode)
-CONFIG_IEEE80211N=y
+# Select event loop implementation
+# eloop = select() loop (default)
+# eloop_win = Windows events and WaitForMultipleObject() loop
+# eloop_none = Empty template
+CONFIG_ELOOP=eloop
 
-# Interworking (IEEE 802.11u)
-# This can be used to enable functionality to improve interworking with
-# external networks (GAS/ANQP to learn more about the networks and network
-# selection based on available credentials).
-#CONFIG_INTERWORKING=y
+# Select layer 2 packet implementation
+# linux = Linux packet socket (default)
+# pcap = libpcap/libdnet/WinPcap
+# freebsd = FreeBSD libpcap
+# winpcap = WinPcap with receive thread
+# ndis = Windows NDISUIO (note: requires CONFIG_USE_NDISUIO=y)
+# none = Empty template
+CONFIG_L2_PACKET=linux
 
-CONFIG_NO_RADIUS=y
 CONFIG_AP=y
-TIZEN_EXT=y
+TIZEN_EXT=y
\ No newline at end of file
index 4e1c1bd..62af04a 100644 (file)
@@ -16,20 +16,23 @@ all: docs
 _wpa_supplicant.png: wpa_supplicant.png
        cp $< $@
 
-docs-pics: wpa_supplicant.png wpa_supplicant.eps hostapd.png hostapd.eps p2p_sm.png p2p_sm.eps p2p_arch.png p2p_arch.eps p2p_arch2.png p2p_arch2.eps
+_wpa_supplicant.eps: wpa_supplicant.eps
+       cp $< $@
+
+docs-pics: wpa_supplicant.png wpa_supplicant.eps hostapd.png hostapd.eps p2p_sm.png p2p_sm.eps p2p_arch.png p2p_arch.eps p2p_arch2.png p2p_arch2.eps _wpa_supplicant.png _wpa_supplicant.eps
 
 docs: docs-pics
        (cd ..; doxygen doc/doxygen.conf; cd doc)
        $(MAKE) -C latex
        cp latex/refman.pdf wpa_supplicant-devel.pdf
 
-html: docs-pics _wpa_supplicant.png
+html: docs-pics
        (cd ..; doxygen doc/doxygen.conf; cd doc)
 
 clean:
        rm -f *~
        rm -f wpa_supplicant.eps wpa_supplicant.png
-       rm -f _wpa_supplicant.png
+       rm -f _wpa_supplicant.png _wpa_supplicant.eps
        rm -f hostapd.eps hostapd.png
        rm -f p2p_sm.eps p2p_sm.png
        rm -f p2p_arch.eps p2p_arch.png
index 26f5f6d..454f179 100644 (file)
@@ -13,7 +13,7 @@
 \ref win_port "Windows port" |
 \ref test_programs "Test programs" ]
 
-%wpa_supplicant implementation is divided into number of independent
+wpa_supplicant implementation is divided into number of independent
 modules. Core code includes functionality for controlling the network
 selection, association, and configuration. Independent modules include
 WPA code (key handshake, PMKSA caching, pre-authentication), EAPOL
@@ -21,82 +21,77 @@ state machine, and EAP state machine and methods. In addition, there
 are number of separate files for generic helper functions.
 
 Both WPA and EAPOL/EAP state machines can be used separately in other
-programs than %wpa_supplicant. As an example, the included test
+programs than wpa_supplicant. As an example, the included test
 programs eapol_test and preauth_test are using these modules.
 
-\ref driver_wrapper "Driver interface API" is defined in driver.h and
+\ref driver_wrapper "Driver interface API" is defined in \ref driver.h and
 all hardware/driver dependent functionality is implemented in
 driver_*.c.
 
 
 \section _wpa_supplicant_core wpa_supplicant core functionality
 
-wpa_supplicant.c
+\ref wpa_supplicant.c
        Program initialization, main control loop
 
-main.c
+\ref wpa_supplicant/main.c
        main() for UNIX-like operating systems and MinGW (Windows); this
        uses command line arguments to configure wpa_supplicant
 
-events.c
-       Driver event processing; wpa_supplicant_event() and related functions
+\ref events.c
+       Driver event processing; \ref wpa_supplicant_event() and related functions
 
-wpa_supplicant_i.h
-       Internal definitions for %wpa_supplicant core; should not be
+\ref wpa_supplicant_i.h
+       Internal definitions for wpa_supplicant core; should not be
        included into independent modules
 
 
 \section generic_helper_func Generic helper functions
 
-%wpa_supplicant uses generic helper functions some of which are shared
+wpa_supplicant uses generic helper functions some of which are shared
 with with hostapd. The following C files are currently used:
 
-eloop.c and eloop.h
+\ref eloop.c and \ref eloop.h
        Event loop (select() loop with registerable timeouts, socket read
        callbacks, and signal callbacks)
 
-common.c and common.h
+\ref common.c and \ref common.h
        Common helper functions
 
-defs.h
+\ref defs.h
        Definitions shared by multiple files
 
-l2_packet.h, l2_packet_linux.c, and l2_packet_pcap.c
+\ref l2_packet.h, \ref l2_packet_linux.c, and \ref l2_packet_pcap.c
        Layer 2 (link) access wrapper (includes native Linux implementation
        and wrappers for libdnet/libpcap). A new l2_packet implementation
        may need to be added when porting to new operating systems that are
        not supported by libdnet/libpcap. Makefile can be used to select which
-       l2_packet implementation is included. l2_packet_linux.c uses Linux
-       packet sockets and l2_packet_pcap.c has a more portable version using
+       l2_packet implementation is included. \ref l2_packet_linux.c uses Linux
+       packet sockets and \ref l2_packet_pcap.c has a more portable version using
        libpcap and libdnet.
 
-pcsc_funcs.c and pcsc_funcs.h
+\ref pcsc_funcs.c and \ref pcsc_funcs.h
        Wrapper for PC/SC lite SIM and smart card readers
 
-priv_netlink.h
+\ref priv_netlink.h
        Private version of netlink definitions from Linux kernel header files;
        this could be replaced with C library header file once suitable
        version becomes commonly available
 
-version.h
+\ref version.h
        Version number definitions
 
-wireless_copy.h
-       Private version of Linux wireless extensions definitions from kernel
-       header files; this could be replaced with C library header file once
-       suitable version becomes commonly available
-
 
 \section crypto_func Cryptographic functions
 
-md5.c and md5.h
+\ref md5.c and \ref md5.h
        MD5 (replaced with a crypto library if TLS support is included)
        HMAC-MD5 (keyed checksum for message authenticity validation)
 
-rc4.c and rc4.h
+\ref rc4.c and \ref rc4.h
        RC4 (broadcast/default key encryption)
 
-sha1.c and sha1.h
+\ref sha1.c and \ref sha1.h
        SHA-1 (replaced with a crypto library if TLS support is included)
        HMAC-SHA-1 (keyed checksum for message authenticity validation)
        PRF-SHA-1 (pseudorandom (key/nonce generation) function)
@@ -104,10 +99,10 @@ sha1.c and sha1.h
        T-PRF (for EAP-FAST)
        TLS-PRF (RFC 2246)
 
-sha256.c and sha256.h
+\ref sha256.c and \ref sha256.h
        SHA-256 (replaced with a crypto library if TLS support is included)
 
-aes_wrap.c, aes_wrap.h, aes.c
+\ref aes-wrap.c, \ref aes_wrap.h, \ref aes.c
        AES (replaced with a crypto library if TLS support is included),
        AES Key Wrap Algorithm with 128-bit KEK, RFC3394 (broadcast/default
        key encryption),
@@ -116,207 +111,205 @@ aes_wrap.c, aes_wrap.h, aes.c
        AES-128 EAX mode encryption/decryption,
        AES-128 CBC
 
-crypto.h
+\ref crypto.h
        Definition of crypto library wrapper
 
-crypto_openssl.c
+\ref crypto_openssl.c
        Wrapper functions for libcrypto (OpenSSL)
 
-crypto_internal.c
+\ref crypto_internal.c
        Wrapper functions for internal crypto implementation
 
-crypto_gnutls.c
+\ref crypto_gnutls.c
        Wrapper functions for libgcrypt (used by GnuTLS)
 
-ms_funcs.c and ms_funcs.h
+\ref ms_funcs.c and \ref ms_funcs.h
        Helper functions for MSCHAPV2 and LEAP
 
-tls.h
+\ref tls.h
        Definition of TLS library wrapper
 
-tls_none.c
+\ref tls_none.c
        Dummy implementation of TLS library wrapper for cases where TLS
        functionality is not included.
 
-tls_openssl.c
+\ref tls_openssl.c
        TLS library wrapper for openssl
 
-tls_internal.c
+\ref tls_internal.c
        TLS library for internal TLS implementation
 
-tls_gnutls.c
+\ref tls_gnutls.c
        TLS library wrapper for GnuTLS
 
 
 \section tls_func TLS library
 
-asn1.c and asn1.h
+\ref asn1.c and \ref asn1.h
        ASN.1 DER parsing
 
-bignum.c and bignum.h
+\ref bignum.c and \ref bignum.h
        Big number math
 
-rsa.c and rsa.h
+\ref rsa.c and \ref rsa.h
        RSA
 
-x509v3.c and x509v3.h
+\ref x509v3.c and \ref x509v3.h
        X.509v3 certificate parsing and processing
 
-tlsv1_client.c, tlsv1_client.h
+\ref tlsv1_client.c, \ref tlsv1_client.h
        TLSv1 client (RFC 2246)
 
-tlsv1_client_i.h
+\ref tlsv1_client_i.h
        Internal structures for TLSv1 client
 
-tlsv1_client_read.c
+\ref tlsv1_client_read.c
        TLSv1 client: read handshake messages
 
-tlsv1_client_write.c
+\ref tlsv1_client_write.c
        TLSv1 client: write handshake messages
 
-tlsv1_common.c and tlsv1_common.h
+\ref tlsv1_common.c and \ref tlsv1_common.h
        Common TLSv1 routines and definitions
 
-tlsv1_cred.c and tlsv1_cred.h
+\ref tlsv1_cred.c and \ref tlsv1_cred.h
        TLSv1 credentials
 
-tlsv1_record.c and tlsv1_record.h
+\ref tlsv1_record.c and \ref tlsv1_record.h
        TLSv1 record protocol
 
 
 \section configuration Configuration
 
-config_ssid.h
+\ref config_ssid.h
        Definition of per network configuration items
 
-config.h
-       Definition of the %wpa_supplicant configuration
+\ref config.h
+       Definition of the wpa_supplicant configuration
 
-config.c
+\ref config.c
        Configuration parser and common functions
 
-config_file.c
+\ref wpa_supplicant/config_file.c
        Configuration backend for text files (e.g., wpa_supplicant.conf)
 
-config_winreg.c
+\ref config_winreg.c
        Configuration backend for Windows registry
 
 
 \section ctrl_iface Control interface
 
-%wpa_supplicant has a \ref ctrl_iface_page "control interface"
+wpa_supplicant has a \ref ctrl_iface_page "control interface"
 that can be used to get status
 information and manage operations from external programs. An example
 command line interface (wpa_cli) and GUI (wpa_gui) for this interface
-are included in the %wpa_supplicant distribution.
+are included in the wpa_supplicant distribution.
 
-ctrl_iface.c and ctrl_iface.h
-       %wpa_supplicant-side of the control interface
+\ref wpa_supplicant/ctrl_iface.c and \ref wpa_supplicant/ctrl_iface.h
+       wpa_supplicant-side of the control interface
 
-ctrl_iface_unix.c
+\ref ctrl_iface_unix.c
        UNIX domain sockets -based control interface backend
 
-ctrl_iface_udp.c
+\ref ctrl_iface_udp.c
        UDP sockets -based control interface backend
 
-ctrl_iface_named_pipe.c
+\ref ctrl_iface_named_pipe.c
        Windows named pipes -based control interface backend
 
-wpa_ctrl.c and wpa_ctrl.h
+\ref wpa_ctrl.c and \ref wpa_ctrl.h
        Library functions for external programs to provide access to the
-       %wpa_supplicant control interface
+       wpa_supplicant control interface
 
-wpa_cli.c
-       Example program for using %wpa_supplicant control interface
+\ref wpa_cli.c
+       Example program for using wpa_supplicant control interface
 
 
 \section wpa_code WPA supplicant
 
-wpa.c and wpa.h
+\ref wpa.c and \ref wpa.h
        WPA state machine and 4-Way/Group Key Handshake processing
 
-preauth.c and preauth.h
+\ref preauth.c and \ref preauth.h
        PMKSA caching and pre-authentication (RSN/WPA2)
 
-wpa_i.h
+\ref wpa_i.h
        Internal definitions for WPA code; not to be included to other modules.
 
 \section eap_peer EAP peer
 
 \ref eap_peer_module "EAP peer implementation" is a separate module that
-can be used by other programs than just %wpa_supplicant.
+can be used by other programs than just wpa_supplicant.
 
-eap.c and eap.h
+\ref eap.c and \ref eap.h
        EAP state machine and method interface
 
-eap_defs.h
+\ref eap_defs.h
        Common EAP definitions
 
-eap_i.h
+\ref eap_i.h
        Internal definitions for EAP state machine and EAP methods; not to be
        included in other modules
 
-eap_sim_common.c and eap_sim_common.h
+\ref eap_sim_common.c and \ref eap_sim_common.h
        Common code for EAP-SIM and EAP-AKA
 
-eap_tls_common.c and eap_tls_common.h
+\ref eap_tls_common.c and \ref eap_tls_common.h
        Common code for EAP-PEAP, EAP-TTLS, and EAP-FAST
 
-eap_tlv.c and eap_tlv.h
-       EAP-TLV code for EAP-PEAP and EAP-FAST
-
-eap_ttls.c and eap_ttls.h
+\ref eap_ttls.c and \ref eap_ttls.h
        EAP-TTLS
 
-eap_pax.c, eap_pax_common.h, eap_pax_common.c
+\ref eap_pax.c, \ref eap_pax_common.h, \ref eap_pax_common.c
        EAP-PAX
 
-eap_psk.c, eap_psk_common.h, eap_psk_common.c
+\ref eap_psk.c, \ref eap_psk_common.h, \ref eap_psk_common.c
        EAP-PSK (note: this is not needed for WPA-PSK)
 
-eap_sake.c, eap_sake_common.h, eap_sake_common.c
+\ref eap_sake.c, \ref eap_sake_common.h, \ref eap_sake_common.c
        EAP-SAKE
 
-eap_gpsk.c, eap_gpsk_common.h, eap_gpsk_common.c
+\ref eap_gpsk.c, \ref eap_gpsk_common.h, \ref eap_gpsk_common.c
        EAP-GPSK
 
-eap_aka.c, eap_fast.c, eap_gtc.c, eap_leap.c, eap_md5.c, eap_mschapv2.c,
-eap_otp.c, eap_peap.c, eap_sim.c, eap_tls.c
+\ref eap_aka.c, \ref eap_fast.c, \ref eap_gtc.c, \ref eap_leap.c,
+\ref eap_md5.c, \ref eap_mschapv2.c, \ref eap_otp.c, \ref eap_peap.c,
+\ref eap_sim.c, \ref eap_tls.c
        Other EAP method implementations
 
 
 \section eapol_supp EAPOL supplicant
 
-eapol_supp_sm.c and eapol_supp_sm.h
+\ref eapol_supp_sm.c and \ref eapol_supp_sm.h
        EAPOL supplicant state machine and IEEE 802.1X processing
 
 
 \section win_port Windows port
 
-ndis_events.c
+\ref ndis_events.c
        Code for receiving NdisMIndicateStatus() events and delivering them to
-       %wpa_supplicant driver_ndis.c in more easier to use form
+       wpa_supplicant \ref driver_ndis.c in more easier to use form
 
-win_if_list.c
+\ref win_if_list.c
        External program for listing current network interface
 
 
 \section test_programs Test programs
 
-radius_client.c and radius_client.h
+\ref radius_client.c and \ref radius_client.h
        RADIUS authentication client implementation for eapol_test
 
-radius.c and radius.h
+\ref radius.c and \ref radius.h
        RADIUS message processing for eapol_test
 
-eapol_test.c
+\ref eapol_test.c
        Standalone EAP testing tool with integrated RADIUS authentication
        client
 
-preauth_test.c
+\ref preauth_test.c
        Standalone RSN pre-authentication tool
 
-wpa_passphrase.c
+\ref wpa_passphrase.c
        WPA ASCII passphrase to PSK conversion
 
 */
index 0d06625..be8916f 100644 (file)
@@ -1,47 +1,47 @@
 /**
-\page ctrl_iface_page %wpa_supplicant control interface
+\page ctrl_iface_page wpa_supplicant control interface
 
-%wpa_supplicant implements a control interface that can be used by
-external programs to control the operations of the %wpa_supplicant
+wpa_supplicant implements a control interface that can be used by
+external programs to control the operations of the wpa_supplicant
 daemon and to get status information and event notifications. There is
-a small C library, in a form of a single C file, wpa_ctrl.c, that
+a small C library, in a form of a single C file, \ref wpa_ctrl.c, that
 provides helper functions to facilitate the use of the control
 interface. External programs can link this file into them and then use
-the library functions documented in wpa_ctrl.h to interact with
-%wpa_supplicant. This library can also be used with C++. wpa_cli.c and
+the library functions documented in \ref wpa_ctrl.h to interact with
+wpa_supplicant. This library can also be used with C++. \ref wpa_cli.c and
 wpa_gui are example programs using this library.
 
 There are multiple mechanisms for inter-process communication. For
-example, Linux version of %wpa_supplicant is using UNIX domain sockets
+example, Linux version of wpa_supplicant is using UNIX domain sockets
 for the control interface and Windows version UDP sockets. The use of
-the functions defined in wpa_ctrl.h can be used to hide the details of
+the functions defined in \ref wpa_ctrl.h can be used to hide the details of
 the used IPC from external programs.
 
 
 \section using_ctrl_iface Using the control interface
 
 External programs, e.g., a GUI or a configuration utility, that need to
-communicate with %wpa_supplicant should link in wpa_ctrl.c. This
+communicate with wpa_supplicant should link in \ref wpa_ctrl.c. This
 allows them to use helper functions to open connection to the control
-interface with wpa_ctrl_open() and to send commands with
-wpa_ctrl_request().
+interface with \ref wpa_ctrl_open() and to send commands with
+\ref wpa_ctrl_request().
 
-%wpa_supplicant uses the control interface for two types of communication:
+wpa_supplicant uses the control interface for two types of communication:
 commands and unsolicited event messages. Commands are a pair of
 messages, a request from the external program and a response from
-%wpa_supplicant. These can be executed using wpa_ctrl_request().
-Unsolicited event messages are sent by %wpa_supplicant to the control
+wpa_supplicant. These can be executed using \ref wpa_ctrl_request().
+Unsolicited event messages are sent by wpa_supplicant to the control
 interface connection without specific request from the external program
 for receiving each message. However, the external program needs to
-attach to the control interface with wpa_ctrl_attach() to receive these
+attach to the control interface with \ref wpa_ctrl_attach() to receive these
 unsolicited messages.
 
 If the control interface connection is used both for commands and
 unsolicited event messages, there is potential for receiving an
 unsolicited message between the command request and response.
-wpa_ctrl_request() caller will need to supply a callback, msg_cb,
+\ref wpa_ctrl_request() caller will need to supply a callback, msg_cb,
 for processing these messages. Often it is easier to open two
-control interface connections by calling wpa_ctrl_open() twice and
+control interface connections by calling \ref wpa_ctrl_open() twice and
 then use one of the connections for commands and the other one for
 unsolicited messages. This way command request/response pairs will
 not be broken by unsolicited messages. wpa_cli is an example of how
@@ -49,20 +49,20 @@ to use only one connection for both purposes and wpa_gui demonstrates
 how to use two separate connections.
 
 Once the control interface connection is not needed anymore, it should
-be closed by calling wpa_ctrl_close(). If the connection was used for
+be closed by calling \ref wpa_ctrl_close(). If the connection was used for
 unsolicited event messages, it should be first detached by calling
-wpa_ctrl_detach().
+\ref wpa_ctrl_detach().
 
 
 \section ctrl_iface_cmds Control interface commands
 
-Following commands can be used with wpa_ctrl_request():
+Following commands can be used with \ref wpa_ctrl_request():
 
 \subsection ctrl_iface_PING PING
 
-This command can be used to test whether %wpa_supplicant is replying
+This command can be used to test whether wpa_supplicant is replying
 to the control interface commands. The expected reply is \c PONG if the
-connection is open and %wpa_supplicant is processing commands.
+connection is open and wpa_supplicant is processing commands.
 
 
 \subsection ctrl_iface_MIB MIB
@@ -217,13 +217,13 @@ Start pre-authentication with the given BSSID.
 \subsection ctrl_iface_ATTACH ATTACH
 
 Attach the connection as a monitor for unsolicited events. This can
-be done with wpa_ctrl_attach().
+be done with \ref wpa_ctrl_attach().
 
 
 \subsection ctrl_iface_DETACH DETACH
 
 Detach the connection as a monitor for unsolicited events. This can
-be done with wpa_ctrl_detach().
+be done with \ref wpa_ctrl_detach().
 
 
 \subsection ctrl_iface_LEVEL LEVEL <debug level>
@@ -233,12 +233,12 @@ Change debug level.
 
 \subsection ctrl_iface_RECONFIGURE RECONFIGURE
 
-Force %wpa_supplicant to re-read its configuration data.
+Force wpa_supplicant to re-read its configuration data.
 
 
 \subsection ctrl_iface_TERMINATE TERMINATE
 
-Terminate %wpa_supplicant process.
+Terminate wpa_supplicant process.
 
 
 \subsection ctrl_iface_BSSID BSSID <network id> <BSSID>
@@ -563,7 +563,7 @@ two parameters are used: availibility period and availability interval
 
 \section ctrl_iface_interactive Interactive requests
 
-If %wpa_supplicant needs additional information during authentication
+If wpa_supplicant needs additional information during authentication
 (e.g., password), it will use a specific prefix, \c CTRL-REQ-
 (\a WPA_CTRL_REQ macro) in an unsolicited event message. An external
 program, e.g., a GUI, can provide such information by using
@@ -583,7 +583,7 @@ CTRL-REQ-<field name>-<network id>-<human readable text>
 CTRL-RSP-<field name>-<network id>-<value>
 \endverbatim
 
-For example, request from %wpa_supplicant:
+For example, request from wpa_supplicant:
 \verbatim
 CTRL-REQ-PASSWORD-1-Password needed for SSID test-network
 \endverbatim
@@ -598,7 +598,7 @@ CTRL-RSP-PASSWORD-1-secret
 
 Get list of supported functionality (eap, pairwise, group,
 proto). Supported functionality is shown as space separate lists of
-values used in the same format as in %wpa_supplicant configuration.
+values used in the same format as in wpa_supplicant configuration.
 If optional argument, 'strict', is added, only the values that the
 driver claims to explicitly support are included. Without this, all
 available capabilities are included if the driver does not provide
@@ -645,8 +645,8 @@ OPEN SHARED LEAP
 
 Change ap_scan value:
 0 = no scanning,
-1 = %wpa_supplicant requests scans and uses scan results to select the AP,
-2 = %wpa_supplicant does not use scanning and just requests driver to
+1 = wpa_supplicant requests scans and uses scan results to select the AP,
+2 = wpa_supplicant does not use scanning and just requests driver to
 associate and take care of AP selection
 
 
@@ -662,14 +662,14 @@ eth0
 
 \section ctrl_iface_events Control interface events
 
-%wpa_supplicant generates number messages based on events like
+wpa_supplicant generates number messages based on events like
 connection or a completion of a task. These are available to external
 programs that attach to receive unsolicited messages over the control
-interface with wpa_ctrl_attach().
+interface with \ref wpa_ctrl_attach().
 
 The event messages will be delivered over the attach control interface
 as text strings that start with the priority level of the message and
-a fixed prefix text as defined in wpa_ctrl.h. After this, optional
+a fixed prefix text as defined in \ref wpa_ctrl.h. After this, optional
 additional information may be included depending on the event
 message. For example, following event message is delivered when new
 scan results are available:
@@ -694,11 +694,11 @@ debug information, but can usually be ignored by external programs.
 
 In most cases, the external program can skip over the priority field
 in the beginning of the event message and then compare the following
-text to the event strings from wpa_ctrl.h that the program is
+text to the event strings from \ref wpa_ctrl.h that the program is
 interested in processing.
 
 Following subsections describe the most common event notifications
-generated by %wpa_supplicant.
+generated by wpa_supplicant.
 
 \subsection ctrl_iface_event_CTRL_REQ CTRL-REQ-
 
@@ -717,7 +717,7 @@ WPA_EVENT_DISCONNECTED: Disconnected, data connection is not available
 
 \subsection ctrl_iface_event_TERMINATING  CTRL-EVENT-TERMINATING
 
-WPA_EVENT_TERMINATING: %wpa_supplicant is exiting
+WPA_EVENT_TERMINATING: wpa_supplicant is exiting
 
 \subsection ctrl_iface_event_PASSWORD_CHANGED CTRL-EVENT-PASSWORD-CHANGED
 
index 1fa7a3a..329e5d0 100644 (file)
@@ -1,8 +1,8 @@
 /**
-\page dbus %wpa_supplicant D-Bus API
+\page dbus wpa_supplicant D-Bus API
 
-This section documents the %wpa_supplicant D-Bus API. Every D-Bus
-interface implemented by %wpa_supplicant is described here including
+This section documents the wpa_supplicant D-Bus API. Every D-Bus
+interface implemented by wpa_supplicant is described here including
 their methods, signals, and properties with arguments, returned
 values, and possible errors.
 
@@ -10,31 +10,36 @@ Interfaces:
 - \ref dbus_main
 - \ref dbus_interface
 - \ref dbus_wps
+- \ref dbus_p2pdevice
 - \ref dbus_bss
 - \ref dbus_network
+- \ref dbus_peer
+- \ref dbus_group
+- \ref dbus_persistent_group
 
 
 \section dbus_main fi.w1.wpa_supplicant1
 
-Interface implemented by the main %wpa_supplicant D-Bus object
+Interface implemented by the main wpa_supplicant D-Bus object
 registered in the bus with fi.w1.wpa_supplicant1 name.
 
 \subsection dbus_main_methods Methods
 
-    <ul>
+<ul>
       <li>
        <h3>CreateInterface ( a{sv} : args ) --> o : interface</h3>
-       <p>Registers a wireless interface in %wpa_supplicant.</p>
+       <p>Registers a wireless interface in wpa_supplicant.</p>
        <h4>Arguments</h4>
        <dl>
          <dt>a{sv} : args</dt>
          <dd>
-           A dictionary with arguments used to add the interface to %wpa_supplicant. The dictionary may contain the following entries:
+           A dictionary with arguments used to add the interface to wpa_supplicant. The dictionary may contain the following entries:
            <table>
              <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th>
              <tr><td>Ifname</td><td>s</td><td>Name of the network interface to control, e.g., wlan0</td><td>Yes</td>
-             <tr><td>Bridge_ifname</td><td>s</td><td>Name of the bridge interface to control, e.g., br0</td><td>No</td>
+             <tr><td>BridgeIfname</td><td>s</td><td>Name of the bridge interface to control, e.g., br0</td><td>No</td>
              <tr><td>Driver</td><td>s</td><td>Driver name which the interface uses, e.g., nl80211</td><td>No</td>
+             <tr><td>ConfigFile</td><td>s</td><td>Configuration file path</td><td>No</td>
            </table>
          </dd>
        </dl>
@@ -46,7 +51,7 @@ registered in the bus with fi.w1.wpa_supplicant1 name.
        <h4>Possible errors</h4>
        <dl>
          <dt>fi.w1.wpa_supplicant1.InterfaceExists</dt>
-         <dd>%wpa_supplicant already controls this interface.</dd>
+         <dd>wpa_supplicant already controls this interface.</dd>
          <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
          <dd>Creating interface failed for an unknown reason.</dd>
          <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
@@ -56,7 +61,7 @@ registered in the bus with fi.w1.wpa_supplicant1 name.
 
       <li>
        <h3>RemoveInterface ( o : interface ) --> nothing</h3>
-       <p>Deregisters a wireless interface from %wpa_supplicant.</p>
+       <p>Deregisters a wireless interface from wpa_supplicant.</p>
        <h4>Arguments</h4>
        <dl>
          <dt>o : interface</dt>
@@ -73,7 +78,7 @@ registered in the bus with fi.w1.wpa_supplicant1 name.
 
       <li>
        <h3>GetInterface ( s : ifname ) --> o : interface</h3>
-       <p>Returns a D-Bus path to an object related to an interface which %wpa_supplicant already controls.</p>
+       <p>Returns a D-Bus path to an object related to an interface which wpa_supplicant already controls.</p>
        <h4>Arguments</h4>
        <dl>
          <dt>s : ifname</dt>
@@ -87,7 +92,7 @@ registered in the bus with fi.w1.wpa_supplicant1 name.
        <h4>Possible errors</h4>
        <dl>
          <dt>fi.w1.wpa_supplicant1.InterfaceUnknown</dt>
-         <dd>An interface with the passed name in not controlled by %wpa_supplicant.</dd>
+         <dd>An interface with the passed name in not controlled by wpa_supplicant.</dd>
          <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
          <dd>Getting an interface object path failed for an unknown reason.</dd>
        </dl>
@@ -96,22 +101,22 @@ registered in the bus with fi.w1.wpa_supplicant1 name.
 
 \subsection dbus_main_properties Properties
 
-    <ul>
+<ul>
       <li>
        <h3>DebugLevel - s - (read/write)</h3>
-       <p>Global %wpa_supplicant debugging level. Possible values are
+       <p>Global wpa_supplicant debugging level. Possible values are
        "msgdump" (verbose debugging), "debug" (debugging),
        "info" (informative), "warning" (warnings), and "error" (errors).</p>
       </li>
 
       <li>
        <h3>DebugTimestamp - b - (read/write)</h3>
-       <p>Global %wpa_supplicant debugging parameter. Determines if timestamps are shown in debug logs.</p>
+       <p>Global wpa_supplicant debugging parameter. Determines if timestamps are shown in debug logs.</p>
       </li>
 
       <li>
        <h3>DebugShowKeys - b - (read/write)</h3>
-       <p>Global %wpa_supplicant debugging parameter. Determines if secrets are shown in debug logs.</p>
+       <p>Global wpa_supplicant debugging parameter. Determines if secrets are shown in debug logs.</p>
       </li>
 
       <li>
@@ -123,14 +128,24 @@ registered in the bus with fi.w1.wpa_supplicant1 name.
        <h3>EapMethods - as - (read)</h3>
        <p>An array with supported EAP methods names.</p>
       </li>
+
+      <li>
+       <h3>Capabilities - as - (read)</h3>
+       <p>An array with supported capabilities (e.g., "ap", "ibss-rsn", "p2p", "interworking").</p>
+      </li>
+
+      <li>
+       <h3>WFDIEs - ay - (read/write)</h3>
+       <p>Wi-Fi Display subelements.</p>
+      </li>
     </ul>
 
 \subsection dbus_main_signals Signals
 
-    <ul>
+<ul>
       <li>
        <h3>InterfaceAdded ( o : interface, a{sv} : properties )</h3>
-       <p>A new interface was added to %wpa_supplicant.</p>
+       <p>A new interface was added to wpa_supplicant.</p>
        <h4>Arguments</h4>
        <dl>
          <dt>o : interface</dt>
@@ -144,7 +159,7 @@ registered in the bus with fi.w1.wpa_supplicant1 name.
 
       <li>
        <h3>InterfaceRemoved ( o : interface )</h3>
-       <p>An interface was removed from %wpa_supplicant.</p>
+       <p>An interface was removed from wpa_supplicant.</p>
        <h4>Arguments</h4>
        <dl>
          <dt>o : interface</dt>
@@ -167,12 +182,12 @@ registered in the bus with fi.w1.wpa_supplicant1 name.
 \section dbus_interface fi.w1.wpa_supplicant1.Interface
 
 Interface implemented by objects related to network interface added to
-%wpa_supplicant, i.e., returned by
+wpa_supplicant, i.e., returned by
 fi.w1.wpa_supplicant1.CreateInterface.
 
 \subsection dbus_interface_methods Methods
 
-    <ul>
+<ul>
       <li>
        <h3>Scan ( a{sv} : args ) --> nothing</h3>
        <p>Triggers a scan.</p>
@@ -187,6 +202,7 @@ fi.w1.wpa_supplicant1.CreateInterface.
              <tr><td>SSIDs</td><td>aay</td><td>Array of SSIDs to scan for (applies only if scan type is active)</td><td>No</td>
              <tr><td>IEs</td><td>aay</td><td>Information elements to used in active scan (applies only if scan type is active)</td><td>No</td>
              <tr><td>Channels</td><td>a(uu)</td><td>Array of frequencies to scan in form of (center, width) in MHz.</td><td>No</td>
+             <tr><td>AllowRoam</td><td>b</td><td>TRUE (or absent) to allow a roaming decision based on the results of this scan, FALSE to prevent a roaming decision.</td><td>No</td>
            </table>
          </dd>
        </dl>
@@ -213,7 +229,7 @@ fi.w1.wpa_supplicant1.CreateInterface.
        <h4>Arguments</h4>
        <dl>
          <dt>a{sv} : args</dt>
-         <dd>A dictionary with network configuration. Dictionary entries are equivalent to entries in the "network" block in %wpa_supplicant configuration file. Entry values should be appropriate type to the entry, e.g., an entry with key "frequency" should have value type int.</dd>
+         <dd>A dictionary with network configuration. Dictionary entries are equivalent to entries in the "network" block in wpa_supplicant configuration file. Entry values should be appropriate type to the entry, e.g., an entry with key "frequency" should have value type int.</dd>
        </dl>
        <h4>Returns</h4>
        <dl>
@@ -249,6 +265,11 @@ fi.w1.wpa_supplicant1.CreateInterface.
       </li>
 
       <li>
+       <h3>RemoveAllNetworks ( ) --> nothing</h3>
+       <p>Remove all configured networks from the interface.</p>
+      </li>
+
+      <li>
        <h3>SelectNetwork ( o : network ) --> nothing</h3>
        <p>Attempt association with a configured network.</p>
        <h4>Arguments</h4>
@@ -266,6 +287,26 @@ fi.w1.wpa_supplicant1.CreateInterface.
       </li>
 
       <li>
+       <h3>Reassociate ( ) --> nothing</h3>
+       <p>Attempt reassociation.</p>
+       <h4>Possible errors</h4>
+       <dl>
+         <dt>fi.w1.wpa_supplicant1.InterfaceDisabled</dt>
+         <dd>The interface is disabled.</dd>
+       </dl>
+      </li>
+
+      <li>
+       <h3>Reattach ( ) --> nothing</h3>
+       <p>Attempt reassociation back to the current BSS.</p>
+       <h4>Possible errors</h4>
+       <dl>
+         <dt>fi.w1.wpa_supplicant1.NotConnected</dt>
+         <dd>Interface is not connected to any network.</dd>
+       </dl>
+      </li>
+
+      <li>
        <h3>AddBlob ( s : name, ay : data ) --> nothing</h3>
        <p>Adds a blob to the interface.</p>
        <h4>Arguments</h4>
@@ -333,6 +374,73 @@ fi.w1.wpa_supplicant1.CreateInterface.
        </dl>
       </li>
       <li>
+       <h3>TDLSDiscover ( s : peer_address ) --> nothing</h3>
+       <p>Initiate a TDLS discovery for a peer.</p>
+       <h4>Arguments</h4>
+       <dl>
+         <dt>s : peer_address</dt>
+         <dd>MAC address for the peer to perform TDLS discovery.</dd>
+       </dl>
+       <h4>Possible errors</h4>
+       <dl>
+         <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+         <dd>The "peer_address" argument is not a properly formatted MAC.</dd>
+         <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+         <dd>Initiating the TDLS operation failed for an unknown reason.</dd>
+       </dl>
+      </li>
+      <li>
+       <h3>TDLSSetup ( s : peer_address ) --> nothing</h3>
+       <p>Setup a TDLS session for a peer.</p>
+       <h4>Arguments</h4>
+       <dl>
+         <dt>s : peer_address</dt>
+         <dd>MAC address for the peer to perform TDLS setup.</dd>
+       </dl>
+       <h4>Possible errors</h4>
+       <dl>
+         <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+         <dd>The "peer_address" argument is not a properly formatted MAC.</dd>
+         <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+         <dd>Initiating the TDLS operation failed for an unknown reason.</dd>
+       </dl>
+      </li>
+      <li>
+       <h3>TDLSStatus ( s : peer_address ) --> s</h3>
+       <p>Return TDLS status with respect to a peer.</p>
+       <h4>Arguments</h4>
+       <dl>
+         <dt>s : peer_address</dt>
+         <dd>MAC address for the peer for which status is requested.</dd>
+       </dl>
+       <h4>Returns</h4>
+       <dl>
+         <dt>s : status</dt>
+         <dd>Current status of the TDLS link with the selected peer.</dd>
+       </dl>
+       <h4>Possible errors</h4>
+       <dl>
+         <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+         <dd>The "peer_address" argument is not a properly formatted MAC.</dd>
+       </dl>
+      </li>
+      <li>
+       <h3>TDLSTeardown ( s : peer_address ) --> nothing</h3>
+       <p>Tear down a TDLS session with a peer.</p>
+       <h4>Arguments</h4>
+       <dl>
+         <dt>s : peer_address</dt>
+         <dd>MAC address for the peer to tear down TDLS connectivity with.</dd>
+       </dl>
+       <h4>Possible errors</h4>
+       <dl>
+         <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+         <dd>The "peer_address" argument is not a properly formatted MAC.</dd>
+         <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+         <dd>Initiating the TDLS operation failed for an unknown reason.</dd>
+       </dl>
+      </li>
+      <li>
        <h3>EAPLogoff ( ) --> nothing</h3>
        <p>IEEE 802.1X EAPOL state machine logoff.</p>
       </li>
@@ -340,11 +448,107 @@ fi.w1.wpa_supplicant1.CreateInterface.
        <h3>EAPLogon ( ) --> nothing</h3>
        <p>IEEE 802.1X EAPOL state machine logon.</p>
       </li>
+
+      <li>
+       <h3>NetworkReply ( o : network, s : field, s : value ) --> nothing</h3>
+       <p>Provide parameter requested by NetworkRequest().</p>
+       <h4>Arguments</h4>
+       <dl>
+         <dt>o : network</dt>
+         <dd>A D-Bus path to an object representing the network (copied from NetworkRequest()).</dd>
+         <dt>s : field</dt>
+         <dd>Requested information (copied from NetworkRequest()).</dd>
+         <dt>s : value</dt>
+         <dd>The requested information (e.g., password for EAP authentication).</dd>
+       </dl>
+       <h4>Possible errors</h4>
+       <dl>
+         <dt>fi.w1.wpa_supplicant1.NetworkUnknown</dt>
+         <dd>A passed path doesn't point to any network object.</dd>
+         <dt>fi.w1.wpa_supplicant1.InvalidArgs</dt>
+         <dd>A passed path doesn't point to any network object.</dd>
+         <dt>fi.w1.wpa_supplicant1.UnknownError</dt>
+         <dd>IEEE 802.1X support was not included in the build.</dd>
+       </dl>
+      </li>
+
+      <li>
+       <h3>SetPKCS11EngineAndModulePath ( s : pkcs11_engine_path, s : pkcs11_module_path ) --> nothing</h3>
+       <p>Set PKCS #11 engine and module path.</p>
+       <h4>Arguments</h4>
+       <dl>
+         <dt>s : pkcs11_engine_path</dt>
+         <dd>PKCS #11 engine path.</dd>
+         <dt>s : pkcs11_module_path</dt>
+         <dd>PKCS #11 module path.</dd>
+       </dl>
+       <h4>Possible errors</h4>
+       <dl>
+         <dt>org.freedesktop.DBus.Error.Failed.InvalidArgs</dt>
+         <dd>Invalid PKCS #11 engine or module path.</dd>
+         <dt>org.freedesktop.DBus.Error.Failed</dt>
+         <dd>Reinit of the EAPOL state machine with the new PKCS #11 engine and module path failed.</dd>
+       </dl>
+      </li>
+      <li>
+       <h3>SignalPoll ( ) --> a{sv} : properties</h3>
+       <p>Fetch signal properties for the current connection.</p>
+       <h4>Returns</h4>
+       <dl>
+         <dt>a{sv} : properties</dt>
+         <dd>
+           <table>
+             <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th>
+             <tr><td>linkspeed</td><td>i</td><td>Link speed (Mbps)</td><td>No</td>
+             <tr><td>noise</td><td>i</td><td>Noise (dBm)</td><td>No</td>
+             <tr><td>width</td><td>s</td><td>Channel width</td><td>No</td>
+             <tr><td>frequency</td><td>u</td><td>Frequency (MHz)</td><td>No</td>
+             <tr><td>rssi</td><td>i</td><td>RSSI (dBm)</td><td>No</td>
+             <tr><td>avg-rssi</td><td>i</td><td>Average RSSI (dBm)</td><td>No</td>
+             <tr><td>center-frq1</td><td>i</td><td>VHT segment 1 frequency (MHz)</td><td>No</td>
+             <tr><td>center-frq2</td><td>i</td><td>VHT segment 2 frequency (MHz)</td><td>No</td>
+           </table>
+         </dd>
+       </dl>
+      </li>
+      <li>
+       <h3>FlushBSS ( u : age ) --> nothing</h3>
+       <p>Flush BSS entries from the cache.</p>
+       <h4>Arguments</h4>
+       <dl>
+         <dt>u : age</dt>
+         <dd>Maximum age in seconds for BSS entries to keep in cache (0 = remove all entries).</dd>
+       </dl>
+      </li>
+
+      <li>
+       <h3>SubscribeProbeReq ( ) --> nothing</h3>
+       <p>Subscribe to receive Probe Request events. This is needed in addition to registering a signal handler for the ProbeRequest signal to avoid flooding D-Bus with all Probe Request indications when no application is interested in them.</p>
+       <h4>Possible errors</h4>
+       <dl>
+         <dt>fi.w1.wpa_supplicant1.SubscriptionInUse</dt>
+         <dd>Another application is already subscribed.</dd>
+         <dt>fi.w1.wpa_supplicant1.NoMemory</dt>
+         <dd>Needed memory was not possible to get allocated.</dd>
+       </dl>
+      </li>
+
+      <li>
+       <h3>UnsubscribeProbeReq ( ) --> nothing</h3>
+       <p>Unsubscribe from receiving Probe Request events.</p>
+       <h4>Possible errors</h4>
+       <dl>
+         <dt>fi.w1.wpa_supplicant1.NoSubscription</dt>
+         <dd>No subscription in place.</dd>
+         <dt>fi.w1.wpa_supplicant1.SubscriptionNotYou</dt>
+         <dd>Subscription in place, but for another process.</dd>
+       </dl>
+      </li>
     </ul>
 
 \subsection dbus_interface_properties Properties
 
-    <ul>
+<ul>
       <li>
        <h3>Capabilities - a{sv} - (read)</h3>
        <p>Capabilities of the interface. Dictionary contains following entries:</p>
@@ -372,22 +576,22 @@ fi.w1.wpa_supplicant1.CreateInterface.
 
       <li>
        <h3>ApScan - u - (read/write)</h3>
-       <p>Identical to ap_scan entry in %wpa_supplicant configuration file. Possible values are 0, 1 or 2.</p>
+       <p>Identical to ap_scan entry in wpa_supplicant configuration file. Possible values are 0, 1 or 2.</p>
       </li>
 
       <li>
        <h3>BSSExpireAge - u - (read/write)</h3>
-       <p>Identical to bss_expiration_age entry in %wpa_supplicant configuration file.</p>
+       <p>Identical to bss_expiration_age entry in wpa_supplicant configuration file.</p>
       </li>
 
       <li>
        <h3>BSSExpireCount - u - (read/write)</h3>
-       <p>Identical to bss_expiration_scan_count entry in %wpa_supplicant configuration file.</p>
+       <p>Identical to bss_expiration_scan_count entry in wpa_supplicant configuration file.</p>
       </li>
 
       <li>
        <h3>Country - s - (read/write)</h3>
-       <p>Identical to country entry in %wpa_supplicant configuration file.</p>
+       <p>Identical to country entry in wpa_supplicant configuration file.</p>
       </li>
 
       <li>
@@ -407,12 +611,17 @@ fi.w1.wpa_supplicant1.CreateInterface.
 
       <li>
        <h3>CurrentBSS - o - (read)</h3>
-       <p>Path to D-Bus object representing BSS which %wpa_supplicant is associated with, or "/" if is not associated at all.</p>
+       <p>Path to D-Bus object representing BSS which wpa_supplicant is associated with, or "/" if is not associated at all.</p>
       </li>
 
       <li>
        <h3>CurrentNetwork - o - (read)</h3>
-       <p>Path to D-Bus object representing configured network which %wpa_supplicant uses at the moment, or "/" if doesn't use any.</p>
+       <p>Path to D-Bus object representing configured network which wpa_supplicant uses at the moment, or "/" if doesn't use any.</p>
+      </li>
+
+      <li>
+       <h3>CurrentAuthMode - s - (read)</h3>
+       <p>Current authentication type.</p>
       </li>
 
       <li>
@@ -432,18 +641,33 @@ fi.w1.wpa_supplicant1.CreateInterface.
 
       <li>
        <h3>FastReauth - b - (read/write)</h3>
-       <p>Identical to fast_reauth entry in %wpa_supplicant configuration file.</p>
+       <p>Identical to fast_reauth entry in wpa_supplicant configuration file.</p>
       </li>
 
       <li>
        <h3>ScanInterval - i - (read/write)</h3>
        <p>Time (in seconds) between scans for a suitable AP. Must be >= 0.</p>
       </li>
+
+      <li>
+       <h3>PKCS11EnginePath - s - (read)</h3>
+       <p>PKCS #11 engine path.</p>
+      </li>
+
+      <li>
+       <h3>PKCS11ModulePath - s - (read)</h3>
+       <p>PKCS #11 module path.</p>
+      </li>
+
+      <li>
+       <h3>DisconnectReason - i - (read)</h3>
+       <p>The most recent IEEE 802.11 reason code for disconnect. Negative value indicates locally generated disconnection.</p>
+      </li>
     </ul>
 
 \subsection dbus_interface_signals Signals
 
-    <ul>
+<ul>
       <li>
        <h3>ScanDone ( b : success )</h3>
        <p>Scanning finished. </p>
@@ -561,20 +785,65 @@ fi.w1.wpa_supplicant1.CreateInterface.
          <dd>A dictionary with pairs of properties names which have changed and theirs new values. Possible dictionary keys are: "ApScan", "Scanning", "State", "CurrentBSS", "CurrentNetwork"</dd>
        </dl>
       </li>
+
+      <li>
+       <h3>Certification ( a{sv} : parameters )</h3>
+       <p>Information about server TLS certificates.</p>
+       <h4>Arguments</h4>
+       <dl>
+         <dt>a{sv} : parameters</dt>
+         <dd>A dictionary with pairs of field names and their values. Possible dictionary keys are: "depth", "subject", "altsubject", "cert_hash", "cert".</dd>
+       </dl>
+      </li>
+
+      <li>
+       <h3>EAP ( s : status, s : parameter )</h3>
+       <p>Information about EAP peer status.</p>
+       <h4>Arguments</h4>
+       <dl>
+         <dt>s : status</dt>
+         <dd>Operation, e.g., "started", "accept proposed method", "remote certificate verification", "eap parameter needed", "completion".</dd>
+         <dt>s : parameter</dt>
+         <dd>Information about the operation, e.g., EAP method name, "success".</dd>
+       </dl>
+      </li>
+
+      <li>
+       <h3>NetworkRequest ( o : network, s : field, s : txt )</h3>
+       <p>Request for network parameter. NetworkResponse() is used to provide the requested parameter.</p>
+       <h4>Arguments</h4>
+       <dl>
+         <dt>o : network</dt>
+         <dd>D-Bus path to an object representing the network.</dd>
+         <dt>s : field</dt>
+         <dd>Requested information, e.g., "PASSWORD".</dd>
+         <dt>txt : field</dt>
+         <dd>Human readable information about the requested information.</dd>
+       </dl>
+      </li>
+
+      <li>
+       <h3>ProbeRequest ( a{sv} : args )</h3>
+       <p>Information about a received Probe Request frame. This signal is delivered only to a single application that has subscribed to received the events with SubscribeProbeReq().</p>
+       <h4>Arguments</h4>
+       <dl>
+         <dt>a{sv} : args</dt>
+         <dd>A dictionary with pairs of field names and their values. Possible dictionary keys are: "addr", "dst", "bssid", "ies", "signal".</dd>
+       </dl>
+      </li>
     </ul>
 
 
 \section dbus_wps fi.w1.wpa_supplicant1.Interface.WPS
 
-Interface implemented by objects related to network interface added to
-%wpa_supplicant, i.e., returned by fi.w1.wpa_supplicant1.CreateInterface.
+Interface for performing WPS (Wi-Fi Simple Config) operations.
 
 \subsection dbus_wps_methods Methods
 
-    <ul>
+<ul>
       <li>
        <h3>Start ( a{sv} : args ) --> a{sv} : output</h3>
-       <p>Starts WPS configuration.</p>
+       <p>Starts WPS configuration. Note: When used with P2P groups, this needs to be issued on the GO group interface.</p>
        <h4>Arguments</h4>
        <dl>
          <dt>a{sv} : args</dt>
@@ -585,7 +854,8 @@ Interface implemented by objects related to network interface added to
              <tr><td>Role</td><td>s</td><td>The device's role. Possible values are "enrollee" and "registrar".</td><td>Yes</td>
              <tr><td>Type</td><td>s</td><td>WPS authentication type. Applies only for enrollee role. Possible values are "pin" and "pbc".</td><td>Yes, for enrollee role; otherwise no</td>
              <tr><td>Pin</td><td>s</td><td>WPS Pin.</td><td>Yes, for registrar role; otherwise optional</td>
-             <tr><td>Bssid</td><td>ay</td><td></td><td>No</td>
+             <tr><td>Bssid</td><td>ay</td><td>Note: This is used to specify the peer MAC address when authorizing WPS connection in AP or P2P GO role.</td><td>No</td>
+             <tr><td>P2PDeviceAddress</td><td>ay</td><td>P2P Device Address of a peer to authorize for PBC connection. Used only in P2P GO role.</td><td>No</td>
            </table>
          </dd>
        </dl>
@@ -611,16 +881,20 @@ Interface implemented by objects related to network interface added to
 
 \subsection dbus_wps_properties Properties
 
-    <ul>
+<ul>
       <li>
        <h3>ProcessCredentials - b - (read/write)</h3>
        <p>Determines if the interface will process the credentials (credentials_processed configuration file parameter).</p>
       </li>
+      <li>
+       <h3>ConfigMethods - s - (read/write)</h3>
+       <p>The currently advertised WPS configuration methods. Available methods: usba ethernet label display ext_nfc_token int_nfc_token nfc_interface push_button keypad virtual_display physical_display virtual_push_button physical_push_button.</p>
+      </li>
     </ul>
 
 \subsection dbus_wps_signals Signals
 
-    <ul>
+<ul>
       <li>
        <h3>Event ( s : name, a{sv} : args )</h3>
        <p>WPS event occurred.</p>
@@ -672,6 +946,553 @@ Interface implemented by objects related to network interface added to
     </ul>
 
 
+\section dbus_p2pdevice fi.w1.wpa_supplicant1.Interface.P2PDevice
+
+Interface for performing P2P (Wi-Fi Peer-to-Peer) P2P Device operations.
+
+\subsection dbus_p2pdevice_methods Methods
+
+<ul>
+  <li>
+    <h3>Find ( a{sv} : args ) --> nothing</h3>
+    <p>Start P2P find operation (i.e., alternating P2P Search and Listen states to discover peers and be discoverable).</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+       A dictionary with parameters for the P2P find operation:
+       <table>
+       <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+       <tr><td>Timeout</td><td>i</td><td>Timeout for operating in seconds</td><td>no</td></tr>
+       <tr><td>RequestedDevicesTypes</td><td>aay</td><td>WPS Device Types to search for</td><td>no</td></tr>
+       <tr><td>DiscoveryType</td><td>s</td><td>"start_with_full" (default, if not specified), "social", "progressive"</td><td>no</td></tr>
+       </table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>StopFind ( nothing ) --> nothing</h3>
+    <p>Stop P2P find operation.</p>
+  </li>
+
+  <li>
+    <h3>Listen ( i : timeout ) --> nothing</h3>
+    <p>Start P2P listen operation (i.e., be discoverable).</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>i : timeout</dt>
+      <dd>Timeout in seconds for stopping the listen operation.</dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>ExtendedListen ( a{sv} : args ) --> nothing</h3>
+    <p>Configure Extended Listen Timing. If the parameters are omitted, this feature is disabled. If the parameters are included, Listen State will be entered every interval msec for at least period msec. Both values have acceptable range of 1-65535 (with interval obviously having to be larger than or equal to duration). If the P2P module is not idle at the time the Extended Listen Timing timeout occurs, the Listen State operation will be skipped.</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+       A dictionary with parameters for extended listen. Leave out all items to disable extended listen.
+       <table>
+       <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+       <tr><td>period</td><td>i</td><td>Extended listen period in milliseconds; 1-65535.</td><td>no</td></tr>
+       <tr><td>interval</td><td>i</td><td>Extended listen interval in milliseconds; 1-65535.</td><td>no</td></tr>
+       </table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>PresenceRequest ( a{sv} : args ) --> nothing</h3>
+    <p>Request a specific GO presence in a P2P group where the local device is a P2P Client. Send a P2P Presence Request to the GO (this is only available when acting as a P2P client). If no duration/interval pairs are given, the request indicates that this client has no special needs for GO presence. The first parameter pair gives the preferred duration and interval values in microseconds. If the second pair is included, that indicates which value would be acceptable.
+    \note This needs to be issued on a P2P group interface if separate group interfaces are used.
+    \bug It would be cleaner to not require .P2PDevice methods to be issued on a group interface. In other words, args['group_object'] could be used to specify the group or this method could be moved to be a .Group PresenceRequest() method.</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+       A dictionary with parameters for the presence request.
+       <table>
+       <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+       <tr><td>duration1</td><td>i</td><td>Duration in microseconds.</td><td>no</td></tr>
+       <tr><td>interval1</td><td>i</td><td>Interval in microseconds.</td><td>no</td></tr>
+       <tr><td>duration2</td><td>i</td><td>Duration in microseconds.</td><td>no</td></tr>
+       <tr><td>interval2</td><td>i</td><td>Interval in microseconds.</td><td>no</td></tr>
+       </table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>ProvisionDiscoveryRequest ( o : peer, s : config_method ) --> nothing</h3>
+  </li>
+
+  <li>
+    <h3>Connect ( a{sv} : args ) --> s : generated_pin</h3>
+    <p>Request a P2P group to be started through GO Negotiation or by joining an already operating group.</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+       A dictionary with parameters for the requested connection:
+       <table>
+       <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+       <tr><td>peer</td><td>o</td><td></td><td>yes</td></tr>
+       <tr><td>persistent</td><td>b</td><td>Whether to form a persistent group.</td><td>no</td></tr>
+       <tr><td>join</td><td>b</td><td>Whether to join an already operating group instead of forming a new group.</td><td>no</td></tr>
+       <tr><td>authorize_only</td><td>b</td><td>Whether to authorize a peer to initiate GO Negotiation instead of initiating immediately.</td><td>no</td></tr>
+       <tr><td>frequency</td><td>i</td><td>Operating frequency in MHz</td><td>no</td></tr>
+       <tr><td>go_intent</td><td>i</td><td>GO intent 0-15</td><td>no</td></tr>
+       <tr><td>wps_method</td><td>s</td><td>"pbc", "display", "keypad", "pin" (alias for "display")</td><td>yes</td></tr>
+       <tr><td>pin</td><td>s</td><td></td><td>no</td></tr>
+       </table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>GroupAdd ( a{sv} : args ) --> nothing</h3>
+    <p>Request a P2P group to be started without GO Negotiation.</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+       A dictionary with parameters for the requested group:
+       <table>
+       <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+       <tr><td>persistent</td><td>b</td><td>Whether to form a persistent group.</td><td>no</td></tr>
+       <tr><td>persistent_group_object</td><td>o</td><td></td><td>no</td></tr>
+       <tr><td>frequency</td><td>i</td><td>Operating frequency in MHz</td><td>no</td></tr>
+       </table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>Invite ( a{sv} : args ) --> nothing</h3>
+    <p>Invite a peer to join an already operating group or to re-invoke a persistent group.</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+       A dictionary with parameters for the invitation:
+       <table>
+       <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+       <tr><td>peer</td><td>o</td><td></td><td>yes</td></tr>
+       <tr><td>persistent_group_object</td><td>o</td><td></td><td>no</td></tr>
+       </table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>Disconnect ( nothing ) --> nothing</h3>
+    <p>Terminate a P2P group.
+    \note This needs to be issued on a P2P group interface if separate group interfaces are used.
+    \bug It would be cleaner to not require .P2PDevice methods to be issued on a group interface. In other words, this would either need to be Disconnect(group_object) or moved to be a .Group Disconnect() method.</p>
+  </li>
+
+  <li>
+    <h3>RejectPeer ( o : peer ) --> nothing</h3>
+    <p>Reject connection attempt from a peer (specified with a device address). This is a mechanism to reject a pending GO Negotiation with a peer and request to automatically block any further connection or discovery of the peer.</p>
+  </li>
+
+  <li>
+    <h3>Flush ( nothing ) --> nothing</h3>
+    <p>Flush P2P peer table and state.</p>
+  </li>
+
+  <li>
+    <h3>AddService ( a{sv} : args ) --> nothing</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+       A dictionary with parameters for the service:
+       <table>
+       <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+       <tr><td>service_type</td><td>s</td><td>"upnp", "bonjour"</td><td>yes</td></tr>
+       <tr><td>version</td><td>u</td><td>Required for UPnP services.</td><td>no</td></tr>
+       <tr><td>service</td><td>s</td><td></td><td></td></tr>
+       <tr><td>query</td><td>ay</td><td></td><td></td></tr>
+       <tr><td>response</td><td>ay</td><td></td><td></td></tr>
+       </table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>DeleteService ( a{sv} : args ) --> nothing</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+       A dictionary with parameters for the service:
+       <table>
+       <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+       <tr><td>service_type</td><td>s</td><td>"upnp", "bonjour"</td><td>yes</td></tr>
+       <tr><td>version</td><td>u</td><td>Required for UPnP services.</td><td>no</td></tr>
+       <tr><td>service</td><td>s</td><td></td><td></td></tr>
+       <tr><td>query</td><td>ay</td><td></td><td></td></tr>
+       </table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>FlushService ( nothing ) --> nothing</h3>
+  </li>
+
+  <li>
+    <h3>ServiceDiscoveryRequest ( a{sv} : args ) --> t : ref</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+       A dictionary with following parameters:
+       <table>
+       <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+       <tr><td>peer_object</td><td>o</td><td></td><td>no</td></tr>
+       <tr><td>service_type</td><td>s</td><td>"upnp"</td><td>no</td></tr>
+       <tr><td>version</td><td>u</td><td>Required for UPnP services.</td><td>no</td></tr>
+       <tr><td>service</td><td>s</td><td></td><td></td></tr>
+       <tr><td>tlv</td><td>ay</td><td></td><td></td></tr>
+       </table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>ServiceDiscoveryResponse ( a{sv} : args ) --> nothing : ref</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+       A dictionary with following parameters:
+       <table>
+       <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+       <tr><td>peer_object</td><td>o</td><td></td><td>yes</td></tr>
+       <tr><td>frequency</td><td>i</td><td></td><td>yes</td></tr>
+       <tr><td>dialog_token</td><td>i</td><td></td><td>yes</td></tr>
+       <tr><td>tlvs</td><td>ay</td><td></td><td>yes</td></tr>
+       </table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>ServiceDiscoveryCancelRequest ( t : args ) --> nothing : ref</h3>
+  </li>
+
+  <li>
+    <h3>ServiceUpdate ( nothing ) --> nothing</h3>
+  </li>
+
+  <li>
+    <h3>ServiceDiscoveryExternal ( i : arg ) --> nothing</h3>
+  </li>
+
+  <li>
+    <h3>AddPersistentGroup ( a{sv} : args ) --> o : path</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : args</dt>
+      <dd>
+       A dictionary with following parameters:
+       <table>
+       <tr><th>Key</th><th>Value type</th><th>Description</th><th>Required</th></tr>
+       <tr><td>bssid</td><td>s</td><td>P2P Device Address of the GO in the persistent group.</td><td>yes</td></tr>
+       <tr><td>ssid</td><td>s</td><td>SSID of the group</td><td>yes</td></tr>
+       <tr><td>psk</td><td>s</td><td>Passphrase (on the GO and optionally on P2P Client) or PSK (on P2P Client if passphrase ise not known)</td><td>yes</td></tr>
+       <tr><td>mode</td><td>s</td><td>"3" on GO or "0" on P2P Client</td><td>yes</td></tr>
+       </table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>RemovePersistentGroup ( o : path ) --> nothing</h3>
+  </li>
+
+  <li>
+    <h3>RemoveAllPersistentGroups ( nothing ) --> nothing</h3>
+  </li>
+</ul>
+
+\subsection dbus_p2pdevice_properties Properties
+
+<ul>
+  <li>
+    <h3>P2PDeviceConfig - a{sv} - (read/write)</h3>
+    <p>Dictionary with following entries. On write, only the included values are changed.</p>
+    <table>
+    <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+    <tr><td>DeviceName</td><td>s</td><td></td></tr>
+    <tr><td>PrimaryDeviceType</td><td>ay</td><td></td></tr>
+    <tr><td>SecondaryDeviceTypes</td><td>aay</td><td></td></tr>
+    <tr><td>VendorExtension</td><td>aay</td><td></td></tr>
+    <tr><td>GOIntent</td><td>u</td><td></td></tr>
+    <tr><td>PersistentReconnect</td><td>b</td><td></td></tr>
+    <tr><td>ListenRegClass</td><td>u</td><td></td></tr>
+    <tr><td>OperRegClass</td><td>u</td><td></td></tr>
+    <tr><td>OperChannel</td><td>u</td><td></td></tr>
+    <tr><td>SsidPostfix</td><td>s</td><td></td></tr>
+    <tr><td>IntraBss</td><td>b</td><td></td></tr>
+    <tr><td>GroupIdle</td><td>u</td><td></td></tr>
+    <tr><td>disassoc_low_ack</td><td>u</td><td></td></tr>
+    <tr><td>NoGroupIface</td><td>b</td><td></td></tr>
+    <tr><td>p2p_search_delay</td><td>u</td><td></td></tr>
+    </table>
+  </li>
+
+  <li>
+    <h3>Peers - ao - (read)</h3>
+  </li>
+
+  <li>
+    <h3>Role - s - (read)</h3>
+    <p>\bug What is this trying to indicate? It does not make much sense to have a P2PDevice property role since there can be multiple concurrent groups and the P2P Device role is always active anyway.</p>
+  </li>
+
+  <li>
+    <h3>Group - o - (read)</h3>
+    <p>\bug What is this trying to indicate? It does not make much sense to have a P2PDevice property Group since there can be multiple concurrent groups.</p>
+  </li>
+
+  <li>
+    <h3>PeerGO - o - (read)</h3>
+    <p>\bug What is this trying to indicate? It does not make much sense to have a P2PDevice property PeerGO since there can be multiple concurrent groups.</p>
+  </li>
+
+  <li>
+    <h3>PersistentGroups - ao - (read)</h3>
+  </li>
+</ul>
+
+\subsection dbus_p2pdevice_signals Signals
+
+<ul>
+  <li>
+    <h3>DeviceFound ( o : path )</h3>
+  </li>
+
+  <li>
+    <h3>DeviceLost ( o : path )</h3>
+  </li>
+
+  <li>
+    <h3>ProvisionDiscoveryRequestDisplayPin ( o : peer_object, s : pin )</h3>
+  </li>
+
+  <li>
+    <h3>ProvisionDiscoveryResponseDisplayPin ( o : peer_object, s : pin )</h3>
+  </li>
+
+  <li>
+    <h3>ProvisionDiscoveryRequestEnterPin ( o : peer_object )</h3>
+  </li>
+
+  <li>
+    <h3>ProvisionDiscoveryResponseEnterPin ( o : peer_object )</h3>
+  </li>
+
+  <li>
+    <h3>ProvisionDiscoveryPBCRequest ( o : peer_object )</h3>
+  </li>
+
+  <li>
+    <h3>ProvisionDiscoveryPBCResponse ( o : peer_object )</h3>
+  </li>
+
+  <li>
+    <h3>ProvisionDiscoveryFailure ( o : peer_object, i : status )</h3>
+  </li>
+
+  <li>
+    <h3>GroupStarted ( a{sv} : properties )</h3>
+    <p>A new P2P group was started or joined.</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : properties</dt>
+      <dd>A dictionary with following information on the added group:
+       <table>
+         <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+         <tr><td>interface_object</td><td>o</td><td>D-Bus path of the interface on which this group is operating on. See \ref dbus_interface.</td></tr>
+         <tr><td>role</td><td>s</td><td>The role of the local device in the group: "GO" or "client".</td></tr>
+         <tr><td>group_object</td><td>o</td><td>D-Bus path of the group. See \ref dbus_group.</td></tr>
+       </table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>GONegotiationSuccess ( a{sv} : properties )</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : properties</dt>
+      <dd>A dictionary with following information:
+       <table>
+         <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+         <tr><td>peer_object</td><td>o</td><td>D-Bus path of the peer. See \ref dbus_peer.</td></tr>
+         <tr><td>status</td><td>i</td><td></td></tr>
+         <tr><td>passphrase</td><td>s</td><td>Passphrase for the group. Included only if this device becomes the GO of the group.</td></tr>
+         <tr><td>role_go</td><td>s</td><td>The role of the local device in the group: "GO" or "client".</td></tr>
+         <tr><td>ssid</td><td>ay</td><td></td></tr>
+         <tr><td>peer_device_addr</td><td>ay</td><td></td></tr>
+         <tr><td>peer_interface_addr</td><td>ay</td><td></td></tr>
+         <tr><td>wps_method</td><td>s</td><td></td></tr>
+         <tr><td>frequency_list</td><td>ai</td><td></td></tr>
+         <tr><td>persistent_group</td><td>i</td><td></td></tr>
+         <tr><td>peer_config_timeout</td><td>u</td><td></td></tr>
+       </table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>GONegotiationFailure ( a{sv} : properties )</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : properties</dt>
+      <dd>A dictionary with following information:
+       <table>
+         <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+         <tr><td>peer_object</td><td>o</td><td>D-Bus path of the peer. See \ref dbus_peer.</td></tr>
+         <tr><td>status</td><td>i</td><td></td></tr>
+       </table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>GONegotiationRequest ( o : path, i : dev_passwd_id )</h3>
+  </li>
+
+  <li>
+    <h3>InvitationResult ( a{sv} : invite_result )</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : invite_result</dt>
+      <dd>A dictionary with following information:
+       <table>
+         <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+         <tr><td>status</td><td>i</td><td></td></tr>
+         <tr><td>BSSID</td><td>ay</td><td>Optionally present</td></tr>
+       </table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>GroupFinished ( a{sv} : properties )</h3>
+    <p>A P2P group was removed.</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : properties</dt>
+      <dd>A dictionary with following information of the removed group:
+       <table>
+         <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+         <tr><td>interface_object</td><td>o</td><td>D-Bus path of the interface on which this group is operating on. See \ref dbus_interface.</td></tr>
+         <tr><td>role</td><td>s</td><td>The role of the local device in the group: "GO" or "client".</td></tr>
+         <tr><td>group_object</td><td>o</td><td>D-Bus path of the group. See \ref dbus_group.</td></tr>
+       </table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>ServiceDiscoveryRequest ( a{sv} : sd_request )</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : sd_request</dt>
+      <dd>A dictionary with following information:
+       <table>
+         <tr><td>peer_object</td><td>o</td><td></td></tr>
+         <tr><td>frequency</td><td>i</td><td></td></tr>
+         <tr><td>dialog_token</td><td>i</td><td></td></tr>
+         <tr><td>update_indicator</td><td>q</td><td></td></tr>
+         <tr><td>tlvs</td><td>ay</td><td></td></tr>
+       </table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>ServiceDiscoveryResponse ( a{sv} : sd_response )</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>a{sv} : sd_response</dt>
+      <dd>A dictionary with following information:
+       <table>
+         <tr><td>peer_object</td><td>o</td><td></td></tr>
+         <tr><td>update_indicator</td><td>q</td><td></td></tr>
+         <tr><td>tlvs</td><td>ay</td><td></td></tr>
+       </table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>PersistentGroupAdded ( o : path, a{sv} : properties )</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>o : path</dt>
+      <dd>D-Bus object path for the persistent group. See \ref dbus_persistent_group.</dd>
+      <dt>a{sv} : properties</dt>
+      <dd>A dictionary with following information:
+       <table>
+       <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+       <tr><td>bssid</td><td>s</td><td>P2P Device Address of the GO in the persistent group.</td></tr>
+       <tr><td>ssid</td><td>s</td><td>SSID of the group</td></tr>
+       <tr><td>psk</td><td>s</td><td>Passphrase (on the GO and optionally on P2P Client) or PSK (on P2P Client if passphrase ise not known)</td></tr>
+       <tr><td>disabled</td><td>s</td><td>Set to "2" to indicate special network block use as a P2P persistent group information</td></tr>
+       <tr><td>mode</td><td>s</td><td>"3" on GO or "0" on P2P Client</td></tr>
+       </table>
+      </dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>PersistentGroupRemoved ( o : path )</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>o : path</dt>
+      <dd>D-Bus object path for the persistent group. See \ref dbus_persistent_group.</dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>WpsFailed ( s : name, a{sv} : args )</h3>
+    <p></p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>s : name</dt>
+      <dd>"fail"</dd>
+      <dt>a{sv} : args</dt>
+      <dd>A dictionary with following information:
+       <table>
+         <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+         <tr><td>msg</td><td>i</td><td></td></tr>
+         <tr><td>config_error</td><td>n</td><td></td></tr>
+       </table>
+      </dd>
+    </dl>
+  </li>
+</ul>
+
 \section dbus_bss fi.w1.wpa_supplicant1.BSS
 
 Interface implemented by objects representing a scanned BSSs, i.e.,
@@ -679,7 +1500,7 @@ scan results.
 
 \subsection dbus_bss_properties Properties
 
-    <ul>
+<ul>
       <li>
        <h3>BSSID - ay - (read)</h3>
        <p>BSSID of the BSS.</p>
@@ -708,6 +1529,13 @@ scan results.
        </table>
       </li>
       <li>
+       <h3>WPS - a{sv} - (read)</h3>
+       <p>WPS information of the BSS. Empty dictionary indicates no WPS support. Dictionary entries are:</p>
+       <table>
+         <tr><td>Type</td><td>s</td><td>"pbc", "pin", ""</td>
+       </table>
+      </li>
+      <li>
        <h3>IEs - ay - (read)</h3>
        <p>All IEs of the BSS as a chain of TLVs</p>
       </li>
@@ -731,11 +1559,15 @@ scan results.
        <h3>Signal - n - (read)</h3>
        <p>Signal strength of the BSS.</p>
       </li>
+      <li>
+       <h3>Age - u - (read)</h3>
+       <p>Number of seconds since the BSS was last seen.</p>
+      </li>
     </ul>
 
 \subsection dbus_bss_signals Signals
 
-    <ul>
+<ul>
       <li>
        <h3>PropertiesChanged ( a{sv} : properties )</h3>
        <p>Some properties have changed.</p>
@@ -755,21 +1587,21 @@ i.e., returned by fi.w1.wpa_supplicant1.Interface.AddNetwork.
 
 \subsection dbus_network_properties Properties
 
-    <ul>
+<ul>
       <li>
        <h3>Enabled - b - (read/write)</h3>
        <p>Determines if the configured network is enabled or not.</p>
       </li>
 
       <li>
-       <h3>Properties - a{sv} - (read)</h3>
-       <p>Properties of the configured network. Dictionary contains entries from "network" block of %wpa_supplicant configuration file. All values are string type, e.g., frequency is "2437", not 2437.
+       <h3>Properties - a{sv} - (read/write)</h3>
+       <p>Properties of the configured network. Dictionary contains entries from "network" block of wpa_supplicant configuration file. All values are string type, e.g., frequency is "2437", not 2437.
       </li>
     </ul>
 
 \subsection dbus_network_signals Signals
 
-    <ul>
+<ul>
       <li>
        <h3>PropertiesChanged ( a{sv} : properties )</h3>
        <p>Some properties have changed.</p>
@@ -781,4 +1613,179 @@ i.e., returned by fi.w1.wpa_supplicant1.Interface.AddNetwork.
       </li>
     </ul>
 
+\section dbus_peer fi.w1.wpa_supplicant1.Peer
+
+Interface implemented by objects representing P2P peer devices.
+
+\subsection dbus_peer_properties Properties
+
+<ul>
+  <li>
+    <h3>DeviceName - s - (read)</h3>
+  </li>
+
+  <li>
+    <h3>PrimaryDeviceType - ay - (read)</h3>
+  </li>
+
+  <li>
+    <h3>config_method - q - (read)</h3>
+  </li>
+
+  <li>
+    <h3>level - i - (read)</h3>
+  </li>
+
+  <li>
+    <h3>devicecapability - y - (read)</h3>
+  </li>
+
+  <li>
+    <h3>groupcapability - y - (read)</h3>
+    <p>Group Capability field from the last frame from which this peer information was updated.
+    \note This field is only for debugging purposes and must not be used to determine whether the peer happens to be operating a group as a GO at the moment.
+    </p>
+  </li>
+
+  <li>
+    <h3>SecondaryDeviceTypes - aay - (read)</h3>
+  </li>
+
+  <li>
+    <h3>VendorExtension - aay - (read)</h3>
+  </li>
+
+  <li>
+    <h3>IEs - ay - (read)</h3>
+    <p>This is a confusingly named property that includes Wi-Fi Display subelements from the peer.
+    \bug This should really be renamed since "IEs" means something completely different..
+    </p>
+  </li>
+
+  <li>
+    <h3>DeviceAddress - ay - (read)</h3>
+    <p>The P2P Device Address of the peer.</p>
+  </li>
+
+  <li>
+    <h3>Groups - ao - (read)</h3>
+    <p>The current groups in which this peer is connected.</p>
+  </li>
+</ul>
+
+\subsection dbus_peer_signals Signals
+
+<ul>
+  <li>
+    <h3>PropertiesChanged ( a{sv} : properties )</h3>
+    <p>Some properties have changed.
+    \deprecated Use org.freedesktop.DBus.Properties.PropertiesChanged instead.</p>
+    \todo Explain how ProertiesChanged signals are supposed to be of any real use with Peer objects (i.e., one signal for multiple peers).
+       <h4>Arguments</h4>
+       <dl>
+         <dt>a{sv} : properties</dt>
+         <dd>A dictionary with pairs of properties names which have changed and their new values.</dd>
+       </dl>
+      </li>
+    </ul>
+
+\section dbus_group fi.w1.wpa_supplicant1.Group
+
+Interface implemented by objects representing active P2P groups.
+
+\subsection dbus_group_properties Properties
+
+<ul>
+  <li>
+    <h3>Members - ao - (read)</h3>
+    <p>Array of D-Bus object paths for the peer devices that are currently connected to the group. This is valid only on the GO device. An empty array is returned in P2P Client role.
+  </li>
+
+  <li>
+    <h3>Group - o - (read)</h3>
+    <p>\todo Why is this here? This D-Bus object path is to this specific group and one needs to know it to fetching this information in the first place..
+    </p>
+  </li>
+
+  <li>
+    <h3>Role - s - (read)</h3>
+    <p>The role of this device in the group: "GO", "client".</p>
+  </li>
+
+  <li>
+    <h3>SSID - ay - (read)</h3>
+    <p>P2P Group SSID.</p>
+  </li>
+
+  <li>
+    <h3>BSSID - ay - (read)</h3>
+    <p>P2P Group BSSID (the P2P Interface Address of the GO).</p>
+  </li>
+
+  <li>
+    <h3>Frequency - q - (read)</h3>
+    <p>The frequency (in MHz) of the group operating channel.</p>
+  </li>
+
+  <li>
+    <h3>Passphrase - s - (read)</h3>
+    <p>Passphrase used in the group. This is always available on the GO. For P2P Client role, this may be available depending on whether the peer GO provided the passphrase during the WPS provisioning step. If not available, an empty string is returned.</p>
+  </li>
+
+  <li>
+    <h3>PSK - ay - (read)</h3>
+    <p>PSK used in the group.</p>
+  </li>
+
+  <li>
+    <h3>WPSVendorExtensions - aay - (read/write)</h3>
+    <p>WPS vendor extension attributes used on the GO. This is valid only the in the GO role. An empty array is returned in P2P Client role. At maximum, 10 separate vendor extension byte arrays can be configured. The GO device will include the configured attributes in WPS exchanges.</p>
+  </li>
+</ul>
+
+\subsection dbus_group_signals Signals
+
+<ul>
+  <li>
+    <h3>PeerJoined ( o : peer )</h3>
+    <p>A peer device has joined the group. This is indicated only on the GO device.</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>o : peer</dt>
+      <dd>A D-Bus path to the object representing the peer. See \ref dbus_peer.</dd>
+    </dl>
+  </li>
+
+  <li>
+    <h3>PeerDisconnected ( o : peer )</h3>
+    <p>A peer device has left the group. This is indicated only on the GO device.</p>
+    <h4>Arguments</h4>
+    <dl>
+      <dt>o : peer</dt>
+      <dd>A D-Bus path to the object representing the peer. See \ref dbus_peer.</dd>
+    </dl>
+  </li>
+</ul>
+
+\section dbus_persistent_group fi.w1.wpa_supplicant1.PersistentGroup
+
+Interface implemented by objects representing persistent P2P groups.
+
+\subsection dbus_persistent_group_properties Properties
+
+<ul>
+  <li>
+    <h3>Properties - a{sv} - (read/write)</h3>
+    <p>Properties of the persistent group. These are same properties as in the \ref dbus_network. When writing this, only the entries to be modified are included, i.e., any item that is not included will be left at its existing value. The following entries are used for persistent groups:</p>
+    <table>
+      <tr><th>Key</th><th>Value type</th><th>Description</th></tr>
+      <tr><td>bssid</td><td>s</td><td>P2P Device Address of the GO in the persistent group.</td></tr>
+      <tr><td>ssid</td><td>s</td><td>SSID of the group</td></tr>
+      <tr><td>psk</td><td>s</td><td>Passphrase (on the GO and optionally on P2P Client) or PSK (on P2P Client if passphrase ise not known)</td></tr>
+      <tr><td>disabled</td><td>s</td><td>Set to "2" to indicate special network block use as a P2P persistent group information</td></tr>
+      <tr><td>mode</td><td>s</td><td>"3" on GO or "0" on P2P Client</td></tr>
+    </table>
+  </li>
+</ul>
+
 */
index 7465afe..15e5bda 100644 (file)
@@ -82,9 +82,9 @@ Enrollee. Minimal UPnP and HTTP functionality is also provided for the
 functionality needed to implement Wi-Fi Protected Setup.
 
 
-\dir wpa_supplicant %wpa_supplicant
+\dir wpa_supplicant wpa_supplicant
 
-%wpa_supplicant-specific code for configuration, control interface, and
+wpa_supplicant-specific code for configuration, control interface, and
 client management.
 
 */
index b8c40e3..06be6a5 100644 (file)
@@ -31,7 +31,7 @@ PROJECT_NAME           = "wpa_supplicant / hostapd"
 # This could be handy for archiving the generated documentation or
 # if some version control system is used.
 
-PROJECT_NUMBER         = 2.0
+PROJECT_NUMBER         = 2.4
 
 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
 # base path where the generated documentation will be put.
@@ -572,6 +572,8 @@ INPUT                  = \
        doc \
        hostapd \
        wpa_supplicant \
+       wpa_supplicant/dbus \
+       eap_example \
        src/ap \
        src/common \
        src/crypto \
@@ -583,6 +585,7 @@ INPUT                  = \
        src/eap_server \
        src/l2_packet \
        src/p2p \
+       src/pae \
        src/radius \
        src/rsn_supp \
        src/tls \
@@ -1533,3 +1536,12 @@ GENERATE_LEGEND        = YES
 # the various graphs.
 
 DOT_CLEANUP            = YES
+
+
+#---------------------------------------------------------------------------
+# Project additions
+#---------------------------------------------------------------------------
+
+# Disable autolink support due to wpa_supplicant getting unfortunately
+# auto-linked to struct wpa_supplicant due to having an underscore in the name.
+AUTOLINK_SUPPORT = FALSE
index 28aea50..a3b470a 100644 (file)
@@ -3,7 +3,7 @@
 
 All hardware and driver dependent functionality is in separate C files
 that implement defined wrapper functions. Other parts
-of the %wpa_supplicant are designed to be hardware, driver, and operating
+of the wpa_supplicant are designed to be hardware, driver, and operating
 system independent.
 
 Driver wrappers need to implement whatever calls are used in the
@@ -13,45 +13,45 @@ code and ioctl() calls and netlink message parsing for Linux Wireless
 Extensions (WE). Since features required for WPA were added only recently to
 Linux Wireless Extensions (in version 18), some driver specific code is used
 in number of driver interface implementations. These driver dependent parts
-can be replaced with generic code in driver_wext.c once the target driver
+can be replaced with generic code in \ref driver_wext.c once the target driver
 includes full support for WE-18. After that, all Linux drivers, at
 least in theory, could use the same driver wrapper code.
 
 A driver wrapper needs to implement some or all of the functions
-defined in driver.h. These functions are registered by filling struct
-wpa_driver_ops with function pointers. Hardware independent parts of
-%wpa_supplicant will call these functions to control the driver/wlan
+defined in \ref driver.h. These functions are registered by filling struct
+\ref wpa_driver_ops with function pointers. Hardware independent parts of
+wpa_supplicant will call these functions to control the driver/wlan
 card. In addition, support for driver events is required. The event
-callback function, wpa_supplicant_event(), and its parameters are
-documented in driver.h. In addition, a pointer to the 'struct
-wpa_driver_ops' needs to be registered in drivers.c file.
+callback function, \ref wpa_supplicant_event(), and its parameters are
+documented in \ref driver.h. In addition, a pointer to the 'struct
+\ref wpa_driver_ops' needs to be registered in \ref drivers.c file.
 
 When porting to other operating systems, the driver wrapper should be
 modified to use the native interface of the target OS. It is possible
 that some extra requirements for the interface between the driver
-wrapper and generic %wpa_supplicant code are discovered during porting
+wrapper and generic wpa_supplicant code are discovered during porting
 to a new operating system. These will be addressed on case by case
 basis by modifying the interface and updating the other driver
 wrappers for this. The goal is to avoid changing this interface
 without very good reasons in order to limit the number of changes
 needed to other wrappers and hardware independent parts of
-%wpa_supplicant. When changes are required, recommended way is to
+wpa_supplicant. When changes are required, recommended way is to
 make them in backwards compatible way that allows existing driver
 interface implementations to be compiled without any modification.
 
 Generic Linux Wireless Extensions functions are implemented in
-driver_wext.c. All Linux driver wrappers can use these when the kernel
+\ref driver_wext.c. All Linux driver wrappers can use these when the kernel
 driver supports the generic ioctl()s and wireless events. Driver
 specific functions are implemented in separate C files, e.g.,
-driver_hostap.c. These files need to define struct wpa_driver_ops
-entry that will be used in wpa_supplicant.c when calling driver
-functions. struct wpa_driver_ops entries are registered in drivers.c.
+\ref driver_hostap.c. These files need to define struct \ref wpa_driver_ops
+entry that will be used in \ref wpa_supplicant.c when calling driver
+functions. struct \ref wpa_driver_ops entries are registered in \ref drivers.c.
 
 In general, it is likely to be useful to first take a look at couple
 of driver interface examples before starting on implementing a new
-one. driver_hostap.c and driver_wext.c include a complete
-implementation for Linux drivers that use %wpa_supplicant-based control
-of WPA IE and roaming. driver_ndis.c (with help from driver_ndis_.c)
+one. \ref driver_hostap.c and \ref driver_wext.c include a complete
+implementation for Linux drivers that use wpa_supplicant-based control
+of WPA IE and roaming. \ref driver_ndis.c (with help from \ref driver_ndis_.c)
 is an example of a complete interface for Windows NDIS interface for
 drivers that generate WPA IE themselves and decide when to roam. These
 example implementations include full support for all security modes.
@@ -61,7 +61,7 @@ example implementations include full support for all security modes.
 
 WPA introduces new requirements for the device driver. At least some
 of these need to be implemented in order to provide enough support for
-%wpa_supplicant.
+wpa_supplicant.
 
 \subsection driver_tkip_ccmp TKIP/CCMP
 
@@ -86,12 +86,12 @@ private ioctls can be used to provide similar functionality.
 
 \subsection driver_roaming Roaming control and scanning support
 
-%wpa_supplicant can optionally control AP selection based on the
+wpa_supplicant can optionally control AP selection based on the
 information received from Beacon and/or Probe Response frames
 (ap_scan=1 mode in configuration). This means that the driver should
 support external control for scan process. In case of Linux, use of
 new Wireless Extensions scan support (i.e., 'iwlist wlan0 scan') is
-recommended. The current driver wrapper (driver_wext.c) uses this for
+recommended. The current driver wrapper (\ref driver_wext.c) uses this for
 scan results.
 
 Scan results must also include the WPA information element. Support for
@@ -99,9 +99,9 @@ this was added in WE-18. With older versions, a custom event can be used
 to provide the full WPA IE (including element id and length) as a hex
 string that is included in the scan results.
 
-%wpa_supplicant needs to also be able to request the driver to
+wpa_supplicant needs to also be able to request the driver to
 associate with a specific BSS. Current Host AP driver and matching
-driver_hostap.c wrapper uses following sequence for this
+\ref driver_hostap.c wrapper uses following sequence for this
 request. Similar/identical mechanism should be usable also with other
 drivers.
 
@@ -113,28 +113,28 @@ drivers.
 
 \subsection driver_wpa_ie WPA IE generation
 
-%wpa_supplicant selects which cipher suites and key management suites
+wpa_supplicant selects which cipher suites and key management suites
 are used. Based on this information, it generates a WPA IE. This is
 provided to the driver interface in the associate call. This does not
 match with Windows NDIS drivers which generate the WPA IE
 themselves.
 
-%wpa_supplicant allows Windows NDIS-like behavior by providing the
+wpa_supplicant allows Windows NDIS-like behavior by providing the
 selected cipher and key management suites in the associate call. If
 the driver generates its own WPA IE and that differs from the one
-generated by %wpa_supplicant, the driver has to inform %wpa_supplicant
+generated by wpa_supplicant, the driver has to inform wpa_supplicant
 about the used WPA IE (i.e., the one it used in (Re)Associate
 Request). This notification is done using EVENT_ASSOCINFO event (see
-driver.h). %wpa_supplicant is normally configured to use
+\ref driver.h). wpa_supplicant is normally configured to use
 ap_scan=2 mode with drivers that control WPA IE generation and roaming.
 
 \subsection driver_events Driver events
 
-%wpa_supplicant needs to receive event callbacks when certain events
+wpa_supplicant needs to receive event callbacks when certain events
 occur (association, disassociation, Michael MIC failure, scan results
 available, PMKSA caching candidate). These events and the callback
-details are defined in driver.h (wpa_supplicant_event() function
-and enum wpa_event_type).
+details are defined in \ref driver.h (\ref wpa_supplicant_event() function
+and enum \ref wpa_event_type).
 
 On Linux, association and disassociation can use existing Wireless
 Extensions event that is reporting new AP with SIOCGIWAP
@@ -153,18 +153,18 @@ AP selection depends on ap_scan configuration:
 
 ap_scan=1:
 
-- %wpa_supplicant requests scan with SIOCSIWSCAN
+- wpa_supplicant requests scan with SIOCSIWSCAN
 - driver reports scan complete with wireless event SIOCGIWSCAN
-- %wpa_supplicant reads scan results with SIOCGIWSCAN (multiple call if
+- wpa_supplicant reads scan results with SIOCGIWSCAN (multiple call if
   a larget buffer is needed)
-- %wpa_supplicant decides which AP to use based on scan results
-- %wpa_supplicant configures driver to associate with the selected BSS
+- wpa_supplicant decides which AP to use based on scan results
+- wpa_supplicant configures driver to associate with the selected BSS
   (SIOCSIWMODE, SIOCSIWGENIE, SIOCSIWAUTH, SIOCSIWFREQ,
    SIOCSIWESSID, SIOCSIWAP)
 
 ap_scan=2:
 
-- %wpa_supplicant configures driver to associate with an SSID
+- wpa_supplicant configures driver to associate with an SSID
   (SIOCSIWMODE, SIOCSIWGENIE, SIOCSIWAUTH, SIOCSIWESSID)
 
 
@@ -174,7 +174,7 @@ After this, both modes use similar steps:
   (Re)AssocReq), driver reports association parameters (AssocReq IEs)
   with wireless event IWEVASSOCREQIE (and optionally IWEVASSOCRESPIE)
 - driver reports association with wireless event SIOCGIWAP
-- %wpa_supplicant takes care of EAPOL frame handling (validating
+- wpa_supplicant takes care of EAPOL frame handling (validating
   information from associnfo and if needed, from scan results if WPA/RSN
   IE from the Beacon frame is not reported through associnfo)
 */
index 6a24829..fc7ea26 100644 (file)
@@ -2,44 +2,44 @@
 \page eap_peer_module EAP peer implementation
 
 Extensible Authentication Protocol (EAP) is an authentication framework
-defined in RFC 3748. %wpa_supplicant uses a separate code module for EAP
+defined in RFC 3748. wpa_supplicant uses a separate code module for EAP
 peer implementation. This module was designed to use only a minimal set
 of direct function calls (mainly, to debug/event functions) in order for
 it to be usable in other programs. The design of the EAP
 implementation is based loosely on RFC 4137. The state machine is
 defined in this RFC and so is the interface between the peer state
 machine and methods. As such, this RFC provides useful information for
-understanding the EAP peer implementation in %wpa_supplicant.
+understanding the EAP peer implementation in wpa_supplicant.
 
 Some of the terminology used in EAP state machine is referring to
 EAPOL (IEEE 802.1X), but there is no strict requirement on the lower
 layer being IEEE 802.1X if EAP module is built for other programs than
-%wpa_supplicant. These terms should be understood to refer to the
+wpa_supplicant. These terms should be understood to refer to the
 lower layer as defined in RFC 4137.
 
 
 \section adding_eap_methods Adding EAP methods
 
 Each EAP method is implemented as a separate module, usually as one C
-file named eap_<name of the method>.c, e.g., eap_md5.c. All EAP
+file named eap_<name of the method>.c, e.g., \ref eap_md5.c. All EAP
 methods use the same interface between the peer state machine and
 method specific functions. This allows new EAP methods to be added
 without modifying the core EAP state machine implementation.
 
 New EAP methods need to be registered by adding them into the build
 (Makefile) and the EAP method registration list in the
-eap_peer_register_methods() function of eap_methods.c. Each EAP
+\ref eap_peer_register_methods() function of \ref eap_methods.c. Each EAP
 method should use a build-time configuration option, e.g., EAP_TLS, in
 order to make it possible to select which of the methods are included
 in the build.
 
-EAP methods must implement the interface defined in eap_i.h. struct
+EAP methods must implement the interface defined in \ref eap_i.h. struct
 eap_method defines the needed function pointers that each EAP method
 must provide. In addition, the EAP type and name are registered using
 this structure. This interface is based on section 4.4 of RFC 4137.
 
 It is recommended that the EAP methods would use generic helper
-functions, eap_msg_alloc() and eap_hdr_validate() when processing
+functions, \ref eap_msg_alloc() and \ref eap_hdr_validate() when processing
 messages. This allows code sharing and can avoid missing some of the
 needed validation steps for received packets. In addition, these
 functions make it easier to change between expanded and legacy EAP
@@ -48,23 +48,23 @@ header, if needed.
 When adding an EAP method that uses a vendor specific EAP type
 (Expanded Type as defined in RFC 3748, Chapter 5.7), the new method
 must be registered by passing vendor id instead of EAP_VENDOR_IETF to
-eap_peer_method_alloc(). These methods must not try to emulate
+\ref eap_peer_method_alloc(). These methods must not try to emulate
 expanded types by registering a legacy EAP method for type 254. See
-eap_vendor_test.c for an example of an EAP method implementation that
+\ref eap_vendor_test.c for an example of an EAP method implementation that
 is implemented as an expanded type.
 
 
 \section used_eap_library Using EAP implementation as a library
 
 The Git repository has an eap_example directory that contains an
-example showing how EAP peer and server code from %wpa_supplicant and
+example showing how EAP peer and server code from wpa_supplicant and
 hostapd can be used as a library. The example program initializes both
 an EAP server and an EAP peer entities and then runs through an
 EAP-PEAP/MSCHAPv2 authentication.
 
-eap_example_peer.c shows the initialization and glue code needed to
-control the EAP peer implementation. eap_example_server.c does the
-same for EAP server. eap_example.c is an example that ties in both the
+\ref eap_example_peer.c shows the initialization and glue code needed to
+control the EAP peer implementation. \ref eap_example_server.c does the
+same for EAP server. \ref eap_example.c is an example that ties in both the
 EAP server and client parts to allow an EAP authentication to be
 shown.
 
@@ -77,9 +77,9 @@ uses IEEE 802.1X EAPOL state machines to control the transmission of
 EAP messages and WiMax supports optional PMK EAP authentication
 mechanism that transmits EAP messages as defined in IEEE 802.16e.
 
-The EAP library links in number of helper functions from src/utils and
-src/crypto directories. Most of these are suitable as-is, but it may
-be desirable to replace the debug output code in src/utils/wpa_debug.c
+The EAP library links in number of helper functions from \ref src/utils and
+\ref src/crypto directories. Most of these are suitable as-is, but it may
+be desirable to replace the debug output code in \ref src/utils/wpa_debug.c
 by dropping this file from the library and re-implementing the
 functions there in a way that better fits in with the main
 application.
index 4aca53d..f60ac79 100644 (file)
@@ -14,32 +14,32 @@ understanding the EAP server implementation in hostapd.
 Some of the terminology used in EAP state machine is referring to
 EAPOL (IEEE 802.1X), but there is no strict requirement on the lower
 layer being IEEE 802.1X if EAP module is built for other programs than
-%wpa_supplicant. These terms should be understood to refer to the
+wpa_supplicant. These terms should be understood to refer to the
 lower layer as defined in RFC 4137.
 
 
 \section adding_eap_methods Adding EAP methods
 
 Each EAP method is implemented as a separate module, usually as one C
-file named eap_<name of the method>.c, e.g., eap_md5.c. All EAP
+file named eap_server_<name of the method>.c, e.g., \ref eap_server_md5.c. All EAP
 methods use the same interface between the server state machine and
 method specific functions. This allows new EAP methods to be added
 without modifying the core EAP state machine implementation.
 
 New EAP methods need to be registered by adding them into the build
 (Makefile) and the EAP method registration list in the
-eap_server_register_methods() function of eap_methods.c. Each EAP
+\ref eap_server_register_methods() function of \ref eap_server_methods.c. Each EAP
 method should use a build-time configuration option, e.g., EAP_TLS, in
 order to make it possible to select which of the methods are included
 in the build.
 
-EAP methods must implement the interface defined in eap_i.h. struct
-eap_method defines the needed function pointers that each EAP method
+EAP methods must implement the interface defined in \ref eap_i.h. struct
+\ref eap_method defines the needed function pointers that each EAP method
 must provide. In addition, the EAP type and name are registered using
 this structure. This interface is based on section 4.4 of RFC 4137.
 
 It is recommended that the EAP methods would use generic helper
-functions, eap_msg_alloc() and eap_hdr_validate() when processing
+functions, \ref eap_msg_alloc() and \ref eap_hdr_validate() when processing
 messages. This allows code sharing and can avoid missing some of the
 needed validation steps for received packets. In addition, these
 functions make it easier to change between expanded and legacy EAP
@@ -48,9 +48,9 @@ header, if needed.
 When adding an EAP method that uses a vendor specific EAP type
 (Expanded Type as defined in RFC 3748, Chapter 5.7), the new method
 must be registered by passing vendor id instead of EAP_VENDOR_IETF to
-eap_server_method_alloc(). These methods must not try to emulate
+\ref eap_server_method_alloc(). These methods must not try to emulate
 expanded types by registering a legacy EAP method for type 254. See
-eap_vendor_test.c for an example of an EAP method implementation that
+\ref eap_server_vendor_test.c for an example of an EAP method implementation that
 is implemented as an expanded type.
 
 */
index af3f0be..ea4ab3a 100644 (file)
@@ -255,7 +255,7 @@ Single
 4 0 0 50 -1 2 14 0.0000 4 195 720 1637 2371 hostapd\001
 4 0 0 50 -1 0 12 0.0000 4 180 600 3825 7125 prism54\001
 4 0 0 50 -1 0 12 0.0000 4 180 510 1875 7125 hostap\001
-4 0 0 50 -1 0 12 0.0000 4 135 600 2850 7125 madwifi\001
+4 0 0 50 -1 0 12 0.0000 4 135 600 2850 7125 nl80211\001
 4 0 0 50 -1 0 12 0.0000 4 135 270 4800 7125 bsd\001
 4 0 0 50 -1 0 12 0.0000 4 105 300 6750 7125 test\001
 4 0 0 50 -1 0 12 0.0000 4 135 420 5775 7125 wired\001
index ae778bc..4d2bac8 100644 (file)
@@ -4,58 +4,58 @@
 hostapd implements a control interface that can be used by
 external programs to control the operations of the hostapd
 daemon and to get status information and event notifications. There is
-a small C library, in a form of a single C file, wpa_ctrl.c, that
+a small C library, in a form of a single C file, \ref wpa_ctrl.c, that
 provides helper functions to facilitate the use of the control
 interface. External programs can link this file into them and then use
-the library functions documented in wpa_ctrl.h to interact with
-%wpa_supplicant. This library can also be used with C++. hostapd_cli.c
+the library functions documented in \ref wpa_ctrl.h to interact with
+wpa_supplicant. This library can also be used with C++. \ref hostapd_cli.c
 is an example program using this library.
 
 There are multiple mechanisms for inter-process communication. For
 example, Linux version of hostapd is using UNIX domain sockets for the
-control interface. The use of the functions defined in wpa_ctrl.h can
+control interface. The use of the functions defined in \ref wpa_ctrl.h can
 be used to hide the details of the used IPC from external programs.
 
 
 \section using_ctrl_iface Using the control interface
 
 External programs, e.g., a GUI or a configuration utility, that need to
-communicate with hostapd should link in wpa_ctrl.c. This
+communicate with hostapd should link in \ref wpa_ctrl.c. This
 allows them to use helper functions to open connection to the control
-interface with wpa_ctrl_open() and to send commands with
-wpa_ctrl_request().
+interface with \ref wpa_ctrl_open() and to send commands with
+\ref wpa_ctrl_request().
 
 hostapd uses the control interface for two types of communication:
 commands and unsolicited event messages. Commands are a pair of
 messages, a request from the external program and a response from
-hostapd. These can be executed using wpa_ctrl_request().
+hostapd. These can be executed using \ref wpa_ctrl_request().
 Unsolicited event messages are sent by hostapd to the control
 interface connection without specific request from the external program
 for receiving each message. However, the external program needs to
-attach to the control interface with wpa_ctrl_attach() to receive these
+attach to the control interface with \ref wpa_ctrl_attach() to receive these
 unsolicited messages.
 
 If the control interface connection is used both for commands and
 unsolicited event messages, there is potential for receiving an
 unsolicited message between the command request and response.
-wpa_ctrl_request() caller will need to supply a callback, msg_cb,
+\ref wpa_ctrl_request() caller will need to supply a callback, msg_cb,
 for processing these messages. Often it is easier to open two
-control interface connections by calling wpa_ctrl_open() twice and
+control interface connections by calling \ref wpa_ctrl_open() twice and
 then use one of the connections for commands and the other one for
 unsolicited messages. This way command request/response pairs will
-not be broken by unsolicited messages. wpa_cli is an example of how
+not be broken by unsolicited messages. \ref wpa_cli.c is an example of how
 to use only one connection for both purposes and wpa_gui demonstrates
 how to use two separate connections.
 
 Once the control interface connection is not needed anymore, it should
-be closed by calling wpa_ctrl_close(). If the connection was used for
+be closed by calling \ref wpa_ctrl_close(). If the connection was used for
 unsolicited event messages, it should be first detached by calling
-wpa_ctrl_detach().
+\ref wpa_ctrl_detach().
 
 
 \section ctrl_iface_cmds Control interface commands
 
-Following commands can be used with wpa_ctrl_request():
+Following commands can be used with \ref wpa_ctrl_request():
 
 \subsection ctrl_iface_PING PING
 
index 26dc929..329afea 100644 (file)
@@ -3,17 +3,17 @@
 
 The goal of this documentation and comments in the source code is to
 give enough information for other developers to understand how
-%wpa_supplicant and hostapd have been implemented, how they can be
+wpa_supplicant and hostapd have been implemented, how they can be
 modified, how new drivers can be supported, and how the source code
 can be ported to other operating systems. If any information is
 missing, feel free to contact Jouni Malinen <j@w1.fi> for more
 information. Contributions as patch files are also very welcome at the
 same address. Please note that this software is licensed under the
 BSD license (the one with advertisement clause removed). All
-contributions to %wpa_supplicant and hostapd are expected to use
+contributions to wpa_supplicant and hostapd are expected to use
 compatible licensing terms.
 
-The source code and read-only access to the combined %wpa_supplicant
+The source code and read-only access to the combined wpa_supplicant
 and hostapd Git repository is available from the project home page at
 http://w1.fi/wpa_supplicant/. This developers' documentation is also
 available as a PDF file from
@@ -22,14 +22,14 @@ http://w1.fi/wpa_supplicant/wpa_supplicant-devel.pdf .
 
 \section _wpa_supplicant wpa_supplicant
 
-%wpa_supplicant is a WPA Supplicant for Linux, BSD and Windows with
+wpa_supplicant is a WPA Supplicant for Linux, BSD and Windows with
 support for WPA and WPA2 (IEEE 802.11i / RSN). Supplicant is the IEEE
 802.1X/WPA component that is used in the client stations. It
 implements key negotiation with a WPA Authenticator and it can optionally
 control roaming and IEEE 802.11 authentication/association of the wlan
 driver.
 
-The design goal for %wpa_supplicant was to use hardware, driver, and
+The design goal for wpa_supplicant was to use hardware, driver, and
 OS independent, portable C code for all WPA functionality. The source
 code is divided into separate C files as shown on the \ref
 code_structure "code structure page". All hardware/driver specific
@@ -41,15 +41,15 @@ the \ref porting "porting page".
 EAPOL (IEEE 802.1X) state machines are implemented as a separate
 module that interacts with \ref eap_peer_module "EAP peer implementation".
 In addition to programs aimed at normal production use,
-%wpa_supplicant source tree includes number of \ref testing_tools
+wpa_supplicant source tree includes number of \ref testing_tools
 "testing and development tools" that make it easier to test the
 programs without having to setup a full test setup with wireless
 cards. These tools can also be used to implement automatic test
 suites.
 
-%wpa_supplicant implements a
+wpa_supplicant implements a
 \ref ctrl_iface_page "control interface" that can be used by
-external programs to control the operations of the %wpa_supplicant
+external programs to control the operations of the wpa_supplicant
 daemon and to get status information and event notifications. There is
 a small C library that provides helper functions to facilitate the use of the
 control interface. This library can also be used with C++.
index 6b11e56..c1a81db 100644 (file)
@@ -3,7 +3,7 @@
 
 Wi-Fi Direct functionality is implemented any many levels in the WLAN
 stack from low-level driver operations to high-level GUI design. This
-document covers the parts that can be user by %wpa_supplicant. However,
+document covers the parts that can be user by wpa_supplicant. However,
 it should be noted that alternative designs are also possible, so some
 of the functionality may reside in other components in the system.
 
@@ -19,16 +19,16 @@ P2P module implements higher layer functionality for management P2P
 groups. It takes care of Device Discovery, Service Discovery, Group
 Owner Negotiation, P2P Invitation. In addition, it maintains
 information about neighboring P2P Devices. This module could be used
-in designs that do not use %wpa_supplicant and it could also reside
+in designs that do not use wpa_supplicant and it could also reside
 inside the driver/firmware component. P2P module API is defined in
-src/p2p/p2p.h.
+\ref src/p2p/p2p.h.
 
 Provisioning step of Group Formation is implemented using WPS
-(src/wps/wps.h).
+(\ref src/wps/wps.h).
 
-%wpa_supplicant includes code in interact with both the P2P module
-(wpa_supplicant/p2p_supplicant.c) and WPS
-(wpa_supplicant/wps_supplicant.c). The driver operations are passed
+wpa_supplicant includes code in interact with both the P2P module
+(\ref wpa_supplicant/p2p_supplicant.c) and WPS
+(\ref wpa_supplicant/wps_supplicant.c). The driver operations are passed
 through these files, i.e., core P2P or WPS code does not interact
 directly with the driver interface.
 
@@ -48,7 +48,7 @@ functionality.
 \subsection p2p_arch_mac80211 P2P architecture with Linux/mac80211/ath9k
 
 An architecture where the P2P module resides inside the
-%wpa_supplicant process is used with Linux mac80211-based drivers,
+wpa_supplicant process is used with Linux mac80211-based drivers,
 e.g., ath9k. The following diagram shows the main components related
 to P2P functionality in such an architecture.
 
@@ -82,7 +82,7 @@ and the alternating Listen and Search states within Find).
 
 \subsection p2p_module_api P2P module API
 
-P2P module API is defined in src/p2p/p2p.h. The API consists of
+P2P module API is defined in \ref src/p2p/p2p.h. The API consists of
 functions for requesting operations and for providing event
 notifications. Similar set of callback functions are configured with
 struct p2p_config to provide callback functions that P2P module can
@@ -92,51 +92,51 @@ used for P2P related operations.
 
 These are the main functions for an upper layer management entity to
 request P2P operations:
-- p2p_find()
-- p2p_stop_find()
-- p2p_listen()
-- p2p_connect()
-- p2p_reject()
-- p2p_prov_disc_req()
-- p2p_sd_request()
-- p2p_sd_cancel_request()
-- p2p_sd_response()
-- p2p_sd_service_update()
-- p2p_invite()
+- \ref p2p_find()
+- \ref p2p_stop_find()
+- \ref p2p_listen()
+- \ref p2p_connect()
+- \ref p2p_reject()
+- \ref p2p_prov_disc_req()
+- \ref p2p_sd_request()
+- \ref p2p_sd_cancel_request()
+- \ref p2p_sd_response()
+- \ref p2p_sd_service_update()
+- \ref p2p_invite()
 
 These are the main callback functions for P2P module to provide event
 notifications to the upper layer management entity:
 
-- p2p_config::dev_found()
-- p2p_config::go_neg_req_rx()
-- p2p_config::go_neg_completed()
-- p2p_config::sd_request()
-- p2p_config::sd_response()
-- p2p_config::prov_disc_req()
-- p2p_config::prov_disc_resp()
-- p2p_config::invitation_process()
-- p2p_config::invitation_received()
-- p2p_config::invitation_result()
+- \ref p2p_config::dev_found()
+- \ref p2p_config::go_neg_req_rx()
+- \ref p2p_config::go_neg_completed()
+- \ref p2p_config::sd_request()
+- \ref p2p_config::sd_response()
+- \ref p2p_config::prov_disc_req()
+- \ref p2p_config::prov_disc_resp()
+- \ref p2p_config::invitation_process()
+- \ref p2p_config::invitation_received()
+- \ref p2p_config::invitation_result()
 
 The P2P module uses following functions to request lower layer driver
 operations:
 
-- p2p_config::p2p_scan()
-- p2p_config::send_probe_resp()
-- p2p_config::send_action()
-- p2p_config::send_action_done()
-- p2p_config::start_listen()
-- p2p_config::stop_listen()
+- \ref p2p_config::p2p_scan()
+- \ref p2p_config::send_probe_resp()
+- \ref p2p_config::send_action()
+- \ref p2p_config::send_action_done()
+- \ref p2p_config::start_listen()
+- \ref p2p_config::stop_listen()
 
 Events from lower layer driver operations are delivered to the P2P
 module with following functions:
 
-- p2p_probe_req_rx()
-- p2p_rx_action()
-- p2p_scan_res_handler()
-- p2p_scan_res_handled()
-- p2p_send_action_cb()
-- p2p_listen_cb()
+- \ref p2p_probe_req_rx()
+- \ref p2p_rx_action()
+- \ref p2p_scan_res_handler()
+- \ref p2p_scan_res_handled()
+- \ref p2p_send_action_cb()
+- \ref p2p_listen_cb()
 
 In addition to the per-device state, the P2P module maintains
 per-group state for group owners. This is initialized with a call to
@@ -144,63 +144,36 @@ p2p_group_init() when a group is created and deinitialized with
 p2p_group_deinit(). The upper layer GO management entity uses
 following functions to interact with the P2P per-group state:
 
-- p2p_group_notif_assoc()
-- p2p_group_notif_disassoc()
-- p2p_group_notif_formation_done()
-- p2p_group_match_dev_type()
+- \ref p2p_group_notif_assoc()
+- \ref p2p_group_notif_disassoc()
+- \ref p2p_group_notif_formation_done()
+- \ref p2p_group_match_dev_type()
 
 The P2P module will use following callback function to update P2P IE
 for GO Beacon and Probe Response frames:
 
-- p2p_group_config::ie_update()
+- \ref p2p_group_config::ie_update()
 
 
 \section p2p_driver P2P driver operations (low-level interface)
 
 The following driver wrapper functions are needed for P2P in addition
 to the standard station/AP mode operations when the P2P module resides
-within %wpa_supplicant:
-- wpa_driver_ops::if_add()
-- wpa_driver_ops::if_remove()
-- wpa_driver_ops::alloc_interface_addr()
-- wpa_driver_ops::release_interface_addr()
-- wpa_driver_ops::remain_on_channel()
-- wpa_driver_ops::cancel_remain_on_channel()
-- wpa_driver_ops::send_action()
-- wpa_driver_ops::probe_req_report()
-- wpa_driver_ops::disable_11b_rates()
+within wpa_supplicant:
+- \ref wpa_driver_ops::if_add()
+- \ref wpa_driver_ops::if_remove()
+- \ref wpa_driver_ops::remain_on_channel()
+- \ref wpa_driver_ops::cancel_remain_on_channel()
+- \ref wpa_driver_ops::send_action()
+- \ref wpa_driver_ops::probe_req_report()
 
 The following driver wrapper events are needed for P2P in addition to
 the standard station/AP mode events when the P2P module resides within
-%wpa_supplicant:
-- wpa_event_type::EVENT_RX_ACTION
-- wpa_event_type::EVENT_REMAIN_ON_CHANNEL
-- wpa_event_type::EVENT_CANCEL_REMAIN_ON_CHANNEL
-- wpa_event_type::EVENT_RX_PROBE_REQ
-
-The following driver wrapper functions are needed for P2P in addition
-to the standard station/AP mode operations when the P2P module resides
-in the driver or firmware:
-- wpa_driver_ops::if_add()
-- wpa_driver_ops::if_remove()
-- wpa_driver_ops::alloc_interface_addr()
-- wpa_driver_ops::release_interface_addr()
-- wpa_driver_ops::disable_11b_rates()
-- wpa_driver_ops::p2p_find()
-- wpa_driver_ops::p2p_stop_find()
-- wpa_driver_ops::p2p_listen()
-- wpa_driver_ops::p2p_connect()
-- wpa_driver_ops::p2p_reject()
-- wpa_driver_ops::wps_success_cb()
-- wpa_driver_ops::p2p_group_formation_failed()
-- wpa_driver_ops::p2p_set_params()
-
-The following driver wrapper events are needed for P2P in addition to
-the standard station/AP mode events when the P2P module resides in the
-driver or firmware:
-- wpa_event_type::EVENT_P2P_DEV_FOUND
-- wpa_event_type::EVENT_P2P_GO_NEG_REQ_RX
-- wpa_event_type::EVENT_P2P_GO_NEG_COMPLETED
+wpa_supplicant:
+- \ref wpa_event_type::EVENT_RX_MGMT
+- \ref wpa_event_type::EVENT_REMAIN_ON_CHANNEL
+- \ref wpa_event_type::EVENT_CANCEL_REMAIN_ON_CHANNEL
+- \ref wpa_event_type::EVENT_RX_PROBE_REQ
 
 
 \section p2p_go_neg P2P device discovery and group formation
@@ -212,40 +185,40 @@ the glue code outside the P2P module depends on the architecture used
 in the system.
 
 An upper layer management entity starts P2P device discovery by
-calling p2p_find(). The P2P module start the discovery by requesting a
-full scan to be completed by calling p2p_config::p2p_scan(). Results
-from the scan will be reported by calling p2p_scan_res_handler() and
+calling \ref p2p_find(). The P2P module start the discovery by requesting a
+full scan to be completed by calling \ref p2p_config::p2p_scan(). Results
+from the scan will be reported by calling \ref p2p_scan_res_handler() and
 after last result, the scan result processing is terminated with a
-call to p2p_scan_res_handled(). The P2P peers that are found during
-the full scan are reported with the p2p_config::dev_found() callback.
+call to \ref p2p_scan_res_handled(). The P2P peers that are found during
+the full scan are reported with the \ref p2p_config::dev_found() callback.
 
 After the full scan, P2P module start alternating between Listen and
 Search states until the device discovery operation times out or
-terminated, e.g., with a call to p2p_stop_find().
+terminated, e.g., with a call to \ref p2p_stop_find().
 
 When going into the Listen state, the P2P module requests the driver
 to be configured to be awake on the listen channel with a call to
-p2p_config::start_listen(). The glue code using the P2P module may
+\ref p2p_config::start_listen(). The glue code using the P2P module may
 implement this, e.g., by using remain-on-channel low-level driver
 functionality for off-channel operation. Once the driver is available
 on the requested channel, notification of this is delivered by calling
-p2p_listen_cb(). The Probe Request frames that are received during the
+\ref p2p_listen_cb(). The Probe Request frames that are received during the
 Listen period are delivered to the P2P module by calling
-p2p_config::p2p_probe_req_rx() and P2P module request a response to
-these to be sent by using p2p_config::send_probe_resp() callback
+\ref p2p_config::p2p_probe_req_rx() and P2P module request a response to
+these to be sent by using \ref p2p_config::send_probe_resp() callback
 function. If a group owner negotiation from another P2P device is
 received during the device discovery phase, that is indicated to the
-upper layer code with the p2p_config::go_neg_req_tx() callback.
+upper layer code with the \ref p2p_config::go_neg_req_tx() callback.
 
 The Search state is implemented by using the normal scan interface,
-i.e., the P2P module will call p2p_config::p2p_scan() just like in the
+i.e., the P2P module will call \ref p2p_config::p2p_scan() just like in the
 full scan phase described. Similarly, scan results from the search
 operation will be delivered to the P2P module using the
-p2p_scan_res_handler() and p2p_scan_res_handled() functions.
+\ref p2p_scan_res_handler() and \ref p2p_scan_res_handled() functions.
 
 Once the upper layer management entity has found a peer with which it
 wants to connect by forming a new group, it initiates group owner
-negotiation by calling p2p_connect(). Before doing this, the upper
+negotiation by calling \ref p2p_connect(). Before doing this, the upper
 layer code is responsible for asking the user to provide the PIN to be
 used during the provisioning step with the peer or the push button
 press for PBC mode. The glue code will need to figure out the intended
@@ -255,25 +228,25 @@ started.
 Optional Provision Discovery mechanism can be used to request the peer
 to display a PIN for the local device to enter (and vice versa). Upper
 layer management entity can request the specific mechanism by calling
-p2p_prov_disc_req(). The response to this will be reported with the
-p2p_config::prov_disc_resp() callback. If the peer device started
+\ref p2p_prov_disc_req(). The response to this will be reported with the
+\ref p2p_config::prov_disc_resp() callback. If the peer device started
 Provision Discovery, an accepted request will be reported with the
-p2p_config::prov_disc_req() callback. The P2P module will
+\ref p2p_config::prov_disc_req() callback. The P2P module will
 automatically accept the Provision Discovery for display and keypad
 methods, but it is up to the upper layer manegement entity to actually
-generate the PIN and to configure it with following p2p_connect() call
+generate the PIN and to configure it with following \ref p2p_connect() call
 to actually authorize the connection.
 
-The P2P module will use p2p_config::send_action() callback to request
+The P2P module will use \ref p2p_config::send_action() callback to request
 lower layer code to transmit an Action frame during group owner
-negotiation. p2p_send_action_cb() is used to report the result of
+negotiation. \ref p2p_send_action_cb() is used to report the result of
 transmission. If the peer is not reachable, the P2P module will try to
 find it by alternating between Action frame send and Listen
 states. The Listen state for this phase will be used similarly to the
 Listen state during device discovery as described above.
 
 Once the group owner negotiation has been completed, its results will
-be reported with the p2p_config::go_neg_completed() callback. The
+be reported with the \ref p2p_config::go_neg_completed() callback. The
 upper layer management code or the glue code using the P2P module API
 is responsible for creating a new group interface and starting
 provisioning step at this point by configuring WPS Registrar or
@@ -282,15 +255,15 @@ results. The upper layer code is also responsible for timing out WPS
 provisioning if it cannot be completed in 15 seconds.
 
 Successful completion of the WPS provisioning is reported with a call
-to p2p_wps_success_cb(). The P2P module will clear its group formation
+to \ref p2p_wps_success_cb(). The P2P module will clear its group formation
 state at this point and allows new group formation attempts to be
 started. The upper layer management code is responsible for configuring
 the GO to accept associations from devices and the client to connect to
 the GO with the provisioned credentials. GO is also responsible for
-calling p2p_group_notif_formation_done() as described below.
+calling \ref p2p_group_notif_formation_done() as described below.
 
 If the WPS provisioning step fails or times out, this is reported with
-a call to p2p_group_formation_failed(). The P2P module will clear its
+a call to \ref p2p_group_formation_failed(). The P2P module will clear its
 group formation state at this point and allows new group formation
 attempts to be started. The upper layer management code is responsible
 for removing the group interface for the failed group.
@@ -313,23 +286,23 @@ Response TLV(s) for responses.
 \subsection p2p_sd_query Quering services of peers
 
 Service discovery is implemented by processing pending queries as a
-part of the device discovery phase. p2p_sd_request() function is used
+part of the device discovery phase. \ref p2p_sd_request() function is used
 to schedule service discovery queries to a specific peer or to all
-discovered peers. p2p_sd_cancel_request() can be used to cancel a
+discovered peers. \ref p2p_sd_cancel_request() can be used to cancel a
 scheduled query. Queries that are specific to a single peer will be
 removed automatically after the response has been received.
 
 After the service discovery queries have been queued, device discovery
-is started with a call to p2p_find(). The pending service discovery
+is started with a call to \ref p2p_find(). The pending service discovery
 queries are then sent whenever a peer is discovered during the find
 operation. Responses to the queries will be reported with the
-p2p_config::sd_response() callback.
+\ref p2p_config::sd_response() callback.
 
 \subsection p2p_sd_response Replying to service discovery queries from peers
 
 The received service discovery requests will be indicated with the
-p2p_config::sd_request() callback. The response to the query is sent
-by calling p2p_sd_response().
+\ref p2p_config::sd_request() callback. The response to the query is sent
+by calling \ref p2p_sd_response().
 
 \subsection p2p_sd_indicator Service update indicator
 
@@ -338,9 +311,9 @@ changes in available services. This works by incrementing Service
 Update Indicator value whenever there is a change in the
 services. This value is included in all SD request and response
 frames. The value received from the peers will be included in the
-p2p_config::sd_request() and p2p_config::sd_response() callbacks. The
+\ref p2p_config::sd_request() and \ref p2p_config::sd_response() callbacks. The
 value to be sent to the peers is incremented with a call to
-p2p_sd_service_update() whenever availibility of the local services
+\ref p2p_sd_service_update() whenever availibility of the local services
 changes.
 
 
@@ -353,29 +326,29 @@ code outside the P2P module depends on the architecture used in the
 system.
 
 When a P2P group interface is created in group owner role, per-group
-data is initialized with p2p_group_init(). This call provides a
+data is initialized with \ref p2p_group_init(). This call provides a
 pointer to the per-device P2P module context and configures the
-per-group operation. The configured p2p_group_config::ie_update()
+per-group operation. The configured \ref p2p_group_config::ie_update()
 callback is used to set the initial P2P IE for Beacon and Probe
 Response frames in the group owner. The AP mode implementation may use
 this information to add IEs into the frames.
 
 Once the group formation has been completed (or if it is skipped in
-case of manual group setup), p2p_group_notif_formation_done() is
+case of manual group setup), \ref p2p_group_notif_formation_done() is
 called. This will allow the P2P module to update the P2P IE for
 Beacon and Probe Response frames.
 
 The SME/MLME code that managements IEEE 802.11 association processing
 needs to inform P2P module whenever a P2P client associates or
 disassociates with the group. This is done by calling
-p2p_group_notif_assoc() and p2p_group_notif_disassoc(). The P2P module
+\ref p2p_group_notif_assoc() and \ref p2p_group_notif_disassoc(). The P2P module
 manages a list of group members and updates the P2P Group Information
 subelement in the P2P IE based on the information from the P2P
-clients. The p2p_group_config::ie_update() callback is used whenever
+clients. The \ref p2p_group_config::ie_update() callback is used whenever
 the P2P IE in Probe Response frames needs to be changed.
 
 The SME/MLME code that takes care of replying to Probe Request frames
-can use p2p_group_match_dev_type() to check whether the Probe Request
+can use \ref p2p_group_match_dev_type() to check whether the Probe Request
 frame request a reply only from groups that include a specific device
 type in one of the clients or GO. A match will be reported if the
 Probe Request does not request a specific device type, so this
@@ -383,13 +356,13 @@ function can be used to filter or received Probe Request frames and
 only the ones that result in non-zero return value need to be replied.
 
 When the P2P group interface for GO role is removed,
-p2p_group_deinit() is used to deinitialize the per-group P2P module
+\ref p2p_group_deinit() is used to deinitialize the per-group P2P module
 state.
 
 
 \section p2p_ctrl_iface P2P control interface
 
-%wpa_supplicant \ref ctrl_iface_page "control interface" can be used
+wpa_supplicant \ref ctrl_iface_page "control interface" can be used
 to manage P2P functionality from an external program (e.g., a GUI or a
 system configuration manager). This interface can be used directly
 through the control interface backend mechanism (e.g., local domain
@@ -438,11 +411,11 @@ control interface commands and events can be used.
 
 \subsection p2p_wpa_cli wpa_cli example
 
-wpa_cli can be used to control %wpa_supplicant in interactive
+wpa_cli can be used to control wpa_supplicant in interactive
 mode. The following sessions show examples of commands used for
 device discovery and group formation. The lines starting with "> " are
 commands from the user (followed by command result indication) and
-lines starting with "<2>" are event messages from %wpa_supplicant.
+lines starting with "<2>" are event messages from wpa_supplicant.
 
 P2P device "Wireless Client":
 
index 7ea6a34..b4b78ef 100644 (file)
@@ -1,14 +1,14 @@
 /**
 \page porting Porting to different target boards and operating systems
 
-%wpa_supplicant was designed to be easily portable to different
+wpa_supplicant was designed to be easily portable to different
 hardware (board, CPU) and software (OS, drivers) targets. It is
 already used with number of operating systems and numerous wireless
-card models and drivers. The main %wpa_supplicant repository includes
+card models and drivers. The main wpa_supplicant repository includes
 support for Linux, FreeBSD, and Windows. In addition, the code has been
 ported to number of other operating systems like VxWorks, PalmOS,
 Windows CE, and Windows Mobile. On the hardware
-side, %wpa_supplicant is used on various systems: desktops, laptops,
+side, wpa_supplicant is used on various systems: desktops, laptops,
 PDAs, and embedded devices with CPUs including x86, PowerPC,
 arm/xscale, and MIPS. Both big and little endian configurations are
 supported.
@@ -16,48 +16,48 @@ supported.
 
 \section ansi_c_extra Extra functions on top of ANSI C
 
-%wpa_supplicant is mostly using ANSI C functions that are available on
+wpa_supplicant is mostly using ANSI C functions that are available on
 most targets. However, couple of additional functions that are common
 on modern UNIX systems are used. Number of these are listed with
-prototypes in common.h (the \verbatim #ifdef CONFIG_ANSI_C_EXTRA \endverbatim
+prototypes in \ref common.h (the \verbatim #ifdef CONFIG_ANSI_C_EXTRA \endverbatim
 block). These functions may need to be implemented or at least defined
 as macros to native functions in the target OS or C library.
 
 Many of the common ANSI C functions are used through a wrapper
-definitions in os.h to allow these to be replaced easily with a
+definitions in \ref os.h to allow these to be replaced easily with a
 platform specific version in case standard C libraries are not
-available. In addition, os.h defines couple of common platform
-specific functions that are implemented in os_unix.c for UNIX like
-targets and in os_win32.c for Win32 API. If the target platform does
+available. In addition, \ref os.h defines couple of common platform
+specific functions that are implemented in \ref os_unix.c for UNIX like
+targets and in \ref os_win32.c for Win32 API. If the target platform does
 not support either of these examples, a new os_*.c file may need to be
 added.
 
 Unless OS_NO_C_LIB_DEFINES is defined, the standard ANSI C and POSIX
 functions are used by defining the os_*() wrappers to use them
 directly in order to avoid extra cost in size and speed. If the target
-platform needs different versions of the functions, os.h can be
+platform needs different versions of the functions, \ref os.h can be
 modified to define the suitable macros or alternatively,
 OS_NO_C_LIB_DEFINES may be defined for the build and the wrapper
 functions can then be implemented in a new os_*.c wrapper file.
 
-common.h defines number of helper macros for handling integers of
+\ref common.h defines number of helper macros for handling integers of
 different size and byte order. Suitable version of these definitions
 may need to be added for the target platform.
 
 
 \section configuration_backend Configuration backend
 
-%wpa_supplicant implements a configuration interface that allows the
+wpa_supplicant implements a configuration interface that allows the
 backend to be easily replaced in order to read configuration data from
-a suitable source depending on the target platform. config.c
+a suitable source depending on the target platform. \ref config.c
 implements the generic code that can be shared with all configuration
 backends. Each backend is implemented in its own config_*.c file.
 
-The included config_file.c backend uses a text file for configuration
-and config_winreg.c uses Windows registry. These files can be used as
+The included \ref config_file.c backend uses a text file for configuration
+and \ref config_winreg.c uses Windows registry. These files can be used as
 an example for a new configuration backend if the target platform uses
 different mechanism for configuration parameters. In addition,
-config_none.c can be used as an empty starting point for building a
+\ref config_none.c can be used as an empty starting point for building a
 new configuration backend.
 
 
@@ -69,19 +69,19 @@ adding a new driver interface module or modifying an existing module
 (driver_*.c) if the new target is similar to one of them. \ref
 driver_wrapper "Driver wrapper implementation" describes the details
 of the driver interface and discusses the tasks involved in porting
-this part of %wpa_supplicant.
+this part of wpa_supplicant.
 
 
 \section l2_packet_porting l2_packet (link layer access)
 
-%wpa_supplicant needs to have access to sending and receiving layer 2
+wpa_supplicant needs to have access to sending and receiving layer 2
 (link layer) packets with two Ethertypes: EAP-over-LAN (EAPOL) 0x888e
-and RSN pre-authentication 0x88c7. l2_packet.h defines the interfaces
-used for this in the core %wpa_supplicant implementation.
+and RSN pre-authentication 0x88c7. \ref l2_packet.h defines the interfaces
+used for this in the core wpa_supplicant implementation.
 
 If the target operating system supports a generic mechanism for link
 layer access, that is likely the best mechanism for providing the
-needed functionality for %wpa_supplicant. Linux packet socket is an
+needed functionality for wpa_supplicant. Linux packet socket is an
 example of such a generic mechanism. If this is not available, a
 separate interface may need to be implemented to the network stack or
 driver. This is usually an intermediate or protocol driver that is
@@ -89,26 +89,27 @@ operating between the device driver and the OS network stack. If such
 a mechanism is not feasible, the interface can also be implemented
 directly in the device driver.
 
-The main %wpa_supplicant repository includes l2_packet implementations
-for Linux using packet sockets (l2_packet_linux.c), more portable
-version using libpcap/libdnet libraries (l2_packet_pcap.c; this
+The main wpa_supplicant repository includes l2_packet implementations
+for Linux using packet sockets (\ref l2_packet_linux.c), more portable
+version using libpcap/libdnet libraries (\ref l2_packet_pcap.c; this
 supports WinPcap, too), and FreeBSD specific version of libpcap
-interface (l2_packet_freebsd.c).
+interface (\ref l2_packet_freebsd.c).
 
 If the target operating system is supported by libpcap (receiving) and
-libdnet (sending), l2_packet_pcap.c can likely be used with minimal or
+libdnet (sending), \ref l2_packet_pcap.c can likely be used with minimal or
 no changes. If this is not a case or a proprietary interface for link
 layer is required, a new l2_packet module may need to be
-added. Alternatively, struct wpa_driver_ops::send_eapol() handler can
+added. Alternatively, for hostapd,
+struct \ref wpa_driver_ops::hapd_send_eapol() handler can
 be used to override the l2_packet library if the link layer access is
 integrated with the driver interface implementation.
 
 
 \section eloop_porting Event loop
 
-%wpa_supplicant uses a single process/thread model and an event loop
+wpa_supplicant uses a single process/thread model and an event loop
 to provide callbacks on events (registered timeout, received packet,
-signal). eloop.h defines the event loop interface. eloop.c is an
+signal). eloop.h defines the event loop interface. \ref eloop.c is an
 implementation of such an event loop using select() and sockets. This
 is suitable for most UNIX/POSIX systems. When porting to other
 operating systems, it may be necessary to replace that implementation
@@ -117,64 +118,64 @@ with OS specific mechanisms that provide similar functionality.
 
 \section ctrl_iface_porting Control interface
 
-%wpa_supplicant uses a \ref ctrl_iface_page "control interface"
+wpa_supplicant uses a \ref ctrl_iface_page "control interface"
 to allow external processed
 to get status information and to control the operations. Currently,
 this is implemented with socket based communication; both UNIX domain
 sockets and UDP sockets are supported. If the target OS does not
 support sockets, this interface will likely need to be modified to use
 another mechanism like message queues. The control interface is
-optional component, so it is also possible to run %wpa_supplicant
+optional component, so it is also possible to run wpa_supplicant
 without porting this part.
 
-The %wpa_supplicant side of the control interface is implemented in
-ctrl_iface.c. Matching client side is implemented as a control
-interface library in wpa_ctrl.c.
+The wpa_supplicant side of the control interface is implemented in
+\ref wpa_supplicant/ctrl_iface.c. Matching client side is implemented as a control
+interface library in \ref wpa_ctrl.c.
 
 
 \section entry_point Program entry point
 
-%wpa_supplicant defines a set of functions that can be used to
+wpa_supplicant defines a set of functions that can be used to
 initialize main supplicant processing. Each operating system has a
 mechanism for starting new processing or threads. This is usually a
 function with a specific set of arguments and calling convention. This
-function is responsible on initializing %wpa_supplicant.
+function is responsible on initializing wpa_supplicant.
 
-main.c includes an entry point for UNIX-like operating system, i.e.,
-main() function that uses command line arguments for setting
-parameters for %wpa_supplicant. When porting to other operating
-systems, similar OS-specific entry point implementation is needed. It
-can be implemented in a new file that is then linked with
-%wpa_supplicant instead of main.o. main.c is also a good example on
-how the initialization process should be done.
+\ref wpa_supplicant/main.c includes an entry point for UNIX-like
+operating system, i.e., main() function that uses command line arguments
+for setting parameters for wpa_supplicant. When porting to other
+operating systems, similar OS-specific entry point implementation is
+needed. It can be implemented in a new file that is then linked with
+wpa_supplicant instead of main.o. \ref wpa_supplicant/main.c is also a
+good example on how the initialization process should be done.
 
 The supplicant initialization functions are defined in
-wpa_supplicant_i.h. In most cases, the entry point function should
+\ref wpa_supplicant_i.h. In most cases, the entry point function should
 start by fetching configuration parameters. After this, a global
-%wpa_supplicant context is initialized with a call to
-wpa_supplicant_init(). After this, existing network interfaces can be
-added with wpa_supplicant_add_iface(). wpa_supplicant_run() is then
+wpa_supplicant context is initialized with a call to
+\ref wpa_supplicant_init(). After this, existing network interfaces can be
+added with \ref wpa_supplicant_add_iface(). \ref wpa_supplicant_run() is then
 used to start the main event loop. Once this returns at program
-termination time, wpa_supplicant_deinit() is used to release global
+termination time, \ref wpa_supplicant_deinit() is used to release global
 context data.
 
-wpa_supplicant_add_iface() and wpa_supplicant_remove_iface() can be
+\ref wpa_supplicant_add_iface() and \ref wpa_supplicant_remove_iface() can be
 used dynamically to add and remove interfaces based on when
-%wpa_supplicant processing is needed for them. This can be done, e.g.,
+wpa_supplicant processing is needed for them. This can be done, e.g.,
 when hotplug network adapters are being inserted and ejected. It is
 also possible to do this when a network interface is being
-enabled/disabled if it is desirable that %wpa_supplicant processing
+enabled/disabled if it is desirable that wpa_supplicant processing
 for the interface is fully enabled/disabled at the same time.
 
 
 \section simple_build Simple build example
 
 One way to start a porting project is to begin with a very simple
-build of %wpa_supplicant with WPA-PSK support and once that is
+build of wpa_supplicant with WPA-PSK support and once that is
 building correctly, start adding features.
 
 Following command can be used to build very simple version of
-%wpa_supplicant:
+wpa_supplicant:
 
 \verbatim
 cc -o wpa_supplicant config.c eloop.c common.c md5.c rc4.c sha1.c \
@@ -192,7 +193,7 @@ including the listed C files.
 Once this version can be build successfully, the end result can be
 made functional by adding a proper program entry point (main*.c),
 driver interface (driver_*.c and matching CONFIG_DRIVER_* define for
-registration in drivers.c), configuration parser/writer (config_*.c),
+registration in \ref drivers.c), configuration parser/writer (config_*.c),
 and layer 2 packet access implementation (l2_packet_*.c). After these
 components have been added, the end result should be a working
 WPA/WPA2-PSK enabled supplicant.
index 01db0b6..d126524 100644 (file)
@@ -3,11 +3,10 @@
 
 [ \ref eapol_test "eapol_test" |
 \ref preauth_test "preauth_test" |
-\ref driver_test "driver_test" |
 \ref unit_tests "Unit tests" |
 \ref wpa_trace "Tracing code" ]
 
-%wpa_supplicant source tree includes number of testing and development
+wpa_supplicant source tree includes number of testing and development
 tools that make it easier to test the programs without having to setup
 a full test setup with wireless cards. In addition, these tools can be
 used to implement automatic tests suites.
@@ -15,7 +14,7 @@ used to implement automatic tests suites.
 \section eapol_test eapol_test - EAP peer and RADIUS client testing
 
 eapol_test is a program that links together the same EAP peer
-implementation that %wpa_supplicant is using and the RADIUS
+implementation that wpa_supplicant is using and the RADIUS
 authentication client code from hostapd. In addition, it has minimal
 glue code to combine these two components in similar ways to IEEE
 802.1X/EAPOL Authenticator state machines. In other words, it
@@ -35,7 +34,7 @@ could be used to implement an automated regression test suite for a
 RADIUS authentication server.
 
 eapol_test uses the same build time configuration file, .config, as
-%wpa_supplicant. This file is used to select which EAP methods are
+wpa_supplicant. This file is used to select which EAP methods are
 included in eapol_test. This program is not built with the default
 Makefile target, so a separate make command needs to be used to
 compile the tool:
@@ -78,7 +77,7 @@ tries to complete EAP authentication based on the network
 configuration from test.conf against the RADIUS server running on the
 local host. A re-authentication is triggered to test fast
 re-authentication. The configuration file uses the same format for
-network blocks as %wpa_supplicant.
+network blocks as wpa_supplicant.
 
 
 \section preauth_test preauth_test - WPA2 pre-authentication and EAP peer testing
@@ -86,8 +85,8 @@ network blocks as %wpa_supplicant.
 preauth_test is similar to eapol_test in the sense that in combines
 EAP peer implementation with something else, in this case, with WPA2
 pre-authentication. This tool can be used to test pre-authentication
-based on the code that %wpa_supplicant is using. As such, it tests
-both the %wpa_supplicant implementation and the functionality of an
+based on the code that wpa_supplicant is using. As such, it tests
+both the wpa_supplicant implementation and the functionality of an
 access point.
 
 preauth_test is built with:
@@ -111,170 +110,9 @@ pre-authentication with AP using BSSID 02:11:22:33:44:55. The
 pre-authentication packets would be sent using the eth0 interface.
 
 
-\section driver_test driver_test - driver interface for testing wpa_supplicant
-
-%wpa_supplicant was designed to support number of different ways to
-communicate with a network device driver. This design uses \ref
-driver_wrapper "driver interface API" and number of driver interface
-implementations. One of these is driver_test.c, i.e., a test driver
-interface that is actually not using any drivers. Instead, it provides
-a mechanism for running %wpa_supplicant without having to have a
-device driver or wireless LAN hardware for that matter.
-
-driver_test can be used to talk directly with hostapd's driver_test
-component to create a test setup where one or more clients and access
-points can be tested within one test host and without having to have
-multiple wireless cards. This makes it easier to test the core code in
-%wpa_supplicant, and hostapd for that matter. Since driver_test uses
-the same driver API than any other driver interface implementation,
-the core code of %wpa_supplicant and hostapd can be tested with the
-same coverage as one would get when using real wireless cards. The
-only area that is not tested is the driver interface implementation
-(driver_*.c).
-
-Having the possibility to use simulated network components makes it
-much easier to do development testing while adding new features and to
-reproduce reported bugs. As such, it is often easiest to just do most
-of the development and bug fixing without using real hardware. Once
-the driver_test setup has been used to implement a new feature or fix
-a bug, the end result can be verified with wireless LAN cards. In many
-cases, this may even be unnecessary, depending on what area the
-feature/bug is relating to. Of course, changes to driver interfaces
-will still require use of real hardware.
-
-Since multiple components can be run within a single host, testing of
-complex network configuration, e.g., large number of clients
-association with an access point, becomes quite easy. All the tests
-can also be automated without having to resort to complex test setup
-using remote access to multiple computers.
-
-driver_test can be included in the %wpa_supplicant build in the same
-way as any other driver interface, i.e., by adding the following line
-into .config:
-
-\verbatim
-CONFIG_DRIVER_TEST=y
-\endverbatim
-
-When running %wpa_supplicant, the test interface is selected by using
-\a -Dtest command line argument. The interface name (\a -i argument)
-can be selected arbitrarily, i.e., it does not need to match with any
-existing network interface. The interface name is used to generate a
-MAC address, so when using multiple clients, each should use a
-different interface, e.g., \a sta1, \a sta2, and so on.
-
-%wpa_supplicant and hostapd are configured in the same way as they
-would be for normal use. Following example shows a simple test setup
-for WPA-PSK.
-
-hostapd is configured with following psk-test.conf configuration file:
-
-\verbatim
-driver=test
-
-interface=ap1
-logger_stdout=-1
-logger_stdout_level=0
-debug=2
-dump_file=/tmp/hostapd.dump
-
-test_socket=/tmp/Test/ap1
-
-ssid=jkm-test-psk
-
-wpa=1
-wpa_key_mgmt=WPA-PSK
-wpa_pairwise=TKIP
-wpa_passphrase=12345678
-\endverbatim
-
-and started with following command:
-
-\verbatim
-hostapd psk-test.conf
-\endverbatim
-
-%wpa_supplicant uses following configuration file:
-
-\verbatim
-driver_param=test_socket=/tmp/Test/ap1
-
-network={
-    ssid="jkm-test-psk"
-    key_mgmt=WPA-PSK
-    psk="12345678"
-}
-\endverbatim
-
-%wpa_supplicant can then be started with following command:
-
-\verbatim
-wpa_supplicant -Dtest -cpsk-test.conf -ista1 -ddK
-\endverbatim
-
-If run without debug information, i.e., with
-
-\verbatim
-wpa_supplicant -Dtest -cpsk-test.conf -ista1
-\endverbatim
-
-%wpa_supplicant completes authentication and prints following events:
-
-\verbatim
-Trying to associate with 02:b8:a6:62:08:5a (SSID='jkm-test-psk' freq=0 MHz)
-Associated with 02:b8:a6:62:08:5a
-WPA: Key negotiation completed with 02:b8:a6:62:08:5a [PTK=TKIP GTK=TKIP]
-CTRL-EVENT-CONNECTED - Connection to 02:b8:a6:62:08:5a completed (auth)
-\endverbatim
-
-If test setup is using multiple clients, it is possible to run
-multiple %wpa_supplicant processes. Alternatively, the support for
-multiple interfaces can be used with just one process to save some
-resources on single-CPU systems. For example, following command runs
-two clients:
-
-\verbatim
-./wpa_supplicant -Dtest -cpsk-test.conf -ista1 \
-       -N -Dtest -cpsk-test.conf -ista2
-\endverbatim
-
-This shows following event log:
-
-\verbatim
-Trying to associate with 02:b8:a6:62:08:5a (SSID='jkm-test-psk' freq=0 MHz)
-Associated with 02:b8:a6:62:08:5a
-WPA: Key negotiation completed with 02:b8:a6:62:08:5a [PTK=TKIP GTK=TKIP]
-CTRL-EVENT-CONNECTED - Connection to 02:b8:a6:62:08:5a completed (auth)
-Trying to associate with 02:b8:a6:62:08:5a (SSID='jkm-test-psk' freq=0 MHz)
-Associated with 02:b8:a6:62:08:5a
-WPA: Key negotiation completed with 02:b8:a6:62:08:5a [PTK=TKIP GTK=TKIP]
-CTRL-EVENT-CONNECTED - Connection to 02:b8:a6:62:08:5a completed (auth)
-\endverbatim
-
-hostapd shows this with following events:
-
-\verbatim
-ap1: STA 02:b5:64:63:30:63 IEEE 802.11: associated
-ap1: STA 02:b5:64:63:30:63 WPA: pairwise key handshake completed (WPA)
-ap1: STA 02:b5:64:63:30:63 WPA: group key handshake completed (WPA)
-ap1: STA 02:2a:c4:18:5b:f3 IEEE 802.11: associated
-ap1: STA 02:2a:c4:18:5b:f3 WPA: pairwise key handshake completed (WPA)
-ap1: STA 02:2a:c4:18:5b:f3 WPA: group key handshake completed (WPA)
-\endverbatim
-
-By default, driver_param is simulating a driver that uses the WPA/RSN
-IE generated by %wpa_supplicant. Driver-generated IE and AssocInfo
-events can be tested by adding \a use_associnfo=1 to the \a driver_param
-line in the configuration file. For example:
-
-\verbatim
-driver_param=test_socket=/tmp/Test/ap1 use_associnfo=1
-\endverbatim
-
-
 \section unit_tests Unit tests
 
-Number of the components (.c files) used in %wpa_supplicant define
+Number of the components (.c files) used in wpa_supplicant define
 their own unit tests for automated validation of the basic
 functionality. Most of the tests for cryptographic algorithms are
 using standard test vectors to validate functionality. These tests can
@@ -293,7 +131,7 @@ exit code if all tests were completed successfully.
 
 \section wpa_trace Tracing code for developer debuggin
 
-%wpa_supplicant and hostapd can be built with tracing code that will
+wpa_supplicant and hostapd can be built with tracing code that will
 track and analyze memory allocations and other resource registrations
 and certain API uses. If incorrect use is detected, a backtrace of the
 call location (and/or allocation location) is shown. This can also be
index 06abfb5..d2c4306 100644 (file)
@@ -100,7 +100,7 @@ Single
 2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2
         4650 6600 4650 6300
 4 0 0 50 -1 0 12 0.0000 4 180 510 2850 7125 hostap\001
-4 0 0 50 -1 0 12 0.0000 4 135 600 3825 7125 madwifi\001
+4 0 0 50 -1 0 12 0.0000 4 135 600 3825 7125 nl80211\001
 -6
 6 3525 7800 5775 8100
 2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5
index 68f3c00..8a48cd3 100644 (file)
@@ -23,8 +23,6 @@ void eap_example_server_deinit(void);
 int eap_example_server_step(void);
 
 
-extern int wpa_debug_level;
-
 int main(int argc, char *argv[])
 {
        int res_s, res_p;
index bfeafa0..2ffc9fc 100644 (file)
@@ -27,6 +27,7 @@ struct eap_peer_ctx {
        Boolean portEnabled;
        Boolean altAccept; /* for EAP */
        Boolean altReject; /* for EAP */
+       Boolean eapTriggerStart;
 
        struct wpabuf *eapReqData; /* for EAP */
 
@@ -71,6 +72,8 @@ static Boolean peer_get_bool(void *ctx, enum eapol_bool_var variable)
                return peer->altAccept;
        case EAPOL_altReject:
                return peer->altReject;
+       case EAPOL_eapTriggerStart:
+               return peer->eapTriggerStart;
        }
        return FALSE;
 }
@@ -110,6 +113,9 @@ static void peer_set_bool(void *ctx, enum eapol_bool_var variable,
        case EAPOL_altReject:
                peer->altReject = value;
                break;
+       case EAPOL_eapTriggerStart:
+               peer->eapTriggerStart = value;
+               break;
        }
 }
 
diff --git a/etc/rc.d/init.d/wpa_supplicant b/etc/rc.d/init.d/wpa_supplicant
deleted file mode 100755 (executable)
index a3272c2..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-#!/bin/sh
-HARDWARE_MODEL=`grep Hardware /proc/cpuinfo | awk "{print \\$3}"`
-/bin/echo "Hardware Model=${HARDWARE_MODEL}"
-
-case $HARDWARE_MODEL in
-               "SLP_PQ")       /bin/echo "This is PQ"
-                       /usr/sbin/wpa_supplicant -u -t -B -ddd -f /var/log/wpa_supplicant.log
-               ;;
-               "U1SLP" | "U1HD")       /bin/echo "This is U1SLP"
-                       /usr/sbin/wpa_supplicant -Dwext -u -t -B -ddd -f /var/log/wpa_supplicant.log
-               ;;
-               "SLP7_C210")    /bin/echo "This is C210"
-                       /usr/sbin/wpa_supplicant -Dwext -u -t -B -ddd -f /var/log/wpa_supplicant.log
-               ;;
-               "SLP10_C210")
-                       /usr/sbin/wpa_supplicant -Dwext -u -t -B -ddd -f /var/log/wpa_supplicant.log
-               ;;
-               *)
-                       /usr/sbin/wpa_supplicant -u -t -B -ddd -f /var/log/wpa_supplicant.log
-               ;;
-esac
index 1d3547f..d3fcff1 100755 (executable)
@@ -15,14 +15,14 @@ start()
                        /usr/sbin/wpa_supplicant -u -t -B -d -Dwext -f/opt/usr/data/network/wpa_supplicant.log
                ;;
                *)
-                       /usr/sbin/wpa_supplicant -u -t -B -d -Dnl80211 -f/opt/usr/data/network/wpa_supplicant.log
+                       /usr/sbin/wpa_supplicant -u -t -B -d -f/opt/usr/data/network/wpa_supplicant.log
                ;;
        esac
 }
 
 stop()
 {
-       /usr/bin/killall wpa_supplicant
+       /usr/bin/pkill -x wpa_supplicant
        /bin/usleep 150000
 }
 
index 9837104..6fcefdd 100644 (file)
@@ -24,28 +24,36 @@ L_CFLAGS += -DVERSION_STR_POSTFIX=\"-$(PLATFORM_VERSION)\"
 # Set Android log name
 L_CFLAGS += -DANDROID_LOG_NAME=\"hostapd\"
 
+# Disable unused parameter warnings
+L_CFLAGS += -Wno-unused-parameter
+
+# Set Android extended P2P functionality
+L_CFLAGS += -DANDROID_P2P
+ifeq ($(BOARD_HOSTAPD_PRIVATE_LIB),)
+L_CFLAGS += -DANDROID_P2P_STUB
+endif
+
+# Use Android specific directory for control interface sockets
+L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
+L_CFLAGS += -DCONFIG_CTRL_IFACE_DIR=\"/data/system/hostapd\"
+
 # To force sizeof(enum) = 4
 ifeq ($(TARGET_ARCH),arm)
 L_CFLAGS += -mabi=aapcs-linux
 endif
 
-# To allow non-ASCII characters in SSID
-L_CFLAGS += -DWPA_UNICODE_SSID
-
-# OpenSSL is configured without engines on Android
-L_CFLAGS += -DOPENSSL_NO_ENGINE
-
 INCLUDES = $(LOCAL_PATH)
 INCLUDES += $(LOCAL_PATH)/src
 INCLUDES += $(LOCAL_PATH)/src/utils
 INCLUDES += external/openssl/include
-# frameworks/base/cmds/keystore is the old location and can be dropped at some
-# point
-INCLUDES += frameworks/base/cmds/keystore
-INCLUDES += system/security/keystore
+INCLUDES += system/security/keystore/include
 ifdef CONFIG_DRIVER_NL80211
+ifneq ($(wildcard external/libnl),)
+INCLUDES += external/libnl/include
+else
 INCLUDES += external/libnl-headers
 endif
+endif
 
 
 ifndef CONFIG_OS
@@ -87,6 +95,7 @@ OBJS += src/ap/preauth_auth.c
 OBJS += src/ap/pmksa_cache_auth.c
 OBJS += src/ap/ieee802_11_shared.c
 OBJS += src/ap/beacon.c
+OBJS += src/ap/bss_load.c
 OBJS_d =
 OBJS_p =
 LIBS =
@@ -125,15 +134,16 @@ OBJS += src/utils/ip_addr.c
 
 OBJS += src/common/ieee802_11_common.c
 OBJS += src/common/wpa_common.c
+OBJS += src/common/hw_features_common.c
 
 OBJS += src/eapol_auth/eapol_auth_sm.c
 
 
 ifndef CONFIG_NO_DUMP_STATE
-# define HOSTAPD_DUMP_STATE to include SIGUSR1 handler for dumping state to
-# a file (undefine it, if you want to save in binary size)
+# define HOSTAPD_DUMP_STATE to include support for dumping internal state
+# through control interface commands (undefine it, if you want to save in
+# binary size)
 L_CFLAGS += -DHOSTAPD_DUMP_STATE
-OBJS += dump_state.c
 OBJS += src/eapol_auth/eapol_auth_dump.c
 endif
 
@@ -171,8 +181,6 @@ OBJS += ctrl_iface.c
 OBJS += src/ap/ctrl_iface_ap.c
 endif
 
-OBJS += src/crypto/md5.c
-
 L_CFLAGS += -DCONFIG_CTRL_IFACE -DCONFIG_CTRL_IFACE_UNIX
 
 ifdef CONFIG_IAPP
@@ -190,6 +198,26 @@ L_CFLAGS += -DCONFIG_PEERKEY
 OBJS += src/ap/peerkey_auth.c
 endif
 
+ifdef CONFIG_HS20
+NEED_AES_OMAC1=y
+CONFIG_PROXYARP=y
+endif
+
+ifdef CONFIG_PROXYARP
+CONFIG_L2_PACKET=y
+endif
+
+ifdef CONFIG_SUITEB
+L_CFLAGS += -DCONFIG_SUITEB
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_SUITEB192
+L_CFLAGS += -DCONFIG_SUITEB192
+NEED_SHA384=y
+endif
+
 ifdef CONFIG_IEEE80211W
 L_CFLAGS += -DCONFIG_IEEE80211W
 NEED_SHA256=y
@@ -212,7 +240,7 @@ NEED_DH_GROUPS=y
 endif
 
 ifdef CONFIG_WNM
-CFLAGS += -DCONFIG_WNM
+L_CFLAGS += -DCONFIG_WNM
 OBJS += src/ap/wnm_ap.c
 endif
 
@@ -344,7 +372,7 @@ ifdef CONFIG_EAP_GPSK
 L_CFLAGS += -DEAP_SERVER_GPSK
 OBJS += src/eap_server/eap_server_gpsk.c src/eap_common/eap_gpsk_common.c
 ifdef CONFIG_EAP_GPSK_SHA256
-L_CFLAGS += -DEAP_SERVER_GPSK_SHA256
+L_CFLAGS += -DEAP_GPSK_SHA256
 endif
 NEED_SHA256=y
 NEED_AES_OMAC1=y
@@ -356,6 +384,13 @@ OBJS += src/eap_server/eap_server_pwd.c src/eap_common/eap_pwd_common.c
 NEED_SHA256=y
 endif
 
+ifdef CONFIG_EAP_EKE
+L_CFLAGS += -DEAP_SERVER_EKE
+OBJS += src/eap_server/eap_server_eke.c src/eap_common/eap_eke_common.c
+NEED_DH_GROUPS=y
+NEED_DH_GROUPS_ALL=y
+endif
+
 ifdef CONFIG_EAP_VENDOR_TEST
 L_CFLAGS += -DEAP_SERVER_VENDOR_TEST
 OBJS += src/eap_server/eap_server_vendor_test.c
@@ -371,10 +406,6 @@ NEED_AES_UNWRAP=y
 endif
 
 ifdef CONFIG_WPS
-ifdef CONFIG_WPS2
-L_CFLAGS += -DCONFIG_WPS2
-endif
-
 L_CFLAGS += -DCONFIG_WPS -DEAP_SERVER_WSC
 OBJS += src/utils/uuid.c
 OBJS += src/ap/wps_hostapd.c
@@ -513,15 +544,12 @@ ifeq ($(CONFIG_TLS), gnutls)
 ifdef TLS_FUNCS
 OBJS += src/crypto/tls_gnutls.c
 LIBS += -lgnutls -lgpg-error
-ifdef CONFIG_GNUTLS_EXTRA
-L_CFLAGS += -DCONFIG_GNUTLS_EXTRA
-LIBS += -lgnutls-extra
-endif
 endif
 OBJS += src/crypto/crypto_gnutls.c
 HOBJS += src/crypto/crypto_gnutls.c
 ifdef NEED_FIPS186_2_PRF
-OBJS += src/crypto/fips_prf_gnutls.c
+OBJS += src/crypto/fips_prf_internal.c
+OBJS += src/crypto/sha1-internal.c
 endif
 LIBS += -lgcrypt
 LIBS_h += -lgcrypt
@@ -541,21 +569,6 @@ CONFIG_INTERNAL_RC4=y
 CONFIG_INTERNAL_DH_GROUP5=y
 endif
 
-ifeq ($(CONFIG_TLS), nss)
-ifdef TLS_FUNCS
-OBJS += src/crypto/tls_nss.c
-LIBS += -lssl3
-endif
-OBJS += src/crypto/crypto_nss.c
-ifdef NEED_FIPS186_2_PRF
-OBJS += src/crypto/fips_prf_nss.c
-endif
-LIBS += -lnss3
-LIBS_h += -lnss3
-CONFIG_INTERNAL_MD4=y
-CONFIG_INTERNAL_DH_GROUP5=y
-endif
-
 ifeq ($(CONFIG_TLS), internal)
 ifndef CONFIG_CRYPTO
 CONFIG_CRYPTO=internal
@@ -662,7 +675,9 @@ ifdef CONFIG_INTERNAL_AES
 AESOBJS += src/crypto/aes-internal.c src/crypto/aes-internal-enc.c
 endif
 
+ifneq ($(CONFIG_TLS), openssl)
 AESOBJS += src/crypto/aes-wrap.c
+endif
 ifdef NEED_AES_EAX
 AESOBJS += src/crypto/aes-eax.c
 NEED_AES_CTR=y
@@ -677,9 +692,11 @@ ifdef NEED_AES_OMAC1
 AESOBJS += src/crypto/aes-omac1.c
 endif
 ifdef NEED_AES_UNWRAP
+ifneq ($(CONFIG_TLS), openssl)
 NEED_AES_DEC=y
 AESOBJS += src/crypto/aes-unwrap.c
 endif
+endif
 ifdef NEED_AES_CBC
 NEED_AES_DEC=y
 AESOBJS += src/crypto/aes-cbc.c
@@ -720,6 +737,10 @@ ifdef NEED_SHA1
 OBJS += $(SHA1OBJS)
 endif
 
+ifneq ($(CONFIG_TLS), openssl)
+OBJS += src/crypto/md5.c
+endif
+
 ifdef NEED_MD5
 ifdef CONFIG_INTERNAL_MD5
 OBJS += src/crypto/md5-internal.c
@@ -758,6 +779,9 @@ ifdef NEED_TLS_PRF_SHA256
 OBJS += src/crypto/sha256-tlsprf.c
 endif
 endif
+ifdef NEED_SHA384
+L_CFLAGS += -DCONFIG_SHA384
+endif
 
 ifdef NEED_DH_GROUPS
 OBJS += src/crypto/dh_groups.c
@@ -813,6 +837,7 @@ OBJS += src/ap/wmm.c
 OBJS += src/ap/ap_list.c
 OBJS += src/ap/ieee802_11.c
 OBJS += src/ap/hw_features.c
+OBJS += src/ap/dfs.c
 L_CFLAGS += -DNEED_AP_MLME
 endif
 ifdef CONFIG_IEEE80211N
@@ -840,12 +865,31 @@ OBJS += src/common/gas.c
 OBJS += src/ap/gas_serv.c
 endif
 
+ifdef CONFIG_PROXYARP
+L_CFLAGS += -DCONFIG_PROXYARP
+OBJS += src/ap/x_snoop.c
+OBJS += src/ap/dhcp_snoop.c
+ifdef CONFIG_IPV6
+OBJS += src/ap/ndisc_snoop.c
+endif
+endif
+
 OBJS += src/drivers/driver_common.c
 
+ifdef CONFIG_ACS
+L_CFLAGS += -DCONFIG_ACS
+OBJS += src/ap/acs.c
+LIBS += -lm
+endif
+
 ifdef CONFIG_NO_STDOUT_DEBUG
 L_CFLAGS += -DCONFIG_NO_STDOUT_DEBUG
 endif
 
+ifdef CONFIG_DEBUG_LINUX_TRACING
+L_CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING
+endif
+
 ifdef CONFIG_DEBUG_FILE
 L_CFLAGS += -DCONFIG_DEBUG_FILE
 endif
@@ -871,7 +915,7 @@ endif
 include $(CLEAR_VARS)
 LOCAL_MODULE := hostapd_cli
 LOCAL_MODULE_TAGS := debug
-LOCAL_SHARED_LIBRARIES := libc libcutils
+LOCAL_SHARED_LIBRARIES := libc libcutils liblog
 LOCAL_CFLAGS := $(L_CFLAGS)
 LOCAL_SRC_FILES := $(OBJS_c)
 LOCAL_C_INCLUDES := $(INCLUDES)
@@ -887,10 +931,14 @@ endif
 ifneq ($(BOARD_HOSTAPD_PRIVATE_LIB),)
 LOCAL_STATIC_LIBRARIES += $(BOARD_HOSTAPD_PRIVATE_LIB)
 endif
-LOCAL_SHARED_LIBRARIES := libc libcutils libcrypto libssl
+LOCAL_SHARED_LIBRARIES := libc libcutils liblog libcrypto libssl
 ifdef CONFIG_DRIVER_NL80211
+ifneq ($(wildcard external/libnl),)
+LOCAL_SHARED_LIBRARIES += libnl
+else
 LOCAL_STATIC_LIBRARIES += libnl_2
 endif
+endif
 LOCAL_CFLAGS := $(L_CFLAGS)
 LOCAL_SRC_FILES := $(OBJS)
 LOCAL_C_INCLUDES := $(INCLUDES)
index 1a4e566..e6f8c6a 100644 (file)
@@ -1,8 +1,190 @@
 ChangeLog for hostapd
 
-????-??-?? - v2.1
-       * added support for simulataneous authentication of equals (SAE) for
+2015-03-15 - v2.4
+       * allow OpenSSL cipher configuration to be set for internal EAP server
+         (openssl_ciphers parameter)
+       * fixed number of small issues based on hwsim test case failures and
+         static analyzer reports
+       * fixed Accounting-Request to not include duplicated Acct-Session-Id
+       * add support for Acct-Multi-Session-Id in RADIUS Accounting messages
+       * add support for PMKSA caching with SAE
+       * add support for generating BSS Load element (bss_load_update_period)
+       * fixed channel switch from VHT to HT
+       * add INTERFACE-ENABLED and INTERFACE-DISABLED ctrl_iface events
+       * add support for learning STA IPv4/IPv6 addresses and configuring
+         ProxyARP support
+       * dropped support for the madwifi driver interface
+       * add support for Suite B (128-bit and 192-bit level) key management and
+         cipher suites
+       * fixed a regression with driver=wired
+       * extend EAPOL-Key msg 1/4 retry workaround for changing SNonce
+       * add BSS_TM_REQ ctrl_iface command to send BSS Transition Management
+         Request frames and BSS-TM-RESP event to indicate response to such
+         frame
+       * add support for EAP Re-Authentication Protocol (ERP)
+       * fixed AP IE in EAPOL-Key 3/4 when both WPA and FT was enabled
+       * fixed a regression in HT 20/40 coex Action frame parsing
+       * set stdout to be line-buffered
+       * add support for vendor specific VHT extension to enable 256 QAM rates
+         (VHT-MCS 8 and 9) on 2.4 GHz band
+       * RADIUS DAS:
+         - extend Disconnect-Request processing to allow matching of multiple
+           sessions
+         - support Acct-Multi-Session-Id as an identifier
+         - allow PMKSA cache entry to be removed without association
+       * expire hostapd STA entry if kernel does not have a matching entry
+       * allow chanlist to be used to specify a subset of channels for ACS
+       * improve ACS behavior on 2.4 GHz band and allow channel bias to be
+         configured with acs_chan_bias parameter
+       * do not reply to a Probe Request frame that includes DSS Parameter Set
+         element in which the channel does not match the current operating
+         channel
+       * add UPDATE_BEACON ctrl_iface command; this can be used to force Beacon
+         frame contents to be updated and to start beaconing on an interface
+         that used start_disabled=1
+       * fixed some RADIUS server failover cases
+
+2014-10-09 - v2.3
+       * fixed number of minor issues identified in static analyzer warnings
+       * fixed DFS and channel switch operation for multi-BSS cases
+       * started to use constant time comparison for various password and hash
+         values to reduce possibility of any externally measurable timing
+         differences
+       * extended explicit clearing of freed memory and expired keys to avoid
+         keeping private data in memory longer than necessary
+       * added support for number of new RADIUS attributes from RFC 7268
+         (Mobility-Domain-Id, WLAN-HESSID, WLAN-Pairwise-Cipher,
+         WLAN-Group-Cipher, WLAN-AKM-Suite, WLAN-Group-Mgmt-Pairwise-Cipher)
+       * fixed GET_CONFIG wpa_pairwise_cipher value
+       * added code to clear bridge FDB entry on station disconnection
+       * fixed PMKSA cache timeout from Session-Timeout for WPA/WPA2 cases
+       * fixed OKC PMKSA cache entry fetch to avoid a possible infinite loop
+         in case the first entry does not match
+       * fixed hostapd_cli action script execution to use more robust mechanism
+         (CVE-2014-3686)
+
+2014-06-04 - v2.2
+       * fixed SAE confirm-before-commit validation to avoid a potential
+         segmentation fault in an unexpected message sequence that could be
+         triggered remotely
+       * extended VHT support
+         - Operating Mode Notification
+         - Power Constraint element (local_pwr_constraint)
+         - Spectrum management capability (spectrum_mgmt_required=1)
+         - fix VHT80 segment picking in ACS
+         - fix vht_capab 'Maximum A-MPDU Length Exponent' handling
+         - fix VHT20
+       * fixed HT40 co-ex scan for some pri/sec channel switches
+       * extended HT40 co-ex support to allow dynamic channel width changes
+         during the lifetime of the BSS
+       * fixed HT40 co-ex support to check for overlapping 20 MHz BSS
+       * fixed MSCHAP UTF-8 to UCS-2 conversion for three-byte encoding;
+         this fixes password with include UTF-8 characters that use
+         three-byte encoding EAP methods that use NtPasswordHash
+       * reverted TLS certificate validation step change in v2.1 that rejected
+         any AAA server certificate with id-kp-clientAuth even if
+         id-kp-serverAuth EKU was included
+       * fixed STA validation step for WPS ER commands to prevent a potential
+         crash if an ER sends an unexpected PutWLANResponse to a station that
+         is disassociated, but not fully removed
+       * enforce full EAP authentication after RADIUS Disconnect-Request by
+         removing the PMKSA cache entry
+       * added support for NAS-IP-Address, NAS-identifier, and NAS-IPv6-Address
+         in RADIUS Disconnect-Request
+       * added mechanism for removing addresses for MAC ACLs by prefixing an
+         entry with "-"
+       * Interworking/Hotspot 2.0 enhancements
+         - support Hotspot 2.0 Release 2
+           * OSEN network for online signup connection
+           * subscription remediation (based on RADIUS server request or
+             control interface HS20_WNM_NOTIF for testing purposes)
+           * Hotspot 2.0 release number indication in WFA RADIUS VSA
+           * deauthentication request (based on RADIUS server request or
+             control interface WNM_DEAUTH_REQ for testing purposes)
+           * Session Info URL RADIUS AVP to trigger ESS Disassociation Imminent
+           * hs20_icon config parameter to configure icon files for OSU
+           * osu_* config parameters for OSU Providers list
+         - do not use Interworking filtering rules on Probe Request if
+           Interworking is disabled to avoid interop issues
+       * added/fixed nl80211 functionality
+         - AP interface teardown optimization
+         - support vendor specific driver command
+           (VENDOR <vendor id> <sub command id> [<hex formatted data>])
+       * fixed PMF protection of Deauthentication frame when this is triggered
+         by session timeout
+       * internal TLS implementation enhancements/fixes
+         - add SHA256-based cipher suites
+         - add DHE-RSA cipher suites
+         - fix X.509 validation of PKCS#1 signature to check for extra data
+       * RADIUS server functionality
+         - add minimal RADIUS accounting server support (hostapd-as-server);
+           this is mainly to enable testing coverage with hwsim scripts
+         - allow authentication log to be written into SQLite databse
+         - added option for TLS protocol testing of an EAP peer by simulating
+           various misbehaviors/known attacks
+         - MAC ACL support for testing purposes
+       * fixed PTK derivation for CCMP-256 and GCMP-256
+       * extended WPS per-station PSK to support ER case
+       * added option to configure the management group cipher
+         (group_mgmt_cipher=AES-128-CMAC (default), BIP-GMAC-128, BIP-GMAC-256,
+         BIP-CMAC-256)
+       * fixed AP mode default TXOP Limit values for AC_VI and AC_VO (these
+         were rounded incorrectly)
+       * added support for postponing FT response in case PMK-R1 needs to be
+         pulled from R0KH
+       * added option to advertise 40 MHz intolerant HT capability with
+         ht_capab=[40-INTOLERANT]
+       * remove WPS 1.0 only support, i.e., WSC 2.0 support is now enabled
+         whenever CONFIG_WPS=y is set
+       * EAP-pwd fixes
+         - fix possible segmentation fault on EAP method deinit if an invalid
+           group is negotiated
+       * fixed RADIUS client retransmit/failover behavior
+         - there was a potential ctash due to freed memory being accessed
+         - failover to a backup server mechanism did not work properly
+       * fixed a possible crash on double DISABLE command when multiple BSSes
+         are enabled
+       * fixed a memory leak in SAE random number generation
+       * fixed GTK rekeying when the station uses FT protocol
+       * fixed off-by-one bounds checking in printf_encode()
+         - this could result in deinial of service in some EAP server cases
+       * various bug fixes
+
+2014-02-04 - v2.1
+       * added support for simultaneous authentication of equals (SAE) for
          stronger password-based authentication with WPA2-Personal
+       * added nl80211 functionality
+         - VHT configuration for nl80211
+         - support split wiphy dump
+         - driver-based MAC ACL
+         - QoS Mapping configuration
+       * added fully automated regression testing with mac80211_hwsim
+       * allow ctrl_iface group to be specified on command line (-G<group>)
+       * allow single hostapd process to control independent WPS interfaces
+         (wps_independent=1) instead of synchronized operations through all
+         configured interfaces within a process
+       * avoid processing received management frames multiple times when using
+         nl80211 with multiple BSSes
+       * added support for DFS (processing radar detection events, CAC, channel
+         re-selection)
+       * added EAP-EKE server
+       * added automatic channel selection (ACS)
+       * added option for using per-BSS (vif) configuration files with
+         -b<phyname>:<config file name>
+       * extended global control interface ADD/REMOVE commands to allow BSSes
+         of a radio to be removed individually without having to add/remove all
+         other BSSes of the radio at the same time
+       * added support for sending debug info to Linux tracing (-T on command
+         line)
+       * replace dump_file functionality with same information being available
+         through the hostapd control interface
+       * added support for using Protected Dual of Public Action frames for
+         GAS/ANQP exchanges when PMF is enabled
+       * added support for WPS+NFC updates
+         - improved protocol
+         - option to fetch and report alternative carrier records for external
+           NFC operations
+       * various bug fixes
 
 2013-01-12 - v2.0
        * added AP-STA-DISCONNECTED ctrl_iface event
index 843fba7..f0d1784 100644 (file)
@@ -6,18 +6,26 @@ ifndef CFLAGS
 CFLAGS = -MMD -O2 -Wall -g
 endif
 
-CFLAGS += -I../src
-CFLAGS += -I../src/utils
+CFLAGS += $(EXTRA_CFLAGS)
+CFLAGS += -I$(abspath ../src)
+CFLAGS += -I$(abspath ../src/utils)
+
+export BINDIR ?= /usr/local/bin/
+
 CFLAGS += -fPIE
 LDFLAGS += -pie
 
-
 # Uncomment following line and set the path to your kernel tree include
 # directory if your C library does not include all header files.
 # CFLAGS += -DUSE_KERNEL_HEADERS -I/usr/src/linux/include
 
 -include .config
 
+ifdef CONFIG_TESTING_OPTIONS
+CFLAGS += -DCONFIG_TESTING_OPTIONS
+CONFIG_WPS_TESTING=y
+endif
+
 ifndef CONFIG_OS
 ifdef CONFIG_NATIVE_WINDOWS
 CONFIG_OS=win32
@@ -57,13 +65,19 @@ OBJS += ../src/ap/preauth_auth.o
 OBJS += ../src/ap/pmksa_cache_auth.o
 OBJS += ../src/ap/ieee802_11_shared.o
 OBJS += ../src/ap/beacon.o
+OBJS += ../src/ap/bss_load.o
 
 OBJS_c = hostapd_cli.o ../src/common/wpa_ctrl.o ../src/utils/os_$(CONFIG_OS).o
 
 ifdef TIZEN_EXT
 CFLAGS += -DTIZEN_EXT
 CFLAGS += -DTIZEN_EXT_SOFTAP
+ifdef CONFIG_BCM_DRIVER_V115
 CFLAGS += -DBCM_DRIVER_V115
+endif
+ifdef CONFIG_TIZEN_WLAN_BOARD_SPRD
+CFLAGS += -DTIZEN_WLAN_BOARD_SPRD
+endif
 CFLAGS += -I../src/drivers
 
 # To force sizeof(enum) = 4
@@ -80,6 +94,11 @@ NEED_SHA1=y
 OBJS += ../src/drivers/drivers.o
 CFLAGS += -DHOSTAPD
 
+ifdef CONFIG_MODULE_TESTS
+CFLAGS += -DCONFIG_MODULE_TESTS
+OBJS += hapd_module_tests.o
+endif
+
 ifdef CONFIG_WPA_TRACE
 CFLAGS += -DWPA_TRACE
 OBJS += ../src/utils/trace.o
@@ -87,10 +106,10 @@ HOBJS += ../src/utils/trace.o
 LDFLAGS += -rdynamic
 CFLAGS += -funwind-tables
 ifdef CONFIG_WPA_TRACE_BFD
-CFLAGS += -DWPA_TRACE_BFD
-LIBS += -lbfd
-LIBS_c += -lbfd
-LIBS_h += -lbfd
+CFLAGS += -DPACKAGE="hostapd" -DWPA_TRACE_BFD
+LIBS += -lbfd -ldl -liberty -lz
+LIBS_c += -lbfd -ldl -liberty -lz
+LIBS_h += -lbfd -ldl -liberty -lz
 endif
 endif
 
@@ -99,6 +118,15 @@ CONFIG_ELOOP=eloop
 endif
 OBJS += ../src/utils/$(CONFIG_ELOOP).o
 OBJS_c += ../src/utils/$(CONFIG_ELOOP).o
+
+ifeq ($(CONFIG_ELOOP), eloop)
+# Using glibc < 2.17 requires -lrt for clock_gettime()
+LIBS += -lrt
+LIBS_c += -lrt
+LIBS_h += -lrt
+LIBS_n += -lrt
+endif
+
 OBJS += ../src/utils/common.o
 OBJS += ../src/utils/wpa_debug.o
 OBJS_c += ../src/utils/wpa_debug.o
@@ -108,15 +136,24 @@ OBJS += ../src/utils/ip_addr.o
 
 OBJS += ../src/common/ieee802_11_common.o
 OBJS += ../src/common/wpa_common.o
+OBJS += ../src/common/hw_features_common.o
 
 OBJS += ../src/eapol_auth/eapol_auth_sm.o
 
 
+ifdef CONFIG_CODE_COVERAGE
+CFLAGS += -O0 -fprofile-arcs -ftest-coverage
+LIBS += -lgcov
+LIBS_c += -lgcov
+LIBS_h += -lgcov
+LIBS_n += -lgcov
+endif
+
 ifndef CONFIG_NO_DUMP_STATE
-# define HOSTAPD_DUMP_STATE to include SIGUSR1 handler for dumping state to
-# a file (undefine it, if you want to save in binary size)
+# define HOSTAPD_DUMP_STATE to include support for dumping internal state
+# through control interface commands (undefine it, if you want to save in
+# binary size)
 CFLAGS += -DHOSTAPD_DUMP_STATE
-OBJS += dump_state.o
 OBJS += ../src/eapol_auth/eapol_auth_dump.o
 endif
 
@@ -154,8 +191,6 @@ OBJS += ctrl_iface.o
 OBJS += ../src/ap/ctrl_iface_ap.o
 endif
 
-OBJS += ../src/crypto/md5.o
-
 CFLAGS += -DCONFIG_CTRL_IFACE -DCONFIG_CTRL_IFACE_UNIX
 
 ifdef CONFIG_IAPP
@@ -173,6 +208,26 @@ CFLAGS += -DCONFIG_PEERKEY
 OBJS += ../src/ap/peerkey_auth.o
 endif
 
+ifdef CONFIG_HS20
+NEED_AES_OMAC1=y
+CONFIG_PROXYARP=y
+endif
+
+ifdef CONFIG_PROXYARP
+CONFIG_L2_PACKET=y
+endif
+
+ifdef CONFIG_SUITEB
+CFLAGS += -DCONFIG_SUITEB
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_SUITEB192
+CFLAGS += -DCONFIG_SUITEB192
+NEED_SHA384=y
+endif
+
 ifdef CONFIG_IEEE80211W
 CFLAGS += -DCONFIG_IEEE80211W
 NEED_SHA256=y
@@ -230,6 +285,12 @@ OBJS += ../src/l2_packet/l2_packet_none.o
 endif
 
 
+ifdef CONFIG_ERP
+CFLAGS += -DCONFIG_ERP
+NEED_SHA256=y
+NEED_HMAC_SHA256_KDF=y
+endif
+
 ifdef CONFIG_EAP_MD5
 CFLAGS += -DEAP_SERVER_MD5
 OBJS += ../src/eap_server/eap_server_md5.o
@@ -326,7 +387,7 @@ ifdef CONFIG_EAP_GPSK
 CFLAGS += -DEAP_SERVER_GPSK
 OBJS += ../src/eap_server/eap_server_gpsk.o ../src/eap_common/eap_gpsk_common.o
 ifdef CONFIG_EAP_GPSK_SHA256
-CFLAGS += -DEAP_SERVER_GPSK_SHA256
+CFLAGS += -DEAP_GPSK_SHA256
 endif
 NEED_SHA256=y
 NEED_AES_OMAC1=y
@@ -338,6 +399,13 @@ OBJS += ../src/eap_server/eap_server_pwd.o ../src/eap_common/eap_pwd_common.o
 NEED_SHA256=y
 endif
 
+ifdef CONFIG_EAP_EKE
+CFLAGS += -DEAP_SERVER_EKE
+OBJS += ../src/eap_server/eap_server_eke.o ../src/eap_common/eap_eke_common.o
+NEED_DH_GROUPS=y
+NEED_DH_GROUPS_ALL=y
+endif
+
 ifdef CONFIG_EAP_VENDOR_TEST
 CFLAGS += -DEAP_SERVER_VENDOR_TEST
 OBJS += ../src/eap_server/eap_server_vendor_test.o
@@ -353,10 +421,6 @@ NEED_AES_UNWRAP=y
 endif
 
 ifdef CONFIG_WPS
-ifdef CONFIG_WPS2
-CFLAGS += -DCONFIG_WPS2
-endif
-
 CFLAGS += -DCONFIG_WPS -DEAP_SERVER_WSC
 OBJS += ../src/utils/uuid.o
 OBJS += ../src/ap/wps_hostapd.o
@@ -499,7 +563,8 @@ endif
 OBJS += ../src/crypto/crypto_gnutls.o
 HOBJS += ../src/crypto/crypto_gnutls.o
 ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_gnutls.o
+OBJS += ../src/crypto/fips_prf_internal.o
+SHA1OBJS += ../src/crypto/sha1-internal.o
 endif
 LIBS += -lgcrypt
 LIBS_h += -lgcrypt
@@ -519,21 +584,6 @@ CONFIG_INTERNAL_RC4=y
 CONFIG_INTERNAL_DH_GROUP5=y
 endif
 
-ifeq ($(CONFIG_TLS), nss)
-ifdef TLS_FUNCS
-OBJS += ../src/crypto/tls_nss.o
-LIBS += -lssl3
-endif
-OBJS += ../src/crypto/crypto_nss.o
-ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_nss.o
-endif
-LIBS += -lnss3
-LIBS_h += -lnss3
-CONFIG_INTERNAL_MD4=y
-CONFIG_INTERNAL_DH_GROUP5=y
-endif
-
 ifeq ($(CONFIG_TLS), internal)
 ifndef CONFIG_CRYPTO
 CONFIG_CRYPTO=internal
@@ -640,7 +690,9 @@ ifdef CONFIG_INTERNAL_AES
 AESOBJS += ../src/crypto/aes-internal.o ../src/crypto/aes-internal-enc.o
 endif
 
+ifneq ($(CONFIG_TLS), openssl)
 AESOBJS += ../src/crypto/aes-wrap.o
+endif
 ifdef NEED_AES_EAX
 AESOBJS += ../src/crypto/aes-eax.o
 NEED_AES_CTR=y
@@ -655,9 +707,11 @@ ifdef NEED_AES_OMAC1
 AESOBJS += ../src/crypto/aes-omac1.o
 endif
 ifdef NEED_AES_UNWRAP
+ifneq ($(CONFIG_TLS), openssl)
 NEED_AES_DEC=y
 AESOBJS += ../src/crypto/aes-unwrap.o
 endif
+endif
 ifdef NEED_AES_CBC
 NEED_AES_DEC=y
 AESOBJS += ../src/crypto/aes-cbc.o
@@ -697,6 +751,10 @@ ifdef NEED_SHA1
 OBJS += $(SHA1OBJS)
 endif
 
+ifneq ($(CONFIG_TLS), openssl)
+OBJS += ../src/crypto/md5.o
+endif
+
 ifdef NEED_MD5
 ifdef CONFIG_INTERNAL_MD5
 OBJS += ../src/crypto/md5-internal.o
@@ -734,6 +792,12 @@ endif
 ifdef NEED_TLS_PRF_SHA256
 OBJS += ../src/crypto/sha256-tlsprf.o
 endif
+ifdef NEED_HMAC_SHA256_KDF
+OBJS += ../src/crypto/sha256-kdf.o
+endif
+endif
+ifdef NEED_SHA384
+CFLAGS += -DCONFIG_SHA384
 endif
 
 ifdef NEED_DH_GROUPS
@@ -790,6 +854,7 @@ OBJS += ../src/ap/wmm.o
 OBJS += ../src/ap/ap_list.o
 OBJS += ../src/ap/ieee802_11.o
 OBJS += ../src/ap/hw_features.o
+OBJS += ../src/ap/dfs.o
 CFLAGS += -DNEED_AP_MLME
 endif
 ifdef CONFIG_IEEE80211N
@@ -817,6 +882,15 @@ OBJS += ../src/common/gas.o
 OBJS += ../src/ap/gas_serv.o
 endif
 
+ifdef CONFIG_PROXYARP
+CFLAGS += -DCONFIG_PROXYARP
+OBJS += ../src/ap/x_snoop.o
+OBJS += ../src/ap/dhcp_snoop.o
+ifdef CONFIG_IPV6
+OBJS += ../src/ap/ndisc_snoop.o
+endif
+endif
+
 OBJS += ../src/drivers/driver_common.o
 
 ifdef CONFIG_WPA_CLI_EDIT
@@ -825,10 +899,20 @@ else
 OBJS_c += ../src/utils/edit_simple.o
 endif
 
+ifdef CONFIG_ACS
+CFLAGS += -DCONFIG_ACS
+OBJS += ../src/ap/acs.o
+LIBS += -lm
+endif
+
 ifdef CONFIG_NO_STDOUT_DEBUG
 CFLAGS += -DCONFIG_NO_STDOUT_DEBUG
 endif
 
+ifdef CONFIG_DEBUG_LINUX_TRACING
+CFLAGS += -DCONFIG_DEBUG_LINUX_TRACING
+endif
+
 ifdef CONFIG_DEBUG_FILE
 CFLAGS += -DCONFIG_DEBUG_FILE
 endif
@@ -839,10 +923,6 @@ LIBS += -lsqlite3
 LIBS_h += -lsqlite3
 endif
 
-ifdef CONFIG_TESTING_OPTIONS
-CFLAGS += -DCONFIG_TESTING_OPTIONS
-endif
-
 ALL=hostapd hostapd_cli
 
 all: verify_config $(ALL)
@@ -853,10 +933,20 @@ ifeq ($(V), 1)
 Q=
 E=true
 endif
+ifeq ($(QUIET), 1)
+Q=@
+E=true
+endif
 
+ifdef CONFIG_CODE_COVERAGE
+%.o: %.c
+       @$(E) "  CC " $<
+       $(Q)cd $(dir $@); $(CC) -c -o $(notdir $@) $(CFLAGS) $(notdir $<)
+else
 %.o: %.c
        $(Q)$(CC) -c -o $@ $(CFLAGS) $<
        @$(E) "  CC " $<
+endif
 
 verify_config:
        @if [ ! -r .config ]; then \
@@ -872,8 +962,10 @@ ifdef TIZEN_EXT
        mkdir -p $(DESTDIR)/usr/sbin
        for i in $(ALL); do cp -f $$i $(DESTDIR)/usr/sbin/$$i; done
 else
-       mkdir -p $(DESTDIR)/usr/local/bin
-       for i in $(ALL); do cp -f $$i $(DESTDIR)/usr/local/bin/$$i; done
+$(DESTDIR)$(BINDIR)/%: %
+       install -D $(<) $(@)
+
+install: $(addprefix $(DESTDIR)$(BINDIR)/,$(ALL))
 endif
 
 ../src/drivers/build.hostapd:
@@ -895,7 +987,8 @@ hostapd_cli: $(OBJS_c)
        $(Q)$(CC) $(LDFLAGS) -o hostapd_cli $(OBJS_c) $(LIBS_c)
        @$(E) "  LD " $@
 
-NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o $(SHA1OBJS) ../src/crypto/md5.o
+NOBJS = nt_password_hash.o ../src/crypto/ms_funcs.o $(SHA1OBJS)
+NOBJS += ../src/utils/common.o
 ifdef NEED_RC4
 ifdef CONFIG_INTERNAL_RC4
 NOBJS += ../src/crypto/rc4.o
@@ -930,9 +1023,15 @@ hlr_auc_gw: $(HOBJS)
        $(Q)$(CC) $(LDFLAGS) -o hlr_auc_gw $(HOBJS) $(LIBS_h)
        @$(E) "  LD " $@
 
+lcov-html:
+       lcov -c -d .. > lcov.info
+       genhtml lcov.info --output-directory lcov-html
+
 clean:
        $(MAKE) -C ../src clean
        rm -f core *~ *.o hostapd hostapd_cli nt_password_hash hlr_auc_gw
-       rm -f *.d
+       rm -f *.d *.gcno *.gcda *.gcov
+       rm -f lcov.info
+       rm -rf lcov-html
 
 -include $(OBJS:%.o=%.d)
index 39b70ca..366b199 100644 (file)
@@ -2,7 +2,7 @@ hostapd - user space IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP
          Authenticator and RADIUS authentication server
 ================================================================
 
-Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 This program is licensed under the BSD license (the one with
@@ -74,12 +74,6 @@ Current hardware/software requirements:
        Please note that station firmware version needs to be 1.7.0 or newer
        to work in WPA mode.
 
-       madwifi driver for cards based on Atheros chip set (ar521x)
-       (http://sourceforge.net/projects/madwifi/)
-       Please note that you will need to add the correct path for
-       madwifi driver root directory in .config (see defconfig file for
-       an example: CFLAGS += -I<path>)
-
        mac80211-based drivers that support AP mode (with driver=nl80211).
        This includes drivers for Atheros (ath9k) and Broadcom (b43)
        chipsets.
index 654b5bc..d5f713a 100644 (file)
@@ -58,12 +58,10 @@ hostapd configuration
 
 WPS is an optional component that needs to be enabled in hostapd build
 configuration (.config). Here is an example configuration that
-includes WPS support and uses madwifi driver interface:
+includes WPS support and uses nl80211 driver interface:
 
-CONFIG_DRIVER_MADWIFI=y
-CFLAGS += -I/usr/src/madwifi-0.9.3
+CONFIG_DRIVER_NL80211=y
 CONFIG_WPS=y
-CONFIG_WPS2=y
 CONFIG_WPS_UPNP=y
 
 Following parameter can be used to enable support for NFC config method:
@@ -75,8 +73,8 @@ Following section shows an example runtime configuration
 (hostapd.conf) that enables WPS:
 
 # Configure the driver and network interface
-driver=madwifi
-interface=ath0
+driver=nl80211
+interface=wlan0
 
 # WPA2-Personal configuration for the AP
 ssid=wps-test
index 51d6656..938aa54 100644 (file)
 # Driver interface for wired authenticator
 #CONFIG_DRIVER_WIRED=y
 
-# Driver interface for madwifi driver
-#CONFIG_DRIVER_MADWIFI=y
-#CFLAGS += -I../../madwifi # change to the madwifi source directory
-
 # Driver interface for drivers using the nl80211 kernel interface
 #CONFIG_DRIVER_NL80211=y
 # driver_nl80211.c requires a rather new libnl (version 1.1) which may not be
@@ -108,8 +104,6 @@ CONFIG_IEEE80211W=y
 
 # Wi-Fi Protected Setup (WPS)
 CONFIG_WPS=y
-# Enable WSC 2.0 support
-CONFIG_WPS2=y
 # Enable UPnP support for external WPS Registrars
 #CONFIG_WPS_UPNP=y
 
@@ -134,7 +128,7 @@ CONFIG_IPV6=y
 #CONFIG_IEEE80211R=y
 
 # Use the hostapd's IEEE 802.11 authentication (ACL), but without
-# the IEEE 802.11 Management capability (e.g., madwifi or FreeBSD/net80211)
+# the IEEE 802.11 Management capability (e.g., FreeBSD/net80211)
 #CONFIG_DRIVER_RADIUS_ACL=y
 
 # IEEE 802.11n (High Throughput) support
@@ -157,7 +151,7 @@ CONFIG_NO_RADIUS=y
 # Remove support for VLANs
 #CONFIG_NO_VLAN=y
 
-# Remove support for dumping state into a file on SIGUSR1 signal
+# Remove support for dumping internal state through control interface commands
 # This can be used to reduce binary size at the cost of disabling a debugging
 # option.
 #CONFIG_NO_DUMP_STATE=y
index 9cc45e9..53143f7 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / Configuration file parser
- * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 #include "config_file.h"
 
 
-extern struct wpa_driver_ops *wpa_drivers[];
+#ifndef CONFIG_NO_RADIUS
+#ifdef EAP_SERVER
+static struct hostapd_radius_attr *
+hostapd_parse_radius_attr(const char *value);
+#endif /* EAP_SERVER */
+#endif /* CONFIG_NO_RADIUS */
 
 
 #ifndef CONFIG_NO_VLAN
@@ -93,11 +98,8 @@ static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss,
 
                vlan->vlan_id = vlan_id;
                os_strlcpy(vlan->ifname, pos, sizeof(vlan->ifname));
-               if (bss->vlan_tail)
-                       bss->vlan_tail->next = vlan;
-               else
-                       bss->vlan = vlan;
-               bss->vlan_tail = vlan;
+               vlan->next = bss->vlan;
+               bss->vlan = vlan;
        }
 
        fclose(f);
@@ -135,6 +137,8 @@ static int hostapd_config_read_maclist(const char *fname,
        }
 
        while (fgets(buf, sizeof(buf), f)) {
+               int i, rem = 0;
+
                line++;
 
                if (buf[0] == '#')
@@ -149,14 +153,32 @@ static int hostapd_config_read_maclist(const char *fname,
                }
                if (buf[0] == '\0')
                        continue;
+               pos = buf;
+               if (buf[0] == '-') {
+                       rem = 1;
+                       pos++;
+               }
 
-               if (hwaddr_aton(buf, addr)) {
+               if (hwaddr_aton(pos, addr)) {
                        wpa_printf(MSG_ERROR, "Invalid MAC address '%s' at "
-                                  "line %d in '%s'", buf, line, fname);
+                                  "line %d in '%s'", pos, line, fname);
                        fclose(f);
                        return -1;
                }
 
+               if (rem) {
+                       i = 0;
+                       while (i < *num) {
+                               if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) ==
+                                   0) {
+                                       os_remove_in_array(*acl, *num,
+                                                          sizeof(**acl), i);
+                                       (*num)--;
+                               } else
+                                       i++;
+                       }
+                       continue;
+               }
                vlan_id = 0;
                pos = buf;
                while (*pos != '\0' && *pos != ' ' && *pos != '\t')
@@ -194,7 +216,7 @@ static int hostapd_config_read_eap_user(const char *fname,
        FILE *f;
        char buf[512], *pos, *start, *pos2;
        int line = 0, ret = 0, num_methods;
-       struct hostapd_eap_user *user, *tail = NULL;
+       struct hostapd_eap_user *user = NULL, *tail = NULL, *new_user = NULL;
 
        if (!fname)
                return 0;
@@ -228,6 +250,28 @@ static int hostapd_config_read_eap_user(const char *fname,
                if (buf[0] == '\0')
                        continue;
 
+#ifndef CONFIG_NO_RADIUS
+               if (user && os_strncmp(buf, "radius_accept_attr=", 19) == 0) {
+                       struct hostapd_radius_attr *attr, *a;
+                       attr = hostapd_parse_radius_attr(buf + 19);
+                       if (attr == NULL) {
+                               wpa_printf(MSG_ERROR, "Invalid radius_auth_req_attr: %s",
+                                          buf + 19);
+                               user = NULL; /* already in the BSS list */
+                               goto failed;
+                       }
+                       if (user->accept_attr == NULL) {
+                               user->accept_attr = attr;
+                       } else {
+                               a = user->accept_attr;
+                               while (a->next)
+                                       a = a->next;
+                               a->next = attr;
+                       }
+                       continue;
+               }
+#endif /* CONFIG_NO_RADIUS */
+
                user = NULL;
 
                if (buf[0] != '"' && buf[0] != '*') {
@@ -322,6 +366,10 @@ static int hostapd_config_read_eap_user(const char *fname,
                                                EAP_TTLS_AUTH_MSCHAPV2;
                                        goto skip_eap;
                                }
+                               if (os_strcmp(start, "MACACL") == 0) {
+                                       user->macacl = 1;
+                                       goto skip_eap;
+                               }
                                wpa_printf(MSG_ERROR, "Unsupported EAP type "
                                           "'%s' on line %d in '%s'",
                                           start, line, fname);
@@ -336,7 +384,7 @@ static int hostapd_config_read_eap_user(const char *fname,
                                break;
                        start = pos3;
                }
-               if (num_methods == 0 && user->ttls_auth == 0) {
+               if (num_methods == 0 && user->ttls_auth == 0 && !user->macacl) {
                        wpa_printf(MSG_ERROR, "No EAP types configured on "
                                   "line %d in '%s'", line, fname);
                        goto failed;
@@ -446,7 +494,7 @@ static int hostapd_config_read_eap_user(const char *fname,
 
        done:
                if (tail == NULL) {
-                       tail = conf->eap_user = user;
+                       tail = new_user = user;
                } else {
                        tail->next = user;
                        tail = user;
@@ -454,17 +502,26 @@ static int hostapd_config_read_eap_user(const char *fname,
                continue;
 
        failed:
-               if (user) {
-                       os_free(user->password);
-                       os_free(user->identity);
-                       os_free(user);
-               }
+               if (user)
+                       hostapd_config_free_eap_user(user);
                ret = -1;
                break;
        }
 
        fclose(f);
 
+       if (ret == 0) {
+               user = conf->eap_user;
+               while (user) {
+                       struct hostapd_eap_user *prev;
+
+                       prev = user;
+                       user = user->next;
+                       hostapd_config_free_eap_user(prev);
+               }
+               conf->eap_user = new_user;
+       }
+
        return ret;
 }
 #endif /* EAP_SERVER */
@@ -635,6 +692,14 @@ static int hostapd_config_parse_key_mgmt(int line, const char *value)
                else if (os_strcmp(start, "FT-SAE") == 0)
                        val |= WPA_KEY_MGMT_FT_SAE;
 #endif /* CONFIG_SAE */
+#ifdef CONFIG_SUITEB
+               else if (os_strcmp(start, "WPA-EAP-SUITE-B") == 0)
+                       val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B;
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SUITEB192
+               else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0)
+                       val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+#endif /* CONFIG_SUITEB192 */
                else {
                        wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
                                   line, start);
@@ -751,30 +816,32 @@ static int hostapd_parse_intlist(int **int_list, char *val)
 
 static int hostapd_config_bss(struct hostapd_config *conf, const char *ifname)
 {
-       struct hostapd_bss_config *bss;
+       struct hostapd_bss_config **all, *bss;
 
        if (*ifname == '\0')
                return -1;
 
-       bss = os_realloc_array(conf->bss, conf->num_bss + 1,
-                              sizeof(struct hostapd_bss_config));
-       if (bss == NULL) {
+       all = os_realloc_array(conf->bss, conf->num_bss + 1,
+                              sizeof(struct hostapd_bss_config *));
+       if (all == NULL) {
                wpa_printf(MSG_ERROR, "Failed to allocate memory for "
                           "multi-BSS entry");
                return -1;
        }
-       conf->bss = bss;
+       conf->bss = all;
 
-       bss = &(conf->bss[conf->num_bss]);
-       os_memset(bss, 0, sizeof(*bss));
+       bss = os_zalloc(sizeof(*bss));
+       if (bss == NULL)
+               return -1;
        bss->radius = os_zalloc(sizeof(*bss->radius));
        if (bss->radius == NULL) {
                wpa_printf(MSG_ERROR, "Failed to allocate memory for "
                           "multi-BSS RADIUS data");
+               os_free(bss);
                return -1;
        }
 
-       conf->num_bss++;
+       conf->bss[conf->num_bss++] = bss;
        conf->last_bss = bss;
 
        hostapd_config_defaults_bss(bss);
@@ -1022,8 +1089,8 @@ static int hostapd_config_ht_capab(struct hostapd_config *conf,
                conf->ht_capab |= HT_CAP_INFO_MAX_AMSDU_SIZE;
        if (os_strstr(capab, "[DSSS_CCK-40]"))
                conf->ht_capab |= HT_CAP_INFO_DSSS_CCK40MHZ;
-       if (os_strstr(capab, "[PSMP]"))
-               conf->ht_capab |= HT_CAP_INFO_PSMP_SUPP;
+       if (os_strstr(capab, "[40-INTOLERANT]"))
+               conf->ht_capab |= HT_CAP_INFO_40MHZ_INTOLERANT;
        if (os_strstr(capab, "[LSIG-TXOP-PROT]"))
                conf->ht_capab |= HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT;
 
@@ -1044,8 +1111,6 @@ static int hostapd_config_vht_capab(struct hostapd_config *conf,
                conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
        if (os_strstr(capab, "[VHT160-80PLUS80]"))
                conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
-       if (os_strstr(capab, "[VHT160-80PLUS80]"))
-               conf->vht_capab |= VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
        if (os_strstr(capab, "[RXLDPC]"))
                conf->vht_capab |= VHT_CAP_RXLDPC;
        if (os_strstr(capab, "[SHORT-GI-80]"))
@@ -1063,15 +1128,15 @@ static int hostapd_config_vht_capab(struct hostapd_config *conf,
        if (os_strstr(capab, "[RX-STBC-1234]"))
                conf->vht_capab |= VHT_CAP_RXSTBC_4;
        if (os_strstr(capab, "[SU-BEAMFORMER]"))
-               conf->vht_capab |= VHT_CAP_MU_BEAMFORMER_CAPABLE;
+               conf->vht_capab |= VHT_CAP_SU_BEAMFORMER_CAPABLE;
        if (os_strstr(capab, "[SU-BEAMFORMEE]"))
-               conf->vht_capab |= VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+               conf->vht_capab |= VHT_CAP_SU_BEAMFORMEE_CAPABLE;
        if (os_strstr(capab, "[BF-ANTENNA-2]") &&
-           (conf->vht_capab & VHT_CAP_MU_BEAMFORMER_CAPABLE))
-               conf->vht_capab |= VHT_CAP_BEAMFORMER_ANTENNAS_MAX;
+           (conf->vht_capab & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
+               conf->vht_capab |= (1 << VHT_CAP_BEAMFORMEE_STS_OFFSET);
        if (os_strstr(capab, "[SOUNDING-DIMENSION-2]") &&
-           (conf->vht_capab & VHT_CAP_MU_BEAMFORMER_CAPABLE))
-               conf->vht_capab |= VHT_CAP_SOUNDING_DIMENTION_MAX;
+           (conf->vht_capab & VHT_CAP_SU_BEAMFORMER_CAPABLE))
+               conf->vht_capab |= (1 << VHT_CAP_SOUNDING_DIMENSION_OFFSET);
        if (os_strstr(capab, "[MU-BEAMFORMER]"))
                conf->vht_capab |= VHT_CAP_MU_BEAMFORMER_CAPABLE;
        if (os_strstr(capab, "[MU-BEAMFORMEE]"))
@@ -1080,8 +1145,20 @@ static int hostapd_config_vht_capab(struct hostapd_config *conf,
                conf->vht_capab |= VHT_CAP_VHT_TXOP_PS;
        if (os_strstr(capab, "[HTC-VHT]"))
                conf->vht_capab |= VHT_CAP_HTC_VHT;
-       if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP0]"))
-               conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT;
+       if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP7]"))
+               conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX;
+       else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP6]"))
+               conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_6;
+       else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP5]"))
+               conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_5;
+       else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP4]"))
+               conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_4;
+       else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP3]"))
+               conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_3;
+       else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP2]"))
+               conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_2;
+       else if (os_strstr(capab, "[MAX-A-MPDU-LEN-EXP1]"))
+               conf->vht_capab |= VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_1;
        if (os_strstr(capab, "[VHT-LINK-ADAPT2]") &&
            (conf->vht_capab & VHT_CAP_HTC_VHT))
                conf->vht_capab |= VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB;
@@ -1097,147 +1174,6 @@ static int hostapd_config_vht_capab(struct hostapd_config *conf,
 #endif /* CONFIG_IEEE80211AC */
 
 
-static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
-                                   struct hostapd_config *conf)
-{
-       if (bss->ieee802_1x && !bss->eap_server &&
-           !bss->radius->auth_servers) {
-               wpa_printf(MSG_ERROR, "Invalid IEEE 802.1X configuration (no "
-                          "EAP authenticator configured).");
-               return -1;
-       }
-
-       if (bss->wpa && bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
-           bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
-               wpa_printf(MSG_ERROR, "WPA-PSK using RADIUS enabled, but no "
-                          "RADIUS checking (macaddr_acl=2) enabled.");
-               return -1;
-       }
-
-       if (bss->wpa && (bss->wpa_key_mgmt & WPA_KEY_MGMT_PSK) &&
-           bss->ssid.wpa_psk == NULL && bss->ssid.wpa_passphrase == NULL &&
-           bss->ssid.wpa_psk_file == NULL &&
-           (bss->wpa_psk_radius != PSK_RADIUS_REQUIRED ||
-            bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH)) {
-               wpa_printf(MSG_ERROR, "WPA-PSK enabled, but PSK or passphrase "
-                          "is not configured.");
-               return -1;
-       }
-
-       if (hostapd_mac_comp_empty(bss->bssid) != 0) {
-               size_t i;
-
-               for (i = 0; i < conf->num_bss; i++) {
-                       if ((&conf->bss[i] != bss) &&
-                           (hostapd_mac_comp(conf->bss[i].bssid,
-                                             bss->bssid) == 0)) {
-                               wpa_printf(MSG_ERROR, "Duplicate BSSID " MACSTR
-                                          " on interface '%s' and '%s'.",
-                                          MAC2STR(bss->bssid),
-                                          conf->bss[i].iface, bss->iface);
-                               return -1;
-                       }
-               }
-       }
-
-#ifdef CONFIG_IEEE80211R
-       if (wpa_key_mgmt_ft(bss->wpa_key_mgmt) &&
-           (bss->nas_identifier == NULL ||
-            os_strlen(bss->nas_identifier) < 1 ||
-            os_strlen(bss->nas_identifier) > FT_R0KH_ID_MAX_LEN)) {
-               wpa_printf(MSG_ERROR, "FT (IEEE 802.11r) requires "
-                          "nas_identifier to be configured as a 1..48 octet "
-                          "string");
-               return -1;
-       }
-#endif /* CONFIG_IEEE80211R */
-
-#ifdef CONFIG_IEEE80211N
-       if (conf->ieee80211n && conf->hw_mode == HOSTAPD_MODE_IEEE80211B) {
-               bss->disable_11n = 1;
-               wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) in 11b mode is not "
-                          "allowed, disabling HT capabilites");
-       }
-
-       if (conf->ieee80211n &&
-           bss->ssid.security_policy == SECURITY_STATIC_WEP) {
-               bss->disable_11n = 1;
-               wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WEP is not "
-                          "allowed, disabling HT capabilities");
-       }
-
-       if (conf->ieee80211n && bss->wpa &&
-           !(bss->wpa_pairwise & WPA_CIPHER_CCMP) &&
-           !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP))) {
-               bss->disable_11n = 1;
-               wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WPA/WPA2 "
-                          "requires CCMP/GCMP to be enabled, disabling HT "
-                          "capabilities");
-       }
-#endif /* CONFIG_IEEE80211N */
-
-#ifdef CONFIG_WPS2
-       if (bss->wps_state && bss->ignore_broadcast_ssid) {
-               wpa_printf(MSG_INFO, "WPS: ignore_broadcast_ssid "
-                          "configuration forced WPS to be disabled");
-               bss->wps_state = 0;
-       }
-
-       if (bss->wps_state && bss->ssid.wep.keys_set && bss->wpa == 0) {
-               wpa_printf(MSG_INFO, "WPS: WEP configuration forced WPS to be "
-                          "disabled");
-               bss->wps_state = 0;
-       }
-
-       if (bss->wps_state && bss->wpa &&
-           (!(bss->wpa & 2) ||
-            !(bss->rsn_pairwise & WPA_CIPHER_CCMP))) {
-               wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without "
-                          "WPA2/CCMP forced WPS to be disabled");
-               bss->wps_state = 0;
-       }
-#endif /* CONFIG_WPS2 */
-
-#ifdef CONFIG_HS20
-       if (bss->hs20 &&
-           (!(bss->wpa & 2) ||
-            !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)))) {
-               wpa_printf(MSG_ERROR, "HS 2.0: WPA2-Enterprise/CCMP "
-                          "configuration is required for Hotspot 2.0 "
-                          "functionality");
-               return -1;
-       }
-#endif /* CONFIG_HS20 */
-
-       return 0;
-}
-
-
-static int hostapd_config_check(struct hostapd_config *conf)
-{
-       size_t i;
-
-       if (conf->ieee80211d && (!conf->country[0] || !conf->country[1])) {
-               wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11d without "
-                          "setting the country_code");
-               return -1;
-       }
-
-       if (conf->ieee80211h && !conf->ieee80211d) {
-               wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11h without "
-                          "IEEE 802.11d enabled");
-               return -1;
-       }
-
-       for (i = 0; i < conf->num_bss; i++) {
-               if (hostapd_config_check_bss(&conf->bss[i], conf))
-                       return -1;
-       }
-
-       return 0;
-}
-
-
 #ifdef CONFIG_INTERWORKING
 static int parse_roaming_consortium(struct hostapd_bss_config *bss, char *pos,
                                    int line)
@@ -1274,26 +1210,34 @@ static int parse_roaming_consortium(struct hostapd_bss_config *bss, char *pos,
 static int parse_lang_string(struct hostapd_lang_string **array,
                             unsigned int *count, char *pos)
 {
-       char *sep;
-       size_t clen, nlen;
+       char *sep, *str = NULL;
+       size_t clen, nlen, slen;
        struct hostapd_lang_string *ls;
+       int ret = -1;
+
+       if (*pos == '"' || (*pos == 'P' && pos[1] == '"')) {
+               str = wpa_config_parse_string(pos, &slen);
+               if (!str)
+                       return -1;
+               pos = str;
+       }
 
        sep = os_strchr(pos, ':');
        if (sep == NULL)
-               return -1;
+               goto fail;
        *sep++ = '\0';
 
        clen = os_strlen(pos);
-       if (clen < 2)
-               return -1;
+       if (clen < 2 || clen > sizeof(ls->lang))
+               goto fail;
        nlen = os_strlen(sep);
        if (nlen > 252)
-               return -1;
+               goto fail;
 
        ls = os_realloc_array(*array, *count + 1,
                              sizeof(struct hostapd_lang_string));
        if (ls == NULL)
-               return -1;
+               goto fail;
 
        *array = ls;
        ls = &(*array)[*count];
@@ -1304,7 +1248,10 @@ static int parse_lang_string(struct hostapd_lang_string **array,
        ls->name_len = nlen;
        os_memcpy(ls->name, sep, nlen);
 
-       return 0;
+       ret = 0;
+fail:
+       os_free(str);
+       return ret;
 }
 
 
@@ -1331,7 +1278,7 @@ static int parse_3gpp_cell_net(struct hostapd_bss_config *bss, char *buf,
 
        count = 1;
        for (pos = buf; *pos; pos++) {
-               if ((*pos < '0' && *pos > '9') && *pos != ';' && *pos != ',')
+               if ((*pos < '0' || *pos > '9') && *pos != ';' && *pos != ',')
                        goto fail;
                if (*pos == ';')
                        count++;
@@ -1535,6 +1482,47 @@ fail:
        return -1;
 }
 
+
+static int parse_qos_map_set(struct hostapd_bss_config *bss,
+                            char *buf, int line)
+{
+       u8 qos_map_set[16 + 2 * 21], count = 0;
+       char *pos = buf;
+       int val;
+
+       for (;;) {
+               if (count == sizeof(qos_map_set)) {
+                       wpa_printf(MSG_ERROR, "Line %d: Too many qos_map_set "
+                                  "parameters '%s'", line, buf);
+                       return -1;
+               }
+
+               val = atoi(pos);
+               if (val > 255 || val < 0) {
+                       wpa_printf(MSG_ERROR, "Line %d: Invalid qos_map_set "
+                                  "'%s'", line, buf);
+                       return -1;
+               }
+
+               qos_map_set[count++] = val;
+               pos = os_strchr(pos, ',');
+               if (!pos)
+                       break;
+               pos++;
+       }
+
+       if (count < 16 || count & 1) {
+               wpa_printf(MSG_ERROR, "Line %d: Invalid qos_map_set '%s'",
+                          line, buf);
+               return -1;
+       }
+
+       os_memcpy(bss->qos_map_set, qos_map_set, count);
+       bss->qos_map_set_len = count;
+
+       return 0;
+}
+
 #endif /* CONFIG_INTERWORKING */
 
 
@@ -1632,7 +1620,7 @@ static int hs20_parse_wan_metrics(struct hostapd_bss_config *bss, char *buf,
 
 fail:
        wpa_printf(MSG_ERROR, "Line %d: Invalid hs20_wan_metrics '%s'",
-                  line, pos);
+                  line, buf);
        os_free(wan_metrics);
        return -1;
 }
@@ -1649,6 +1637,197 @@ static int hs20_parse_oper_friendly_name(struct hostapd_bss_config *bss,
        }
        return 0;
 }
+
+
+static int hs20_parse_icon(struct hostapd_bss_config *bss, char *pos)
+{
+       struct hs20_icon *icon;
+       char *end;
+
+       icon = os_realloc_array(bss->hs20_icons, bss->hs20_icons_count + 1,
+                               sizeof(struct hs20_icon));
+       if (icon == NULL)
+               return -1;
+       bss->hs20_icons = icon;
+       icon = &bss->hs20_icons[bss->hs20_icons_count];
+       os_memset(icon, 0, sizeof(*icon));
+
+       icon->width = atoi(pos);
+       pos = os_strchr(pos, ':');
+       if (pos == NULL)
+               return -1;
+       pos++;
+
+       icon->height = atoi(pos);
+       pos = os_strchr(pos, ':');
+       if (pos == NULL)
+               return -1;
+       pos++;
+
+       end = os_strchr(pos, ':');
+       if (end == NULL || end - pos > 3)
+               return -1;
+       os_memcpy(icon->language, pos, end - pos);
+       pos = end + 1;
+
+       end = os_strchr(pos, ':');
+       if (end == NULL || end - pos > 255)
+               return -1;
+       os_memcpy(icon->type, pos, end - pos);
+       pos = end + 1;
+
+       end = os_strchr(pos, ':');
+       if (end == NULL || end - pos > 255)
+               return -1;
+       os_memcpy(icon->name, pos, end - pos);
+       pos = end + 1;
+
+       if (os_strlen(pos) > 255)
+               return -1;
+       os_memcpy(icon->file, pos, os_strlen(pos));
+
+       bss->hs20_icons_count++;
+
+       return 0;
+}
+
+
+static int hs20_parse_osu_ssid(struct hostapd_bss_config *bss,
+                              char *pos, int line)
+{
+       size_t slen;
+       char *str;
+
+       str = wpa_config_parse_string(pos, &slen);
+       if (str == NULL || slen < 1 || slen > HOSTAPD_MAX_SSID_LEN) {
+               wpa_printf(MSG_ERROR, "Line %d: Invalid SSID '%s'", line, pos);
+               os_free(str);
+               return -1;
+       }
+
+       os_memcpy(bss->osu_ssid, str, slen);
+       bss->osu_ssid_len = slen;
+       os_free(str);
+
+       return 0;
+}
+
+
+static int hs20_parse_osu_server_uri(struct hostapd_bss_config *bss,
+                                    char *pos, int line)
+{
+       struct hs20_osu_provider *p;
+
+       p = os_realloc_array(bss->hs20_osu_providers,
+                            bss->hs20_osu_providers_count + 1, sizeof(*p));
+       if (p == NULL)
+               return -1;
+
+       bss->hs20_osu_providers = p;
+       bss->last_osu = &bss->hs20_osu_providers[bss->hs20_osu_providers_count];
+       bss->hs20_osu_providers_count++;
+       os_memset(bss->last_osu, 0, sizeof(*p));
+       bss->last_osu->server_uri = os_strdup(pos);
+
+       return 0;
+}
+
+
+static int hs20_parse_osu_friendly_name(struct hostapd_bss_config *bss,
+                                       char *pos, int line)
+{
+       if (bss->last_osu == NULL) {
+               wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+               return -1;
+       }
+
+       if (parse_lang_string(&bss->last_osu->friendly_name,
+                             &bss->last_osu->friendly_name_count, pos)) {
+               wpa_printf(MSG_ERROR, "Line %d: Invalid osu_friendly_name '%s'",
+                          line, pos);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int hs20_parse_osu_nai(struct hostapd_bss_config *bss,
+                             char *pos, int line)
+{
+       if (bss->last_osu == NULL) {
+               wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+               return -1;
+       }
+
+       os_free(bss->last_osu->osu_nai);
+       bss->last_osu->osu_nai = os_strdup(pos);
+       if (bss->last_osu->osu_nai == NULL)
+               return -1;
+
+       return 0;
+}
+
+
+static int hs20_parse_osu_method_list(struct hostapd_bss_config *bss, char *pos,
+                                     int line)
+{
+       if (bss->last_osu == NULL) {
+               wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+               return -1;
+       }
+
+       if (hostapd_parse_intlist(&bss->last_osu->method_list, pos)) {
+               wpa_printf(MSG_ERROR, "Line %d: Invalid osu_method_list", line);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int hs20_parse_osu_icon(struct hostapd_bss_config *bss, char *pos,
+                              int line)
+{
+       char **n;
+       struct hs20_osu_provider *p = bss->last_osu;
+
+       if (p == NULL) {
+               wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+               return -1;
+       }
+
+       n = os_realloc_array(p->icons, p->icons_count + 1, sizeof(char *));
+       if (n == NULL)
+               return -1;
+       p->icons = n;
+       p->icons[p->icons_count] = os_strdup(pos);
+       if (p->icons[p->icons_count] == NULL)
+               return -1;
+       p->icons_count++;
+
+       return 0;
+}
+
+
+static int hs20_parse_osu_service_desc(struct hostapd_bss_config *bss,
+                                      char *pos, int line)
+{
+       if (bss->last_osu == NULL) {
+               wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+               return -1;
+       }
+
+       if (parse_lang_string(&bss->last_osu->service_desc,
+                             &bss->last_osu->service_desc_count, pos)) {
+               wpa_printf(MSG_ERROR, "Line %d: Invalid osu_service_desc '%s'",
+                          line, pos);
+               return -1;
+       }
+
+       return 0;
+}
+
 #endif /* CONFIG_HS20 */
 
 
@@ -1677,1330 +1856,1434 @@ static struct wpabuf * hostapd_parse_bin(const char *buf)
 #endif /* CONFIG_WPS_NFC */
 
 
+#ifdef CONFIG_ACS
+static int hostapd_config_parse_acs_chan_bias(struct hostapd_config *conf,
+                                             char *pos)
+{
+       struct acs_bias *bias = NULL, *tmp;
+       unsigned int num = 0;
+       char *end;
+
+       while (*pos) {
+               tmp = os_realloc_array(bias, num + 1, sizeof(*bias));
+               if (!tmp)
+                       goto fail;
+               bias = tmp;
+
+               bias[num].channel = atoi(pos);
+               if (bias[num].channel <= 0)
+                       goto fail;
+               pos = os_strchr(pos, ':');
+               if (!pos)
+                       goto fail;
+               pos++;
+               bias[num].bias = strtod(pos, &end);
+               if (end == pos || bias[num].bias < 0.0)
+                       goto fail;
+               pos = end;
+               if (*pos != ' ' && *pos != '\0')
+                       goto fail;
+               num++;
+       }
+
+       os_free(conf->acs_chan_bias);
+       conf->acs_chan_bias = bias;
+       conf->num_acs_chan_bias = num;
+
+       return 0;
+fail:
+       os_free(bias);
+       return -1;
+}
+#endif /* CONFIG_ACS */
+
+
 static int hostapd_config_fill(struct hostapd_config *conf,
                               struct hostapd_bss_config *bss,
                               char *buf, char *pos, int line)
 {
-       int errors = 0;
-
-       {
-               if (os_strcmp(buf, "interface") == 0) {
-                       os_strlcpy(conf->bss[0].iface, pos,
-                                  sizeof(conf->bss[0].iface));
-               } else if (os_strcmp(buf, "bridge") == 0) {
-                       os_strlcpy(bss->bridge, pos, sizeof(bss->bridge));
-               } else if (os_strcmp(buf, "vlan_bridge") == 0) {
-                       os_strlcpy(bss->vlan_bridge, pos,
-                                  sizeof(bss->vlan_bridge));
-               } else if (os_strcmp(buf, "wds_bridge") == 0) {
-                       os_strlcpy(bss->wds_bridge, pos,
-                                  sizeof(bss->wds_bridge));
-               } else if (os_strcmp(buf, "driver") == 0) {
-                       int j;
-                       /* clear to get error below if setting is invalid */
-                       conf->driver = NULL;
-                       for (j = 0; wpa_drivers[j]; j++) {
-                               if (os_strcmp(pos, wpa_drivers[j]->name) == 0)
-                               {
-                                       conf->driver = wpa_drivers[j];
-                                       break;
-                               }
-                       }
-                       if (conf->driver == NULL) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid/"
-                                          "unknown driver '%s'", line, pos);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "debug") == 0) {
-                       wpa_printf(MSG_DEBUG, "Line %d: DEPRECATED: 'debug' "
-                                  "configuration variable is not used "
-                                  "anymore", line);
-               } else if (os_strcmp(buf, "logger_syslog_level") == 0) {
-                       bss->logger_syslog_level = atoi(pos);
-               } else if (os_strcmp(buf, "logger_stdout_level") == 0) {
-                       bss->logger_stdout_level = atoi(pos);
-               } else if (os_strcmp(buf, "logger_syslog") == 0) {
-                       bss->logger_syslog = atoi(pos);
-               } else if (os_strcmp(buf, "logger_stdout") == 0) {
-                       bss->logger_stdout = atoi(pos);
-               } else if (os_strcmp(buf, "dump_file") == 0) {
-                       bss->dump_log_name = os_strdup(pos);
-               } else if (os_strcmp(buf, "ssid") == 0) {
-                       bss->ssid.ssid_len = os_strlen(pos);
-                       if (bss->ssid.ssid_len > HOSTAPD_MAX_SSID_LEN ||
-                           bss->ssid.ssid_len < 1) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid SSID "
-                                          "'%s'", line, pos);
-                               errors++;
-                       } else {
-                               os_memcpy(bss->ssid.ssid, pos,
-                                         bss->ssid.ssid_len);
-                               bss->ssid.ssid_set = 1;
-                       }
-               } else if (os_strcmp(buf, "ssid2") == 0) {
-                       size_t slen;
-                       char *str = wpa_config_parse_string(pos, &slen);
-                       if (str == NULL || slen < 1 ||
-                                  slen > HOSTAPD_MAX_SSID_LEN) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid SSID "
-                                          "'%s'", line, pos);
-                               errors++;
-                       } else {
-                               os_memcpy(bss->ssid.ssid, str, slen);
-                               bss->ssid.ssid_len = slen;
-                               bss->ssid.ssid_set = 1;
+       if (os_strcmp(buf, "interface") == 0) {
+               os_strlcpy(conf->bss[0]->iface, pos,
+                          sizeof(conf->bss[0]->iface));
+       } else if (os_strcmp(buf, "bridge") == 0) {
+               os_strlcpy(bss->bridge, pos, sizeof(bss->bridge));
+       } else if (os_strcmp(buf, "vlan_bridge") == 0) {
+               os_strlcpy(bss->vlan_bridge, pos, sizeof(bss->vlan_bridge));
+       } else if (os_strcmp(buf, "wds_bridge") == 0) {
+               os_strlcpy(bss->wds_bridge, pos, sizeof(bss->wds_bridge));
+       } else if (os_strcmp(buf, "driver") == 0) {
+               int j;
+               /* clear to get error below if setting is invalid */
+               conf->driver = NULL;
+               for (j = 0; wpa_drivers[j]; j++) {
+                       if (os_strcmp(pos, wpa_drivers[j]->name) == 0) {
+                               conf->driver = wpa_drivers[j];
+                               break;
                        }
+               }
+               if (conf->driver == NULL) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: invalid/unknown driver '%s'",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "driver_params") == 0) {
+               os_free(conf->driver_params);
+               conf->driver_params = os_strdup(pos);
+       } else if (os_strcmp(buf, "debug") == 0) {
+               wpa_printf(MSG_DEBUG, "Line %d: DEPRECATED: 'debug' configuration variable is not used anymore",
+                          line);
+       } else if (os_strcmp(buf, "logger_syslog_level") == 0) {
+               bss->logger_syslog_level = atoi(pos);
+       } else if (os_strcmp(buf, "logger_stdout_level") == 0) {
+               bss->logger_stdout_level = atoi(pos);
+       } else if (os_strcmp(buf, "logger_syslog") == 0) {
+               bss->logger_syslog = atoi(pos);
+       } else if (os_strcmp(buf, "logger_stdout") == 0) {
+               bss->logger_stdout = atoi(pos);
+       } else if (os_strcmp(buf, "dump_file") == 0) {
+               wpa_printf(MSG_INFO, "Line %d: DEPRECATED: 'dump_file' configuration variable is not used anymore",
+                          line);
+       } else if (os_strcmp(buf, "ssid") == 0) {
+               bss->ssid.ssid_len = os_strlen(pos);
+               if (bss->ssid.ssid_len > HOSTAPD_MAX_SSID_LEN ||
+                   bss->ssid.ssid_len < 1) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'",
+                                  line, pos);
+                       return 1;
+               }
+               os_memcpy(bss->ssid.ssid, pos, bss->ssid.ssid_len);
+               bss->ssid.ssid_set = 1;
+       } else if (os_strcmp(buf, "ssid2") == 0) {
+               size_t slen;
+               char *str = wpa_config_parse_string(pos, &slen);
+               if (str == NULL || slen < 1 || slen > HOSTAPD_MAX_SSID_LEN) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'",
+                                  line, pos);
                        os_free(str);
-               } else if (os_strcmp(buf, "utf8_ssid") == 0) {
-                       bss->ssid.utf8_ssid = atoi(pos) > 0;
-               } else if (os_strcmp(buf, "macaddr_acl") == 0) {
-                       bss->macaddr_acl = atoi(pos);
-                       if (bss->macaddr_acl != ACCEPT_UNLESS_DENIED &&
-                           bss->macaddr_acl != DENY_UNLESS_ACCEPTED &&
-                           bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
-                               wpa_printf(MSG_ERROR, "Line %d: unknown "
-                                          "macaddr_acl %d",
-                                          line, bss->macaddr_acl);
-                       }
-               } else if (os_strcmp(buf, "accept_mac_file") == 0) {
-                       if (hostapd_config_read_maclist(pos, &bss->accept_mac,
-                                                       &bss->num_accept_mac))
-                       {
-                               wpa_printf(MSG_ERROR, "Line %d: Failed to "
-                                          "read accept_mac_file '%s'",
-                                          line, pos);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "deny_mac_file") == 0) {
-                       if (hostapd_config_read_maclist(pos, &bss->deny_mac,
-                                                       &bss->num_deny_mac)) {
-                               wpa_printf(MSG_ERROR, "Line %d: Failed to "
-                                          "read deny_mac_file '%s'",
-                                          line, pos);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "wds_sta") == 0) {
-                       bss->wds_sta = atoi(pos);
-               } else if (os_strcmp(buf, "ap_isolate") == 0) {
-                       bss->isolate = atoi(pos);
-               } else if (os_strcmp(buf, "ap_max_inactivity") == 0) {
-                       bss->ap_max_inactivity = atoi(pos);
-               } else if (os_strcmp(buf, "skip_inactivity_poll") == 0) {
-                       bss->skip_inactivity_poll = atoi(pos);
-               } else if (os_strcmp(buf, "country_code") == 0) {
-                       os_memcpy(conf->country, pos, 2);
-                       /* FIX: make this configurable */
-                       conf->country[2] = ' ';
-               } else if (os_strcmp(buf, "ieee80211d") == 0) {
-                       conf->ieee80211d = atoi(pos);
-               } else if (os_strcmp(buf, "ieee80211h") == 0) {
-                       conf->ieee80211h = atoi(pos);
-               } else if (os_strcmp(buf, "ieee8021x") == 0) {
-                       bss->ieee802_1x = atoi(pos);
-               } else if (os_strcmp(buf, "eapol_version") == 0) {
-                       bss->eapol_version = atoi(pos);
-                       if (bss->eapol_version < 1 ||
-                           bss->eapol_version > 2) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid EAPOL "
-                                          "version (%d): '%s'.",
-                                          line, bss->eapol_version, pos);
-                               errors++;
-                       } else
-                               wpa_printf(MSG_DEBUG, "eapol_version=%d",
-                                          bss->eapol_version);
+                       return 1;
+               }
+               os_memcpy(bss->ssid.ssid, str, slen);
+               bss->ssid.ssid_len = slen;
+               bss->ssid.ssid_set = 1;
+               os_free(str);
+       } else if (os_strcmp(buf, "utf8_ssid") == 0) {
+               bss->ssid.utf8_ssid = atoi(pos) > 0;
+       } else if (os_strcmp(buf, "macaddr_acl") == 0) {
+               bss->macaddr_acl = atoi(pos);
+               if (bss->macaddr_acl != ACCEPT_UNLESS_DENIED &&
+                   bss->macaddr_acl != DENY_UNLESS_ACCEPTED &&
+                   bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
+                       wpa_printf(MSG_ERROR, "Line %d: unknown macaddr_acl %d",
+                                  line, bss->macaddr_acl);
+               }
+       } else if (os_strcmp(buf, "accept_mac_file") == 0) {
+               if (hostapd_config_read_maclist(pos, &bss->accept_mac,
+                                               &bss->num_accept_mac)) {
+                       wpa_printf(MSG_ERROR, "Line %d: Failed to read accept_mac_file '%s'",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "deny_mac_file") == 0) {
+               if (hostapd_config_read_maclist(pos, &bss->deny_mac,
+                                               &bss->num_deny_mac)) {
+                       wpa_printf(MSG_ERROR, "Line %d: Failed to read deny_mac_file '%s'",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "wds_sta") == 0) {
+               bss->wds_sta = atoi(pos);
+       } else if (os_strcmp(buf, "start_disabled") == 0) {
+               bss->start_disabled = atoi(pos);
+       } else if (os_strcmp(buf, "ap_isolate") == 0) {
+               bss->isolate = atoi(pos);
+       } else if (os_strcmp(buf, "ap_max_inactivity") == 0) {
+               bss->ap_max_inactivity = atoi(pos);
+       } else if (os_strcmp(buf, "skip_inactivity_poll") == 0) {
+               bss->skip_inactivity_poll = atoi(pos);
+       } else if (os_strcmp(buf, "country_code") == 0) {
+               os_memcpy(conf->country, pos, 2);
+               /* FIX: make this configurable */
+               conf->country[2] = ' ';
+       } else if (os_strcmp(buf, "ieee80211d") == 0) {
+               conf->ieee80211d = atoi(pos);
+       } else if (os_strcmp(buf, "ieee80211h") == 0) {
+               conf->ieee80211h = atoi(pos);
+       } else if (os_strcmp(buf, "ieee8021x") == 0) {
+               bss->ieee802_1x = atoi(pos);
+       } else if (os_strcmp(buf, "eapol_version") == 0) {
+               bss->eapol_version = atoi(pos);
+               if (bss->eapol_version < 1 || bss->eapol_version > 2) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: invalid EAPOL version (%d): '%s'.",
+                                  line, bss->eapol_version, pos);
+                       return 1;
+               }
+               wpa_printf(MSG_DEBUG, "eapol_version=%d", bss->eapol_version);
 #ifdef EAP_SERVER
-               } else if (os_strcmp(buf, "eap_authenticator") == 0) {
-                       bss->eap_server = atoi(pos);
-                       wpa_printf(MSG_ERROR, "Line %d: obsolete "
-                                  "eap_authenticator used; this has been "
-                                  "renamed to eap_server", line);
-               } else if (os_strcmp(buf, "eap_server") == 0) {
-                       bss->eap_server = atoi(pos);
-               } else if (os_strcmp(buf, "eap_user_file") == 0) {
-                       if (hostapd_config_read_eap_user(pos, bss))
-                               errors++;
-               } else if (os_strcmp(buf, "ca_cert") == 0) {
-                       os_free(bss->ca_cert);
-                       bss->ca_cert = os_strdup(pos);
-               } else if (os_strcmp(buf, "server_cert") == 0) {
-                       os_free(bss->server_cert);
-                       bss->server_cert = os_strdup(pos);
-               } else if (os_strcmp(buf, "private_key") == 0) {
-                       os_free(bss->private_key);
-                       bss->private_key = os_strdup(pos);
-               } else if (os_strcmp(buf, "private_key_passwd") == 0) {
-                       os_free(bss->private_key_passwd);
-                       bss->private_key_passwd = os_strdup(pos);
-               } else if (os_strcmp(buf, "check_crl") == 0) {
-                       bss->check_crl = atoi(pos);
-               } else if (os_strcmp(buf, "ocsp_stapling_response") == 0) {
-                       os_free(bss->ocsp_stapling_response);
-                       bss->ocsp_stapling_response = os_strdup(pos);
-               } else if (os_strcmp(buf, "dh_file") == 0) {
-                       os_free(bss->dh_file);
-                       bss->dh_file = os_strdup(pos);
-               } else if (os_strcmp(buf, "fragment_size") == 0) {
-                       bss->fragment_size = atoi(pos);
+       } else if (os_strcmp(buf, "eap_authenticator") == 0) {
+               bss->eap_server = atoi(pos);
+               wpa_printf(MSG_ERROR, "Line %d: obsolete eap_authenticator used; this has been renamed to eap_server", line);
+       } else if (os_strcmp(buf, "eap_server") == 0) {
+               bss->eap_server = atoi(pos);
+       } else if (os_strcmp(buf, "eap_user_file") == 0) {
+               if (hostapd_config_read_eap_user(pos, bss))
+                       return 1;
+       } else if (os_strcmp(buf, "ca_cert") == 0) {
+               os_free(bss->ca_cert);
+               bss->ca_cert = os_strdup(pos);
+       } else if (os_strcmp(buf, "server_cert") == 0) {
+               os_free(bss->server_cert);
+               bss->server_cert = os_strdup(pos);
+       } else if (os_strcmp(buf, "private_key") == 0) {
+               os_free(bss->private_key);
+               bss->private_key = os_strdup(pos);
+       } else if (os_strcmp(buf, "private_key_passwd") == 0) {
+               os_free(bss->private_key_passwd);
+               bss->private_key_passwd = os_strdup(pos);
+       } else if (os_strcmp(buf, "check_crl") == 0) {
+               bss->check_crl = atoi(pos);
+       } else if (os_strcmp(buf, "ocsp_stapling_response") == 0) {
+               os_free(bss->ocsp_stapling_response);
+               bss->ocsp_stapling_response = os_strdup(pos);
+       } else if (os_strcmp(buf, "dh_file") == 0) {
+               os_free(bss->dh_file);
+               bss->dh_file = os_strdup(pos);
+       } else if (os_strcmp(buf, "openssl_ciphers") == 0) {
+               os_free(bss->openssl_ciphers);
+               bss->openssl_ciphers = os_strdup(pos);
+       } else if (os_strcmp(buf, "fragment_size") == 0) {
+               bss->fragment_size = atoi(pos);
 #ifdef EAP_SERVER_FAST
-               } else if (os_strcmp(buf, "pac_opaque_encr_key") == 0) {
-                       os_free(bss->pac_opaque_encr_key);
-                       bss->pac_opaque_encr_key = os_malloc(16);
-                       if (bss->pac_opaque_encr_key == NULL) {
-                               wpa_printf(MSG_ERROR, "Line %d: No memory for "
-                                          "pac_opaque_encr_key", line);
-                               errors++;
-                       } else if (hexstr2bin(pos, bss->pac_opaque_encr_key,
-                                             16)) {
-                               wpa_printf(MSG_ERROR, "Line %d: Invalid "
-                                          "pac_opaque_encr_key", line);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "eap_fast_a_id") == 0) {
-                       size_t idlen = os_strlen(pos);
-                       if (idlen & 1) {
-                               wpa_printf(MSG_ERROR, "Line %d: Invalid "
-                                          "eap_fast_a_id", line);
-                               errors++;
-                       } else {
-                               os_free(bss->eap_fast_a_id);
-                               bss->eap_fast_a_id = os_malloc(idlen / 2);
-                               if (bss->eap_fast_a_id == NULL ||
-                                   hexstr2bin(pos, bss->eap_fast_a_id,
-                                              idlen / 2)) {
-                                       wpa_printf(MSG_ERROR, "Line %d: "
-                                                  "Failed to parse "
-                                                  "eap_fast_a_id", line);
-                                       errors++;
-                               } else
-                                       bss->eap_fast_a_id_len = idlen / 2;
-                       }
-               } else if (os_strcmp(buf, "eap_fast_a_id_info") == 0) {
-                       os_free(bss->eap_fast_a_id_info);
-                       bss->eap_fast_a_id_info = os_strdup(pos);
-               } else if (os_strcmp(buf, "eap_fast_prov") == 0) {
-                       bss->eap_fast_prov = atoi(pos);
-               } else if (os_strcmp(buf, "pac_key_lifetime") == 0) {
-                       bss->pac_key_lifetime = atoi(pos);
-               } else if (os_strcmp(buf, "pac_key_refresh_time") == 0) {
-                       bss->pac_key_refresh_time = atoi(pos);
+       } else if (os_strcmp(buf, "pac_opaque_encr_key") == 0) {
+               os_free(bss->pac_opaque_encr_key);
+               bss->pac_opaque_encr_key = os_malloc(16);
+               if (bss->pac_opaque_encr_key == NULL) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: No memory for pac_opaque_encr_key",
+                                  line);
+                       return 1;
+               } else if (hexstr2bin(pos, bss->pac_opaque_encr_key, 16)) {
+                       wpa_printf(MSG_ERROR, "Line %d: Invalid pac_opaque_encr_key",
+                                  line);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "eap_fast_a_id") == 0) {
+               size_t idlen = os_strlen(pos);
+               if (idlen & 1) {
+                       wpa_printf(MSG_ERROR, "Line %d: Invalid eap_fast_a_id",
+                                  line);
+                       return 1;
+               }
+               os_free(bss->eap_fast_a_id);
+               bss->eap_fast_a_id = os_malloc(idlen / 2);
+               if (bss->eap_fast_a_id == NULL ||
+                   hexstr2bin(pos, bss->eap_fast_a_id, idlen / 2)) {
+                       wpa_printf(MSG_ERROR, "Line %d: Failed to parse eap_fast_a_id",
+                                  line);
+                       os_free(bss->eap_fast_a_id);
+                       bss->eap_fast_a_id = NULL;
+                       return 1;
+               } else {
+                       bss->eap_fast_a_id_len = idlen / 2;
+               }
+       } else if (os_strcmp(buf, "eap_fast_a_id_info") == 0) {
+               os_free(bss->eap_fast_a_id_info);
+               bss->eap_fast_a_id_info = os_strdup(pos);
+       } else if (os_strcmp(buf, "eap_fast_prov") == 0) {
+               bss->eap_fast_prov = atoi(pos);
+       } else if (os_strcmp(buf, "pac_key_lifetime") == 0) {
+               bss->pac_key_lifetime = atoi(pos);
+       } else if (os_strcmp(buf, "pac_key_refresh_time") == 0) {
+               bss->pac_key_refresh_time = atoi(pos);
 #endif /* EAP_SERVER_FAST */
 #ifdef EAP_SERVER_SIM
-               } else if (os_strcmp(buf, "eap_sim_db") == 0) {
-                       os_free(bss->eap_sim_db);
-                       bss->eap_sim_db = os_strdup(pos);
-               } else if (os_strcmp(buf, "eap_sim_aka_result_ind") == 0) {
-                       bss->eap_sim_aka_result_ind = atoi(pos);
+       } else if (os_strcmp(buf, "eap_sim_db") == 0) {
+               os_free(bss->eap_sim_db);
+               bss->eap_sim_db = os_strdup(pos);
+       } else if (os_strcmp(buf, "eap_sim_aka_result_ind") == 0) {
+               bss->eap_sim_aka_result_ind = atoi(pos);
 #endif /* EAP_SERVER_SIM */
 #ifdef EAP_SERVER_TNC
-               } else if (os_strcmp(buf, "tnc") == 0) {
-                       bss->tnc = atoi(pos);
+       } else if (os_strcmp(buf, "tnc") == 0) {
+               bss->tnc = atoi(pos);
 #endif /* EAP_SERVER_TNC */
 #ifdef EAP_SERVER_PWD
-               } else if (os_strcmp(buf, "pwd_group") == 0) {
-                       bss->pwd_group = atoi(pos);
+       } else if (os_strcmp(buf, "pwd_group") == 0) {
+               bss->pwd_group = atoi(pos);
 #endif /* EAP_SERVER_PWD */
+       } else if (os_strcmp(buf, "eap_server_erp") == 0) {
+               bss->eap_server_erp = atoi(pos);
 #endif /* EAP_SERVER */
-               } else if (os_strcmp(buf, "eap_message") == 0) {
-                       char *term;
-                       bss->eap_req_id_text = os_strdup(pos);
-                       if (bss->eap_req_id_text == NULL) {
-                               wpa_printf(MSG_ERROR, "Line %d: Failed to "
-                                          "allocate memory for "
-                                          "eap_req_id_text", line);
-                               errors++;
-                               return errors;
-                       }
-                       bss->eap_req_id_text_len =
-                               os_strlen(bss->eap_req_id_text);
-                       term = os_strstr(bss->eap_req_id_text, "\\0");
-                       if (term) {
-                               *term++ = '\0';
-                               os_memmove(term, term + 1,
-                                          bss->eap_req_id_text_len -
-                                          (term - bss->eap_req_id_text) - 1);
-                               bss->eap_req_id_text_len--;
-                       }
-               } else if (os_strcmp(buf, "wep_key_len_broadcast") == 0) {
-                       bss->default_wep_key_len = atoi(pos);
-                       if (bss->default_wep_key_len > 13) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid WEP "
-                                          "key len %lu (= %lu bits)", line,
-                                          (unsigned long)
-                                          bss->default_wep_key_len,
-                                          (unsigned long)
-                                          bss->default_wep_key_len * 8);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "wep_key_len_unicast") == 0) {
-                       bss->individual_wep_key_len = atoi(pos);
-                       if (bss->individual_wep_key_len < 0 ||
-                           bss->individual_wep_key_len > 13) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid WEP "
-                                          "key len %d (= %d bits)", line,
-                                          bss->individual_wep_key_len,
-                                          bss->individual_wep_key_len * 8);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "wep_rekey_period") == 0) {
-                       bss->wep_rekeying_period = atoi(pos);
-                       if (bss->wep_rekeying_period < 0) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid "
-                                          "period %d",
-                                          line, bss->wep_rekeying_period);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "eap_reauth_period") == 0) {
-                       bss->eap_reauth_period = atoi(pos);
-                       if (bss->eap_reauth_period < 0) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid "
-                                          "period %d",
-                                          line, bss->eap_reauth_period);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "eapol_key_index_workaround") == 0) {
-                       bss->eapol_key_index_workaround = atoi(pos);
+       } else if (os_strcmp(buf, "eap_message") == 0) {
+               char *term;
+               os_free(bss->eap_req_id_text);
+               bss->eap_req_id_text = os_strdup(pos);
+               if (bss->eap_req_id_text == NULL) {
+                       wpa_printf(MSG_ERROR, "Line %d: Failed to allocate memory for eap_req_id_text",
+                                  line);
+                       return 1;
+               }
+               bss->eap_req_id_text_len = os_strlen(bss->eap_req_id_text);
+               term = os_strstr(bss->eap_req_id_text, "\\0");
+               if (term) {
+                       *term++ = '\0';
+                       os_memmove(term, term + 1,
+                                  bss->eap_req_id_text_len -
+                                  (term - bss->eap_req_id_text) - 1);
+                       bss->eap_req_id_text_len--;
+               }
+       } else if (os_strcmp(buf, "erp_send_reauth_start") == 0) {
+               bss->erp_send_reauth_start = atoi(pos);
+       } else if (os_strcmp(buf, "erp_domain") == 0) {
+               os_free(bss->erp_domain);
+               bss->erp_domain = os_strdup(pos);
+       } else if (os_strcmp(buf, "wep_key_len_broadcast") == 0) {
+               bss->default_wep_key_len = atoi(pos);
+               if (bss->default_wep_key_len > 13) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid WEP key len %lu (= %lu bits)",
+                                  line,
+                                  (unsigned long) bss->default_wep_key_len,
+                                  (unsigned long)
+                                  bss->default_wep_key_len * 8);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "wep_key_len_unicast") == 0) {
+               bss->individual_wep_key_len = atoi(pos);
+               if (bss->individual_wep_key_len < 0 ||
+                   bss->individual_wep_key_len > 13) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid WEP key len %d (= %d bits)",
+                                  line, bss->individual_wep_key_len,
+                                  bss->individual_wep_key_len * 8);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "wep_rekey_period") == 0) {
+               bss->wep_rekeying_period = atoi(pos);
+               if (bss->wep_rekeying_period < 0) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid period %d",
+                                  line, bss->wep_rekeying_period);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "eap_reauth_period") == 0) {
+               bss->eap_reauth_period = atoi(pos);
+               if (bss->eap_reauth_period < 0) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid period %d",
+                                  line, bss->eap_reauth_period);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "eapol_key_index_workaround") == 0) {
+               bss->eapol_key_index_workaround = atoi(pos);
 #ifdef CONFIG_IAPP
-               } else if (os_strcmp(buf, "iapp_interface") == 0) {
-                       bss->ieee802_11f = 1;
-                       os_strlcpy(bss->iapp_iface, pos,
-                                  sizeof(bss->iapp_iface));
+       } else if (os_strcmp(buf, "iapp_interface") == 0) {
+               bss->ieee802_11f = 1;
+               os_strlcpy(bss->iapp_iface, pos, sizeof(bss->iapp_iface));
 #endif /* CONFIG_IAPP */
-               } else if (os_strcmp(buf, "own_ip_addr") == 0) {
-                       if (hostapd_parse_ip_addr(pos, &bss->own_ip_addr)) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid IP "
-                                          "address '%s'", line, pos);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "nas_identifier") == 0) {
-                       bss->nas_identifier = os_strdup(pos);
+       } else if (os_strcmp(buf, "own_ip_addr") == 0) {
+               if (hostapd_parse_ip_addr(pos, &bss->own_ip_addr)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: invalid IP address '%s'",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "nas_identifier") == 0) {
+               os_free(bss->nas_identifier);
+               bss->nas_identifier = os_strdup(pos);
 #ifndef CONFIG_NO_RADIUS
-               } else if (os_strcmp(buf, "auth_server_addr") == 0) {
-                       if (hostapd_config_read_radius_addr(
-                                   &bss->radius->auth_servers,
-                                   &bss->radius->num_auth_servers, pos, 1812,
-                                   &bss->radius->auth_server)) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid IP "
-                                          "address '%s'", line, pos);
-                               errors++;
-                       }
-               } else if (bss->radius->auth_server &&
-                          os_strcmp(buf, "auth_server_port") == 0) {
-                       bss->radius->auth_server->port = atoi(pos);
-               } else if (bss->radius->auth_server &&
-                          os_strcmp(buf, "auth_server_shared_secret") == 0) {
-                       int len = os_strlen(pos);
-                       if (len == 0) {
-                               /* RFC 2865, Ch. 3 */
-                               wpa_printf(MSG_ERROR, "Line %d: empty shared "
-                                          "secret is not allowed.", line);
-                               errors++;
-                       }
-                       bss->radius->auth_server->shared_secret =
-                               (u8 *) os_strdup(pos);
-                       bss->radius->auth_server->shared_secret_len = len;
-               } else if (os_strcmp(buf, "acct_server_addr") == 0) {
-                       if (hostapd_config_read_radius_addr(
-                                   &bss->radius->acct_servers,
-                                   &bss->radius->num_acct_servers, pos, 1813,
-                                   &bss->radius->acct_server)) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid IP "
-                                          "address '%s'", line, pos);
-                               errors++;
-                       }
-               } else if (bss->radius->acct_server &&
-                          os_strcmp(buf, "acct_server_port") == 0) {
-                       bss->radius->acct_server->port = atoi(pos);
-               } else if (bss->radius->acct_server &&
-                          os_strcmp(buf, "acct_server_shared_secret") == 0) {
-                       int len = os_strlen(pos);
-                       if (len == 0) {
-                               /* RFC 2865, Ch. 3 */
-                               wpa_printf(MSG_ERROR, "Line %d: empty shared "
-                                          "secret is not allowed.", line);
-                               errors++;
-                       }
-                       bss->radius->acct_server->shared_secret =
-                               (u8 *) os_strdup(pos);
-                       bss->radius->acct_server->shared_secret_len = len;
-               } else if (os_strcmp(buf, "radius_retry_primary_interval") ==
-                          0) {
-                       bss->radius->retry_primary_interval = atoi(pos);
-               } else if (os_strcmp(buf, "radius_acct_interim_interval") == 0)
-               {
-                       bss->acct_interim_interval = atoi(pos);
-               } else if (os_strcmp(buf, "radius_request_cui") == 0) {
-                       bss->radius_request_cui = atoi(pos);
-               } else if (os_strcmp(buf, "radius_auth_req_attr") == 0) {
-                       struct hostapd_radius_attr *attr, *a;
-                       attr = hostapd_parse_radius_attr(pos);
-                       if (attr == NULL) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid "
-                                          "radius_auth_req_attr", line);
-                               errors++;
-                       } else if (bss->radius_auth_req_attr == NULL) {
-                               bss->radius_auth_req_attr = attr;
-                       } else {
-                               a = bss->radius_auth_req_attr;
-                               while (a->next)
-                                       a = a->next;
-                               a->next = attr;
-                       }
-               } else if (os_strcmp(buf, "radius_acct_req_attr") == 0) {
-                       struct hostapd_radius_attr *attr, *a;
-                       attr = hostapd_parse_radius_attr(pos);
-                       if (attr == NULL) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid "
-                                          "radius_acct_req_attr", line);
-                               errors++;
-                       } else if (bss->radius_acct_req_attr == NULL) {
-                               bss->radius_acct_req_attr = attr;
-                       } else {
-                               a = bss->radius_acct_req_attr;
-                               while (a->next)
-                                       a = a->next;
-                               a->next = attr;
-                       }
-               } else if (os_strcmp(buf, "radius_das_port") == 0) {
-                       bss->radius_das_port = atoi(pos);
-               } else if (os_strcmp(buf, "radius_das_client") == 0) {
-                       if (hostapd_parse_das_client(bss, pos) < 0) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid "
-                                          "DAS client", line);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "radius_das_time_window") == 0) {
-                       bss->radius_das_time_window = atoi(pos);
-               } else if (os_strcmp(buf, "radius_das_require_event_timestamp")
-                          == 0) {
-                       bss->radius_das_require_event_timestamp = atoi(pos);
+       } else if (os_strcmp(buf, "radius_client_addr") == 0) {
+               if (hostapd_parse_ip_addr(pos, &bss->radius->client_addr)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: invalid IP address '%s'",
+                                  line, pos);
+                       return 1;
+               }
+               bss->radius->force_client_addr = 1;
+       } else if (os_strcmp(buf, "auth_server_addr") == 0) {
+               if (hostapd_config_read_radius_addr(
+                           &bss->radius->auth_servers,
+                           &bss->radius->num_auth_servers, pos, 1812,
+                           &bss->radius->auth_server)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: invalid IP address '%s'",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (bss->radius->auth_server &&
+                  os_strcmp(buf, "auth_server_addr_replace") == 0) {
+               if (hostapd_parse_ip_addr(pos,
+                                         &bss->radius->auth_server->addr)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: invalid IP address '%s'",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (bss->radius->auth_server &&
+                  os_strcmp(buf, "auth_server_port") == 0) {
+               bss->radius->auth_server->port = atoi(pos);
+       } else if (bss->radius->auth_server &&
+                  os_strcmp(buf, "auth_server_shared_secret") == 0) {
+               int len = os_strlen(pos);
+               if (len == 0) {
+                       /* RFC 2865, Ch. 3 */
+                       wpa_printf(MSG_ERROR, "Line %d: empty shared secret is not allowed",
+                                  line);
+                       return 1;
+               }
+               os_free(bss->radius->auth_server->shared_secret);
+               bss->radius->auth_server->shared_secret = (u8 *) os_strdup(pos);
+               bss->radius->auth_server->shared_secret_len = len;
+       } else if (os_strcmp(buf, "acct_server_addr") == 0) {
+               if (hostapd_config_read_radius_addr(
+                           &bss->radius->acct_servers,
+                           &bss->radius->num_acct_servers, pos, 1813,
+                           &bss->radius->acct_server)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: invalid IP address '%s'",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (bss->radius->acct_server &&
+                  os_strcmp(buf, "acct_server_addr_replace") == 0) {
+               if (hostapd_parse_ip_addr(pos,
+                                         &bss->radius->acct_server->addr)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: invalid IP address '%s'",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (bss->radius->acct_server &&
+                  os_strcmp(buf, "acct_server_port") == 0) {
+               bss->radius->acct_server->port = atoi(pos);
+       } else if (bss->radius->acct_server &&
+                  os_strcmp(buf, "acct_server_shared_secret") == 0) {
+               int len = os_strlen(pos);
+               if (len == 0) {
+                       /* RFC 2865, Ch. 3 */
+                       wpa_printf(MSG_ERROR, "Line %d: empty shared secret is not allowed",
+                                  line);
+                       return 1;
+               }
+               os_free(bss->radius->acct_server->shared_secret);
+               bss->radius->acct_server->shared_secret = (u8 *) os_strdup(pos);
+               bss->radius->acct_server->shared_secret_len = len;
+       } else if (os_strcmp(buf, "radius_retry_primary_interval") == 0) {
+               bss->radius->retry_primary_interval = atoi(pos);
+       } else if (os_strcmp(buf, "radius_acct_interim_interval") == 0) {
+               bss->acct_interim_interval = atoi(pos);
+       } else if (os_strcmp(buf, "radius_request_cui") == 0) {
+               bss->radius_request_cui = atoi(pos);
+       } else if (os_strcmp(buf, "radius_auth_req_attr") == 0) {
+               struct hostapd_radius_attr *attr, *a;
+               attr = hostapd_parse_radius_attr(pos);
+               if (attr == NULL) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: invalid radius_auth_req_attr",
+                                  line);
+                       return 1;
+               } else if (bss->radius_auth_req_attr == NULL) {
+                       bss->radius_auth_req_attr = attr;
+               } else {
+                       a = bss->radius_auth_req_attr;
+                       while (a->next)
+                               a = a->next;
+                       a->next = attr;
+               }
+       } else if (os_strcmp(buf, "radius_acct_req_attr") == 0) {
+               struct hostapd_radius_attr *attr, *a;
+               attr = hostapd_parse_radius_attr(pos);
+               if (attr == NULL) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: invalid radius_acct_req_attr",
+                                  line);
+                       return 1;
+               } else if (bss->radius_acct_req_attr == NULL) {
+                       bss->radius_acct_req_attr = attr;
+               } else {
+                       a = bss->radius_acct_req_attr;
+                       while (a->next)
+                               a = a->next;
+                       a->next = attr;
+               }
+       } else if (os_strcmp(buf, "radius_das_port") == 0) {
+               bss->radius_das_port = atoi(pos);
+       } else if (os_strcmp(buf, "radius_das_client") == 0) {
+               if (hostapd_parse_das_client(bss, pos) < 0) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid DAS client",
+                                  line);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "radius_das_time_window") == 0) {
+               bss->radius_das_time_window = atoi(pos);
+       } else if (os_strcmp(buf, "radius_das_require_event_timestamp") == 0) {
+               bss->radius_das_require_event_timestamp = atoi(pos);
 #endif /* CONFIG_NO_RADIUS */
-               } else if (os_strcmp(buf, "auth_algs") == 0) {
-                       bss->auth_algs = atoi(pos);
-                       if (bss->auth_algs == 0) {
-                               wpa_printf(MSG_ERROR, "Line %d: no "
-                                          "authentication algorithms allowed",
-                                          line);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "max_num_sta") == 0) {
-                       bss->max_num_sta = atoi(pos);
-                       if (bss->max_num_sta < 0 ||
-                           bss->max_num_sta > MAX_STA_COUNT) {
-                               wpa_printf(MSG_ERROR, "Line %d: Invalid "
-                                          "max_num_sta=%d; allowed range "
-                                          "0..%d", line, bss->max_num_sta,
-                                          MAX_STA_COUNT);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "wpa") == 0) {
-                       bss->wpa = atoi(pos);
-               } else if (os_strcmp(buf, "wpa_group_rekey") == 0) {
-                       bss->wpa_group_rekey = atoi(pos);
-               } else if (os_strcmp(buf, "wpa_strict_rekey") == 0) {
-                       bss->wpa_strict_rekey = atoi(pos);
-               } else if (os_strcmp(buf, "wpa_gmk_rekey") == 0) {
-                       bss->wpa_gmk_rekey = atoi(pos);
-               } else if (os_strcmp(buf, "wpa_ptk_rekey") == 0) {
-                       bss->wpa_ptk_rekey = atoi(pos);
-               } else if (os_strcmp(buf, "wpa_passphrase") == 0) {
-                       int len = os_strlen(pos);
-                       if (len < 8 || len > 63) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid WPA "
-                                          "passphrase length %d (expected "
-                                          "8..63)", line, len);
-                               errors++;
-                       } else {
-                               os_free(bss->ssid.wpa_passphrase);
-                               bss->ssid.wpa_passphrase = os_strdup(pos);
-                               os_free(bss->ssid.wpa_psk);
-                               bss->ssid.wpa_psk = NULL;
-                       }
-               } else if (os_strcmp(buf, "wpa_psk") == 0) {
-                       os_free(bss->ssid.wpa_psk);
-                       bss->ssid.wpa_psk =
-                               os_zalloc(sizeof(struct hostapd_wpa_psk));
-                       if (bss->ssid.wpa_psk == NULL)
-                               errors++;
-                       else if (hexstr2bin(pos, bss->ssid.wpa_psk->psk,
-                                           PMK_LEN) ||
-                                pos[PMK_LEN * 2] != '\0') {
-                               wpa_printf(MSG_ERROR, "Line %d: Invalid PSK "
-                                          "'%s'.", line, pos);
-                               errors++;
-                       } else {
-                               bss->ssid.wpa_psk->group = 1;
-                               os_free(bss->ssid.wpa_passphrase);
-                               bss->ssid.wpa_passphrase = NULL;
-                       }
-               } else if (os_strcmp(buf, "wpa_psk_file") == 0) {
-                       os_free(bss->ssid.wpa_psk_file);
-                       bss->ssid.wpa_psk_file = os_strdup(pos);
-                       if (!bss->ssid.wpa_psk_file) {
-                               wpa_printf(MSG_ERROR, "Line %d: allocation "
-                                          "failed", line);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "wpa_key_mgmt") == 0) {
-                       bss->wpa_key_mgmt =
-                               hostapd_config_parse_key_mgmt(line, pos);
-                       if (bss->wpa_key_mgmt == -1)
-                               errors++;
-               } else if (os_strcmp(buf, "wpa_psk_radius") == 0) {
-                       bss->wpa_psk_radius = atoi(pos);
-                       if (bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
-                           bss->wpa_psk_radius != PSK_RADIUS_ACCEPTED &&
-                           bss->wpa_psk_radius != PSK_RADIUS_REQUIRED) {
-                               wpa_printf(MSG_ERROR, "Line %d: unknown "
-                                          "wpa_psk_radius %d",
-                                          line, bss->wpa_psk_radius);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "wpa_pairwise") == 0) {
-                       bss->wpa_pairwise =
-                               hostapd_config_parse_cipher(line, pos);
-                       if (bss->wpa_pairwise == -1 ||
-                           bss->wpa_pairwise == 0)
-                               errors++;
-                       else if (bss->wpa_pairwise &
-                                (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 |
-                                 WPA_CIPHER_WEP104)) {
-                               wpa_printf(MSG_ERROR, "Line %d: unsupported "
-                                          "pairwise cipher suite '%s'",
-                                          bss->wpa_pairwise, pos);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "rsn_pairwise") == 0) {
-                       bss->rsn_pairwise =
-                               hostapd_config_parse_cipher(line, pos);
-                       if (bss->rsn_pairwise == -1 ||
-                           bss->rsn_pairwise == 0)
-                               errors++;
-                       else if (bss->rsn_pairwise &
-                                (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 |
-                                 WPA_CIPHER_WEP104)) {
-                               wpa_printf(MSG_ERROR, "Line %d: unsupported "
-                                          "pairwise cipher suite '%s'",
-                                          bss->rsn_pairwise, pos);
-                               errors++;
-                       }
+       } else if (os_strcmp(buf, "auth_algs") == 0) {
+               bss->auth_algs = atoi(pos);
+               if (bss->auth_algs == 0) {
+                       wpa_printf(MSG_ERROR, "Line %d: no authentication algorithms allowed",
+                                  line);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "max_num_sta") == 0) {
+               bss->max_num_sta = atoi(pos);
+               if (bss->max_num_sta < 0 ||
+                   bss->max_num_sta > MAX_STA_COUNT) {
+                       wpa_printf(MSG_ERROR, "Line %d: Invalid max_num_sta=%d; allowed range 0..%d",
+                                  line, bss->max_num_sta, MAX_STA_COUNT);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "wpa") == 0) {
+               bss->wpa = atoi(pos);
+       } else if (os_strcmp(buf, "wpa_group_rekey") == 0) {
+               bss->wpa_group_rekey = atoi(pos);
+       } else if (os_strcmp(buf, "wpa_strict_rekey") == 0) {
+               bss->wpa_strict_rekey = atoi(pos);
+       } else if (os_strcmp(buf, "wpa_gmk_rekey") == 0) {
+               bss->wpa_gmk_rekey = atoi(pos);
+       } else if (os_strcmp(buf, "wpa_ptk_rekey") == 0) {
+               bss->wpa_ptk_rekey = atoi(pos);
+       } else if (os_strcmp(buf, "wpa_passphrase") == 0) {
+               int len = os_strlen(pos);
+               if (len < 8 || len > 63) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid WPA passphrase length %d (expected 8..63)",
+                                  line, len);
+                       return 1;
+               }
+               os_free(bss->ssid.wpa_passphrase);
+               bss->ssid.wpa_passphrase = os_strdup(pos);
+               if (bss->ssid.wpa_passphrase) {
+                       hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
+                       bss->ssid.wpa_passphrase_set = 1;
+               }
+       } else if (os_strcmp(buf, "wpa_psk") == 0) {
+               hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
+               bss->ssid.wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk));
+               if (bss->ssid.wpa_psk == NULL)
+                       return 1;
+               if (hexstr2bin(pos, bss->ssid.wpa_psk->psk, PMK_LEN) ||
+                   pos[PMK_LEN * 2] != '\0') {
+                       wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.",
+                                  line, pos);
+                       hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
+                       return 1;
+               }
+               bss->ssid.wpa_psk->group = 1;
+               os_free(bss->ssid.wpa_passphrase);
+               bss->ssid.wpa_passphrase = NULL;
+               bss->ssid.wpa_psk_set = 1;
+       } else if (os_strcmp(buf, "wpa_psk_file") == 0) {
+               os_free(bss->ssid.wpa_psk_file);
+               bss->ssid.wpa_psk_file = os_strdup(pos);
+               if (!bss->ssid.wpa_psk_file) {
+                       wpa_printf(MSG_ERROR, "Line %d: allocation failed",
+                                  line);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "wpa_key_mgmt") == 0) {
+               bss->wpa_key_mgmt = hostapd_config_parse_key_mgmt(line, pos);
+               if (bss->wpa_key_mgmt == -1)
+                       return 1;
+       } else if (os_strcmp(buf, "wpa_psk_radius") == 0) {
+               bss->wpa_psk_radius = atoi(pos);
+               if (bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
+                   bss->wpa_psk_radius != PSK_RADIUS_ACCEPTED &&
+                   bss->wpa_psk_radius != PSK_RADIUS_REQUIRED) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: unknown wpa_psk_radius %d",
+                                  line, bss->wpa_psk_radius);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "wpa_pairwise") == 0) {
+               bss->wpa_pairwise = hostapd_config_parse_cipher(line, pos);
+               if (bss->wpa_pairwise == -1 || bss->wpa_pairwise == 0)
+                       return 1;
+               if (bss->wpa_pairwise &
+                   (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
+                       wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'",
+                                  bss->wpa_pairwise, pos);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "rsn_pairwise") == 0) {
+               bss->rsn_pairwise = hostapd_config_parse_cipher(line, pos);
+               if (bss->rsn_pairwise == -1 || bss->rsn_pairwise == 0)
+                       return 1;
+               if (bss->rsn_pairwise &
+                   (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
+                       wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'",
+                                  bss->rsn_pairwise, pos);
+                       return 1;
+               }
 #ifdef CONFIG_RSN_PREAUTH
-               } else if (os_strcmp(buf, "rsn_preauth") == 0) {
-                       bss->rsn_preauth = atoi(pos);
-               } else if (os_strcmp(buf, "rsn_preauth_interfaces") == 0) {
-                       bss->rsn_preauth_interfaces = os_strdup(pos);
+       } else if (os_strcmp(buf, "rsn_preauth") == 0) {
+               bss->rsn_preauth = atoi(pos);
+       } else if (os_strcmp(buf, "rsn_preauth_interfaces") == 0) {
+               os_free(bss->rsn_preauth_interfaces);
+               bss->rsn_preauth_interfaces = os_strdup(pos);
 #endif /* CONFIG_RSN_PREAUTH */
 #ifdef CONFIG_PEERKEY
-               } else if (os_strcmp(buf, "peerkey") == 0) {
-                       bss->peerkey = atoi(pos);
+       } else if (os_strcmp(buf, "peerkey") == 0) {
+               bss->peerkey = atoi(pos);
 #endif /* CONFIG_PEERKEY */
 #ifdef CONFIG_IEEE80211R
-               } else if (os_strcmp(buf, "mobility_domain") == 0) {
-                       if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN ||
-                           hexstr2bin(pos, bss->mobility_domain,
-                                      MOBILITY_DOMAIN_ID_LEN) != 0) {
-                               wpa_printf(MSG_DEBUG, "Line %d: Invalid "
-                                          "mobility_domain '%s'", line, pos);
-                               errors++;
-                               return errors;
-                       }
-               } else if (os_strcmp(buf, "r1_key_holder") == 0) {
-                       if (os_strlen(pos) != 2 * FT_R1KH_ID_LEN ||
-                           hexstr2bin(pos, bss->r1_key_holder,
-                                      FT_R1KH_ID_LEN) != 0) {
-                               wpa_printf(MSG_DEBUG, "Line %d: Invalid "
-                                          "r1_key_holder '%s'", line, pos);
-                               errors++;
-                               return errors;
-                       }
-               } else if (os_strcmp(buf, "r0_key_lifetime") == 0) {
-                       bss->r0_key_lifetime = atoi(pos);
-               } else if (os_strcmp(buf, "reassociation_deadline") == 0) {
-                       bss->reassociation_deadline = atoi(pos);
-               } else if (os_strcmp(buf, "r0kh") == 0) {
-                       if (add_r0kh(bss, pos) < 0) {
-                               wpa_printf(MSG_DEBUG, "Line %d: Invalid "
-                                          "r0kh '%s'", line, pos);
-                               errors++;
-                               return errors;
-                       }
-               } else if (os_strcmp(buf, "r1kh") == 0) {
-                       if (add_r1kh(bss, pos) < 0) {
-                               wpa_printf(MSG_DEBUG, "Line %d: Invalid "
-                                          "r1kh '%s'", line, pos);
-                               errors++;
-                               return errors;
-                       }
-               } else if (os_strcmp(buf, "pmk_r1_push") == 0) {
-                       bss->pmk_r1_push = atoi(pos);
-               } else if (os_strcmp(buf, "ft_over_ds") == 0) {
-                       bss->ft_over_ds = atoi(pos);
+       } else if (os_strcmp(buf, "mobility_domain") == 0) {
+               if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN ||
+                   hexstr2bin(pos, bss->mobility_domain,
+                              MOBILITY_DOMAIN_ID_LEN) != 0) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: Invalid mobility_domain '%s'",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "r1_key_holder") == 0) {
+               if (os_strlen(pos) != 2 * FT_R1KH_ID_LEN ||
+                   hexstr2bin(pos, bss->r1_key_holder, FT_R1KH_ID_LEN) != 0) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: Invalid r1_key_holder '%s'",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "r0_key_lifetime") == 0) {
+               bss->r0_key_lifetime = atoi(pos);
+       } else if (os_strcmp(buf, "reassociation_deadline") == 0) {
+               bss->reassociation_deadline = atoi(pos);
+       } else if (os_strcmp(buf, "r0kh") == 0) {
+               if (add_r0kh(bss, pos) < 0) {
+                       wpa_printf(MSG_DEBUG, "Line %d: Invalid r0kh '%s'",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "r1kh") == 0) {
+               if (add_r1kh(bss, pos) < 0) {
+                       wpa_printf(MSG_DEBUG, "Line %d: Invalid r1kh '%s'",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "pmk_r1_push") == 0) {
+               bss->pmk_r1_push = atoi(pos);
+       } else if (os_strcmp(buf, "ft_over_ds") == 0) {
+               bss->ft_over_ds = atoi(pos);
 #endif /* CONFIG_IEEE80211R */
 #ifndef CONFIG_NO_CTRL_IFACE
-               } else if (os_strcmp(buf, "ctrl_interface") == 0) {
-                       os_free(bss->ctrl_interface);
-                       bss->ctrl_interface = os_strdup(pos);
-               } else if (os_strcmp(buf, "ctrl_interface_group") == 0) {
+       } else if (os_strcmp(buf, "ctrl_interface") == 0) {
+               os_free(bss->ctrl_interface);
+               bss->ctrl_interface = os_strdup(pos);
+       } else if (os_strcmp(buf, "ctrl_interface_group") == 0) {
 #ifndef CONFIG_NATIVE_WINDOWS
-                       struct group *grp;
-                       char *endp;
-                       const char *group = pos;
-
-                       grp = getgrnam(group);
-                       if (grp) {
-                               bss->ctrl_interface_gid = grp->gr_gid;
-                               bss->ctrl_interface_gid_set = 1;
-                               wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d"
-                                          " (from group name '%s')",
-                                          bss->ctrl_interface_gid, group);
-                               return errors;
-                       }
+               struct group *grp;
+               char *endp;
+               const char *group = pos;
 
-                       /* Group name not found - try to parse this as gid */
-                       bss->ctrl_interface_gid = strtol(group, &endp, 10);
-                       if (*group == '\0' || *endp != '\0') {
-                               wpa_printf(MSG_DEBUG, "Line %d: Invalid group "
-                                          "'%s'", line, group);
-                               errors++;
-                               return errors;
-                       }
+               grp = getgrnam(group);
+               if (grp) {
+                       bss->ctrl_interface_gid = grp->gr_gid;
                        bss->ctrl_interface_gid_set = 1;
-                       wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d",
-                                  bss->ctrl_interface_gid);
+                       wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d (from group name '%s')",
+                                  bss->ctrl_interface_gid, group);
+                       return 0;
+               }
+
+               /* Group name not found - try to parse this as gid */
+               bss->ctrl_interface_gid = strtol(group, &endp, 10);
+               if (*group == '\0' || *endp != '\0') {
+                       wpa_printf(MSG_DEBUG, "Line %d: Invalid group '%s'",
+                                  line, group);
+                       return 1;
+               }
+               bss->ctrl_interface_gid_set = 1;
+               wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d",
+                          bss->ctrl_interface_gid);
 #endif /* CONFIG_NATIVE_WINDOWS */
 #endif /* CONFIG_NO_CTRL_IFACE */
 #ifdef RADIUS_SERVER
-               } else if (os_strcmp(buf, "radius_server_clients") == 0) {
-                       os_free(bss->radius_server_clients);
-                       bss->radius_server_clients = os_strdup(pos);
-               } else if (os_strcmp(buf, "radius_server_auth_port") == 0) {
-                       bss->radius_server_auth_port = atoi(pos);
-               } else if (os_strcmp(buf, "radius_server_ipv6") == 0) {
-                       bss->radius_server_ipv6 = atoi(pos);
+       } else if (os_strcmp(buf, "radius_server_clients") == 0) {
+               os_free(bss->radius_server_clients);
+               bss->radius_server_clients = os_strdup(pos);
+       } else if (os_strcmp(buf, "radius_server_auth_port") == 0) {
+               bss->radius_server_auth_port = atoi(pos);
+       } else if (os_strcmp(buf, "radius_server_acct_port") == 0) {
+               bss->radius_server_acct_port = atoi(pos);
+       } else if (os_strcmp(buf, "radius_server_ipv6") == 0) {
+               bss->radius_server_ipv6 = atoi(pos);
 #endif /* RADIUS_SERVER */
-               } else if (os_strcmp(buf, "test_socket") == 0) {
-                       os_free(bss->test_socket);
-                       bss->test_socket = os_strdup(pos);
-               } else if (os_strcmp(buf, "use_pae_group_addr") == 0) {
-                       bss->use_pae_group_addr = atoi(pos);
-               } else if (os_strcmp(buf, "hw_mode") == 0) {
-                       if (os_strcmp(pos, "a") == 0)
-                               conf->hw_mode = HOSTAPD_MODE_IEEE80211A;
-                       else if (os_strcmp(pos, "b") == 0)
-                               conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
-                       else if (os_strcmp(pos, "g") == 0)
-                               conf->hw_mode = HOSTAPD_MODE_IEEE80211G;
-                       else if (os_strcmp(pos, "ad") == 0)
-                               conf->hw_mode = HOSTAPD_MODE_IEEE80211AD;
-                       else {
-                               wpa_printf(MSG_ERROR, "Line %d: unknown "
-                                          "hw_mode '%s'", line, pos);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "wps_rf_bands") == 0) {
-                       if (os_strcmp(pos, "a") == 0)
-                               bss->wps_rf_bands = WPS_RF_50GHZ;
-                       else if (os_strcmp(pos, "g") == 0 ||
-                                os_strcmp(pos, "b") == 0)
-                               bss->wps_rf_bands = WPS_RF_24GHZ;
-                       else if (os_strcmp(pos, "ag") == 0 ||
-                                os_strcmp(pos, "ga") == 0)
-                               bss->wps_rf_bands =
-                                       WPS_RF_24GHZ | WPS_RF_50GHZ;
-                       else {
-                               wpa_printf(MSG_ERROR, "Line %d: unknown "
-                                          "wps_rf_band '%s'", line, pos);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "channel") == 0) {
+       } else if (os_strcmp(buf, "use_pae_group_addr") == 0) {
+               bss->use_pae_group_addr = atoi(pos);
+       } else if (os_strcmp(buf, "hw_mode") == 0) {
+               if (os_strcmp(pos, "a") == 0)
+                       conf->hw_mode = HOSTAPD_MODE_IEEE80211A;
+               else if (os_strcmp(pos, "b") == 0)
+                       conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
+               else if (os_strcmp(pos, "g") == 0)
+                       conf->hw_mode = HOSTAPD_MODE_IEEE80211G;
+               else if (os_strcmp(pos, "ad") == 0)
+                       conf->hw_mode = HOSTAPD_MODE_IEEE80211AD;
+               else {
+                       wpa_printf(MSG_ERROR, "Line %d: unknown hw_mode '%s'",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "wps_rf_bands") == 0) {
+               if (os_strcmp(pos, "a") == 0)
+                       bss->wps_rf_bands = WPS_RF_50GHZ;
+               else if (os_strcmp(pos, "g") == 0 ||
+                        os_strcmp(pos, "b") == 0)
+                       bss->wps_rf_bands = WPS_RF_24GHZ;
+               else if (os_strcmp(pos, "ag") == 0 ||
+                        os_strcmp(pos, "ga") == 0)
+                       bss->wps_rf_bands = WPS_RF_24GHZ | WPS_RF_50GHZ;
+               else {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: unknown wps_rf_band '%s'",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "channel") == 0) {
+               if (os_strcmp(pos, "acs_survey") == 0) {
+#ifndef CONFIG_ACS
+                       wpa_printf(MSG_ERROR, "Line %d: tries to enable ACS but CONFIG_ACS disabled",
+                                  line);
+                       return 1;
+#else /* CONFIG_ACS */
+                       conf->channel = 0;
+#endif /* CONFIG_ACS */
+               } else
                        conf->channel = atoi(pos);
-               } else if (os_strcmp(buf, "beacon_int") == 0) {
-                       int val = atoi(pos);
-                       /* MIB defines range as 1..65535, but very small values
-                        * cause problems with the current implementation.
-                        * Since it is unlikely that this small numbers are
-                        * useful in real life scenarios, do not allow beacon
-                        * period to be set below 15 TU. */
-                       if (val < 15 || val > 65535) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid "
-                                          "beacon_int %d (expected "
-                                          "15..65535)", line, val);
-                               errors++;
-                       } else
-                               conf->beacon_int = val;
-               } else if (os_strcmp(buf, "dtim_period") == 0) {
-                       bss->dtim_period = atoi(pos);
-                       if (bss->dtim_period < 1 || bss->dtim_period > 255) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid "
-                                          "dtim_period %d",
-                                          line, bss->dtim_period);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "rts_threshold") == 0) {
-                       conf->rts_threshold = atoi(pos);
-                       if (conf->rts_threshold < 0 ||
-                           conf->rts_threshold > 2347) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid "
-                                          "rts_threshold %d",
-                                          line, conf->rts_threshold);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "fragm_threshold") == 0) {
-                       conf->fragm_threshold = atoi(pos);
-                       if (conf->fragm_threshold < 256 ||
-                           conf->fragm_threshold > 2346) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid "
-                                          "fragm_threshold %d",
-                                          line, conf->fragm_threshold);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "send_probe_response") == 0) {
-                       int val = atoi(pos);
-                       if (val != 0 && val != 1) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid "
-                                          "send_probe_response %d (expected "
-                                          "0 or 1)", line, val);
-                       } else
-                               conf->send_probe_response = val;
-               } else if (os_strcmp(buf, "supported_rates") == 0) {
-                       if (hostapd_parse_intlist(&conf->supported_rates, pos))
-                       {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid rate "
-                                          "list", line);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "basic_rates") == 0) {
-                       if (hostapd_parse_intlist(&conf->basic_rates, pos)) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid rate "
-                                          "list", line);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "preamble") == 0) {
-                       if (atoi(pos))
-                               conf->preamble = SHORT_PREAMBLE;
-                       else
-                               conf->preamble = LONG_PREAMBLE;
-               } else if (os_strcmp(buf, "ignore_broadcast_ssid") == 0) {
-                       bss->ignore_broadcast_ssid = atoi(pos);
-               } else if (os_strcmp(buf, "wep_default_key") == 0) {
-                       bss->ssid.wep.idx = atoi(pos);
-                       if (bss->ssid.wep.idx > 3) {
-                               wpa_printf(MSG_ERROR, "Invalid "
-                                          "wep_default_key index %d",
-                                          bss->ssid.wep.idx);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "wep_key0") == 0 ||
-                          os_strcmp(buf, "wep_key1") == 0 ||
-                          os_strcmp(buf, "wep_key2") == 0 ||
-                          os_strcmp(buf, "wep_key3") == 0) {
-                       if (hostapd_config_read_wep(&bss->ssid.wep,
-                                                   buf[7] - '0', pos)) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid WEP "
-                                          "key '%s'", line, buf);
-                               errors++;
-                       }
+       } else if (os_strcmp(buf, "chanlist") == 0) {
+               if (hostapd_parse_intlist(&conf->chanlist, pos)) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid channel list",
+                                  line);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "beacon_int") == 0) {
+               int val = atoi(pos);
+               /* MIB defines range as 1..65535, but very small values
+                * cause problems with the current implementation.
+                * Since it is unlikely that this small numbers are
+                * useful in real life scenarios, do not allow beacon
+                * period to be set below 15 TU. */
+               if (val < 15 || val > 65535) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid beacon_int %d (expected 15..65535)",
+                                  line, val);
+                       return 1;
+               }
+               conf->beacon_int = val;
+#ifdef CONFIG_ACS
+       } else if (os_strcmp(buf, "acs_num_scans") == 0) {
+               int val = atoi(pos);
+               if (val <= 0 || val > 100) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid acs_num_scans %d (expected 1..100)",
+                                  line, val);
+                       return 1;
+               }
+               conf->acs_num_scans = val;
+       } else if (os_strcmp(buf, "acs_chan_bias") == 0) {
+               if (hostapd_config_parse_acs_chan_bias(conf, pos)) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid acs_chan_bias",
+                                  line);
+                       return -1;
+               }
+#endif /* CONFIG_ACS */
+       } else if (os_strcmp(buf, "dtim_period") == 0) {
+               bss->dtim_period = atoi(pos);
+               if (bss->dtim_period < 1 || bss->dtim_period > 255) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid dtim_period %d",
+                                  line, bss->dtim_period);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "bss_load_update_period") == 0) {
+               bss->bss_load_update_period = atoi(pos);
+               if (bss->bss_load_update_period < 0 ||
+                   bss->bss_load_update_period > 100) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: invalid bss_load_update_period %d",
+                                  line, bss->bss_load_update_period);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "rts_threshold") == 0) {
+               conf->rts_threshold = atoi(pos);
+               if (conf->rts_threshold < 0 || conf->rts_threshold > 2347) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: invalid rts_threshold %d",
+                                  line, conf->rts_threshold);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "fragm_threshold") == 0) {
+               conf->fragm_threshold = atoi(pos);
+               if (conf->fragm_threshold < 256 ||
+                   conf->fragm_threshold > 2346) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: invalid fragm_threshold %d",
+                                  line, conf->fragm_threshold);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "send_probe_response") == 0) {
+               int val = atoi(pos);
+               if (val != 0 && val != 1) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid send_probe_response %d (expected 0 or 1)",
+                                  line, val);
+                       return 1;
+               }
+               conf->send_probe_response = val;
+       } else if (os_strcmp(buf, "supported_rates") == 0) {
+               if (hostapd_parse_intlist(&conf->supported_rates, pos)) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid rate list",
+                                  line);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "basic_rates") == 0) {
+               if (hostapd_parse_intlist(&conf->basic_rates, pos)) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid rate list",
+                                  line);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "preamble") == 0) {
+               if (atoi(pos))
+                       conf->preamble = SHORT_PREAMBLE;
+               else
+                       conf->preamble = LONG_PREAMBLE;
+       } else if (os_strcmp(buf, "ignore_broadcast_ssid") == 0) {
+               bss->ignore_broadcast_ssid = atoi(pos);
+       } else if (os_strcmp(buf, "wep_default_key") == 0) {
+               bss->ssid.wep.idx = atoi(pos);
+               if (bss->ssid.wep.idx > 3) {
+                       wpa_printf(MSG_ERROR,
+                                  "Invalid wep_default_key index %d",
+                                  bss->ssid.wep.idx);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "wep_key0") == 0 ||
+                  os_strcmp(buf, "wep_key1") == 0 ||
+                  os_strcmp(buf, "wep_key2") == 0 ||
+                  os_strcmp(buf, "wep_key3") == 0) {
+               if (hostapd_config_read_wep(&bss->ssid.wep,
+                                           buf[7] - '0', pos)) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid WEP key '%s'",
+                                  line, buf);
+                       return 1;
+               }
 #ifndef CONFIG_NO_VLAN
-               } else if (os_strcmp(buf, "dynamic_vlan") == 0) {
-                       bss->ssid.dynamic_vlan = atoi(pos);
-               } else if (os_strcmp(buf, "vlan_file") == 0) {
-                       if (hostapd_config_read_vlan_file(bss, pos)) {
-                               wpa_printf(MSG_ERROR, "Line %d: failed to "
-                                          "read VLAN file '%s'", line, pos);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "vlan_naming") == 0) {
-                       bss->ssid.vlan_naming = atoi(pos);
-                       if (bss->ssid.vlan_naming >= DYNAMIC_VLAN_NAMING_END ||
-                           bss->ssid.vlan_naming < 0) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid "
-                                          "naming scheme %d", line,
-                                           bss->ssid.vlan_naming);
-                               errors++;
-                        }
+       } else if (os_strcmp(buf, "dynamic_vlan") == 0) {
+               bss->ssid.dynamic_vlan = atoi(pos);
+       } else if (os_strcmp(buf, "vlan_file") == 0) {
+               if (hostapd_config_read_vlan_file(bss, pos)) {
+                       wpa_printf(MSG_ERROR, "Line %d: failed to read VLAN file '%s'",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "vlan_naming") == 0) {
+               bss->ssid.vlan_naming = atoi(pos);
+               if (bss->ssid.vlan_naming >= DYNAMIC_VLAN_NAMING_END ||
+                   bss->ssid.vlan_naming < 0) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: invalid naming scheme %d",
+                                  line, bss->ssid.vlan_naming);
+                       return 1;
+               }
 #ifdef CONFIG_FULL_DYNAMIC_VLAN
-               } else if (os_strcmp(buf, "vlan_tagged_interface") == 0) {
-                       bss->ssid.vlan_tagged_interface = os_strdup(pos);
+       } else if (os_strcmp(buf, "vlan_tagged_interface") == 0) {
+               os_free(bss->ssid.vlan_tagged_interface);
+               bss->ssid.vlan_tagged_interface = os_strdup(pos);
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
 #endif /* CONFIG_NO_VLAN */
-               } else if (os_strcmp(buf, "ap_table_max_size") == 0) {
-                       conf->ap_table_max_size = atoi(pos);
-               } else if (os_strcmp(buf, "ap_table_expiration_time") == 0) {
-                       conf->ap_table_expiration_time = atoi(pos);
-               } else if (os_strncmp(buf, "tx_queue_", 9) == 0) {
-                       if (hostapd_config_tx_queue(conf, buf, pos)) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid TX "
-                                          "queue item", line);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "wme_enabled") == 0 ||
-                          os_strcmp(buf, "wmm_enabled") == 0) {
-                       bss->wmm_enabled = atoi(pos);
-               } else if (os_strcmp(buf, "uapsd_advertisement_enabled") == 0) {
-                       bss->wmm_uapsd = atoi(pos);
-               } else if (os_strncmp(buf, "wme_ac_", 7) == 0 ||
-                          os_strncmp(buf, "wmm_ac_", 7) == 0) {
-                       if (hostapd_config_wmm_ac(conf->wmm_ac_params, buf,
-                                                 pos)) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid WMM "
-                                          "ac item", line);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "bss") == 0) {
-                       if (hostapd_config_bss(conf, pos)) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid bss "
-                                          "item", line);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "bssid") == 0) {
-                       if (hwaddr_aton(pos, bss->bssid)) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid bssid "
-                                          "item", line);
-                               errors++;
-                       }
+       } else if (os_strcmp(buf, "ap_table_max_size") == 0) {
+               conf->ap_table_max_size = atoi(pos);
+       } else if (os_strcmp(buf, "ap_table_expiration_time") == 0) {
+               conf->ap_table_expiration_time = atoi(pos);
+       } else if (os_strncmp(buf, "tx_queue_", 9) == 0) {
+               if (hostapd_config_tx_queue(conf, buf, pos)) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid TX queue item",
+                                  line);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "wme_enabled") == 0 ||
+                  os_strcmp(buf, "wmm_enabled") == 0) {
+               bss->wmm_enabled = atoi(pos);
+       } else if (os_strcmp(buf, "uapsd_advertisement_enabled") == 0) {
+               bss->wmm_uapsd = atoi(pos);
+       } else if (os_strncmp(buf, "wme_ac_", 7) == 0 ||
+                  os_strncmp(buf, "wmm_ac_", 7) == 0) {
+               if (hostapd_config_wmm_ac(conf->wmm_ac_params, buf, pos)) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid WMM ac item",
+                                  line);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "bss") == 0) {
+               if (hostapd_config_bss(conf, pos)) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid bss item",
+                                  line);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "bssid") == 0) {
+               if (hwaddr_aton(pos, bss->bssid)) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid bssid item",
+                                  line);
+                       return 1;
+               }
 #ifdef CONFIG_IEEE80211W
-               } else if (os_strcmp(buf, "ieee80211w") == 0) {
-                       bss->ieee80211w = atoi(pos);
-               } else if (os_strcmp(buf, "assoc_sa_query_max_timeout") == 0) {
-                       bss->assoc_sa_query_max_timeout = atoi(pos);
-                       if (bss->assoc_sa_query_max_timeout == 0) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid "
-                                          "assoc_sa_query_max_timeout", line);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "assoc_sa_query_retry_timeout") == 0)
-               {
-                       bss->assoc_sa_query_retry_timeout = atoi(pos);
-                       if (bss->assoc_sa_query_retry_timeout == 0) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid "
-                                          "assoc_sa_query_retry_timeout",
-                                          line);
-                               errors++;
-                       }
+       } else if (os_strcmp(buf, "ieee80211w") == 0) {
+               bss->ieee80211w = atoi(pos);
+       } else if (os_strcmp(buf, "group_mgmt_cipher") == 0) {
+               if (os_strcmp(pos, "AES-128-CMAC") == 0) {
+                       bss->group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC;
+               } else if (os_strcmp(pos, "BIP-GMAC-128") == 0) {
+                       bss->group_mgmt_cipher = WPA_CIPHER_BIP_GMAC_128;
+               } else if (os_strcmp(pos, "BIP-GMAC-256") == 0) {
+                       bss->group_mgmt_cipher = WPA_CIPHER_BIP_GMAC_256;
+               } else if (os_strcmp(pos, "BIP-CMAC-256") == 0) {
+                       bss->group_mgmt_cipher = WPA_CIPHER_BIP_CMAC_256;
+               } else {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid group_mgmt_cipher: %s",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "assoc_sa_query_max_timeout") == 0) {
+               bss->assoc_sa_query_max_timeout = atoi(pos);
+               if (bss->assoc_sa_query_max_timeout == 0) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid assoc_sa_query_max_timeout",
+                                  line);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "assoc_sa_query_retry_timeout") == 0) {
+               bss->assoc_sa_query_retry_timeout = atoi(pos);
+               if (bss->assoc_sa_query_retry_timeout == 0) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid assoc_sa_query_retry_timeout",
+                                  line);
+                       return 1;
+               }
 #endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_IEEE80211N
-               } else if (os_strcmp(buf, "ieee80211n") == 0) {
-                       conf->ieee80211n = atoi(pos);
-               } else if (os_strcmp(buf, "ht_capab") == 0) {
-                       if (hostapd_config_ht_capab(conf, pos) < 0) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid "
-                                          "ht_capab", line);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "require_ht") == 0) {
-                       conf->require_ht = atoi(pos);
+       } else if (os_strcmp(buf, "ieee80211n") == 0) {
+               conf->ieee80211n = atoi(pos);
+       } else if (os_strcmp(buf, "ht_capab") == 0) {
+               if (hostapd_config_ht_capab(conf, pos) < 0) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid ht_capab",
+                                  line);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "require_ht") == 0) {
+               conf->require_ht = atoi(pos);
+       } else if (os_strcmp(buf, "obss_interval") == 0) {
+               conf->obss_interval = atoi(pos);
 #endif /* CONFIG_IEEE80211N */
 #ifdef CONFIG_IEEE80211AC
-               } else if (os_strcmp(buf, "ieee80211ac") == 0) {
-                       conf->ieee80211ac = atoi(pos);
-               } else if (os_strcmp(buf, "vht_capab") == 0) {
-                       if (hostapd_config_vht_capab(conf, pos) < 0) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid "
-                                          "vht_capab", line);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "require_vht") == 0) {
-                       conf->require_vht = atoi(pos);
-               } else if (os_strcmp(buf, "vht_oper_chwidth") == 0) {
-                       conf->vht_oper_chwidth = atoi(pos);
-               } else if (os_strcmp(buf, "vht_oper_centr_freq_seg0_idx") == 0)
-               {
-                       conf->vht_oper_centr_freq_seg0_idx = atoi(pos);
-               } else if (os_strcmp(buf, "vht_oper_centr_freq_seg1_idx") == 0)
-               {
-                       conf->vht_oper_centr_freq_seg1_idx = atoi(pos);
+       } else if (os_strcmp(buf, "ieee80211ac") == 0) {
+               conf->ieee80211ac = atoi(pos);
+       } else if (os_strcmp(buf, "vht_capab") == 0) {
+               if (hostapd_config_vht_capab(conf, pos) < 0) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid vht_capab",
+                                  line);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "require_vht") == 0) {
+               conf->require_vht = atoi(pos);
+       } else if (os_strcmp(buf, "vht_oper_chwidth") == 0) {
+               conf->vht_oper_chwidth = atoi(pos);
+       } else if (os_strcmp(buf, "vht_oper_centr_freq_seg0_idx") == 0) {
+               conf->vht_oper_centr_freq_seg0_idx = atoi(pos);
+       } else if (os_strcmp(buf, "vht_oper_centr_freq_seg1_idx") == 0) {
+               conf->vht_oper_centr_freq_seg1_idx = atoi(pos);
+       } else if (os_strcmp(buf, "vendor_vht") == 0) {
+               bss->vendor_vht = atoi(pos);
 #endif /* CONFIG_IEEE80211AC */
-               } else if (os_strcmp(buf, "max_listen_interval") == 0) {
-                       bss->max_listen_interval = atoi(pos);
-               } else if (os_strcmp(buf, "disable_pmksa_caching") == 0) {
-                       bss->disable_pmksa_caching = atoi(pos);
-               } else if (os_strcmp(buf, "okc") == 0) {
-                       bss->okc = atoi(pos);
+       } else if (os_strcmp(buf, "max_listen_interval") == 0) {
+               bss->max_listen_interval = atoi(pos);
+       } else if (os_strcmp(buf, "disable_pmksa_caching") == 0) {
+               bss->disable_pmksa_caching = atoi(pos);
+       } else if (os_strcmp(buf, "okc") == 0) {
+               bss->okc = atoi(pos);
 #ifdef CONFIG_WPS
-               } else if (os_strcmp(buf, "wps_state") == 0) {
-                       bss->wps_state = atoi(pos);
-                       if (bss->wps_state < 0 || bss->wps_state > 2) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid "
-                                          "wps_state", line);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "wps_independent") == 0) {
-                       bss->wps_independent = atoi(pos);
-               } else if (os_strcmp(buf, "ap_setup_locked") == 0) {
-                       bss->ap_setup_locked = atoi(pos);
-               } else if (os_strcmp(buf, "uuid") == 0) {
-                       if (uuid_str2bin(pos, bss->uuid)) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid UUID",
-                                          line);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "wps_pin_requests") == 0) {
-                       os_free(bss->wps_pin_requests);
-                       bss->wps_pin_requests = os_strdup(pos);
-               } else if (os_strcmp(buf, "device_name") == 0) {
-                       if (os_strlen(pos) > 32) {
-                               wpa_printf(MSG_ERROR, "Line %d: Too long "
-                                          "device_name", line);
-                               errors++;
-                       }
-                       os_free(bss->device_name);
-                       bss->device_name = os_strdup(pos);
-               } else if (os_strcmp(buf, "manufacturer") == 0) {
-                       if (os_strlen(pos) > 64) {
-                               wpa_printf(MSG_ERROR, "Line %d: Too long "
-                                          "manufacturer", line);
-                               errors++;
-                       }
-                       os_free(bss->manufacturer);
-                       bss->manufacturer = os_strdup(pos);
-               } else if (os_strcmp(buf, "model_name") == 0) {
-                       if (os_strlen(pos) > 32) {
-                               wpa_printf(MSG_ERROR, "Line %d: Too long "
-                                          "model_name", line);
-                               errors++;
-                       }
-                       os_free(bss->model_name);
-                       bss->model_name = os_strdup(pos);
-               } else if (os_strcmp(buf, "model_number") == 0) {
-                       if (os_strlen(pos) > 32) {
-                               wpa_printf(MSG_ERROR, "Line %d: Too long "
-                                          "model_number", line);
-                               errors++;
-                       }
-                       os_free(bss->model_number);
-                       bss->model_number = os_strdup(pos);
-               } else if (os_strcmp(buf, "serial_number") == 0) {
-                       if (os_strlen(pos) > 32) {
-                               wpa_printf(MSG_ERROR, "Line %d: Too long "
-                                          "serial_number", line);
-                               errors++;
-                       }
-                       os_free(bss->serial_number);
-                       bss->serial_number = os_strdup(pos);
-               } else if (os_strcmp(buf, "device_type") == 0) {
-                       if (wps_dev_type_str2bin(pos, bss->device_type))
-                               errors++;
-               } else if (os_strcmp(buf, "config_methods") == 0) {
-                       os_free(bss->config_methods);
-                       bss->config_methods = os_strdup(pos);
-               } else if (os_strcmp(buf, "os_version") == 0) {
-                       if (hexstr2bin(pos, bss->os_version, 4)) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid "
-                                          "os_version", line);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "ap_pin") == 0) {
-                       os_free(bss->ap_pin);
-                       bss->ap_pin = os_strdup(pos);
-               } else if (os_strcmp(buf, "skip_cred_build") == 0) {
-                       bss->skip_cred_build = atoi(pos);
-               } else if (os_strcmp(buf, "extra_cred") == 0) {
-                       os_free(bss->extra_cred);
-                       bss->extra_cred =
-                               (u8 *) os_readfile(pos, &bss->extra_cred_len);
-                       if (bss->extra_cred == NULL) {
-                               wpa_printf(MSG_ERROR, "Line %d: could not "
-                                          "read Credentials from '%s'",
-                                          line, pos);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "wps_cred_processing") == 0) {
-                       bss->wps_cred_processing = atoi(pos);
-               } else if (os_strcmp(buf, "ap_settings") == 0) {
-                       os_free(bss->ap_settings);
-                       bss->ap_settings =
-                               (u8 *) os_readfile(pos, &bss->ap_settings_len);
-                       if (bss->ap_settings == NULL) {
-                               wpa_printf(MSG_ERROR, "Line %d: could not "
-                                          "read AP Settings from '%s'",
-                                          line, pos);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "upnp_iface") == 0) {
-                       bss->upnp_iface = os_strdup(pos);
-               } else if (os_strcmp(buf, "friendly_name") == 0) {
-                       os_free(bss->friendly_name);
-                       bss->friendly_name = os_strdup(pos);
-               } else if (os_strcmp(buf, "manufacturer_url") == 0) {
-                       os_free(bss->manufacturer_url);
-                       bss->manufacturer_url = os_strdup(pos);
-               } else if (os_strcmp(buf, "model_description") == 0) {
-                       os_free(bss->model_description);
-                       bss->model_description = os_strdup(pos);
-               } else if (os_strcmp(buf, "model_url") == 0) {
-                       os_free(bss->model_url);
-                       bss->model_url = os_strdup(pos);
-               } else if (os_strcmp(buf, "upc") == 0) {
-                       os_free(bss->upc);
-                       bss->upc = os_strdup(pos);
-               } else if (os_strcmp(buf, "pbc_in_m1") == 0) {
-                       bss->pbc_in_m1 = atoi(pos);
+       } else if (os_strcmp(buf, "wps_state") == 0) {
+               bss->wps_state = atoi(pos);
+               if (bss->wps_state < 0 || bss->wps_state > 2) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid wps_state",
+                                  line);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "wps_independent") == 0) {
+               bss->wps_independent = atoi(pos);
+       } else if (os_strcmp(buf, "ap_setup_locked") == 0) {
+               bss->ap_setup_locked = atoi(pos);
+       } else if (os_strcmp(buf, "uuid") == 0) {
+               if (uuid_str2bin(pos, bss->uuid)) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid UUID", line);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "wps_pin_requests") == 0) {
+               os_free(bss->wps_pin_requests);
+               bss->wps_pin_requests = os_strdup(pos);
+       } else if (os_strcmp(buf, "device_name") == 0) {
+               if (os_strlen(pos) > 32) {
+                       wpa_printf(MSG_ERROR, "Line %d: Too long "
+                                  "device_name", line);
+                       return 1;
+               }
+               os_free(bss->device_name);
+               bss->device_name = os_strdup(pos);
+       } else if (os_strcmp(buf, "manufacturer") == 0) {
+               if (os_strlen(pos) > 64) {
+                       wpa_printf(MSG_ERROR, "Line %d: Too long manufacturer",
+                                  line);
+                       return 1;
+               }
+               os_free(bss->manufacturer);
+               bss->manufacturer = os_strdup(pos);
+       } else if (os_strcmp(buf, "model_name") == 0) {
+               if (os_strlen(pos) > 32) {
+                       wpa_printf(MSG_ERROR, "Line %d: Too long model_name",
+                                  line);
+                       return 1;
+               }
+               os_free(bss->model_name);
+               bss->model_name = os_strdup(pos);
+       } else if (os_strcmp(buf, "model_number") == 0) {
+               if (os_strlen(pos) > 32) {
+                       wpa_printf(MSG_ERROR, "Line %d: Too long model_number",
+                                  line);
+                       return 1;
+               }
+               os_free(bss->model_number);
+               bss->model_number = os_strdup(pos);
+       } else if (os_strcmp(buf, "serial_number") == 0) {
+               if (os_strlen(pos) > 32) {
+                       wpa_printf(MSG_ERROR, "Line %d: Too long serial_number",
+                                  line);
+                       return 1;
+               }
+               os_free(bss->serial_number);
+               bss->serial_number = os_strdup(pos);
+       } else if (os_strcmp(buf, "device_type") == 0) {
+               if (wps_dev_type_str2bin(pos, bss->device_type))
+                       return 1;
+       } else if (os_strcmp(buf, "config_methods") == 0) {
+               os_free(bss->config_methods);
+               bss->config_methods = os_strdup(pos);
+       } else if (os_strcmp(buf, "os_version") == 0) {
+               if (hexstr2bin(pos, bss->os_version, 4)) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid os_version",
+                                  line);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "ap_pin") == 0) {
+               os_free(bss->ap_pin);
+               bss->ap_pin = os_strdup(pos);
+       } else if (os_strcmp(buf, "skip_cred_build") == 0) {
+               bss->skip_cred_build = atoi(pos);
+       } else if (os_strcmp(buf, "extra_cred") == 0) {
+               os_free(bss->extra_cred);
+               bss->extra_cred = (u8 *) os_readfile(pos, &bss->extra_cred_len);
+               if (bss->extra_cred == NULL) {
+                       wpa_printf(MSG_ERROR, "Line %d: could not read Credentials from '%s'",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "wps_cred_processing") == 0) {
+               bss->wps_cred_processing = atoi(pos);
+       } else if (os_strcmp(buf, "ap_settings") == 0) {
+               os_free(bss->ap_settings);
+               bss->ap_settings =
+                       (u8 *) os_readfile(pos, &bss->ap_settings_len);
+               if (bss->ap_settings == NULL) {
+                       wpa_printf(MSG_ERROR, "Line %d: could not read AP Settings from '%s'",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "upnp_iface") == 0) {
+               os_free(bss->upnp_iface);
+               bss->upnp_iface = os_strdup(pos);
+       } else if (os_strcmp(buf, "friendly_name") == 0) {
+               os_free(bss->friendly_name);
+               bss->friendly_name = os_strdup(pos);
+       } else if (os_strcmp(buf, "manufacturer_url") == 0) {
+               os_free(bss->manufacturer_url);
+               bss->manufacturer_url = os_strdup(pos);
+       } else if (os_strcmp(buf, "model_description") == 0) {
+               os_free(bss->model_description);
+               bss->model_description = os_strdup(pos);
+       } else if (os_strcmp(buf, "model_url") == 0) {
+               os_free(bss->model_url);
+               bss->model_url = os_strdup(pos);
+       } else if (os_strcmp(buf, "upc") == 0) {
+               os_free(bss->upc);
+               bss->upc = os_strdup(pos);
+       } else if (os_strcmp(buf, "pbc_in_m1") == 0) {
+               bss->pbc_in_m1 = atoi(pos);
+       } else if (os_strcmp(buf, "server_id") == 0) {
+               os_free(bss->server_id);
+               bss->server_id = os_strdup(pos);
 #ifdef CONFIG_WPS_NFC
-               } else if (os_strcmp(buf, "wps_nfc_dev_pw_id") == 0) {
-                       bss->wps_nfc_dev_pw_id = atoi(pos);
-                       if (bss->wps_nfc_dev_pw_id < 0x10 ||
-                           bss->wps_nfc_dev_pw_id > 0xffff) {
-                               wpa_printf(MSG_ERROR, "Line %d: Invalid "
-                                          "wps_nfc_dev_pw_id value", line);
-                               errors++;
-                       }
-                       bss->wps_nfc_pw_from_config = 1;
-               } else if (os_strcmp(buf, "wps_nfc_dh_pubkey") == 0) {
-                       wpabuf_free(bss->wps_nfc_dh_pubkey);
-                       bss->wps_nfc_dh_pubkey = hostapd_parse_bin(pos);
-                       bss->wps_nfc_pw_from_config = 1;
-               } else if (os_strcmp(buf, "wps_nfc_dh_privkey") == 0) {
-                       wpabuf_free(bss->wps_nfc_dh_privkey);
-                       bss->wps_nfc_dh_privkey = hostapd_parse_bin(pos);
-                       bss->wps_nfc_pw_from_config = 1;
-               } else if (os_strcmp(buf, "wps_nfc_dev_pw") == 0) {
-                       wpabuf_free(bss->wps_nfc_dev_pw);
-                       bss->wps_nfc_dev_pw = hostapd_parse_bin(pos);
-                       bss->wps_nfc_pw_from_config = 1;
+       } else if (os_strcmp(buf, "wps_nfc_dev_pw_id") == 0) {
+               bss->wps_nfc_dev_pw_id = atoi(pos);
+               if (bss->wps_nfc_dev_pw_id < 0x10 ||
+                   bss->wps_nfc_dev_pw_id > 0xffff) {
+                       wpa_printf(MSG_ERROR, "Line %d: Invalid wps_nfc_dev_pw_id value",
+                                  line);
+                       return 1;
+               }
+               bss->wps_nfc_pw_from_config = 1;
+       } else if (os_strcmp(buf, "wps_nfc_dh_pubkey") == 0) {
+               wpabuf_free(bss->wps_nfc_dh_pubkey);
+               bss->wps_nfc_dh_pubkey = hostapd_parse_bin(pos);
+               bss->wps_nfc_pw_from_config = 1;
+       } else if (os_strcmp(buf, "wps_nfc_dh_privkey") == 0) {
+               wpabuf_free(bss->wps_nfc_dh_privkey);
+               bss->wps_nfc_dh_privkey = hostapd_parse_bin(pos);
+               bss->wps_nfc_pw_from_config = 1;
+       } else if (os_strcmp(buf, "wps_nfc_dev_pw") == 0) {
+               wpabuf_free(bss->wps_nfc_dev_pw);
+               bss->wps_nfc_dev_pw = hostapd_parse_bin(pos);
+               bss->wps_nfc_pw_from_config = 1;
 #endif /* CONFIG_WPS_NFC */
 #endif /* CONFIG_WPS */
 #ifdef CONFIG_P2P_MANAGER
-               } else if (os_strcmp(buf, "manage_p2p") == 0) {
-                       int manage = atoi(pos);
-                       if (manage)
-                               bss->p2p |= P2P_MANAGE;
-                       else
-                               bss->p2p &= ~P2P_MANAGE;
-               } else if (os_strcmp(buf, "allow_cross_connection") == 0) {
-                       if (atoi(pos))
-                               bss->p2p |= P2P_ALLOW_CROSS_CONNECTION;
-                       else
-                               bss->p2p &= ~P2P_ALLOW_CROSS_CONNECTION;
+       } else if (os_strcmp(buf, "manage_p2p") == 0) {
+               if (atoi(pos))
+                       bss->p2p |= P2P_MANAGE;
+               else
+                       bss->p2p &= ~P2P_MANAGE;
+       } else if (os_strcmp(buf, "allow_cross_connection") == 0) {
+               if (atoi(pos))
+                       bss->p2p |= P2P_ALLOW_CROSS_CONNECTION;
+               else
+                       bss->p2p &= ~P2P_ALLOW_CROSS_CONNECTION;
 #endif /* CONFIG_P2P_MANAGER */
-               } else if (os_strcmp(buf, "disassoc_low_ack") == 0) {
-                       bss->disassoc_low_ack = atoi(pos);
-               } else if (os_strcmp(buf, "tdls_prohibit") == 0) {
-                       int val = atoi(pos);
-                       if (val)
-                               bss->tdls |= TDLS_PROHIBIT;
-                       else
-                               bss->tdls &= ~TDLS_PROHIBIT;
-               } else if (os_strcmp(buf, "tdls_prohibit_chan_switch") == 0) {
-                       int val = atoi(pos);
-                       if (val)
-                               bss->tdls |= TDLS_PROHIBIT_CHAN_SWITCH;
-                       else
-                               bss->tdls &= ~TDLS_PROHIBIT_CHAN_SWITCH;
+       } else if (os_strcmp(buf, "disassoc_low_ack") == 0) {
+               bss->disassoc_low_ack = atoi(pos);
+       } else if (os_strcmp(buf, "tdls_prohibit") == 0) {
+               if (atoi(pos))
+                       bss->tdls |= TDLS_PROHIBIT;
+               else
+                       bss->tdls &= ~TDLS_PROHIBIT;
+       } else if (os_strcmp(buf, "tdls_prohibit_chan_switch") == 0) {
+               if (atoi(pos))
+                       bss->tdls |= TDLS_PROHIBIT_CHAN_SWITCH;
+               else
+                       bss->tdls &= ~TDLS_PROHIBIT_CHAN_SWITCH;
 #ifdef CONFIG_RSN_TESTING
-               } else if (os_strcmp(buf, "rsn_testing") == 0) {
-                       extern int rsn_testing;
-                       rsn_testing = atoi(pos);
+       } else if (os_strcmp(buf, "rsn_testing") == 0) {
+               extern int rsn_testing;
+               rsn_testing = atoi(pos);
 #endif /* CONFIG_RSN_TESTING */
-               } else if (os_strcmp(buf, "time_advertisement") == 0) {
-                       bss->time_advertisement = atoi(pos);
-               } else if (os_strcmp(buf, "time_zone") == 0) {
-                       size_t tz_len = os_strlen(pos);
-                       if (tz_len < 4 || tz_len > 255) {
-                               wpa_printf(MSG_DEBUG, "Line %d: invalid "
-                                          "time_zone", line);
-                               errors++;
-                               return errors;
-                       }
-                       os_free(bss->time_zone);
-                       bss->time_zone = os_strdup(pos);
-                       if (bss->time_zone == NULL)
-                               errors++;
+       } else if (os_strcmp(buf, "time_advertisement") == 0) {
+               bss->time_advertisement = atoi(pos);
+       } else if (os_strcmp(buf, "time_zone") == 0) {
+               size_t tz_len = os_strlen(pos);
+               if (tz_len < 4 || tz_len > 255) {
+                       wpa_printf(MSG_DEBUG, "Line %d: invalid time_zone",
+                                  line);
+                       return 1;
+               }
+               os_free(bss->time_zone);
+               bss->time_zone = os_strdup(pos);
+               if (bss->time_zone == NULL)
+                       return 1;
 #ifdef CONFIG_WNM
-               } else if (os_strcmp(buf, "wnm_sleep_mode") == 0) {
-                       bss->wnm_sleep_mode = atoi(pos);
-               } else if (os_strcmp(buf, "bss_transition") == 0) {
-                       bss->bss_transition = atoi(pos);
+       } else if (os_strcmp(buf, "wnm_sleep_mode") == 0) {
+               bss->wnm_sleep_mode = atoi(pos);
+       } else if (os_strcmp(buf, "bss_transition") == 0) {
+               bss->bss_transition = atoi(pos);
 #endif /* CONFIG_WNM */
 #ifdef CONFIG_INTERWORKING
-               } else if (os_strcmp(buf, "interworking") == 0) {
-                       bss->interworking = atoi(pos);
-               } else if (os_strcmp(buf, "access_network_type") == 0) {
-                       bss->access_network_type = atoi(pos);
-                       if (bss->access_network_type < 0 ||
-                           bss->access_network_type > 15) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid "
-                                          "access_network_type", line);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "internet") == 0) {
-                       bss->internet = atoi(pos);
-               } else if (os_strcmp(buf, "asra") == 0) {
-                       bss->asra = atoi(pos);
-               } else if (os_strcmp(buf, "esr") == 0) {
-                       bss->esr = atoi(pos);
-               } else if (os_strcmp(buf, "uesa") == 0) {
-                       bss->uesa = atoi(pos);
-               } else if (os_strcmp(buf, "venue_group") == 0) {
-                       bss->venue_group = atoi(pos);
-                       bss->venue_info_set = 1;
-               } else if (os_strcmp(buf, "venue_type") == 0) {
-                       bss->venue_type = atoi(pos);
-                       bss->venue_info_set = 1;
-               } else if (os_strcmp(buf, "hessid") == 0) {
-                       if (hwaddr_aton(pos, bss->hessid)) {
-                               wpa_printf(MSG_ERROR, "Line %d: invalid "
-                                          "hessid", line);
-                               errors++;
-                       }
-               } else if (os_strcmp(buf, "roaming_consortium") == 0) {
-                       if (parse_roaming_consortium(bss, pos, line) < 0)
-                               errors++;
-               } else if (os_strcmp(buf, "venue_name") == 0) {
-                       if (parse_venue_name(bss, pos, line) < 0)
-                               errors++;
-               } else if (os_strcmp(buf, "network_auth_type") == 0) {
-                       u8 auth_type;
-                       u16 redirect_url_len;
-                       if (hexstr2bin(pos, &auth_type, 1)) {
-                               wpa_printf(MSG_ERROR, "Line %d: Invalid "
-                                          "network_auth_type '%s'",
-                                          line, pos);
-                               errors++;
-                               return errors;
-                       }
-                       if (auth_type == 0 || auth_type == 2)
-                               redirect_url_len = os_strlen(pos + 2);
-                       else
-                               redirect_url_len = 0;
-                       os_free(bss->network_auth_type);
-                       bss->network_auth_type =
-                               os_malloc(redirect_url_len + 3 + 1);
-                       if (bss->network_auth_type == NULL) {
-                               errors++;
-                               return errors;
-                       }
-                       *bss->network_auth_type = auth_type;
-                       WPA_PUT_LE16(bss->network_auth_type + 1,
-                                    redirect_url_len);
-                       if (redirect_url_len)
-                               os_memcpy(bss->network_auth_type + 3,
-                                         pos + 2, redirect_url_len);
-                       bss->network_auth_type_len = 3 + redirect_url_len;
-               } else if (os_strcmp(buf, "ipaddr_type_availability") == 0) {
-                       if (hexstr2bin(pos, &bss->ipaddr_type_availability, 1))
-                       {
-                               wpa_printf(MSG_ERROR, "Line %d: Invalid "
-                                          "ipaddr_type_availability '%s'",
-                                          line, pos);
-                               bss->ipaddr_type_configured = 0;
-                               errors++;
-                               return errors;
-                       }
-                       bss->ipaddr_type_configured = 1;
-               } else if (os_strcmp(buf, "domain_name") == 0) {
-                       int j, num_domains, domain_len, domain_list_len = 0;
-                       char *tok_start, *tok_prev;
-                       u8 *domain_list, *domain_ptr;
-
-                       domain_list_len = os_strlen(pos) + 1;
-                       domain_list = os_malloc(domain_list_len);
-                       if (domain_list == NULL) {
-                               errors++;
-                               return errors;
-                       }
-
-                       domain_ptr = domain_list;
-                       tok_prev = pos;
-                       num_domains = 1;
-                       while ((tok_prev = os_strchr(tok_prev, ','))) {
-                               num_domains++;
-                               tok_prev++;
-                       }
-                       tok_prev = pos;
-                       for (j = 0; j < num_domains; j++) {
-                               tok_start = os_strchr(tok_prev, ',');
-                               if (tok_start) {
-                                       domain_len = tok_start - tok_prev;
-                                       *domain_ptr = domain_len;
-                                       os_memcpy(domain_ptr + 1, tok_prev,
-                                                 domain_len);
-                                       domain_ptr += domain_len + 1;
-                                       tok_prev = ++tok_start;
-                               } else {
-                                       domain_len = os_strlen(tok_prev);
-                                       *domain_ptr = domain_len;
-                                       os_memcpy(domain_ptr + 1, tok_prev,
-                                                 domain_len);
-                                       domain_ptr += domain_len + 1;
-                               }
+       } else if (os_strcmp(buf, "interworking") == 0) {
+               bss->interworking = atoi(pos);
+       } else if (os_strcmp(buf, "access_network_type") == 0) {
+               bss->access_network_type = atoi(pos);
+               if (bss->access_network_type < 0 ||
+                   bss->access_network_type > 15) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: invalid access_network_type",
+                                  line);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "internet") == 0) {
+               bss->internet = atoi(pos);
+       } else if (os_strcmp(buf, "asra") == 0) {
+               bss->asra = atoi(pos);
+       } else if (os_strcmp(buf, "esr") == 0) {
+               bss->esr = atoi(pos);
+       } else if (os_strcmp(buf, "uesa") == 0) {
+               bss->uesa = atoi(pos);
+       } else if (os_strcmp(buf, "venue_group") == 0) {
+               bss->venue_group = atoi(pos);
+               bss->venue_info_set = 1;
+       } else if (os_strcmp(buf, "venue_type") == 0) {
+               bss->venue_type = atoi(pos);
+               bss->venue_info_set = 1;
+       } else if (os_strcmp(buf, "hessid") == 0) {
+               if (hwaddr_aton(pos, bss->hessid)) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid hessid", line);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "roaming_consortium") == 0) {
+               if (parse_roaming_consortium(bss, pos, line) < 0)
+                       return 1;
+       } else if (os_strcmp(buf, "venue_name") == 0) {
+               if (parse_venue_name(bss, pos, line) < 0)
+                       return 1;
+       } else if (os_strcmp(buf, "network_auth_type") == 0) {
+               u8 auth_type;
+               u16 redirect_url_len;
+               if (hexstr2bin(pos, &auth_type, 1)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: Invalid network_auth_type '%s'",
+                                  line, pos);
+                       return 1;
+               }
+               if (auth_type == 0 || auth_type == 2)
+                       redirect_url_len = os_strlen(pos + 2);
+               else
+                       redirect_url_len = 0;
+               os_free(bss->network_auth_type);
+               bss->network_auth_type = os_malloc(redirect_url_len + 3 + 1);
+               if (bss->network_auth_type == NULL)
+                       return 1;
+               *bss->network_auth_type = auth_type;
+               WPA_PUT_LE16(bss->network_auth_type + 1, redirect_url_len);
+               if (redirect_url_len)
+                       os_memcpy(bss->network_auth_type + 3, pos + 2,
+                                 redirect_url_len);
+               bss->network_auth_type_len = 3 + redirect_url_len;
+       } else if (os_strcmp(buf, "ipaddr_type_availability") == 0) {
+               if (hexstr2bin(pos, &bss->ipaddr_type_availability, 1)) {
+                       wpa_printf(MSG_ERROR, "Line %d: Invalid ipaddr_type_availability '%s'",
+                                  line, pos);
+                       bss->ipaddr_type_configured = 0;
+                       return 1;
+               }
+               bss->ipaddr_type_configured = 1;
+       } else if (os_strcmp(buf, "domain_name") == 0) {
+               int j, num_domains, domain_len, domain_list_len = 0;
+               char *tok_start, *tok_prev;
+               u8 *domain_list, *domain_ptr;
+
+               domain_list_len = os_strlen(pos) + 1;
+               domain_list = os_malloc(domain_list_len);
+               if (domain_list == NULL)
+                       return 1;
+
+               domain_ptr = domain_list;
+               tok_prev = pos;
+               num_domains = 1;
+               while ((tok_prev = os_strchr(tok_prev, ','))) {
+                       num_domains++;
+                       tok_prev++;
+               }
+               tok_prev = pos;
+               for (j = 0; j < num_domains; j++) {
+                       tok_start = os_strchr(tok_prev, ',');
+                       if (tok_start) {
+                               domain_len = tok_start - tok_prev;
+                               *domain_ptr = domain_len;
+                               os_memcpy(domain_ptr + 1, tok_prev, domain_len);
+                               domain_ptr += domain_len + 1;
+                               tok_prev = ++tok_start;
+                       } else {
+                               domain_len = os_strlen(tok_prev);
+                               *domain_ptr = domain_len;
+                               os_memcpy(domain_ptr + 1, tok_prev, domain_len);
+                               domain_ptr += domain_len + 1;
                        }
+               }
 
-                       os_free(bss->domain_name);
-                       bss->domain_name = domain_list;
-                       bss->domain_name_len = domain_list_len;
-               } else if (os_strcmp(buf, "anqp_3gpp_cell_net") == 0) {
-                       if (parse_3gpp_cell_net(bss, pos, line) < 0)
-                               errors++;
-               } else if (os_strcmp(buf, "nai_realm") == 0) {
-                       if (parse_nai_realm(bss, pos, line) < 0)
-                               errors++;
-               } else if (os_strcmp(buf, "gas_frag_limit") == 0) {
-                       bss->gas_frag_limit = atoi(pos);
-               } else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
-                       bss->gas_comeback_delay = atoi(pos);
+               os_free(bss->domain_name);
+               bss->domain_name = domain_list;
+               bss->domain_name_len = domain_list_len;
+       } else if (os_strcmp(buf, "anqp_3gpp_cell_net") == 0) {
+               if (parse_3gpp_cell_net(bss, pos, line) < 0)
+                       return 1;
+       } else if (os_strcmp(buf, "nai_realm") == 0) {
+               if (parse_nai_realm(bss, pos, line) < 0)
+                       return 1;
+       } else if (os_strcmp(buf, "gas_frag_limit") == 0) {
+               bss->gas_frag_limit = atoi(pos);
+       } else if (os_strcmp(buf, "gas_comeback_delay") == 0) {
+               bss->gas_comeback_delay = atoi(pos);
+       } else if (os_strcmp(buf, "qos_map_set") == 0) {
+               if (parse_qos_map_set(bss, pos, line) < 0)
+                       return 1;
 #endif /* CONFIG_INTERWORKING */
 #ifdef CONFIG_RADIUS_TEST
-               } else if (os_strcmp(buf, "dump_msk_file") == 0) {
-                       os_free(bss->dump_msk_file);
-                       bss->dump_msk_file = os_strdup(pos);
+       } else if (os_strcmp(buf, "dump_msk_file") == 0) {
+               os_free(bss->dump_msk_file);
+               bss->dump_msk_file = os_strdup(pos);
 #endif /* CONFIG_RADIUS_TEST */
 #ifdef CONFIG_HS20
-               } else if (os_strcmp(buf, "hs20") == 0) {
-                       bss->hs20 = atoi(pos);
-               } else if (os_strcmp(buf, "disable_dgaf") == 0) {
-                       bss->disable_dgaf = atoi(pos);
-               } else if (os_strcmp(buf, "hs20_oper_friendly_name") == 0) {
-                       if (hs20_parse_oper_friendly_name(bss, pos, line) < 0)
-                               errors++;
-               } else if (os_strcmp(buf, "hs20_wan_metrics") == 0) {
-                       if (hs20_parse_wan_metrics(bss, pos, line) < 0) {
-                               errors++;
-                               return errors;
-                       }
-               } else if (os_strcmp(buf, "hs20_conn_capab") == 0) {
-                       if (hs20_parse_conn_capab(bss, pos, line) < 0) {
-                               errors++;
-                               return errors;
-                       }
-               } else if (os_strcmp(buf, "hs20_operating_class") == 0) {
-                       u8 *oper_class;
-                       size_t oper_class_len;
-                       oper_class_len = os_strlen(pos);
-                       if (oper_class_len < 2 || (oper_class_len & 0x01)) {
-                               wpa_printf(MSG_ERROR, "Line %d: Invalid "
-                                          "hs20_operating_class '%s'",
-                                          line, pos);
-                               errors++;
-                               return errors;
-                       }
-                       oper_class_len /= 2;
-                       oper_class = os_malloc(oper_class_len);
-                       if (oper_class == NULL) {
-                               errors++;
-                               return errors;
-                       }
-                       if (hexstr2bin(pos, oper_class, oper_class_len)) {
-                               wpa_printf(MSG_ERROR, "Line %d: Invalid "
-                                          "hs20_operating_class '%s'",
-                                          line, pos);
-                               os_free(oper_class);
-                               errors++;
-                               return errors;
-                       }
-                       os_free(bss->hs20_operating_class);
-                       bss->hs20_operating_class = oper_class;
-                       bss->hs20_operating_class_len = oper_class_len;
+       } else if (os_strcmp(buf, "hs20") == 0) {
+               bss->hs20 = atoi(pos);
+       } else if (os_strcmp(buf, "disable_dgaf") == 0) {
+               bss->disable_dgaf = atoi(pos);
+       } else if (os_strcmp(buf, "proxy_arp") == 0) {
+               bss->proxy_arp = atoi(pos);
+       } else if (os_strcmp(buf, "osen") == 0) {
+               bss->osen = atoi(pos);
+       } else if (os_strcmp(buf, "anqp_domain_id") == 0) {
+               bss->anqp_domain_id = atoi(pos);
+       } else if (os_strcmp(buf, "hs20_deauth_req_timeout") == 0) {
+               bss->hs20_deauth_req_timeout = atoi(pos);
+       } else if (os_strcmp(buf, "hs20_oper_friendly_name") == 0) {
+               if (hs20_parse_oper_friendly_name(bss, pos, line) < 0)
+                       return 1;
+       } else if (os_strcmp(buf, "hs20_wan_metrics") == 0) {
+               if (hs20_parse_wan_metrics(bss, pos, line) < 0)
+                       return 1;
+       } else if (os_strcmp(buf, "hs20_conn_capab") == 0) {
+               if (hs20_parse_conn_capab(bss, pos, line) < 0) {
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "hs20_operating_class") == 0) {
+               u8 *oper_class;
+               size_t oper_class_len;
+               oper_class_len = os_strlen(pos);
+               if (oper_class_len < 2 || (oper_class_len & 0x01)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: Invalid hs20_operating_class '%s'",
+                                  line, pos);
+                       return 1;
+               }
+               oper_class_len /= 2;
+               oper_class = os_malloc(oper_class_len);
+               if (oper_class == NULL)
+                       return 1;
+               if (hexstr2bin(pos, oper_class, oper_class_len)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: Invalid hs20_operating_class '%s'",
+                                  line, pos);
+                       os_free(oper_class);
+                       return 1;
+               }
+               os_free(bss->hs20_operating_class);
+               bss->hs20_operating_class = oper_class;
+               bss->hs20_operating_class_len = oper_class_len;
+       } else if (os_strcmp(buf, "hs20_icon") == 0) {
+               if (hs20_parse_icon(bss, pos) < 0) {
+                       wpa_printf(MSG_ERROR, "Line %d: Invalid hs20_icon '%s'",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "osu_ssid") == 0) {
+               if (hs20_parse_osu_ssid(bss, pos, line) < 0)
+                       return 1;
+       } else if (os_strcmp(buf, "osu_server_uri") == 0) {
+               if (hs20_parse_osu_server_uri(bss, pos, line) < 0)
+                       return 1;
+       } else if (os_strcmp(buf, "osu_friendly_name") == 0) {
+               if (hs20_parse_osu_friendly_name(bss, pos, line) < 0)
+                       return 1;
+       } else if (os_strcmp(buf, "osu_nai") == 0) {
+               if (hs20_parse_osu_nai(bss, pos, line) < 0)
+                       return 1;
+       } else if (os_strcmp(buf, "osu_method_list") == 0) {
+               if (hs20_parse_osu_method_list(bss, pos, line) < 0)
+                       return 1;
+       } else if (os_strcmp(buf, "osu_icon") == 0) {
+               if (hs20_parse_osu_icon(bss, pos, line) < 0)
+                       return 1;
+       } else if (os_strcmp(buf, "osu_service_desc") == 0) {
+               if (hs20_parse_osu_service_desc(bss, pos, line) < 0)
+                       return 1;
+       } else if (os_strcmp(buf, "subscr_remediation_url") == 0) {
+               os_free(bss->subscr_remediation_url);
+               bss->subscr_remediation_url = os_strdup(pos);
+       } else if (os_strcmp(buf, "subscr_remediation_method") == 0) {
+               bss->subscr_remediation_method = atoi(pos);
 #endif /* CONFIG_HS20 */
 #ifdef CONFIG_TESTING_OPTIONS
-#define PARSE_TEST_PROBABILITY(_val)                                   \
-               } else if (os_strcmp(buf, #_val) == 0) {                \
-                       char *end;                                      \
-                                                                       \
-                       conf->_val = strtod(pos, &end);                 \
-                       if (*end || conf->_val < 0.0d ||                \
-                           conf->_val > 1.0d) {                        \
-                               wpa_printf(MSG_ERROR,                   \
-                                          "Line %d: Invalid value '%s'", \
-                                          line, pos);                  \
-                               errors++;                               \
-                               return errors;                          \
-                       }
-               PARSE_TEST_PROBABILITY(ignore_probe_probability)
-               PARSE_TEST_PROBABILITY(ignore_auth_probability)
-               PARSE_TEST_PROBABILITY(ignore_assoc_probability)
-               PARSE_TEST_PROBABILITY(ignore_reassoc_probability)
-               PARSE_TEST_PROBABILITY(corrupt_gtk_rekey_mic_probability)
+#define PARSE_TEST_PROBABILITY(_val)                           \
+       } else if (os_strcmp(buf, #_val) == 0) {                \
+               char *end;                                      \
+                                                               \
+               conf->_val = strtod(pos, &end);                 \
+               if (*end || conf->_val < 0.0 ||                 \
+                   conf->_val > 1.0) {                         \
+                       wpa_printf(MSG_ERROR,                   \
+                                  "Line %d: Invalid value '%s'", \
+                                  line, pos);                  \
+                       return 1;                               \
+               }
+       PARSE_TEST_PROBABILITY(ignore_probe_probability)
+       PARSE_TEST_PROBABILITY(ignore_auth_probability)
+       PARSE_TEST_PROBABILITY(ignore_assoc_probability)
+       PARSE_TEST_PROBABILITY(ignore_reassoc_probability)
+       PARSE_TEST_PROBABILITY(corrupt_gtk_rekey_mic_probability)
+       } else if (os_strcmp(buf, "bss_load_test") == 0) {
+               WPA_PUT_LE16(bss->bss_load_test, atoi(pos));
+               pos = os_strchr(pos, ':');
+               if (pos == NULL) {
+                       wpa_printf(MSG_ERROR, "Line %d: Invalid bss_load_test",
+                                  line);
+                       return 1;
+               }
+               pos++;
+               bss->bss_load_test[2] = atoi(pos);
+               pos = os_strchr(pos, ':');
+               if (pos == NULL) {
+                       wpa_printf(MSG_ERROR, "Line %d: Invalid bss_load_test",
+                                  line);
+                       return 1;
+               }
+               pos++;
+               WPA_PUT_LE16(&bss->bss_load_test[3], atoi(pos));
+               bss->bss_load_test_set = 1;
+       } else if (os_strcmp(buf, "radio_measurements") == 0) {
+               bss->radio_measurements = atoi(pos);
 #endif /* CONFIG_TESTING_OPTIONS */
-               } else if (os_strcmp(buf, "vendor_elements") == 0) {
-                       struct wpabuf *elems;
-                       size_t len = os_strlen(pos);
-                       if (len & 0x01) {
-                               wpa_printf(MSG_ERROR, "Line %d: Invalid "
-                                          "vendor_elements '%s'", line, pos);
-                               return 1;
-                       }
-                       len /= 2;
-                       if (len == 0) {
-                               wpabuf_free(bss->vendor_elements);
-                               bss->vendor_elements = NULL;
-                               return 0;
-                       }
-
-                       elems = wpabuf_alloc(len);
-                       if (elems == NULL)
-                               return 1;
-
-                       if (hexstr2bin(pos, wpabuf_put(elems, len), len)) {
-                               wpabuf_free(elems);
-                               wpa_printf(MSG_ERROR, "Line %d: Invalid "
-                                          "vendor_elements '%s'", line, pos);
-                               return 1;
-                       }
-
+       } else if (os_strcmp(buf, "vendor_elements") == 0) {
+               struct wpabuf *elems;
+               size_t len = os_strlen(pos);
+               if (len & 0x01) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: Invalid vendor_elements '%s'",
+                                  line, pos);
+                       return 1;
+               }
+               len /= 2;
+               if (len == 0) {
                        wpabuf_free(bss->vendor_elements);
-                       bss->vendor_elements = elems;
-               } else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0) {
-                       bss->sae_anti_clogging_threshold = atoi(pos);
-               } else if (os_strcmp(buf, "sae_groups") == 0) {
-                       if (hostapd_parse_intlist(&bss->sae_groups, pos)) {
-                               wpa_printf(MSG_ERROR, "Line %d: Invalid "
-                                          "sae_groups value '%s'", line, pos);
-                               return 1;
-                       }
-               } else {
-                       wpa_printf(MSG_ERROR, "Line %d: unknown configuration "
-                                  "item '%s'", line, buf);
-                       errors++;
+                       bss->vendor_elements = NULL;
+                       return 0;
                }
-       }
 
-       return errors;
-}
+               elems = wpabuf_alloc(len);
+               if (elems == NULL)
+                       return 1;
 
+               if (hexstr2bin(pos, wpabuf_put(elems, len), len)) {
+                       wpabuf_free(elems);
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: Invalid vendor_elements '%s'",
+                                  line, pos);
+                       return 1;
+               }
 
-static void hostapd_set_security_params(struct hostapd_bss_config *bss)
-{
-       if (bss->individual_wep_key_len == 0) {
-               /* individual keys are not use; can use key idx0 for
-                * broadcast keys */
-               bss->broadcast_key_idx_min = 0;
-       }
-
-       if ((bss->wpa & 2) && bss->rsn_pairwise == 0)
-               bss->rsn_pairwise = bss->wpa_pairwise;
-       bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise,
-                                                   bss->rsn_pairwise);
-
-       bss->radius->auth_server = bss->radius->auth_servers;
-       bss->radius->acct_server = bss->radius->acct_servers;
-
-       if (bss->wpa && bss->ieee802_1x) {
-               bss->ssid.security_policy = SECURITY_WPA;
-       } else if (bss->wpa) {
-               bss->ssid.security_policy = SECURITY_WPA_PSK;
-       } else if (bss->ieee802_1x) {
-               int cipher = WPA_CIPHER_NONE;
-               bss->ssid.security_policy = SECURITY_IEEE_802_1X;
-               bss->ssid.wep.default_len = bss->default_wep_key_len;
-               if (bss->default_wep_key_len)
-                       cipher = bss->default_wep_key_len >= 13 ?
-                               WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40;
-               bss->wpa_group = cipher;
-               bss->wpa_pairwise = cipher;
-               bss->rsn_pairwise = cipher;
-       } else if (bss->ssid.wep.keys_set) {
-               int cipher = WPA_CIPHER_WEP40;
-               if (bss->ssid.wep.len[0] >= 13)
-                       cipher = WPA_CIPHER_WEP104;
-               bss->ssid.security_policy = SECURITY_STATIC_WEP;
-               bss->wpa_group = cipher;
-               bss->wpa_pairwise = cipher;
-               bss->rsn_pairwise = cipher;
+               wpabuf_free(bss->vendor_elements);
+               bss->vendor_elements = elems;
+       } else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0) {
+               bss->sae_anti_clogging_threshold = atoi(pos);
+       } else if (os_strcmp(buf, "sae_groups") == 0) {
+               if (hostapd_parse_intlist(&bss->sae_groups, pos)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: Invalid sae_groups value '%s'",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "local_pwr_constraint") == 0) {
+               int val = atoi(pos);
+               if (val < 0 || val > 255) {
+                       wpa_printf(MSG_ERROR, "Line %d: Invalid local_pwr_constraint %d (expected 0..255)",
+                                  line, val);
+                       return 1;
+               }
+               conf->local_pwr_constraint = val;
+       } else if (os_strcmp(buf, "spectrum_mgmt_required") == 0) {
+               conf->spectrum_mgmt_required = atoi(pos);
+       } else if (os_strcmp(buf, "wowlan_triggers") == 0) {
+               os_free(bss->wowlan_triggers);
+               bss->wowlan_triggers = os_strdup(pos);
        } else {
-               bss->ssid.security_policy = SECURITY_PLAINTEXT;
-               bss->wpa_group = WPA_CIPHER_NONE;
-               bss->wpa_pairwise = WPA_CIPHER_NONE;
-               bss->rsn_pairwise = WPA_CIPHER_NONE;
+               wpa_printf(MSG_ERROR,
+                          "Line %d: unknown configuration item '%s'",
+                          line, buf);
+               return 1;
        }
+
+       return 0;
 }
 
 
@@ -3012,7 +3295,6 @@ static void hostapd_set_security_params(struct hostapd_bss_config *bss)
 struct hostapd_config * hostapd_config_read(const char *fname)
 {
        struct hostapd_config *conf;
-       struct hostapd_bss_config *bss;
        FILE *f;
        char buf[512], *pos;
        int line = 0;
@@ -3041,9 +3323,11 @@ struct hostapd_config * hostapd_config_read(const char *fname)
                return NULL;
        }
 
-       bss = conf->last_bss = conf->bss;
+       conf->last_bss = conf->bss[0];
 
        while (fgets(buf, sizeof(buf), f)) {
+               struct hostapd_bss_config *bss;
+
                bss = conf->last_bss;
                line++;
 
@@ -3075,9 +3359,9 @@ struct hostapd_config * hostapd_config_read(const char *fname)
        fclose(f);
 
        for (i = 0; i < conf->num_bss; i++)
-               hostapd_set_security_params(&conf->bss[i]);
+               hostapd_set_security_params(conf->bss[i], 1);
 
-       if (hostapd_config_check(conf))
+       if (hostapd_config_check(conf, 1))
                errors++;
 
 #ifndef WPA_IGNORE_CONFIG_ERRORS
@@ -3107,9 +3391,9 @@ int hostapd_set_iface(struct hostapd_config *conf,
        }
 
        for (i = 0; i < conf->num_bss; i++)
-               hostapd_set_security_params(&conf->bss[i]);
+               hostapd_set_security_params(conf->bss[i], 0);
 
-       if (hostapd_config_check(conf)) {
+       if (hostapd_config_check(conf, 0)) {
                wpa_printf(MSG_ERROR, "Configuration check failed");
                return -1;
        }
index 021cbe5..86f1aa6 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / UNIX domain socket -based control interface
- * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 
 #ifndef CONFIG_NATIVE_WINDOWS
 
+#ifdef CONFIG_TESTING_OPTIONS
+#include <net/ethernet.h>
+#include <netinet/ip.h>
+#endif /* CONFIG_TESTING_OPTIONS */
+
 #include <sys/un.h>
 #include <sys/stat.h>
 #include <stddef.h>
 #include "utils/eloop.h"
 #include "common/version.h"
 #include "common/ieee802_11_defs.h"
+#include "crypto/tls.h"
 #include "drivers/driver.h"
 #include "radius/radius_client.h"
+#include "radius/radius_server.h"
+#include "l2_packet/l2_packet.h"
 #include "ap/hostapd.h"
 #include "ap/ap_config.h"
 #include "ap/ieee802_1x.h"
 #include "ap/wps_hostapd.h"
 #include "ap/ctrl_iface_ap.h"
 #include "ap/ap_drv_ops.h"
+#include "ap/hs20.h"
+#include "ap/wnm_ap.h"
 #include "ap/wpa_auth.h"
+#include "ap/beacon.h"
 #include "wps/wps_defs.h"
 #include "wps/wps.h"
 #include "config_file.h"
@@ -82,15 +93,15 @@ static int hostapd_ctrl_iface_detach(struct hostapd_data *hapd,
                    os_memcmp(from->sun_path, dst->addr.sun_path,
                              fromlen - offsetof(struct sockaddr_un, sun_path))
                    == 0) {
+                       wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached",
+                                   (u8 *) from->sun_path,
+                                   fromlen -
+                                   offsetof(struct sockaddr_un, sun_path));
                        if (prev == NULL)
                                hapd->ctrl_dst = dst->next;
                        else
                                prev->next = dst->next;
                        os_free(dst);
-                       wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached",
-                                   (u8 *) from->sun_path,
-                                   fromlen -
-                                   offsetof(struct sockaddr_un, sun_path));
                        return 0;
                }
                prev = dst;
@@ -237,14 +248,14 @@ static int hostapd_ctrl_iface_wps_check_pin(
                if (!wps_pin_valid(pin_val)) {
                        wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit");
                        ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n");
-                       if (ret < 0 || (size_t) ret >= buflen)
+                       if (os_snprintf_error(buflen, ret))
                                return -1;
                        return ret;
                }
        }
 
        ret = os_snprintf(buf, buflen, "%s", pin);
-       if (ret < 0 || (size_t) ret >= buflen)
+       if (os_snprintf_error(buflen, ret))
                return -1;
 
        return ret;
@@ -397,13 +408,70 @@ static int hostapd_ctrl_iface_nfc_get_handover_sel(struct hostapd_data *hapd,
 static int hostapd_ctrl_iface_nfc_report_handover(struct hostapd_data *hapd,
                                                  char *cmd)
 {
-       /*
-        * Since NFC connection handover provided full WPS Credential, there is
-        * no need for additional operations within hostapd. Just report this in
-        * debug log.
-        */
-       wpa_printf(MSG_DEBUG, "NFC: Connection handover reported: %s", cmd);
-       return 0;
+       size_t len;
+       struct wpabuf *req, *sel;
+       int ret;
+       char *pos, *role, *type, *pos2;
+
+       role = cmd;
+       pos = os_strchr(role, ' ');
+       if (pos == NULL)
+               return -1;
+       *pos++ = '\0';
+
+       type = pos;
+       pos = os_strchr(type, ' ');
+       if (pos == NULL)
+               return -1;
+       *pos++ = '\0';
+
+       pos2 = os_strchr(pos, ' ');
+       if (pos2 == NULL)
+               return -1;
+       *pos2++ = '\0';
+
+       len = os_strlen(pos);
+       if (len & 0x01)
+               return -1;
+       len /= 2;
+
+       req = wpabuf_alloc(len);
+       if (req == NULL)
+               return -1;
+       if (hexstr2bin(pos, wpabuf_put(req, len), len) < 0) {
+               wpabuf_free(req);
+               return -1;
+       }
+
+       len = os_strlen(pos2);
+       if (len & 0x01) {
+               wpabuf_free(req);
+               return -1;
+       }
+       len /= 2;
+
+       sel = wpabuf_alloc(len);
+       if (sel == NULL) {
+               wpabuf_free(req);
+               return -1;
+       }
+       if (hexstr2bin(pos2, wpabuf_put(sel, len), len) < 0) {
+               wpabuf_free(req);
+               wpabuf_free(sel);
+               return -1;
+       }
+
+       if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "WPS") == 0) {
+               ret = hostapd_wps_nfc_report_handover(hapd, req, sel);
+       } else {
+               wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover "
+                          "reported: role=%s type=%s", role, type);
+               ret = -1;
+       }
+       wpabuf_free(req);
+       wpabuf_free(sel);
+
+       return ret;
 }
 
 #endif /* CONFIG_WPS_NFC */
@@ -487,8 +555,253 @@ static int hostapd_ctrl_iface_wps_config(struct hostapd_data *hapd, char *txt)
 
        return hostapd_wps_config_ap(hapd, ssid, auth, encr, key);
 }
+
+
+static const char * pbc_status_str(enum pbc_status status)
+{
+       switch (status) {
+       case WPS_PBC_STATUS_DISABLE:
+               return "Disabled";
+       case WPS_PBC_STATUS_ACTIVE:
+               return "Active";
+       case WPS_PBC_STATUS_TIMEOUT:
+               return "Timed-out";
+       case WPS_PBC_STATUS_OVERLAP:
+               return "Overlap";
+       default:
+               return "Unknown";
+       }
+}
+
+
+static int hostapd_ctrl_iface_wps_get_status(struct hostapd_data *hapd,
+                                            char *buf, size_t buflen)
+{
+       int ret;
+       char *pos, *end;
+
+       pos = buf;
+       end = buf + buflen;
+
+       ret = os_snprintf(pos, end - pos, "PBC Status: %s\n",
+                         pbc_status_str(hapd->wps_stats.pbc_status));
+
+       if (os_snprintf_error(end - pos, ret))
+               return pos - buf;
+       pos += ret;
+
+       ret = os_snprintf(pos, end - pos, "Last WPS result: %s\n",
+                         (hapd->wps_stats.status == WPS_STATUS_SUCCESS ?
+                          "Success":
+                          (hapd->wps_stats.status == WPS_STATUS_FAILURE ?
+                           "Failed" : "None")));
+
+       if (os_snprintf_error(end - pos, ret))
+               return pos - buf;
+       pos += ret;
+
+       /* If status == Failure - Add possible Reasons */
+       if(hapd->wps_stats.status == WPS_STATUS_FAILURE &&
+          hapd->wps_stats.failure_reason > 0) {
+               ret = os_snprintf(pos, end - pos,
+                                 "Failure Reason: %s\n",
+                                 wps_ei_str(hapd->wps_stats.failure_reason));
+
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+
+       if (hapd->wps_stats.status) {
+               ret = os_snprintf(pos, end - pos, "Peer Address: " MACSTR "\n",
+                                 MAC2STR(hapd->wps_stats.peer_addr));
+
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+
+       return pos - buf;
+}
+
 #endif /* CONFIG_WPS */
 
+#ifdef CONFIG_HS20
+
+static int hostapd_ctrl_iface_hs20_wnm_notif(struct hostapd_data *hapd,
+                                            const char *cmd)
+{
+       u8 addr[ETH_ALEN];
+       const char *url;
+
+       if (hwaddr_aton(cmd, addr))
+               return -1;
+       url = cmd + 17;
+       if (*url == '\0') {
+               url = NULL;
+       } else {
+               if (*url != ' ')
+                       return -1;
+               url++;
+               if (*url == '\0')
+                       url = NULL;
+       }
+
+       return hs20_send_wnm_notification(hapd, addr, 1, url);
+}
+
+
+static int hostapd_ctrl_iface_hs20_deauth_req(struct hostapd_data *hapd,
+                                             const char *cmd)
+{
+       u8 addr[ETH_ALEN];
+       int code, reauth_delay, ret;
+       const char *pos;
+       size_t url_len;
+       struct wpabuf *req;
+
+       /* <STA MAC Addr> <Code(0/1)> <Re-auth-Delay(sec)> [URL] */
+       if (hwaddr_aton(cmd, addr))
+               return -1;
+
+       pos = os_strchr(cmd, ' ');
+       if (pos == NULL)
+               return -1;
+       pos++;
+       code = atoi(pos);
+
+       pos = os_strchr(pos, ' ');
+       if (pos == NULL)
+               return -1;
+       pos++;
+       reauth_delay = atoi(pos);
+
+       url_len = 0;
+       pos = os_strchr(pos, ' ');
+       if (pos) {
+               pos++;
+               url_len = os_strlen(pos);
+       }
+
+       req = wpabuf_alloc(4 + url_len);
+       if (req == NULL)
+               return -1;
+       wpabuf_put_u8(req, code);
+       wpabuf_put_le16(req, reauth_delay);
+       wpabuf_put_u8(req, url_len);
+       if (pos)
+               wpabuf_put_data(req, pos, url_len);
+
+       wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification to " MACSTR
+                  " to indicate imminent deauthentication (code=%d "
+                  "reauth_delay=%d)", MAC2STR(addr), code, reauth_delay);
+       ret = hs20_send_wnm_notification_deauth_req(hapd, addr, req);
+       wpabuf_free(req);
+       return ret;
+}
+
+#endif /* CONFIG_HS20 */
+
+
+#ifdef CONFIG_INTERWORKING
+
+static int hostapd_ctrl_iface_set_qos_map_set(struct hostapd_data *hapd,
+                                             const char *cmd)
+{
+       u8 qos_map_set[16 + 2 * 21], count = 0;
+       const char *pos = cmd;
+       int val, ret;
+
+       for (;;) {
+               if (count == sizeof(qos_map_set)) {
+                       wpa_printf(MSG_ERROR, "Too many qos_map_set parameters");
+                       return -1;
+               }
+
+               val = atoi(pos);
+               if (val < 0 || val > 255) {
+                       wpa_printf(MSG_INFO, "Invalid QoS Map Set");
+                       return -1;
+               }
+
+               qos_map_set[count++] = val;
+               pos = os_strchr(pos, ',');
+               if (!pos)
+                       break;
+               pos++;
+       }
+
+       if (count < 16 || count & 1) {
+               wpa_printf(MSG_INFO, "Invalid QoS Map Set");
+               return -1;
+       }
+
+       ret = hostapd_drv_set_qos_map(hapd, qos_map_set, count);
+       if (ret) {
+               wpa_printf(MSG_INFO, "Failed to set QoS Map Set");
+               return -1;
+       }
+
+       os_memcpy(hapd->conf->qos_map_set, qos_map_set, count);
+       hapd->conf->qos_map_set_len = count;
+
+       return 0;
+}
+
+
+static int hostapd_ctrl_iface_send_qos_map_conf(struct hostapd_data *hapd,
+                                               const char *cmd)
+{
+       u8 addr[ETH_ALEN];
+       struct sta_info *sta;
+       struct wpabuf *buf;
+       u8 *qos_map_set = hapd->conf->qos_map_set;
+       u8 qos_map_set_len = hapd->conf->qos_map_set_len;
+       int ret;
+
+       if (!qos_map_set_len) {
+               wpa_printf(MSG_INFO, "QoS Map Set is not set");
+               return -1;
+       }
+
+       if (hwaddr_aton(cmd, addr))
+               return -1;
+
+       sta = ap_get_sta(hapd, addr);
+       if (sta == NULL) {
+               wpa_printf(MSG_DEBUG, "Station " MACSTR " not found "
+                          "for QoS Map Configuration message",
+                          MAC2STR(addr));
+               return -1;
+       }
+
+       if (!sta->qos_map_enabled) {
+               wpa_printf(MSG_DEBUG, "Station " MACSTR " did not indicate "
+                          "support for QoS Map", MAC2STR(addr));
+               return -1;
+       }
+
+       buf = wpabuf_alloc(2 + 2 + qos_map_set_len);
+       if (buf == NULL)
+               return -1;
+
+       wpabuf_put_u8(buf, WLAN_ACTION_QOS);
+       wpabuf_put_u8(buf, QOS_QOS_MAP_CONFIG);
+
+       /* QoS Map Set Element */
+       wpabuf_put_u8(buf, WLAN_EID_QOS_MAP_SET);
+       wpabuf_put_u8(buf, qos_map_set_len);
+       wpabuf_put_data(buf, qos_map_set, qos_map_set_len);
+
+       ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+                                     wpabuf_head(buf), wpabuf_len(buf));
+       wpabuf_free(buf);
+
+       return ret;
+}
+
+#endif /* CONFIG_INTERWORKING */
+
 
 #ifdef CONFIG_WNM
 
@@ -496,9 +809,8 @@ static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
                                                const char *cmd)
 {
        u8 addr[ETH_ALEN];
-       u8 buf[1000], *pos;
-       struct ieee80211_mgmt *mgmt;
        int disassoc_timer;
+       struct sta_info *sta;
 
        if (hwaddr_aton(cmd, addr))
                return -1;
@@ -506,31 +818,15 @@ static int hostapd_ctrl_iface_disassoc_imminent(struct hostapd_data *hapd,
                return -1;
        disassoc_timer = atoi(cmd + 17);
 
-       os_memset(buf, 0, sizeof(buf));
-       mgmt = (struct ieee80211_mgmt *) buf;
-       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-                                          WLAN_FC_STYPE_ACTION);
-       os_memcpy(mgmt->da, addr, ETH_ALEN);
-       os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
-       os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
-       mgmt->u.action.category = WLAN_ACTION_WNM;
-       mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
-       mgmt->u.action.u.bss_tm_req.dialog_token = 1;
-       mgmt->u.action.u.bss_tm_req.req_mode =
-               WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
-       mgmt->u.action.u.bss_tm_req.disassoc_timer =
-               host_to_le16(disassoc_timer);
-       mgmt->u.action.u.bss_tm_req.validity_interval = 0;
-
-       pos = mgmt->u.action.u.bss_tm_req.variable;
-
-       if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
-               wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
-                          "Management Request frame");
+       sta = ap_get_sta(hapd, addr);
+       if (sta == NULL) {
+               wpa_printf(MSG_DEBUG, "Station " MACSTR
+                          " not found for disassociation imminent message",
+                          MAC2STR(addr));
                return -1;
        }
 
-       return 0;
+       return wnm_send_disassoc_imminent(hapd, sta, disassoc_timer);
 }
 
 
@@ -539,14 +835,20 @@ static int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd,
 {
        u8 addr[ETH_ALEN];
        const char *url, *timerstr;
-       u8 buf[1000], *pos;
-       struct ieee80211_mgmt *mgmt;
-       size_t url_len;
        int disassoc_timer;
+       struct sta_info *sta;
 
        if (hwaddr_aton(cmd, addr))
                return -1;
 
+       sta = ap_get_sta(hapd, addr);
+       if (sta == NULL) {
+               wpa_printf(MSG_DEBUG, "Station " MACSTR
+                          " not found for ESS disassociation imminent message",
+                          MAC2STR(addr));
+               return -1;
+       }
+
        timerstr = cmd + 17;
        if (*timerstr != ' ')
                return -1;
@@ -559,75 +861,195 @@ static int hostapd_ctrl_iface_ess_disassoc(struct hostapd_data *hapd,
        if (url == NULL)
                return -1;
        url++;
-       url_len = os_strlen(url);
-       if (url_len > 255)
-               return -1;
 
-       os_memset(buf, 0, sizeof(buf));
-       mgmt = (struct ieee80211_mgmt *) buf;
-       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-                                          WLAN_FC_STYPE_ACTION);
-       os_memcpy(mgmt->da, addr, ETH_ALEN);
-       os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
-       os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
-       mgmt->u.action.category = WLAN_ACTION_WNM;
-       mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
-       mgmt->u.action.u.bss_tm_req.dialog_token = 1;
-       mgmt->u.action.u.bss_tm_req.req_mode =
-               WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
-       mgmt->u.action.u.bss_tm_req.disassoc_timer =
-               host_to_le16(disassoc_timer);
-       mgmt->u.action.u.bss_tm_req.validity_interval = 0x01;
+       return wnm_send_ess_disassoc_imminent(hapd, sta, url, disassoc_timer);
+}
+
 
-       pos = mgmt->u.action.u.bss_tm_req.variable;
+static int hostapd_ctrl_iface_bss_tm_req(struct hostapd_data *hapd,
+                                        const char *cmd)
+{
+       u8 addr[ETH_ALEN];
+       const char *pos, *end;
+       int disassoc_timer = 0;
+       struct sta_info *sta;
+       u8 req_mode = 0, valid_int = 0x01;
+       u8 bss_term_dur[12];
+       char *url = NULL;
+       int ret;
+       u8 nei_rep[1000];
+       u8 *nei_pos = nei_rep;
 
-       /* Session Information URL */
-       *pos++ = url_len;
-       os_memcpy(pos, url, url_len);
-       pos += url_len;
+       if (hwaddr_aton(cmd, addr)) {
+               wpa_printf(MSG_DEBUG, "Invalid STA MAC address");
+               return -1;
+       }
 
-       if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
-               wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
-                          "Management Request frame");
+       sta = ap_get_sta(hapd, addr);
+       if (sta == NULL) {
+               wpa_printf(MSG_DEBUG, "Station " MACSTR
+                          " not found for BSS TM Request message",
+                          MAC2STR(addr));
                return -1;
        }
 
-       /* send disassociation frame after time-out */
-       if (disassoc_timer) {
-               struct sta_info *sta;
-               int timeout, beacon_int;
-
-               /*
-                * Prevent STA from reconnecting using cached PMKSA to force
-                * full authentication with the authentication server (which may
-                * decide to reject the connection),
-                */
-               wpa_auth_pmksa_remove(hapd->wpa_auth, addr);
-
-               sta = ap_get_sta(hapd, addr);
-               if (sta == NULL) {
-                       wpa_printf(MSG_DEBUG, "Station " MACSTR " not found "
-                                  "for ESS disassociation imminent message",
-                                  MAC2STR(addr));
+       pos = os_strstr(cmd, " disassoc_timer=");
+       if (pos) {
+               pos += 16;
+               disassoc_timer = atoi(pos);
+               if (disassoc_timer < 0 || disassoc_timer > 65535) {
+                       wpa_printf(MSG_DEBUG, "Invalid disassoc_timer");
                        return -1;
                }
+       }
 
-               beacon_int = hapd->iconf->beacon_int;
-               if (beacon_int < 1)
-                       beacon_int = 100; /* best guess */
-               /* Calculate timeout in ms based on beacon_int in TU */
-               timeout = disassoc_timer * beacon_int * 128 / 125;
-               wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR
-                          " set to %d ms", MAC2STR(addr), timeout);
+       pos = os_strstr(cmd, " valid_int=");
+       if (pos) {
+               pos += 11;
+               valid_int = atoi(pos);
+       }
 
-               sta->timeout_next = STA_DISASSOC_FROM_CLI;
-               eloop_cancel_timeout(ap_handle_timer, hapd, sta);
-               eloop_register_timeout(timeout / 1000,
-                                      timeout % 1000 * 1000,
-                                      ap_handle_timer, hapd, sta);
+       pos = os_strstr(cmd, " bss_term=");
+       if (pos) {
+               pos += 10;
+               req_mode |= WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED;
+               /* TODO: TSF configurable/learnable */
+               bss_term_dur[0] = 4; /* Subelement ID */
+               bss_term_dur[1] = 10; /* Length */
+               os_memset(bss_term_dur, 2, 8);
+               end = os_strchr(pos, ',');
+               if (end == NULL) {
+                       wpa_printf(MSG_DEBUG, "Invalid bss_term data");
+                       return -1;
+               }
+               end++;
+               WPA_PUT_LE16(&bss_term_dur[10], atoi(end));
        }
 
-       return 0;
+
+       /*
+        * BSS Transition Candidate List Entries - Neighbor Report elements
+        * neighbor=<BSSID>,<BSSID Information>,<Operating Class>,
+        * <Channel Number>,<PHY Type>[,<hexdump of Optional Subelements>]
+        */
+       pos = cmd;
+       while (pos) {
+               u8 *nei_start;
+               long int val;
+               char *endptr, *tmp;
+
+               pos = os_strstr(pos, " neighbor=");
+               if (!pos)
+                       break;
+               if (nei_pos + 15 > nei_rep + sizeof(nei_rep)) {
+                       wpa_printf(MSG_DEBUG,
+                                  "Not enough room for additional neighbor");
+                       return -1;
+               }
+               pos += 10;
+
+               nei_start = nei_pos;
+               *nei_pos++ = WLAN_EID_NEIGHBOR_REPORT;
+               nei_pos++; /* length to be filled in */
+
+               if (hwaddr_aton(pos, nei_pos)) {
+                       wpa_printf(MSG_DEBUG, "Invalid BSSID");
+                       return -1;
+               }
+               nei_pos += ETH_ALEN;
+               pos += 17;
+               if (*pos != ',') {
+                       wpa_printf(MSG_DEBUG, "Missing BSSID Information");
+                       return -1;
+               }
+               pos++;
+
+               val = strtol(pos, &endptr, 0);
+               WPA_PUT_LE32(nei_pos, val);
+               nei_pos += 4;
+               if (*endptr != ',') {
+                       wpa_printf(MSG_DEBUG, "Missing Operating Class");
+                       return -1;
+               }
+               pos = endptr + 1;
+
+               *nei_pos++ = atoi(pos); /* Operating Class */
+               pos = os_strchr(pos, ',');
+               if (pos == NULL) {
+                       wpa_printf(MSG_DEBUG, "Missing Channel Number");
+                       return -1;
+               }
+               pos++;
+
+               *nei_pos++ = atoi(pos); /* Channel Number */
+               pos = os_strchr(pos, ',');
+               if (pos == NULL) {
+                       wpa_printf(MSG_DEBUG, "Missing PHY Type");
+                       return -1;
+               }
+               pos++;
+
+               *nei_pos++ = atoi(pos); /* PHY Type */
+               end = os_strchr(pos, ' ');
+               tmp = os_strchr(pos, ',');
+               if (tmp && (!end || tmp < end)) {
+                       /* Optional Subelements (hexdump) */
+                       size_t len;
+
+                       pos = tmp + 1;
+                       end = os_strchr(pos, ' ');
+                       if (end)
+                               len = end - pos;
+                       else
+                               len = os_strlen(pos);
+                       if (nei_pos + len / 2 > nei_rep + sizeof(nei_rep)) {
+                               wpa_printf(MSG_DEBUG,
+                                          "Not enough room for neighbor subelements");
+                               return -1;
+                       }
+                       if (len & 0x01 ||
+                           hexstr2bin(pos, nei_pos, len / 2) < 0) {
+                               wpa_printf(MSG_DEBUG,
+                                          "Invalid neighbor subelement info");
+                               return -1;
+                       }
+                       nei_pos += len / 2;
+                       pos = end;
+               }
+
+               nei_start[1] = nei_pos - nei_start - 2;
+       }
+
+       pos = os_strstr(cmd, " url=");
+       if (pos) {
+               size_t len;
+               pos += 5;
+               end = os_strchr(pos, ' ');
+               if (end)
+                       len = end - pos;
+               else
+                       len = os_strlen(pos);
+               url = os_malloc(len + 1);
+               if (url == NULL)
+                       return -1;
+               os_memcpy(url, pos, len);
+               url[len] = '\0';
+               req_mode |= WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
+       }
+
+       if (os_strstr(cmd, " pref=1"))
+               req_mode |= WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED;
+       if (os_strstr(cmd, " abridged=1"))
+               req_mode |= WNM_BSS_TM_REQ_ABRIDGED;
+       if (os_strstr(cmd, " disassoc_imminent=1"))
+               req_mode |= WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+
+       ret = wnm_send_bss_tm_req(hapd, sta, req_mode, disassoc_timer,
+                                 valid_int, bss_term_dur, url,
+                                 nei_pos > nei_rep ? nei_rep : NULL,
+                                 nei_pos - nei_rep);
+       os_free(url);
+       return ret;
 }
 
 #endif /* CONFIG_WNM */
@@ -647,7 +1069,7 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
                          MAC2STR(hapd->own_addr),
                          wpa_ssid_txt(hapd->conf->ssid.ssid,
                                       hapd->conf->ssid.ssid_len));
-       if (ret < 0 || ret >= end - pos)
+       if (os_snprintf_error(end - pos, ret))
                return pos - buf;
        pos += ret;
 
@@ -656,7 +1078,7 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
                          hapd->conf->wps_state == 0 ? "disabled" :
                          (hapd->conf->wps_state == 1 ? "not configured" :
                           "configured"));
-       if (ret < 0 || ret >= end - pos)
+       if (os_snprintf_error(end - pos, ret))
                return pos - buf;
        pos += ret;
 
@@ -664,7 +1086,7 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
            hapd->conf->ssid.wpa_passphrase) {
                ret = os_snprintf(pos, end - pos, "passphrase=%s\n",
                                  hapd->conf->ssid.wpa_passphrase);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
@@ -676,7 +1098,7 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
                wpa_snprintf_hex(hex, sizeof(hex),
                                 hapd->conf->ssid.wpa_psk->psk, PMK_LEN);
                ret = os_snprintf(pos, end - pos, "psk=%s\n", hex);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
@@ -684,53 +1106,83 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
 
        if (hapd->conf->wpa && hapd->conf->wpa_key_mgmt) {
                ret = os_snprintf(pos, end - pos, "key_mgmt=");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
 
                if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) {
                        ret = os_snprintf(pos, end - pos, "WPA-PSK ");
-                       if (ret < 0 || ret >= end - pos)
+                       if (os_snprintf_error(end - pos, ret))
                                return pos - buf;
                        pos += ret;
                }
                if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
                        ret = os_snprintf(pos, end - pos, "WPA-EAP ");
-                       if (ret < 0 || ret >= end - pos)
+                       if (os_snprintf_error(end - pos, ret))
                                return pos - buf;
                        pos += ret;
                }
 #ifdef CONFIG_IEEE80211R
                if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) {
                        ret = os_snprintf(pos, end - pos, "FT-PSK ");
-                       if (ret < 0 || ret >= end - pos)
+                       if (os_snprintf_error(end - pos, ret))
                                return pos - buf;
                        pos += ret;
                }
                if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
                        ret = os_snprintf(pos, end - pos, "FT-EAP ");
-                       if (ret < 0 || ret >= end - pos)
+                       if (os_snprintf_error(end - pos, ret))
+                               return pos - buf;
+                       pos += ret;
+               }
+#ifdef CONFIG_SAE
+               if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_SAE) {
+                       ret = os_snprintf(pos, end - pos, "FT-SAE ");
+                       if (os_snprintf_error(end - pos, ret))
                                return pos - buf;
                        pos += ret;
                }
+#endif /* CONFIG_SAE */
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_IEEE80211W
                if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
                        ret = os_snprintf(pos, end - pos, "WPA-PSK-SHA256 ");
-                       if (ret < 0 || ret >= end - pos)
+                       if (os_snprintf_error(end - pos, ret))
                                return pos - buf;
                        pos += ret;
                }
                if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
                        ret = os_snprintf(pos, end - pos, "WPA-EAP-SHA256 ");
-                       if (ret < 0 || ret >= end - pos)
+                       if (os_snprintf_error(end - pos, ret))
                                return pos - buf;
                        pos += ret;
                }
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_SAE
+               if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_SAE) {
+                       ret = os_snprintf(pos, end - pos, "SAE ");
+                       if (os_snprintf_error(end - pos, ret))
+                               return pos - buf;
+                       pos += ret;
+               }
+#endif /* CONFIG_SAE */
+               if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+                       ret = os_snprintf(pos, end - pos, "WPA-EAP-SUITE-B ");
+                       if (os_snprintf_error(end - pos, ret))
+                               return pos - buf;
+                       pos += ret;
+               }
+               if (hapd->conf->wpa_key_mgmt &
+                   WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+                       ret = os_snprintf(pos, end - pos,
+                                         "WPA-EAP-SUITE-B-192 ");
+                       if (os_snprintf_error(end - pos, ret))
+                               return pos - buf;
+                       pos += ret;
+               }
 
                ret = os_snprintf(pos, end - pos, "\n");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
@@ -738,14 +1190,14 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
        if (hapd->conf->wpa) {
                ret = os_snprintf(pos, end - pos, "group_cipher=%s\n",
                                  wpa_cipher_txt(hapd->conf->wpa_group));
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
 
        if ((hapd->conf->wpa & WPA_PROTO_RSN) && hapd->conf->rsn_pairwise) {
                ret = os_snprintf(pos, end - pos, "rsn_pairwise_cipher=");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
 
@@ -756,25 +1208,25 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
                pos += ret;
 
                ret = os_snprintf(pos, end - pos, "\n");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
 
        if ((hapd->conf->wpa & WPA_PROTO_WPA) && hapd->conf->wpa_pairwise) {
                ret = os_snprintf(pos, end - pos, "wpa_pairwise_cipher=");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
 
-               ret = wpa_write_ciphers(pos, end, hapd->conf->rsn_pairwise,
+               ret = wpa_write_ciphers(pos, end, hapd->conf->wpa_pairwise,
                                        " ");
                if (ret < 0)
                        return pos - buf;
                pos += ret;
 
                ret = os_snprintf(pos, end - pos, "\n");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
@@ -815,6 +1267,10 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
                wps_testing_dummy_cred = atoi(value);
                wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d",
                           wps_testing_dummy_cred);
+       } else if (os_strcasecmp(cmd, "wps_corrupt_pkhash") == 0) {
+               wps_corrupt_pkhash = atoi(value);
+               wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d",
+                          wps_corrupt_pkhash);
 #endif /* CONFIG_WPS_TESTING */
 #ifdef CONFIG_INTERWORKING
        } else if (os_strcasecmp(cmd, "gas_frag_limit") == 0) {
@@ -824,8 +1280,44 @@ static int hostapd_ctrl_iface_set(struct hostapd_data *hapd, char *cmd)
                else
                        hapd->gas_frag_limit = val;
 #endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_TESTING_OPTIONS
+       } else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) {
+               hapd->ext_mgmt_frame_handling = atoi(value);
+       } else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) {
+               hapd->ext_eapol_frame_io = atoi(value);
+#endif /* CONFIG_TESTING_OPTIONS */
        } else {
+               struct sta_info *sta;
+               int vlan_id;
+
                ret = hostapd_set_iface(hapd->iconf, hapd->conf, cmd, value);
+               if (ret)
+                       return ret;
+
+               if (os_strcasecmp(cmd, "deny_mac_file") == 0) {
+                       for (sta = hapd->sta_list; sta; sta = sta->next) {
+                               if (hostapd_maclist_found(
+                                           hapd->conf->deny_mac,
+                                           hapd->conf->num_deny_mac, sta->addr,
+                                           &vlan_id) &&
+                                   (!vlan_id || vlan_id == sta->vlan_id))
+                                       ap_sta_disconnect(
+                                               hapd, sta, sta->addr,
+                                               WLAN_REASON_UNSPECIFIED);
+                       }
+               } else if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED &&
+                          os_strcasecmp(cmd, "accept_mac_file") == 0) {
+                       for (sta = hapd->sta_list; sta; sta = sta->next) {
+                               if (!hostapd_maclist_found(
+                                           hapd->conf->accept_mac,
+                                           hapd->conf->num_accept_mac,
+                                           sta->addr, &vlan_id) ||
+                                   (vlan_id && vlan_id != sta->vlan_id))
+                                       ap_sta_disconnect(
+                                               hapd, sta, sta->addr,
+                                               WLAN_REASON_UNSPECIFIED);
+                       }
+               }
        }
 
        return ret;
@@ -841,7 +1333,12 @@ static int hostapd_ctrl_iface_get(struct hostapd_data *hapd, char *cmd,
 
        if (os_strcmp(cmd, "version") == 0) {
                res = os_snprintf(buf, buflen, "%s", VERSION_STR);
-               if (res < 0 || (unsigned int) res >= buflen)
+               if (os_snprintf_error(buflen, res))
+                       return -1;
+               return res;
+       } else if (os_strcmp(cmd, "tls_library") == 0) {
+               res = tls_get_library_version(buf, buflen);
+               if (os_snprintf_error(buflen, res))
                        return -1;
                return res;
        }
@@ -880,11 +1377,481 @@ static int hostapd_ctrl_iface_disable(struct hostapd_iface *iface)
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
+
+static int hostapd_ctrl_iface_radar(struct hostapd_data *hapd, char *cmd)
+{
+       union wpa_event_data data;
+       char *pos, *param;
+       enum wpa_event_type event;
+
+       wpa_printf(MSG_DEBUG, "RADAR TEST: %s", cmd);
+
+       os_memset(&data, 0, sizeof(data));
+
+       param = os_strchr(cmd, ' ');
+       if (param == NULL)
+               return -1;
+       *param++ = '\0';
+
+       if (os_strcmp(cmd, "DETECTED") == 0)
+               event = EVENT_DFS_RADAR_DETECTED;
+       else if (os_strcmp(cmd, "CAC-FINISHED") == 0)
+               event = EVENT_DFS_CAC_FINISHED;
+       else if (os_strcmp(cmd, "CAC-ABORTED") == 0)
+               event = EVENT_DFS_CAC_ABORTED;
+       else if (os_strcmp(cmd, "NOP-FINISHED") == 0)
+               event = EVENT_DFS_NOP_FINISHED;
+       else {
+               wpa_printf(MSG_DEBUG, "Unsupported RADAR test command: %s",
+                          cmd);
+               return -1;
+       }
+
+       pos = os_strstr(param, "freq=");
+       if (pos)
+               data.dfs_event.freq = atoi(pos + 5);
+
+       pos = os_strstr(param, "ht_enabled=1");
+       if (pos)
+               data.dfs_event.ht_enabled = 1;
+
+       pos = os_strstr(param, "chan_offset=");
+       if (pos)
+               data.dfs_event.chan_offset = atoi(pos + 12);
+
+       pos = os_strstr(param, "chan_width=");
+       if (pos)
+               data.dfs_event.chan_width = atoi(pos + 11);
+
+       pos = os_strstr(param, "cf1=");
+       if (pos)
+               data.dfs_event.cf1 = atoi(pos + 4);
+
+       pos = os_strstr(param, "cf2=");
+       if (pos)
+               data.dfs_event.cf2 = atoi(pos + 4);
+
+       wpa_supplicant_event(hapd, event, &data);
+
+       return 0;
+}
+
+
+static int hostapd_ctrl_iface_mgmt_tx(struct hostapd_data *hapd, char *cmd)
+{
+       size_t len;
+       u8 *buf;
+       int res;
+
+       wpa_printf(MSG_DEBUG, "External MGMT TX: %s", cmd);
+
+       len = os_strlen(cmd);
+       if (len & 1)
+               return -1;
+       len /= 2;
+
+       buf = os_malloc(len);
+       if (buf == NULL)
+               return -1;
+
+       if (hexstr2bin(cmd, buf, len) < 0) {
+               os_free(buf);
+               return -1;
+       }
+
+       res = hostapd_drv_send_mlme(hapd, buf, len, 0);
+       os_free(buf);
+       return res;
+}
+
+
+static int hostapd_ctrl_iface_eapol_rx(struct hostapd_data *hapd, char *cmd)
+{
+       char *pos;
+       u8 src[ETH_ALEN], *buf;
+       int used;
+       size_t len;
+
+       wpa_printf(MSG_DEBUG, "External EAPOL RX: %s", cmd);
+
+       pos = cmd;
+       used = hwaddr_aton2(pos, src);
+       if (used < 0)
+               return -1;
+       pos += used;
+       while (*pos == ' ')
+               pos++;
+
+       len = os_strlen(pos);
+       if (len & 1)
+               return -1;
+       len /= 2;
+
+       buf = os_malloc(len);
+       if (buf == NULL)
+               return -1;
+
+       if (hexstr2bin(pos, buf, len) < 0) {
+               os_free(buf);
+               return -1;
+       }
+
+       ieee802_1x_receive(hapd, src, buf, len);
+       os_free(buf);
+
+       return 0;
+}
+
+
+static u16 ipv4_hdr_checksum(const void *buf, size_t len)
+{
+       size_t i;
+       u32 sum = 0;
+       const u16 *pos = buf;
+
+       for (i = 0; i < len / 2; i++)
+               sum += *pos++;
+
+       while (sum >> 16)
+               sum = (sum & 0xffff) + (sum >> 16);
+
+       return sum ^ 0xffff;
+}
+
+
+#define HWSIM_PACKETLEN 1500
+#define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header))
+
+void hostapd_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf,
+                         size_t len)
+{
+       struct hostapd_data *hapd = ctx;
+       const struct ether_header *eth;
+       const struct iphdr *ip;
+       const u8 *pos;
+       unsigned int i;
+
+       if (len != HWSIM_PACKETLEN)
+               return;
+
+       eth = (const struct ether_header *) buf;
+       ip = (const struct iphdr *) (eth + 1);
+       pos = (const u8 *) (ip + 1);
+
+       if (ip->ihl != 5 || ip->version != 4 ||
+           ntohs(ip->tot_len) != HWSIM_IP_LEN)
+               return;
+
+       for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) {
+               if (*pos != (u8) i)
+                       return;
+               pos++;
+       }
+
+       wpa_msg(hapd->msg_ctx, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR,
+               MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost));
+}
+
+
+static int hostapd_ctrl_iface_data_test_config(struct hostapd_data *hapd,
+                                              char *cmd)
+{
+       int enabled = atoi(cmd);
+       char *pos;
+       const char *ifname;
+
+       if (!enabled) {
+               if (hapd->l2_test) {
+                       l2_packet_deinit(hapd->l2_test);
+                       hapd->l2_test = NULL;
+                       wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+                               "test data: Disabled");
+               }
+               return 0;
+       }
+
+       if (hapd->l2_test)
+               return 0;
+
+       pos = os_strstr(cmd, " ifname=");
+       if (pos)
+               ifname = pos + 8;
+       else
+               ifname = hapd->conf->iface;
+
+       hapd->l2_test = l2_packet_init(ifname, hapd->own_addr,
+                                       ETHERTYPE_IP, hostapd_data_test_rx,
+                                       hapd, 1);
+       if (hapd->l2_test == NULL)
+               return -1;
+
+       wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: Enabled");
+
+       return 0;
+}
+
+
+static int hostapd_ctrl_iface_data_test_tx(struct hostapd_data *hapd, char *cmd)
+{
+       u8 dst[ETH_ALEN], src[ETH_ALEN];
+       char *pos;
+       int used;
+       long int val;
+       u8 tos;
+       u8 buf[HWSIM_PACKETLEN];
+       struct ether_header *eth;
+       struct iphdr *ip;
+       u8 *dpos;
+       unsigned int i;
+
+       if (hapd->l2_test == NULL)
+               return -1;
+
+       /* format: <dst> <src> <tos> */
+
+       pos = cmd;
+       used = hwaddr_aton2(pos, dst);
+       if (used < 0)
+               return -1;
+       pos += used;
+       while (*pos == ' ')
+               pos++;
+       used = hwaddr_aton2(pos, src);
+       if (used < 0)
+               return -1;
+       pos += used;
+
+       val = strtol(pos, NULL, 0);
+       if (val < 0 || val > 0xff)
+               return -1;
+       tos = val;
+
+       eth = (struct ether_header *) buf;
+       os_memcpy(eth->ether_dhost, dst, ETH_ALEN);
+       os_memcpy(eth->ether_shost, src, ETH_ALEN);
+       eth->ether_type = htons(ETHERTYPE_IP);
+       ip = (struct iphdr *) (eth + 1);
+       os_memset(ip, 0, sizeof(*ip));
+       ip->ihl = 5;
+       ip->version = 4;
+       ip->ttl = 64;
+       ip->tos = tos;
+       ip->tot_len = htons(HWSIM_IP_LEN);
+       ip->protocol = 1;
+       ip->saddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 1);
+       ip->daddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 2);
+       ip->check = ipv4_hdr_checksum(ip, sizeof(*ip));
+       dpos = (u8 *) (ip + 1);
+       for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++)
+               *dpos++ = i;
+
+       if (l2_packet_send(hapd->l2_test, dst, ETHERTYPE_IP, buf,
+                          HWSIM_PACKETLEN) < 0)
+               return -1;
+
+       wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: TX dst=" MACSTR
+               " src=" MACSTR " tos=0x%x", MAC2STR(dst), MAC2STR(src), tos);
+
+       return 0;
+}
+
+
+static int hostapd_ctrl_iface_data_test_frame(struct hostapd_data *hapd,
+                                             char *cmd)
+{
+       u8 *buf;
+       struct ether_header *eth;
+       struct l2_packet_data *l2 = NULL;
+       size_t len;
+       u16 ethertype;
+       int res = -1;
+       const char *ifname = hapd->conf->iface;
+
+       if (os_strncmp(cmd, "ifname=", 7) == 0) {
+               cmd += 7;
+               ifname = cmd;
+               cmd = os_strchr(cmd, ' ');
+               if (cmd == NULL)
+                       return -1;
+               *cmd++ = '\0';
+       }
+
+       len = os_strlen(cmd);
+       if (len & 1 || len < ETH_HLEN * 2)
+               return -1;
+       len /= 2;
+
+       buf = os_malloc(len);
+       if (buf == NULL)
+               return -1;
+
+       if (hexstr2bin(cmd, buf, len) < 0)
+               goto done;
+
+       eth = (struct ether_header *) buf;
+       ethertype = ntohs(eth->ether_type);
+
+       l2 = l2_packet_init(ifname, hapd->own_addr, ethertype,
+                           hostapd_data_test_rx, hapd, 1);
+       if (l2 == NULL)
+               goto done;
+
+       res = l2_packet_send(l2, eth->ether_dhost, ethertype, buf, len);
+       wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "test data: TX frame res=%d", res);
+done:
+       if (l2)
+               l2_packet_deinit(l2);
+       os_free(buf);
+
+       return res < 0 ? -1 : 0;
+}
+
+
+static int hostapd_ctrl_test_alloc_fail(struct hostapd_data *hapd, char *cmd)
+{
+#ifdef WPA_TRACE_BFD
+       extern char wpa_trace_fail_func[256];
+       extern unsigned int wpa_trace_fail_after;
+       char *pos;
+
+       wpa_trace_fail_after = atoi(cmd);
+       pos = os_strchr(cmd, ':');
+       if (pos) {
+               pos++;
+               os_strlcpy(wpa_trace_fail_func, pos,
+                          sizeof(wpa_trace_fail_func));
+       } else {
+               wpa_trace_fail_after = 0;
+       }
+
+       return 0;
+#else /* WPA_TRACE_BFD */
+       return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int hostapd_ctrl_get_alloc_fail(struct hostapd_data *hapd,
+                                      char *buf, size_t buflen)
+{
+#ifdef WPA_TRACE_BFD
+       extern char wpa_trace_fail_func[256];
+       extern unsigned int wpa_trace_fail_after;
+
+       return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after,
+                          wpa_trace_fail_func);
+#else /* WPA_TRACE_BFD */
+       return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static int hostapd_ctrl_iface_chan_switch(struct hostapd_iface *iface,
+                                         char *pos)
+{
+#ifdef NEED_AP_MLME
+       struct csa_settings settings;
+       int ret;
+       unsigned int i;
+
+       ret = hostapd_parse_csa_settings(pos, &settings);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < iface->num_bss; i++) {
+               ret = hostapd_switch_channel(iface->bss[i], &settings);
+               if (ret) {
+                       /* FIX: What do we do if CSA fails in the middle of
+                        * submitting multi-BSS CSA requests? */
+                       return ret;
+               }
+       }
+
+       return 0;
+#else /* NEED_AP_MLME */
+       return -1;
+#endif /* NEED_AP_MLME */
+}
+
+
+static int hostapd_ctrl_iface_mib(struct hostapd_data *hapd, char *reply,
+                                 int reply_size, const char *param)
+{
+#ifdef RADIUS_SERVER
+       if (os_strcmp(param, "radius_server") == 0) {
+               return radius_server_get_mib(hapd->radius_srv, reply,
+                                            reply_size);
+       }
+#endif /* RADIUS_SERVER */
+       return -1;
+}
+
+
+static int hostapd_ctrl_iface_vendor(struct hostapd_data *hapd, char *cmd,
+                                    char *buf, size_t buflen)
+{
+       int ret;
+       char *pos;
+       u8 *data = NULL;
+       unsigned int vendor_id, subcmd;
+       struct wpabuf *reply;
+       size_t data_len = 0;
+
+       /* cmd: <vendor id> <subcommand id> [<hex formatted data>] */
+       vendor_id = strtoul(cmd, &pos, 16);
+       if (!isblank(*pos))
+               return -EINVAL;
+
+       subcmd = strtoul(pos, &pos, 10);
+
+       if (*pos != '\0') {
+               if (!isblank(*pos++))
+                       return -EINVAL;
+               data_len = os_strlen(pos);
+       }
+
+       if (data_len) {
+               data_len /= 2;
+               data = os_malloc(data_len);
+               if (!data)
+                       return -ENOBUFS;
+
+               if (hexstr2bin(pos, data, data_len)) {
+                       wpa_printf(MSG_DEBUG,
+                                  "Vendor command: wrong parameter format");
+                       os_free(data);
+                       return -EINVAL;
+               }
+       }
+
+       reply = wpabuf_alloc((buflen - 1) / 2);
+       if (!reply) {
+               os_free(data);
+               return -ENOBUFS;
+       }
+
+       ret = hostapd_drv_vendor_cmd(hapd, vendor_id, subcmd, data, data_len,
+                                    reply);
+
+       if (ret == 0)
+               ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply),
+                                      wpabuf_len(reply));
+
+       wpabuf_free(reply);
+       os_free(data);
+
+       return ret;
+}
+
+
 static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
                                       void *sock_ctx)
 {
        struct hostapd_data *hapd = eloop_ctx;
-       char buf[256];
+       char buf[4096];
        int res;
        struct sockaddr_un from;
        socklen_t fromlen = sizeof(from);
@@ -896,7 +1863,8 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
        res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
                       (struct sockaddr *) &from, &fromlen);
        if (res < 0) {
-               perror("recvfrom(ctrl_iface)");
+               wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+                          strerror(errno));
                return;
        }
        buf[res] = '\0';
@@ -906,8 +1874,11 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
 
        reply = os_malloc(reply_size);
        if (reply == NULL) {
-               sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
-                      fromlen);
+               if (sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
+                          fromlen) < 0) {
+                       wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
+                                  strerror(errno));
+               }
                return;
        }
 
@@ -920,6 +1891,11 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
        } else if (os_strncmp(buf, "RELOG", 5) == 0) {
                if (wpa_debug_reopen_file() < 0)
                        reply_len = -1;
+       } else if (os_strcmp(buf, "STATUS") == 0) {
+               reply_len = hostapd_ctrl_iface_status(hapd, reply,
+                                                     reply_size);
+       } else if (os_strcmp(buf, "STATUS-DRIVER") == 0) {
+               reply_len = hostapd_drv_status(hapd, reply, reply_size);
        } else if (os_strcmp(buf, "MIB") == 0) {
                reply_len = ieee802_11_get_mib(hapd, reply, reply_size);
                if (reply_len >= 0) {
@@ -949,6 +1925,9 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
                                reply_len += res;
                }
 #endif /* CONFIG_NO_RADIUS */
+       } else if (os_strncmp(buf, "MIB ", 4) == 0) {
+               reply_len = hostapd_ctrl_iface_mib(hapd, reply, reply_size,
+                                                  buf + 4);
        } else if (os_strcmp(buf, "STA-FIRST") == 0) {
                reply_len = hostapd_ctrl_iface_sta_first(hapd, reply,
                                                         reply_size);
@@ -977,6 +1956,9 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
        } else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
                if (hostapd_ctrl_iface_disassociate(hapd, buf + 13))
                        reply_len = -1;
+       } else if (os_strcmp(buf, "STOP_AP") == 0) {
+               if (hostapd_ctrl_iface_stop_ap(hapd))
+                       reply_len = -1;
 #ifdef CONFIG_IEEE80211W
 #ifdef NEED_AP_MLME
        } else if (os_strncmp(buf, "SA_QUERY ", 9) == 0) {
@@ -1003,6 +1985,9 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
        } else if (os_strncmp(buf, "WPS_CONFIG ", 11) == 0) {
                if (hostapd_ctrl_iface_wps_config(hapd, buf + 11) < 0)
                        reply_len = -1;
+       } else if (os_strncmp(buf, "WPS_GET_STATUS", 13) == 0) {
+               reply_len = hostapd_ctrl_iface_wps_get_status(hapd, reply,
+                                                             reply_size);
 #ifdef CONFIG_WPS_NFC
        } else if (os_strncmp(buf, "WPS_NFC_TAG_READ ", 17) == 0) {
                if (hostapd_ctrl_iface_wps_nfc_tag_read(hapd, buf + 17))
@@ -1021,6 +2006,22 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
                        reply_len = -1;
 #endif /* CONFIG_WPS_NFC */
 #endif /* CONFIG_WPS */
+#ifdef CONFIG_INTERWORKING
+       } else if (os_strncmp(buf, "SET_QOS_MAP_SET ", 16) == 0) {
+               if (hostapd_ctrl_iface_set_qos_map_set(hapd, buf + 16))
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "SEND_QOS_MAP_CONF ", 18) == 0) {
+               if (hostapd_ctrl_iface_send_qos_map_conf(hapd, buf + 18))
+                       reply_len = -1;
+#endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_HS20
+       } else if (os_strncmp(buf, "HS20_WNM_NOTIF ", 15) == 0) {
+               if (hostapd_ctrl_iface_hs20_wnm_notif(hapd, buf + 15))
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "HS20_DEAUTH_REQ ", 16) == 0) {
+               if (hostapd_ctrl_iface_hs20_deauth_req(hapd, buf + 16))
+                       reply_len = -1;
+#endif /* CONFIG_HS20 */
 #ifdef CONFIG_WNM
        } else if (os_strncmp(buf, "DISASSOC_IMMINENT ", 18) == 0) {
                if (hostapd_ctrl_iface_disassoc_imminent(hapd, buf + 18))
@@ -1028,6 +2029,9 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
        } else if (os_strncmp(buf, "ESS_DISASSOC ", 13) == 0) {
                if (hostapd_ctrl_iface_ess_disassoc(hapd, buf + 13))
                        reply_len = -1;
+       } else if (os_strncmp(buf, "BSS_TM_REQ ", 11) == 0) {
+               if (hostapd_ctrl_iface_bss_tm_req(hapd, buf + 11))
+                       reply_len = -1;
 #endif /* CONFIG_WNM */
        } else if (os_strcmp(buf, "GET_CONFIG") == 0) {
                reply_len = hostapd_ctrl_iface_get_config(hapd, reply,
@@ -1047,6 +2051,46 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
        } else if (os_strncmp(buf, "DISABLE", 7) == 0) {
                if (hostapd_ctrl_iface_disable(hapd->iface))
                        reply_len = -1;
+       } else if (os_strcmp(buf, "UPDATE_BEACON") == 0) {
+               if (ieee802_11_set_beacon(hapd))
+                       reply_len = -1;
+#ifdef CONFIG_TESTING_OPTIONS
+       } else if (os_strncmp(buf, "RADAR ", 6) == 0) {
+               if (hostapd_ctrl_iface_radar(hapd, buf + 6))
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) {
+               if (hostapd_ctrl_iface_mgmt_tx(hapd, buf + 8))
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) {
+               if (hostapd_ctrl_iface_eapol_rx(hapd, buf + 9) < 0)
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "DATA_TEST_CONFIG ", 17) == 0) {
+               if (hostapd_ctrl_iface_data_test_config(hapd, buf + 17) < 0)
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "DATA_TEST_TX ", 13) == 0) {
+               if (hostapd_ctrl_iface_data_test_tx(hapd, buf + 13) < 0)
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "DATA_TEST_FRAME ", 16) == 0) {
+               if (hostapd_ctrl_iface_data_test_frame(hapd, buf + 16) < 0)
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "TEST_ALLOC_FAIL ", 16) == 0) {
+               if (hostapd_ctrl_test_alloc_fail(hapd, buf + 16) < 0)
+                       reply_len = -1;
+       } else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
+               reply_len = hostapd_ctrl_get_alloc_fail(hapd, reply,
+                                                       reply_size);
+#endif /* CONFIG_TESTING_OPTIONS */
+       } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
+               if (hostapd_ctrl_iface_chan_switch(hapd->iface, buf + 12))
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "VENDOR ", 7) == 0) {
+               reply_len = hostapd_ctrl_iface_vendor(hapd, buf + 7, reply,
+                                                     reply_size);
+       } else if (os_strcmp(buf, "ERP_FLUSH") == 0) {
+               ieee802_1x_erp_flush(hapd);
+#ifdef RADIUS_SERVER
+               radius_server_erp_flush(hapd->radius_srv);
+#endif /* RADIUS_SERVER */
        } else {
                os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
                reply_len = 16;
@@ -1056,7 +2100,11 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx,
                os_memcpy(reply, "FAIL\n", 5);
                reply_len = 5;
        }
-       sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen);
+       if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
+                  fromlen) < 0) {
+               wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
+                          strerror(errno));
+       }
        os_free(reply);
 }
 
@@ -1111,7 +2159,8 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
                        wpa_printf(MSG_DEBUG, "Using existing control "
                                   "interface directory.");
                } else {
-                       perror("mkdir[ctrl_interface]");
+                       wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s",
+                                  strerror(errno));
                        goto fail;
                }
        }
@@ -1119,7 +2168,8 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
        if (hapd->conf->ctrl_interface_gid_set &&
            chown(hapd->conf->ctrl_interface, -1,
                  hapd->conf->ctrl_interface_gid) < 0) {
-               perror("chown[ctrl_interface]");
+               wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
+                          strerror(errno));
                return -1;
        }
 
@@ -1127,7 +2177,8 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
            hapd->iface->interfaces->ctrl_iface_group &&
            chown(hapd->conf->ctrl_interface, -1,
                  hapd->iface->interfaces->ctrl_iface_group) < 0) {
-               perror("chown[ctrl_interface]");
+               wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
+                          strerror(errno));
                return -1;
        }
 
@@ -1152,7 +2203,7 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
 
        s = socket(PF_UNIX, SOCK_DGRAM, 0);
        if (s < 0) {
-               perror("socket(PF_UNIX)");
+               wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
                goto fail;
        }
 
@@ -1173,15 +2224,16 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
                                   " allow connections - assuming it was left"
                                   "over from forced program termination");
                        if (unlink(fname) < 0) {
-                               perror("unlink[ctrl_iface]");
-                               wpa_printf(MSG_ERROR, "Could not unlink "
-                                          "existing ctrl_iface socket '%s'",
-                                          fname);
+                               wpa_printf(MSG_ERROR,
+                                          "Could not unlink existing ctrl_iface socket '%s': %s",
+                                          fname, strerror(errno));
                                goto fail;
                        }
                        if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
                            0) {
-                               perror("hostapd-ctrl-iface: bind(PF_UNIX)");
+                               wpa_printf(MSG_ERROR,
+                                          "hostapd-ctrl-iface: bind(PF_UNIX): %s",
+                                          strerror(errno));
                                goto fail;
                        }
                        wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
@@ -1199,26 +2251,32 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd)
 
        if (hapd->conf->ctrl_interface_gid_set &&
            chown(fname, -1, hapd->conf->ctrl_interface_gid) < 0) {
-               perror("chown[ctrl_interface/ifname]");
+               wpa_printf(MSG_ERROR, "chown[ctrl_interface/ifname]: %s",
+                          strerror(errno));
                goto fail;
        }
 
        if (!hapd->conf->ctrl_interface_gid_set &&
            hapd->iface->interfaces->ctrl_iface_group &&
            chown(fname, -1, hapd->iface->interfaces->ctrl_iface_group) < 0) {
-               perror("chown[ctrl_interface/ifname]");
+               wpa_printf(MSG_ERROR, "chown[ctrl_interface/ifname]: %s",
+                          strerror(errno));
                goto fail;
        }
 
        if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
-               perror("chmod[ctrl_interface/ifname]");
+               wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s",
+                          strerror(errno));
                goto fail;
        }
        os_free(fname);
 
        hapd->ctrl_sock = s;
-       eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd,
-                                NULL);
+       if (eloop_register_read_sock(s, hostapd_ctrl_iface_receive, hapd,
+                                    NULL) < 0) {
+               hostapd_ctrl_iface_deinit(hapd);
+               return -1;
+       }
        hapd->msg_ctx = hapd;
        wpa_msg_register_cb(hostapd_ctrl_iface_msg_cb);
 
@@ -1256,17 +2314,26 @@ void hostapd_ctrl_iface_deinit(struct hostapd_data *hapd)
                                           "directory not empty - leaving it "
                                           "behind");
                        } else {
-                               perror("rmdir[ctrl_interface]");
+                               wpa_printf(MSG_ERROR,
+                                          "rmdir[ctrl_interface=%s]: %s",
+                                          hapd->conf->ctrl_interface,
+                                          strerror(errno));
                        }
                }
        }
 
        dst = hapd->ctrl_dst;
+       hapd->ctrl_dst = NULL;
        while (dst) {
                prev = dst;
                dst = dst->next;
                os_free(prev);
        }
+
+#ifdef CONFIG_TESTING_OPTIONS
+       l2_packet_deinit(hapd->l2_test);
+       hapd->l2_test = NULL;
+#endif /* CONFIG_TESTING_OPTIONS */
 }
 
 
@@ -1292,6 +2359,16 @@ static int hostapd_ctrl_iface_remove(struct hapd_interfaces *interfaces,
 }
 
 
+static void hostapd_ctrl_iface_flush(struct hapd_interfaces *interfaces)
+{
+#ifdef CONFIG_WPS_TESTING
+       wps_version_number = 0x20;
+       wps_testing_dummy_cred = 0;
+       wps_corrupt_pkhash = 0;
+#endif /* CONFIG_WPS_TESTING */
+}
+
+
 static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
                                              void *sock_ctx)
 {
@@ -1306,10 +2383,12 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
        res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
                       (struct sockaddr *) &from, &fromlen);
        if (res < 0) {
-               perror("recvfrom(ctrl_iface)");
+               wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+                          strerror(errno));
                return;
        }
        buf[res] = '\0';
+       wpa_printf(MSG_DEBUG, "Global ctrl_iface command: %s", buf);
 
        os_memcpy(reply, "OK\n", 3);
        reply_len = 3;
@@ -1317,12 +2396,23 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
        if (os_strcmp(buf, "PING") == 0) {
                os_memcpy(reply, "PONG\n", 5);
                reply_len = 5;
+       } else if (os_strncmp(buf, "RELOG", 5) == 0) {
+               if (wpa_debug_reopen_file() < 0)
+                       reply_len = -1;
+       } else if (os_strcmp(buf, "FLUSH") == 0) {
+               hostapd_ctrl_iface_flush(interfaces);
        } else if (os_strncmp(buf, "ADD ", 4) == 0) {
                if (hostapd_ctrl_iface_add(interfaces, buf + 4) < 0)
                        reply_len = -1;
        } else if (os_strncmp(buf, "REMOVE ", 7) == 0) {
                if (hostapd_ctrl_iface_remove(interfaces, buf + 7) < 0)
                        reply_len = -1;
+#ifdef CONFIG_MODULE_TESTS
+       } else if (os_strcmp(buf, "MODULE_TESTS") == 0) {
+               int hapd_module_tests(void);
+               if (hapd_module_tests() < 0)
+                       reply_len = -1;
+#endif /* CONFIG_MODULE_TESTS */
        } else {
                wpa_printf(MSG_DEBUG, "Unrecognized global ctrl_iface command "
                           "ignored");
@@ -1334,7 +2424,11 @@ static void hostapd_global_ctrl_iface_receive(int sock, void *eloop_ctx,
                reply_len = 5;
        }
 
-       sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from, fromlen);
+       if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
+                  fromlen) < 0) {
+               wpa_printf(MSG_DEBUG, "CTRL: sendto failed: %s",
+                          strerror(errno));
+       }
 }
 
 
@@ -1375,13 +2469,15 @@ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
                        wpa_printf(MSG_DEBUG, "Using existing control "
                                   "interface directory.");
                } else {
-                       perror("mkdir[ctrl_interface]");
+                       wpa_printf(MSG_ERROR, "mkdir[ctrl_interface]: %s",
+                                  strerror(errno));
                        goto fail;
                }
        } else if (interface->ctrl_iface_group &&
                   chown(interface->global_iface_path, -1,
                         interface->ctrl_iface_group) < 0) {
-               perror("chown[ctrl_interface]");
+               wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
+                          strerror(errno));
                goto fail;
        }
 
@@ -1391,7 +2487,7 @@ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
 
        s = socket(PF_UNIX, SOCK_DGRAM, 0);
        if (s < 0) {
-               perror("socket(PF_UNIX)");
+               wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
                goto fail;
        }
 
@@ -1412,15 +2508,15 @@ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
                                   " allow connections - assuming it was left"
                                   "over from forced program termination");
                        if (unlink(fname) < 0) {
-                               perror("unlink[ctrl_iface]");
-                               wpa_printf(MSG_ERROR, "Could not unlink "
-                                          "existing ctrl_iface socket '%s'",
-                                          fname);
+                               wpa_printf(MSG_ERROR,
+                                          "Could not unlink existing ctrl_iface socket '%s': %s",
+                                          fname, strerror(errno));
                                goto fail;
                        }
                        if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) <
                            0) {
-                               perror("bind(PF_UNIX)");
+                               wpa_printf(MSG_ERROR, "bind(PF_UNIX): %s",
+                                          strerror(errno));
                                goto fail;
                        }
                        wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
@@ -1438,12 +2534,14 @@ int hostapd_global_ctrl_iface_init(struct hapd_interfaces *interface)
 
        if (interface->ctrl_iface_group &&
            chown(fname, -1, interface->ctrl_iface_group) < 0) {
-               perror("chown[ctrl_interface]");
+               wpa_printf(MSG_ERROR, "chown[ctrl_interface]: %s",
+                          strerror(errno));
                goto fail;
        }
 
        if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
-               perror("chmod[ctrl_interface/ifname]");
+               wpa_printf(MSG_ERROR, "chmod[ctrl_interface/ifname]: %s",
+                          strerror(errno));
                goto fail;
        }
        os_free(fname);
@@ -1486,7 +2584,10 @@ void hostapd_global_ctrl_iface_deinit(struct hapd_interfaces *interfaces)
                                           "directory not empty - leaving it "
                                           "behind");
                        } else {
-                               perror("rmdir[ctrl_interface]");
+                               wpa_printf(MSG_ERROR,
+                                          "rmdir[ctrl_interface=%s]: %s",
+                                          interfaces->global_iface_path,
+                                          strerror(errno));
                        }
                }
                os_free(interfaces->global_iface_path);
index 317fe74..4cde2b5 100644 (file)
@@ -15,13 +15,22 @@ CONFIG_DRIVER_HOSTAP=y
 # Driver interface for wired authenticator
 #CONFIG_DRIVER_WIRED=y
 
-# Driver interface for madwifi driver
-#CONFIG_DRIVER_MADWIFI=y
-#CFLAGS += -I../../madwifi # change to the madwifi source directory
-
 # Driver interface for drivers using the nl80211 kernel interface
 CONFIG_DRIVER_NL80211=y
 
+# driver_nl80211.c requires libnl. If you are compiling it yourself
+# you may need to point hostapd to your version of libnl.
+#
+#CFLAGS += -I$<path to libnl include files>
+#LIBS += -L$<path to libnl library files>
+
+# Use libnl v2.0 (or 3.0) libraries.
+#CONFIG_LIBNL20=y
+
+# Use libnl 3.2 libraries (if this is selected, CONFIG_LIBNL20 is ignored)
+#CONFIG_LIBNL32=y
+
+
 # Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
 #CONFIG_DRIVER_BSD=y
 #CFLAGS += -I/usr/local/include
@@ -42,14 +51,14 @@ CONFIG_RSN_PREAUTH=y
 CONFIG_PEERKEY=y
 
 # IEEE 802.11w (management frame protection)
-# This version is an experimental implementation based on IEEE 802.11w/D1.0
-# draft and is subject to change since the standard has not yet been finalized.
-# Driver support is also needed for IEEE 802.11w.
-#CONFIG_IEEE80211W=y
+CONFIG_IEEE80211W=y
 
 # Integrated EAP server
 CONFIG_EAP=y
 
+# EAP Re-authentication Protocol (ERP) in integrated EAP server
+CONFIG_ERP=y
+
 # EAP-MD5 for the integrated EAP server
 CONFIG_EAP_MD5=y
 
@@ -96,16 +105,13 @@ CONFIG_EAP_TTLS=y
 #CONFIG_EAP_GPSK_SHA256=y
 
 # EAP-FAST for the integrated EAP server
-# Note: Default OpenSSL package does not include support for all the
-# functionality needed for EAP-FAST. If EAP-FAST is enabled with OpenSSL,
-# the OpenSSL library must be patched (openssl-0.9.9-session-ticket.patch)
-# to add the needed functions.
+# Note: If OpenSSL is used as the TLS library, OpenSSL 1.0 or newer is needed
+# for EAP-FAST support. Older OpenSSL releases would need to be patched, e.g.,
+# with openssl-0.9.8x-tls-extensions.patch, to add the needed functions.
 #CONFIG_EAP_FAST=y
 
 # Wi-Fi Protected Setup (WPS)
 #CONFIG_WPS=y
-# Enable WSC 2.0 support
-#CONFIG_WPS2=y
 # Enable UPnP support for external WPS Registrars
 #CONFIG_WPS_UPNP=y
 # Enable WPS support with NFC config method
@@ -117,6 +123,9 @@ CONFIG_EAP_TTLS=y
 # Trusted Network Connect (EAP-TNC)
 #CONFIG_EAP_TNC=y
 
+# EAP-EKE for the integrated EAP server
+#CONFIG_EAP_EKE=y
+
 # PKCS#12 (PFX) support (used to read private key and certificate file from
 # a file that usually has extension .p12 or .pfx)
 CONFIG_PKCS12=y
@@ -132,7 +141,7 @@ CONFIG_IPV6=y
 #CONFIG_IEEE80211R=y
 
 # Use the hostapd's IEEE 802.11 authentication (ACL), but without
-# the IEEE 802.11 Management capability (e.g., madwifi or FreeBSD/net80211)
+# the IEEE 802.11 Management capability (e.g., FreeBSD/net80211)
 #CONFIG_DRIVER_RADIUS_ACL=y
 
 # IEEE 802.11n (High Throughput) support
@@ -154,6 +163,12 @@ CONFIG_IPV6=y
 # Disabled by default.
 #CONFIG_DEBUG_FILE=y
 
+# Add support for sending all debug messages (regardless of debug verbosity)
+# to the Linux kernel tracing facility. This helps debug the entire stack by
+# making it easy to record everything happening from the driver up into the
+# same file, e.g., using trace-cmd.
+#CONFIG_DEBUG_LINUX_TRACING=y
+
 # Remove support for RADIUS accounting
 #CONFIG_NO_ACCOUNTING=y
 
@@ -171,7 +186,7 @@ CONFIG_IPV6=y
 # Note: This requires libnl 3.1 or newer.
 #CONFIG_VLAN_NETLINK=y
 
-# Remove support for dumping state into a file on SIGUSR1 signal
+# Remove support for dumping internal state through control interface commands
 # This can be used to reduce binary size at the cost of disabling a debugging
 # option.
 #CONFIG_NO_DUMP_STATE=y
@@ -275,3 +290,27 @@ CONFIG_IPV6=y
 # certain percentage of probe requests or auth/(re)assoc frames.
 #
 #CONFIG_TESTING_OPTIONS=y
+
+# Automatic Channel Selection
+# This will allow hostapd to pick the channel automatically when channel is set
+# to "acs_survey" or "0". Eventually, other ACS algorithms can be added in
+# similar way.
+#
+# Automatic selection is currently only done through initialization, later on
+# we hope to do background checks to keep us moving to more ideal channels as
+# time goes by. ACS is currently only supported through the nl80211 driver and
+# your driver must have survey dump capability that is filled by the driver
+# during scanning.
+#
+# You can customize the ACS survey algorithm with the hostapd.conf variable
+# acs_num_scans.
+#
+# Supported ACS drivers:
+# * ath9k
+# * ath5k
+# * ath10k
+#
+# For more details refer to:
+# http://wireless.kernel.org/en/users/Documentation/acs
+#
+#CONFIG_ACS=y
index 0a7ff91..8477c21 100644 (file)
@@ -44,6 +44,13 @@ int eap_server_register_methods(void)
                ret = eap_server_unauth_tls_register();
 #endif /* EAP_SERVER_TLS */
 
+#ifdef EAP_SERVER_TLS
+#ifdef CONFIG_HS20
+       if (ret == 0)
+               ret = eap_server_wfa_unauth_tls_register();
+#endif /* CONFIG_HS20 */
+#endif /* EAP_SERVER_TLS */
+
 #ifdef EAP_SERVER_MSCHAPV2
        if (ret == 0)
                ret = eap_server_mschapv2_register();
@@ -134,5 +141,10 @@ int eap_server_register_methods(void)
                ret = eap_server_pwd_register();
 #endif /* EAP_SERVER_PWD */
 
+#ifdef EAP_SERVER_EKE
+       if (ret == 0)
+               ret = eap_server_eke_register();
+#endif /* EAP_SERVER_EKE */
+
        return ret;
 }
diff --git a/hostapd/hapd_module_tests.c b/hostapd/hapd_module_tests.c
new file mode 100644 (file)
index 0000000..f7887eb
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * hostapd 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.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+
+int hapd_module_tests(void)
+{
+       wpa_printf(MSG_INFO, "hostapd module tests");
+       return 0;
+}
index e04e2e9..42d59db 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator
- * Copyright (c) 2005-2007, 2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2007, 2012-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -18,6 +18,9 @@
  * SIM-REQ-AUTH <IMSI> <max_chal>
  * SIM-RESP-AUTH <IMSI> Kc1:SRES1:RAND1 Kc2:SRES2:RAND2 [Kc3:SRES3:RAND3]
  * SIM-RESP-AUTH <IMSI> FAILURE
+ * GSM-AUTH-REQ <IMSI> RAND1:RAND2[:RAND3]
+ * GSM-AUTH-RESP <IMSI> Kc1:SRES1:Kc2:SRES2[:Kc3:SRES3]
+ * GSM-AUTH-RESP <IMSI> FAILURE
  *
  * EAP-AKA / UMTS query/response:
  * AKA-REQ-AUTH <IMSI>
  * IMSI and max_chal are sent as an ASCII string,
  * Kc/SRES/RAND/AUTN/IK/CK/RES/AUTS as hex strings.
  *
- * The example implementation here reads GSM authentication triplets from a
+ * An example implementation here reads GSM authentication triplets from a
  * text file in IMSI:Kc:SRES:RAND format, IMSI in ASCII, other fields as hex
  * strings. This is used to simulate an HLR/AuC. As such, it is not very useful
  * for real life authentication, but it is useful both as an example
  * implementation and for EAP-SIM/AKA/AKA' testing.
  *
+ * For a stronger example design, Milenage and GSM-Milenage algorithms can be
+ * used to dynamically generate authenticatipn information for EAP-AKA/AKA' and
+ * EAP-SIM, respectively, if Ki is known.
+ *
  * SQN generation follows the not time-based Profile 2 described in
  * 3GPP TS 33.102 Annex C.3.2. The length of IND is 5 bits by default, but this
  * can be changed with a command line options if needed.
@@ -58,6 +65,7 @@ static char *milenage_file = NULL;
 static int update_milenage = 0;
 static int sqn_changes = 0;
 static int ind_len = 5;
+static int stdout_debug = 1;
 
 /* GSM triplets */
 struct gsm_triplet {
@@ -214,6 +222,9 @@ static int db_update_milenage_sqn(struct milenage_parameters *m)
 {
        char cmd[128], val[13], *pos;
 
+       if (sqlite_db == NULL)
+               return 0;
+
        pos = val;
        pos += wpa_snprintf_hex(pos, sizeof(val), m->sqn, 6);
        *pos = '\0';
@@ -611,31 +622,30 @@ static struct milenage_parameters * get_milenage(const char *imsi)
 }
 
 
-static void sim_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
-                        char *imsi)
+static int sim_req_auth(char *imsi, char *resp, size_t resp_len)
 {
        int count, max_chal, ret;
        char *pos;
-       char reply[1000], *rpos, *rend;
+       char *rpos, *rend;
        struct milenage_parameters *m;
        struct gsm_triplet *g;
 
-       reply[0] = '\0';
+       resp[0] = '\0';
 
        pos = strchr(imsi, ' ');
        if (pos) {
                *pos++ = '\0';
                max_chal = atoi(pos);
-               if (max_chal < 1 || max_chal < EAP_SIM_MAX_CHAL)
+               if (max_chal < 1 || max_chal > EAP_SIM_MAX_CHAL)
                        max_chal = EAP_SIM_MAX_CHAL;
        } else
                max_chal = EAP_SIM_MAX_CHAL;
 
-       rend = &reply[sizeof(reply)];
-       rpos = reply;
+       rend = resp + resp_len;
+       rpos = resp;
        ret = snprintf(rpos, rend - rpos, "SIM-RESP-AUTH %s", imsi);
        if (ret < 0 || ret >= rend - rpos)
-               return;
+               return -1;
        rpos += ret;
 
        m = get_milenage(imsi);
@@ -643,7 +653,7 @@ static void sim_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
                u8 _rand[16], sres[4], kc[8];
                for (count = 0; count < max_chal; count++) {
                        if (random_get_bytes(_rand, 16) < 0)
-                               return;
+                               return -1;
                        gsm_milenage(m->opc, m->ki, _rand, sres, kc);
                        *rpos++ = ' ';
                        rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8);
@@ -653,7 +663,7 @@ static void sim_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
                        rpos += wpa_snprintf_hex(rpos, rend - rpos, _rand, 16);
                }
                *rpos = '\0';
-               goto send;
+               return 0;
        }
 
        count = 0;
@@ -677,15 +687,61 @@ static void sim_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
                printf("No GSM triplets found for %s\n", imsi);
                ret = snprintf(rpos, rend - rpos, " FAILURE");
                if (ret < 0 || ret >= rend - rpos)
-                       return;
+                       return -1;
                rpos += ret;
        }
 
-send:
-       printf("Send: %s\n", reply);
-       if (sendto(s, reply, rpos - reply, 0,
-                  (struct sockaddr *) from, fromlen) < 0)
-               perror("send");
+       return 0;
+}
+
+
+static int gsm_auth_req(char *imsi, char *resp, size_t resp_len)
+{
+       int count, ret;
+       char *pos, *rpos, *rend;
+       struct milenage_parameters *m;
+
+       resp[0] = '\0';
+
+       pos = os_strchr(imsi, ' ');
+       if (!pos)
+               return -1;
+       *pos++ = '\0';
+
+       rend = resp + resp_len;
+       rpos = resp;
+       ret = os_snprintf(rpos, rend - rpos, "GSM-AUTH-RESP %s", imsi);
+       if (os_snprintf_error(rend - rpos, ret))
+               return -1;
+       rpos += ret;
+
+       m = get_milenage(imsi);
+       if (m) {
+               u8 _rand[16], sres[4], kc[8];
+               for (count = 0; count < EAP_SIM_MAX_CHAL; count++) {
+                       if (hexstr2bin(pos, _rand, 16) != 0)
+                               return -1;
+                       gsm_milenage(m->opc, m->ki, _rand, sres, kc);
+                       *rpos++ = count == 0 ? ' ' : ':';
+                       rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8);
+                       *rpos++ = ':';
+                       rpos += wpa_snprintf_hex(rpos, rend - rpos, sres, 4);
+                       pos += 16 * 2;
+                       if (*pos != ':')
+                               break;
+                       pos++;
+               }
+               *rpos = '\0';
+               return 0;
+       }
+
+       printf("No GSM triplets found for %s\n", imsi);
+       ret = os_snprintf(rpos, rend - rpos, " FAILURE");
+       if (os_snprintf_error(rend - rpos, ret))
+               return -1;
+       rpos += ret;
+
+       return 0;
 }
 
 
@@ -711,11 +767,10 @@ static void inc_sqn(u8 *sqn)
 }
 
 
-static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
-                        char *imsi)
+static int aka_req_auth(char *imsi, char *resp, size_t resp_len)
 {
        /* AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES> */
-       char reply[1000], *pos, *end;
+       char *pos, *end;
        u8 _rand[EAP_AKA_RAND_LEN];
        u8 autn[EAP_AKA_AUTN_LEN];
        u8 ik[EAP_AKA_IK_LEN];
@@ -729,16 +784,18 @@ static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
        m = get_milenage(imsi);
        if (m) {
                if (random_get_bytes(_rand, EAP_AKA_RAND_LEN) < 0)
-                       return;
+                       return -1;
                res_len = EAP_AKA_RES_MAX_LEN;
                inc_sqn(m->sqn);
 #ifdef CONFIG_SQLITE
                db_update_milenage_sqn(m);
 #endif /* CONFIG_SQLITE */
                sqn_changes = 1;
-               printf("AKA: Milenage with SQN=%02x%02x%02x%02x%02x%02x\n",
-                      m->sqn[0], m->sqn[1], m->sqn[2],
-                      m->sqn[3], m->sqn[4], m->sqn[5]);
+               if (stdout_debug) {
+                       printf("AKA: Milenage with SQN=%02x%02x%02x%02x%02x%02x\n",
+                              m->sqn[0], m->sqn[1], m->sqn[2],
+                              m->sqn[3], m->sqn[4], m->sqn[5]);
+               }
                milenage_generate(m->opc, m->amf, m->ki, m->sqn, _rand,
                                  autn, ik, ck, res, &res_len);
        } else {
@@ -756,18 +813,18 @@ static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
 #endif /* AKA_USE_FIXED_TEST_VALUES */
        }
 
-       pos = reply;
-       end = &reply[sizeof(reply)];
+       pos = resp;
+       end = resp + resp_len;
        ret = snprintf(pos, end - pos, "AKA-RESP-AUTH %s ", imsi);
        if (ret < 0 || ret >= end - pos)
-               return;
+               return -1;
        pos += ret;
        if (failed) {
                ret = snprintf(pos, end - pos, "FAILURE");
                if (ret < 0 || ret >= end - pos)
-                       return;
+                       return -1;
                pos += ret;
-               goto done;
+               return 0;
        }
        pos += wpa_snprintf_hex(pos, end - pos, _rand, EAP_AKA_RAND_LEN);
        *pos++ = ' ';
@@ -779,65 +836,87 @@ static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen,
        *pos++ = ' ';
        pos += wpa_snprintf_hex(pos, end - pos, res, res_len);
 
-done:
-       printf("Send: %s\n", reply);
-
-       if (sendto(s, reply, pos - reply, 0, (struct sockaddr *) from,
-                  fromlen) < 0)
-               perror("send");
+       return 0;
 }
 
 
-static void aka_auts(int s, struct sockaddr_un *from, socklen_t fromlen,
-                    char *imsi)
+static int aka_auts(char *imsi, char *resp, size_t resp_len)
 {
        char *auts, *__rand;
        u8 _auts[EAP_AKA_AUTS_LEN], _rand[EAP_AKA_RAND_LEN], sqn[6];
        struct milenage_parameters *m;
 
+       resp[0] = '\0';
+
        /* AKA-AUTS <IMSI> <AUTS> <RAND> */
 
        auts = strchr(imsi, ' ');
        if (auts == NULL)
-               return;
+               return -1;
        *auts++ = '\0';
 
        __rand = strchr(auts, ' ');
        if (__rand == NULL)
-               return;
+               return -1;
        *__rand++ = '\0';
 
-       printf("AKA-AUTS: IMSI=%s AUTS=%s RAND=%s\n", imsi, auts, __rand);
+       if (stdout_debug) {
+               printf("AKA-AUTS: IMSI=%s AUTS=%s RAND=%s\n",
+                      imsi, auts, __rand);
+       }
        if (hexstr2bin(auts, _auts, EAP_AKA_AUTS_LEN) ||
            hexstr2bin(__rand, _rand, EAP_AKA_RAND_LEN)) {
                printf("Could not parse AUTS/RAND\n");
-               return;
+               return -1;
        }
 
        m = get_milenage(imsi);
        if (m == NULL) {
                printf("Unknown IMSI: %s\n", imsi);
-               return;
+               return -1;
        }
 
        if (milenage_auts(m->opc, m->ki, _rand, _auts, sqn)) {
                printf("AKA-AUTS: Incorrect MAC-S\n");
        } else {
                memcpy(m->sqn, sqn, 6);
-               printf("AKA-AUTS: Re-synchronized: "
-                      "SQN=%02x%02x%02x%02x%02x%02x\n",
-                      sqn[0], sqn[1], sqn[2], sqn[3], sqn[4], sqn[5]);
+               if (stdout_debug) {
+                       printf("AKA-AUTS: Re-synchronized: "
+                              "SQN=%02x%02x%02x%02x%02x%02x\n",
+                              sqn[0], sqn[1], sqn[2], sqn[3], sqn[4], sqn[5]);
+               }
 #ifdef CONFIG_SQLITE
                db_update_milenage_sqn(m);
 #endif /* CONFIG_SQLITE */
                sqn_changes = 1;
        }
+
+       return 0;
+}
+
+
+static int process_cmd(char *cmd, char *resp, size_t resp_len)
+{
+       if (os_strncmp(cmd, "SIM-REQ-AUTH ", 13) == 0)
+               return sim_req_auth(cmd + 13, resp, resp_len);
+
+       if (os_strncmp(cmd, "GSM-AUTH-REQ ", 13) == 0)
+               return gsm_auth_req(cmd + 13, resp, resp_len);
+
+       if (os_strncmp(cmd, "AKA-REQ-AUTH ", 13) == 0)
+               return aka_req_auth(cmd + 13, resp, resp_len);
+
+       if (os_strncmp(cmd, "AKA-AUTS ", 9) == 0)
+               return aka_auts(cmd + 9, resp, resp_len);
+
+       printf("Unknown request: %s\n", cmd);
+       return -1;
 }
 
 
 static int process(int s)
 {
-       char buf[1000];
+       char buf[1000], resp[1000];
        struct sockaddr_un from;
        socklen_t fromlen;
        ssize_t res;
@@ -859,14 +938,21 @@ static int process(int s)
 
        printf("Received: %s\n", buf);
 
-       if (strncmp(buf, "SIM-REQ-AUTH ", 13) == 0)
-               sim_req_auth(s, &from, fromlen, buf + 13);
-       else if (strncmp(buf, "AKA-REQ-AUTH ", 13) == 0)
-               aka_req_auth(s, &from, fromlen, buf + 13);
-       else if (strncmp(buf, "AKA-AUTS ", 9) == 0)
-               aka_auts(s, &from, fromlen, buf + 9);
-       else
-               printf("Unknown request: %s\n", buf);
+       if (process_cmd(buf, resp, sizeof(resp)) < 0) {
+               printf("Failed to process request\n");
+               return -1;
+       }
+
+       if (resp[0] == '\0') {
+               printf("No response\n");
+               return 0;
+       }
+
+       printf("Send: %s\n", resp);
+
+       if (sendto(s, resp, os_strlen(resp), 0, (struct sockaddr *) &from,
+                  fromlen) < 0)
+               perror("send");
 
        return 0;
 }
@@ -894,8 +980,10 @@ static void cleanup(void)
                os_free(prev);
        }
 
-       close(serv_sock);
-       unlink(socket_path);
+       if (serv_sock >= 0)
+               close(serv_sock);
+       if (socket_path)
+               unlink(socket_path);
 
 #ifdef CONFIG_SQLITE
        if (sqlite_db) {
@@ -917,12 +1005,12 @@ static void usage(void)
 {
        printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA "
               "database/authenticator\n"
-              "Copyright (c) 2005-2007, 2012, Jouni Malinen <j@w1.fi>\n"
+              "Copyright (c) 2005-2007, 2012-2013, Jouni Malinen <j@w1.fi>\n"
               "\n"
               "usage:\n"
               "hlr_auc_gw [-hu] [-s<socket path>] [-g<triplet file>] "
               "[-m<milenage file>] \\\n"
-              "        [-D<DB file>] [-i<IND len in bits>]\n"
+              "        [-D<DB file>] [-i<IND len in bits>] [command]\n"
               "\n"
               "options:\n"
               "  -h = show this usage help\n"
@@ -932,7 +1020,15 @@ static void usage(void)
               "  -g<triplet file> = path for GSM authentication triplets\n"
               "  -m<milenage file> = path for Milenage keys\n"
               "  -D<DB file> = path to SQLite database\n"
-              "  -i<IND len in bits> = IND length for SQN (default: 5)\n",
+              "  -i<IND len in bits> = IND length for SQN (default: 5)\n"
+              "\n"
+              "If the optional command argument, like "
+              "\"AKA-REQ-AUTH <IMSI>\" is used, a single\n"
+              "command is processed with response sent to stdout. Otherwise, "
+              "hlr_auc_gw opens\n"
+              "a control interface and processes commands sent through it "
+              "(e.g., by EAP server\n"
+              "in hostapd).\n",
               default_socket_path);
 }
 
@@ -942,6 +1038,7 @@ int main(int argc, char *argv[])
        int c;
        char *gsm_triplet_file = NULL;
        char *sqlite_db_file = NULL;
+       int ret = 0;
 
        if (os_program_init())
                return -1;
@@ -1005,18 +1102,31 @@ int main(int argc, char *argv[])
        if (milenage_file && read_milenage(milenage_file) < 0)
                return -1;
 
-       serv_sock = open_socket(socket_path);
-       if (serv_sock < 0)
-               return -1;
+       if (optind == argc) {
+               serv_sock = open_socket(socket_path);
+               if (serv_sock < 0)
+                       return -1;
 
-       printf("Listening for requests on %s\n", socket_path);
+               printf("Listening for requests on %s\n", socket_path);
 
-       atexit(cleanup);
-       signal(SIGTERM, handle_term);
-       signal(SIGINT, handle_term);
+               atexit(cleanup);
+               signal(SIGTERM, handle_term);
+               signal(SIGINT, handle_term);
 
-       for (;;)
-               process(serv_sock);
+               for (;;)
+                       process(serv_sock);
+       } else {
+               char buf[1000];
+               socket_path = NULL;
+               stdout_debug = 0;
+               if (process_cmd(argv[optind], buf, sizeof(buf)) < 0) {
+                       printf("FAIL\n");
+                       ret = -1;
+               } else {
+                       printf("%s\n", buf);
+               }
+               cleanup();
+       }
 
 #ifdef CONFIG_SQLITE
        if (sqlite_db) {
@@ -1027,5 +1137,5 @@ int main(int argc, char *argv[])
 
        os_program_deinit();
 
-       return 0;
+       return ret;
 }
index b4456bb..d19d862 100644 (file)
@@ -12,7 +12,7 @@ daemon.
 .B hostapd
 is a user space daemon for access point and authentication servers.
 It implements IEEE 802.11 access point management, IEEE 802.1X/WPA/WPA2/EAP Authenticators and RADIUS authentication server.
-The current version supports Linux (Host AP, madwifi, mac80211-based drivers) and FreeBSD (net80211).
+The current version supports Linux (Host AP, mac80211-based drivers) and FreeBSD (net80211).
 
 .B hostapd
 is designed to be a "daemon" program that runs in the background and acts as the backend component controlling authentication.
index de10c4e..9e81e9e 100644 (file)
@@ -2,10 +2,10 @@
 # Empty lines and lines starting with # are ignored
 
 # AP netdevice name (without 'ap' postfix, i.e., wlan0 uses wlan0ap for
-# management frames); ath0 for madwifi
+# management frames with the Host AP driver); wlan0 with many nl80211 drivers
 interface=wlan0
 
-# In case of madwifi, atheros, and nl80211 driver interfaces, an additional
+# In case of atheros and nl80211 driver interfaces, an additional
 # configuration parameter, bridge, may be used to notify hostapd if the
 # interface is included in a bridge. This parameter is not used with Host AP
 # driver. If the bridge parameter is not set, the drivers will automatically
@@ -18,12 +18,15 @@ interface=wlan0
 # interface is also created.
 #bridge=br0
 
-# Driver interface type (hostap/wired/madwifi/test/none/nl80211/bsd);
+# Driver interface type (hostap/wired/none/nl80211/bsd);
 # default: hostap). nl80211 is used with all Linux mac80211 drivers.
 # Use driver=none if building hostapd as a standalone RADIUS server that does
 # not control any wireless/wired driver.
 # driver=hostap
 
+# Driver interface parameters (mainly for development testing use)
+# driver_params=<params>
+
 # hostapd event logger configuration
 #
 # Two output method: syslog and stdout (only usable if not forking to
@@ -51,9 +54,6 @@ logger_syslog_level=2
 logger_stdout=-1
 logger_stdout_level=2
 
-# Dump file for state information (on SIGUSR1)
-dump_file=/tmp/hostapd.dump
-
 # Interface for separate control program. If this is specified, hostapd
 # will create this directory and a UNIX domain socket for listening to requests
 # from external programs (CLI/GUI, etc.) for status information and
@@ -111,6 +111,20 @@ ssid=test
 # (default: 0 = disabled)
 #ieee80211h=1
 
+# Add Power Constraint element to Beacon and Probe Response frames
+# This config option adds Power Constraint element when applicable and Country
+# element is added. Power Constraint element is required by Transmit Power
+# Control. This can be used only with ieee80211d=1.
+# Valid values are 0..255.
+#local_pwr_constraint=3
+
+# Set Spectrum Management subfield in the Capability Information field.
+# This config option forces the Spectrum Management bit to be set. When this
+# option is not set, the value of the Spectrum Management bit depends on whether
+# DFS or TPC is required by regulatory authorities. This can be used only with
+# ieee80211d=1 and local_pwr_constraint configured.
+#spectrum_mgmt_required=1
+
 # Operation mode (a = IEEE 802.11a, b = IEEE 802.11b, g = IEEE 802.11g,
 # ad = IEEE 802.11ad (60 GHz); a/g options are used with IEEE 802.11n, too, to
 # specify band)
@@ -121,8 +135,44 @@ hw_mode=g
 # (default: 0, i.e., not set)
 # Please note that some drivers do not use this value from hostapd and the
 # channel will need to be configured separately with iwconfig.
+#
+# If CONFIG_ACS build option is enabled, the channel can be selected
+# automatically at run time by setting channel=acs_survey or channel=0, both of
+# which will enable the ACS survey based algorithm.
 channel=1
 
+# ACS tuning - Automatic Channel Selection
+# See: http://wireless.kernel.org/en/users/Documentation/acs
+#
+# You can customize the ACS survey algorithm with following variables:
+#
+# acs_num_scans requirement is 1..100 - number of scans to be performed that
+# are used to trigger survey data gathering of an underlying device driver.
+# Scans are passive and typically take a little over 100ms (depending on the
+# driver) on each available channel for given hw_mode. Increasing this value
+# means sacrificing startup time and gathering more data wrt channel
+# interference that may help choosing a better channel. This can also help fine
+# tune the ACS scan time in case a driver has different scan dwell times.
+#
+# acs_chan_bias is a space-separated list of <channel>:<bias> pairs. It can be
+# used to increase (or decrease) the likelihood of a specific channel to be
+# selected by the ACS algorithm. The total interference factor for each channel
+# gets multiplied by the specified bias value before finding the channel with
+# the lowest value. In other words, values between 0.0 and 1.0 can be used to
+# make a channel more likely to be picked while values larger than 1.0 make the
+# specified channel less likely to be picked. This can be used, e.g., to prefer
+# the commonly used 2.4 GHz band channels 1, 6, and 11 (which is the default
+# behavior on 2.4 GHz band if no acs_chan_bias parameter is specified).
+#
+# Defaults:
+#acs_num_scans=5
+#acs_chan_bias=1:0.8 6:0.8 11:0.8
+
+# Channel list restriction. This option allows hostapd to select one of the
+# provided channels when a channel should be automatically selected.
+# Default: not set (allow any enabled channel to be selected)
+#chanlist=100 104 108 112 116
+
 # Beacon interval in kus (1.024 ms) (default: 100; range 15..65535)
 beacon_int=100
 
@@ -182,7 +232,7 @@ fragm_threshold=2346
 # Station MAC address -based authentication
 # Please note that this kind of access control requires a driver that uses
 # hostapd to take care of management frame processing and as such, this can be
-# used with driver=hostap or driver=nl80211, but not with driver=madwifi.
+# used with driver=hostap or driver=nl80211, but not with driver=atheros.
 # 0 = accept unless in deny list
 # 1 = deny unless in accept list
 # 2 = use external RADIUS server (accept/deny lists are searched first)
@@ -389,10 +439,24 @@ wmm_ac_vo_acm=0
 # use a separate bridge.
 #wds_bridge=wds-br0
 
+# Start the AP with beaconing disabled by default.
+#start_disabled=0
+
 # Client isolation can be used to prevent low-level bridging of frames between
 # associated stations in the BSS. By default, this bridging is allowed.
 #ap_isolate=1
 
+# BSS Load update period (in BUs)
+# This field is used to enable and configure adding a BSS Load element into
+# Beacon and Probe Response frames.
+#bss_load_update_period=50
+
+# Fixed BSS Load value for testing purposes
+# This field can be used to configure hostapd to add a fixed BSS Load element
+# into Beacon and Probe Response frames for testing purposes. The format is
+# <station count>:<channel utilization>:<available admission capacity>
+#bss_load_test=12:80:20000
+
 ##### IEEE 802.11n related configuration ######################################
 
 # ieee80211n: Whether IEEE 802.11n (HT) is enabled
@@ -405,7 +469,7 @@ wmm_ac_vo_acm=0
 # LDPC coding capability: [LDPC] = supported
 # Supported channel width set: [HT40-] = both 20 MHz and 40 MHz with secondary
 #      channel below the primary channel; [HT40+] = both 20 MHz and 40 MHz
-#      with secondary channel below the primary channel
+#      with secondary channel above the primary channel
 #      (20 MHz only if neither is set)
 #      Note: There are limits on which channels can be used with HT40- and
 #      HT40+. Following table shows the channels that may be available for
@@ -432,13 +496,20 @@ wmm_ac_vo_acm=0
 # Maximum A-MSDU length: [MAX-AMSDU-7935] for 7935 octets (3839 octets if not
 #      set)
 # DSSS/CCK Mode in 40 MHz: [DSSS_CCK-40] = allowed (not allowed if not set)
-# PSMP support: [PSMP] (disabled if not set)
+# 40 MHz intolerant [40-INTOLERANT] (not advertised if not set)
 # L-SIG TXOP protection support: [LSIG-TXOP-PROT] (disabled if not set)
 #ht_capab=[HT40-][SHORT-GI-20][SHORT-GI-40]
 
 # Require stations to support HT PHY (reject association if they do not)
 #require_ht=1
 
+# If set non-zero, require stations to perform scans of overlapping
+# channels to test for stations which would be affected by 40 MHz traffic.
+# This parameter sets the interval in seconds between these scans. Setting this
+# to non-zero allows 2.4 GHz band AP to move dynamically to a 40 MHz channel if
+# no co-existence issues with neighboring devices are found.
+#obss_interval=0
+
 ##### IEEE 802.11ac related configuration #####################################
 
 # ieee80211ac: Whether IEEE 802.11ac (VHT) is enabled
@@ -633,6 +704,17 @@ eapol_key_index_workaround=0
 # is only used by one station.
 #use_pae_group_addr=1
 
+# EAP Re-authentication Protocol (ERP) authenticator (RFC 6696)
+#
+# Whether to initiate EAP authentication with EAP-Initiate/Re-auth-Start before
+# EAP-Identity/Request
+#erp_send_reauth_start=1
+#
+# Domain name for EAP-Initiate/Re-auth-Start. Omitted from the message if not
+# set (no local ER server). This is also used by the integrated EAP server if
+# ERP is enabled (eap_server_erp=1).
+#erp_domain=example.com
+
 ##### Integrated EAP server ###################################################
 
 # Optionally, hostapd can be configured to use an integrated EAP server
@@ -666,6 +748,11 @@ eap_server=0
 # Passphrase for private key
 #private_key_passwd=secret passphrase
 
+# Server identity
+# EAP methods that provide mechanism for authenticated server identity delivery
+# use this value. If not set, "hostapd" is used as a default.
+#server_id=server.example.com
+
 # Enable CRL verification.
 # Note: hostapd does not yet support CRL downloading based on CDP. Thus, a
 # valid CRL signed by the CA is required to be included in the ca_cert file.
@@ -703,6 +790,15 @@ eap_server=0
 # "openssl dhparam -out /etc/hostapd.dh.pem 1024"
 #dh_file=/etc/hostapd.dh.pem
 
+# OpenSSL cipher string
+#
+# This is an OpenSSL specific configuration option for configuring the default
+# ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the default.
+# See https://www.openssl.org/docs/apps/ciphers.html for OpenSSL documentation
+# on cipher suite configuration. This is applicable only if hostapd is built to
+# use OpenSSL.
+#openssl_ciphers=DEFAULT:!EXP:!LOW
+
 # Fragment size for EAP methods
 #fragment_size=1400
 
@@ -764,6 +860,10 @@ eap_server=0
 # EAP method is enabled, the peer will be allowed to connect without TNC.
 #tnc=1
 
+# EAP Re-authentication Protocol (ERP) - RFC 6696
+#
+# Whether to enable ERP on the EAP server.
+#eap_server_erp=1
 
 ##### IEEE 802.11f - Inter-Access Point Protocol (IAPP) #######################
 
@@ -785,6 +885,12 @@ own_ip_addr=127.0.0.1
 # 48 octets long.
 #nas_identifier=ap.example.com
 
+# RADIUS client forced local IP address for the access point
+# Normally the local IP address is determined automatically based on configured
+# IP addresses, but this field can be used to force a specific address to be
+# used, e.g., when the device has multiple IP addresses.
+#radius_client_addr=127.0.0.1
+
 # RADIUS authentication server
 #auth_server_addr=127.0.0.1
 #auth_server_port=1812
@@ -933,6 +1039,11 @@ own_ip_addr=127.0.0.1
 # The UDP port number for the RADIUS authentication server
 #radius_server_auth_port=1812
 
+# The UDP port number for the RADIUS accounting server
+# Commenting this out or setting this to 0 can be used to disable RADIUS
+# accounting while still enabling RADIUS authentication.
+#radius_server_acct_port=1813
+
 # Use IPv6 with RADIUS server (IPv4 will also be supported using IPv6 API)
 #radius_server_ipv6=1
 
@@ -1039,6 +1150,17 @@ own_ip_addr=127.0.0.1
 # 2 = required
 #ieee80211w=0
 
+# Group management cipher suite
+# Default: AES-128-CMAC (BIP)
+# Other options (depending on driver support):
+# BIP-GMAC-128
+# BIP-GMAC-256
+# BIP-CMAC-256
+# Note: All the stations connecting to the BSS will also need to support the
+# selected cipher. The default AES-128-CMAC is the only option that is commonly
+# available in deployed devices.
+#group_mgmt_cipher=AES-128-CMAC
+
 # Association SA Query maximum timeout (in TU = 1.024 ms; for MFP)
 # (maximum time to wait for a SA Query response)
 # dot11AssociationSAQueryMaximumTimeout, 1...4294967295
@@ -1363,6 +1485,11 @@ own_ip_addr=127.0.0.1
 # 1 = enabled
 #bss_transition=1
 
+# Proxy ARP
+# 0 = disabled (default)
+# 1 = enabled
+#proxy_arp=1
+
 ##### IEEE 802.11u-2011 #######################################################
 
 # Enable Interworking service
@@ -1429,6 +1556,9 @@ own_ip_addr=127.0.0.1
 # information to be complete.
 #venue_name=eng:Example venue
 #venue_name=fin:Esimerkkipaikka
+# Alternative format for language:value strings:
+# (double quoted string, printf-escaped string)
+#venue_name=P"eng:Example\nvenue"
 
 # Network Authentication Type
 # This parameter indicates what type of network authentication is used in the
@@ -1480,6 +1610,8 @@ own_ip_addr=127.0.0.1
 #          accordance with IETF RFC 4282
 # NAI Realm(s): Semi-colon delimited NAI Realm(s)
 # EAP Method: <EAP Method>[:<[AuthParam1:Val1]>][<[AuthParam2:Val2]>][...]
+# EAP Method types, see:
+# http://www.iana.org/assignments/eap-numbers/eap-numbers.xhtml#eap-numbers-4
 # AuthParam (Table 8-188 in IEEE Std 802.11-2012):
 # ID 2 = Non-EAP Inner Authentication Type
 #      1 = PAP, 2 = CHAP, 3 = MSCHAP, 4 = MSCHAPV2
@@ -1493,6 +1625,23 @@ own_ip_addr=127.0.0.1
 # username/password
 #nai_realm=0,example.org,13[5:6],21[2:4][5:7]
 
+# QoS Map Set configuration
+#
+# Comma delimited QoS Map Set in decimal values
+# (see IEEE Std 802.11-2012, 8.4.2.97)
+#
+# format:
+# [<DSCP Exceptions[DSCP,UP]>,]<UP 0 range[low,high]>,...<UP 7 range[low,high]>
+#
+# There can be up to 21 optional DSCP Exceptions which are pairs of DSCP Value
+# (0..63 or 255) and User Priority (0..7). This is followed by eight DSCP Range
+# descriptions with DSCP Low Value and DSCP High Value pairs (0..63 or 255) for
+# each UP starting from 0. If both low and high value are set to 255, the
+# corresponding UP is not used.
+#
+# default: not set
+#qos_map_set=53,2,22,6,8,15,0,7,255,255,16,31,32,39,255,255,40,47,255,255
+
 ##### Hotspot 2.0 #############################################################
 
 # Enable Hotspot 2.0 support
@@ -1505,6 +1654,21 @@ own_ip_addr=127.0.0.1
 # forging such frames to other stations in the BSS.
 #disable_dgaf=1
 
+# OSU Server-Only Authenticated L2 Encryption Network
+#osen=1
+
+# ANQP Domain ID (0..65535)
+# An identifier for a set of APs in an ESS that share the same common ANQP
+# information. 0 = Some of the ANQP information is unique to this AP (default).
+#anqp_domain_id=1234
+
+# Deauthentication request timeout
+# If the RADIUS server indicates that the station is not allowed to connect to
+# the BSS/ESS, the AP can allow the station some time to download a
+# notification page (URL included in the message). This parameter sets that
+# timeout in seconds.
+#hs20_deauth_req_timeout=60
+
 # Operator Friendly Name
 # This parameter can be used to configure one or more Operator Friendly Name
 # Duples. Each entry has a two or three character language code (ISO-639)
@@ -1548,6 +1712,32 @@ own_ip_addr=127.0.0.1
 # channels 36-48):
 #hs20_operating_class=5173
 
+# OSU icons
+# <Icon Width>:<Icon Height>:<Language code>:<Icon Type>:<Name>:<file path>
+#hs20_icon=32:32:eng:image/png:icon32:/tmp/icon32.png
+#hs20_icon=64:64:eng:image/png:icon64:/tmp/icon64.png
+
+# OSU SSID (see ssid2 for format description)
+# This is the SSID used for all OSU connections to all the listed OSU Providers.
+#osu_ssid="example"
+
+# OSU Providers
+# One or more sets of following parameter. Each OSU provider is started by the
+# mandatory osu_server_uri item. The other parameters add information for the
+# last added OSU provider.
+#
+#osu_server_uri=https://example.com/osu/
+#osu_friendly_name=eng:Example operator
+#osu_friendly_name=fin:Esimerkkipalveluntarjoaja
+#osu_nai=anonymous@example.com
+#osu_method_list=1 0
+#osu_icon=icon32
+#osu_icon=icon64
+#osu_service_desc=eng:Example services
+#osu_service_desc=fin:Esimerkkipalveluja
+#
+#osu_server_uri=...
+
 ##### TESTING OPTIONS #########################################################
 #
 # The options in this section are only available when the build configuration
@@ -1591,6 +1781,11 @@ own_ip_addr=127.0.0.1
 # - is not the same as the MAC address of the radio
 # - is not the same as any other explicitly specified BSSID
 #
+# Not all drivers support multiple BSSes. The exact mechanism for determining
+# the driver capabilities is driver specific. With the current (i.e., a recent
+# kernel) drivers using nl80211, this information can be checked with "iw list"
+# (search for "valid interface combinations").
+#
 # Please note that hostapd uses some of the values configured for the first BSS
 # as the defaults for the following BSSes. However, it is recommended that all
 # BSSes include explicit configuration of all relevant configuration items.
index 12a2c61..00edc95 100644 (file)
 # TTLS-CHAP, TTLS-MSCHAP, TTLS-MSCHAPV2. TTLS-PAP and TTLS-CHAP require a
 # plaintext password while TTLS-MSCHAP and TTLS-MSCHAPV2 can use NT password
 # hash.
+#
+# Arbitrary RADIUS attributes can be added into Access-Accept packets similarly
+# to the way radius_auth_req_attr is used for Access-Request packet in
+# hostapd.conf. For EAP server, this is configured separately for each user
+# entry with radius_accept_attr=<value> line(s) following the main user entry
+# line.
 
 # Phase 1 users
 "user"         MD5     "password"
index f688327..826db34 100644 (file)
@@ -2,6 +2,7 @@ CREATE TABLE users(
        identity TEXT PRIMARY KEY,
        methods TEXT,
        password TEXT,
+       remediation TEXT,
        phase2 INTEGER
 );
 
@@ -15,3 +16,11 @@ INSERT INTO users(identity,methods,password,phase2) VALUES ('DOMAIN\mschapv2 use
 
 INSERT INTO wildcards(identity,methods) VALUES ('','TTLS,TLS');
 INSERT INTO wildcards(identity,methods) VALUES ('0','AKA');
+
+CREATE TABLE authlog(
+       timestamp TEXT,
+       session TEXT,
+       nas_ip TEXT,
+       username TEXT,
+       note TEXT
+);
index 661f709..3f00cbb 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd - command line interface for hostapd daemon
- * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -18,7 +18,7 @@
 
 static const char *hostapd_cli_version =
 "hostapd_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> and contributors";
 
 
 static const char *hostapd_cli_license =
@@ -79,6 +79,7 @@ static const char *commands_help =
 #endif /* CONFIG_WPS_NFC */
 "   wps_ap_pin <cmd> [params..]  enable/disable AP PIN\n"
 "   wps_config <SSID> <auth> <encr> <key>  configure AP\n"
+"   wps_get_status       show current WPS status\n"
 #endif /* CONFIG_WPS */
 "   get_config           show current configuration\n"
 "   help                 show this usage help\n"
@@ -90,7 +91,12 @@ static const char *commands_help =
 static struct wpa_ctrl *ctrl_conn;
 static int hostapd_cli_quit = 0;
 static int hostapd_cli_attached = 0;
-static const char *ctrl_iface_dir = "/var/run/hostapd";
+
+#ifndef CONFIG_CTRL_IFACE_DIR
+#define CONFIG_CTRL_IFACE_DIR "/var/run/hostapd"
+#endif /* CONFIG_CTRL_IFACE_DIR */
+static const char *ctrl_iface_dir = CONFIG_CTRL_IFACE_DIR;
+
 static char *ctrl_ifname = NULL;
 static const char *pid_file = NULL;
 static const char *action_file = NULL;
@@ -210,8 +216,21 @@ static int hostapd_cli_cmd_relog(struct wpa_ctrl *ctrl, int argc, char *argv[])
 }
 
 
+static int hostapd_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       if (argc > 0 && os_strcmp(argv[0], "driver") == 0)
+               return wpa_ctrl_command(ctrl, "STATUS-DRIVER");
+       return wpa_ctrl_command(ctrl, "STATUS");
+}
+
+
 static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
+       if (argc > 0) {
+               char buf[100];
+               os_snprintf(buf, sizeof(buf), "MIB %s", argv[0]);
+               return wpa_ctrl_command(ctrl, buf);
+       }
        return wpa_ctrl_command(ctrl, "MIB");
 }
 
@@ -219,28 +238,19 @@ static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[])
 static int hostapd_cli_exec(const char *program, const char *arg1,
                            const char *arg2)
 {
-       char *cmd;
+       char *arg;
        size_t len;
        int res;
-       int ret = 0;
 
-       len = os_strlen(program) + os_strlen(arg1) + os_strlen(arg2) + 3;
-       cmd = os_malloc(len);
-       if (cmd == NULL)
+       len = os_strlen(arg1) + os_strlen(arg2) + 2;
+       arg = os_malloc(len);
+       if (arg == NULL)
                return -1;
-       res = os_snprintf(cmd, len, "%s %s %s", program, arg1, arg2);
-       if (res < 0 || (size_t) res >= len) {
-               os_free(cmd);
-               return -1;
-       }
-       cmd[len - 1] = '\0';
-#ifndef _WIN32_WCE
-       if (system(cmd) < 0)
-               ret = -1;
-#endif /* _WIN32_WCE */
-       os_free(cmd);
+       os_snprintf(arg, len, "%s %s", arg1, arg2);
+       res = os_exec(program, arg, 1);
+       os_free(arg);
 
-       return ret;
+       return res;
 }
 
 
@@ -264,12 +274,15 @@ static void hostapd_cli_action_process(char *msg, size_t len)
 static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
        char buf[64];
-       if (argc != 1) {
-               printf("Invalid 'sta' command - exactly one argument, STA "
+       if (argc < 1) {
+               printf("Invalid 'sta' command - at least one argument, STA "
                       "address, is required.\n");
                return -1;
        }
-       snprintf(buf, sizeof(buf), "STA %s", argv[0]);
+       if (argc > 1)
+               snprintf(buf, sizeof(buf), "STA %s %s", argv[0], argv[1]);
+       else
+               snprintf(buf, sizeof(buf), "STA %s", argv[0]);
        return wpa_ctrl_command(ctrl, buf);
 }
 
@@ -380,7 +393,7 @@ static int hostapd_cli_cmd_wps_check_pin(struct wpa_ctrl *ctrl, int argc,
        else
                res = os_snprintf(cmd, sizeof(cmd), "WPS_CHECK_PIN %s",
                                  argv[0]);
-       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+       if (os_snprintf_error(sizeof(cmd), res)) {
                printf("Too long WPS_CHECK_PIN command.\n");
                return -1;
        }
@@ -443,7 +456,7 @@ static int hostapd_cli_cmd_wps_nfc_config_token(struct wpa_ctrl *ctrl,
 
        res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_CONFIG_TOKEN %s",
                          argv[0]);
-       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+       if (os_snprintf_error(sizeof(cmd), res)) {
                printf("Too long WPS_NFC_CONFIG_TOKEN command.\n");
                return -1;
        }
@@ -464,7 +477,7 @@ static int hostapd_cli_cmd_wps_nfc_token(struct wpa_ctrl *ctrl,
        }
 
        res = os_snprintf(cmd, sizeof(cmd), "WPS_NFC_TOKEN %s", argv[0]);
-       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+       if (os_snprintf_error(sizeof(cmd), res)) {
                printf("Too long WPS_NFC_TOKEN command.\n");
                return -1;
        }
@@ -486,7 +499,7 @@ static int hostapd_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl,
 
        res = os_snprintf(cmd, sizeof(cmd), "NFC_GET_HANDOVER_SEL %s %s",
                          argv[0], argv[1]);
-       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+       if (os_snprintf_error(sizeof(cmd), res)) {
                printf("Too long NFC_GET_HANDOVER_SEL command.\n");
                return -1;
        }
@@ -517,6 +530,13 @@ static int hostapd_cli_cmd_wps_ap_pin(struct wpa_ctrl *ctrl, int argc,
 }
 
 
+static int hostapd_cli_cmd_wps_get_status(struct wpa_ctrl *ctrl, int argc,
+                                         char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "WPS_GET_STATUS");
+}
+
+
 static int hostapd_cli_cmd_wps_config(struct wpa_ctrl *ctrl, int argc,
                                      char *argv[])
 {
@@ -576,7 +596,7 @@ static int hostapd_cli_cmd_disassoc_imminent(struct wpa_ctrl *ctrl, int argc,
 
        res = os_snprintf(buf, sizeof(buf), "DISASSOC_IMMINENT %s %s",
                          argv[0], argv[1]);
-       if (res < 0 || res >= (int) sizeof(buf))
+       if (os_snprintf_error(sizeof(buf), res))
                return -1;
        return wpa_ctrl_command(ctrl, buf);
 }
@@ -596,8 +616,35 @@ static int hostapd_cli_cmd_ess_disassoc(struct wpa_ctrl *ctrl, int argc,
 
        res = os_snprintf(buf, sizeof(buf), "ESS_DISASSOC %s %s %s",
                          argv[0], argv[1], argv[2]);
-       if (res < 0 || res >= (int) sizeof(buf))
+       if (os_snprintf_error(sizeof(buf), res))
+               return -1;
+       return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int hostapd_cli_cmd_bss_tm_req(struct wpa_ctrl *ctrl, int argc,
+                                     char *argv[])
+{
+       char buf[2000], *tmp;
+       int res, i, total;
+
+       if (argc < 1) {
+               printf("Invalid 'bss_tm_req' command - at least one argument (STA addr) is needed\n");
                return -1;
+       }
+
+       res = os_snprintf(buf, sizeof(buf), "BSS_TM_REQ %s", argv[0]);
+       if (os_snprintf_error(sizeof(buf), res))
+               return -1;
+
+       total = res;
+       for (i = 1; i < argc; i++) {
+               tmp = &buf[total];
+               res = os_snprintf(tmp, sizeof(buf) - total, " %s", argv[i]);
+               if (os_snprintf_error(sizeof(buf) - total, res))
+                       return -1;
+               total += res;
+       }
        return wpa_ctrl_command(ctrl, buf);
 }
 
@@ -675,6 +722,90 @@ static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc,
 }
 
 
+static int hostapd_cli_cmd_set_qos_map_set(struct wpa_ctrl *ctrl,
+                                          int argc, char *argv[])
+{
+       char buf[200];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid 'set_qos_map_set' command - "
+                      "one argument (comma delimited QoS map set) "
+                      "is needed\n");
+               return -1;
+       }
+
+       res = os_snprintf(buf, sizeof(buf), "SET_QOS_MAP_SET %s", argv[0]);
+       if (os_snprintf_error(sizeof(buf), res))
+               return -1;
+       return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int hostapd_cli_cmd_send_qos_map_conf(struct wpa_ctrl *ctrl,
+                                            int argc, char *argv[])
+{
+       char buf[50];
+       int res;
+
+       if (argc != 1) {
+               printf("Invalid 'send_qos_map_conf' command - "
+                      "one argument (STA addr) is needed\n");
+               return -1;
+       }
+
+       res = os_snprintf(buf, sizeof(buf), "SEND_QOS_MAP_CONF %s", argv[0]);
+       if (os_snprintf_error(sizeof(buf), res))
+               return -1;
+       return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int hostapd_cli_cmd_hs20_wnm_notif(struct wpa_ctrl *ctrl, int argc,
+                                         char *argv[])
+{
+       char buf[300];
+       int res;
+
+       if (argc < 2) {
+               printf("Invalid 'hs20_wnm_notif' command - two arguments (STA "
+                      "addr and URL) are needed\n");
+               return -1;
+       }
+
+       res = os_snprintf(buf, sizeof(buf), "HS20_WNM_NOTIF %s %s",
+                         argv[0], argv[1]);
+       if (os_snprintf_error(sizeof(buf), res))
+               return -1;
+       return wpa_ctrl_command(ctrl, buf);
+}
+
+
+static int hostapd_cli_cmd_hs20_deauth_req(struct wpa_ctrl *ctrl, int argc,
+                                          char *argv[])
+{
+       char buf[300];
+       int res;
+
+       if (argc < 3) {
+               printf("Invalid 'hs20_deauth_req' command - at least three arguments (STA addr, Code, Re-auth Delay) are needed\n");
+               return -1;
+       }
+
+       if (argc > 3)
+               res = os_snprintf(buf, sizeof(buf),
+                                 "HS20_DEAUTH_REQ %s %s %s %s",
+                                 argv[0], argv[1], argv[2], argv[3]);
+       else
+               res = os_snprintf(buf, sizeof(buf),
+                                 "HS20_DEAUTH_REQ %s %s %s",
+                                 argv[0], argv[1], argv[2]);
+       if (os_snprintf_error(sizeof(buf), res))
+               return -1;
+       return wpa_ctrl_command(ctrl, buf);
+}
+
+
 static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
        hostapd_cli_quit = 1;
@@ -729,8 +860,10 @@ static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc,
        }
 
        hostapd_cli_close_connection();
-       free(ctrl_ifname);
-       ctrl_ifname = strdup(argv[0]);
+       os_free(ctrl_ifname);
+       ctrl_ifname = os_strdup(argv[0]);
+       if (ctrl_ifname == NULL)
+               return -1;
 
        if (hostapd_cli_open_connection(ctrl_ifname)) {
                printf("Connected to interface '%s.\n", ctrl_ifname);
@@ -760,7 +893,7 @@ static int hostapd_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
        }
 
        res = os_snprintf(cmd, sizeof(cmd), "SET %s %s", argv[0], argv[1]);
-       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+       if (os_snprintf_error(sizeof(cmd), res)) {
                printf("Too long SET command.\n");
                return -1;
        }
@@ -780,7 +913,7 @@ static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
        }
 
        res = os_snprintf(cmd, sizeof(cmd), "GET %s", argv[0]);
-       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+       if (os_snprintf_error(sizeof(cmd), res)) {
                printf("Too long GET command.\n");
                return -1;
        }
@@ -788,6 +921,94 @@ static int hostapd_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
 }
 
 
+static int hostapd_cli_cmd_chan_switch(struct wpa_ctrl *ctrl,
+                                      int argc, char *argv[])
+{
+       char cmd[256];
+       int res;
+       int i;
+       char *tmp;
+       int total;
+
+       if (argc < 2) {
+               printf("Invalid chan_switch command: needs at least two "
+                      "arguments (count and freq)\n"
+                      "usage: <cs_count> <freq> [sec_channel_offset=] "
+                      "[center_freq1=] [center_freq2=] [bandwidth=] "
+                      "[blocktx] [ht|vht]\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "CHAN_SWITCH %s %s",
+                         argv[0], argv[1]);
+       if (os_snprintf_error(sizeof(cmd), res)) {
+               printf("Too long CHAN_SWITCH command.\n");
+               return -1;
+       }
+
+       total = res;
+       for (i = 2; i < argc; i++) {
+               tmp = cmd + total;
+               res = os_snprintf(tmp, sizeof(cmd) - total, " %s", argv[i]);
+               if (os_snprintf_error(sizeof(cmd) - total, res)) {
+                       printf("Too long CHAN_SWITCH command.\n");
+                       return -1;
+               }
+               total += res;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int hostapd_cli_cmd_enable(struct wpa_ctrl *ctrl, int argc,
+                                     char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "ENABLE");
+}
+
+
+static int hostapd_cli_cmd_reload(struct wpa_ctrl *ctrl, int argc,
+                                     char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "RELOAD");
+}
+
+
+static int hostapd_cli_cmd_disable(struct wpa_ctrl *ctrl, int argc,
+                                     char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "DISABLE");
+}
+
+
+static int hostapd_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[256];
+       int res;
+
+       if (argc < 2 || argc > 3) {
+               printf("Invalid vendor command\n"
+                      "usage: <vendor id> <command id> [<hex formatted command argument>]\n");
+               return -1;
+       }
+
+       res = os_snprintf(cmd, sizeof(cmd), "VENDOR %s %s %s", argv[0], argv[1],
+                         argc == 3 ? argv[2] : "");
+       if (os_snprintf_error(sizeof(cmd), res)) {
+               printf("Too long VENDOR command.\n");
+               return -1;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int hostapd_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc,
+                                    char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "ERP_FLUSH");
+}
+
+
 struct hostapd_cli_cmd {
        const char *cmd;
        int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]);
@@ -797,6 +1018,7 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = {
        { "ping", hostapd_cli_cmd_ping },
        { "mib", hostapd_cli_cmd_mib },
        { "relog", hostapd_cli_cmd_relog },
+       { "status", hostapd_cli_cmd_status },
        { "sta", hostapd_cli_cmd_sta },
        { "all_sta", hostapd_cli_cmd_all_sta },
        { "new_sta", hostapd_cli_cmd_new_sta },
@@ -818,9 +1040,11 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = {
 #endif /* CONFIG_WPS_NFC */
        { "wps_ap_pin", hostapd_cli_cmd_wps_ap_pin },
        { "wps_config", hostapd_cli_cmd_wps_config },
+       { "wps_get_status", hostapd_cli_cmd_wps_get_status },
 #endif /* CONFIG_WPS */
        { "disassoc_imminent", hostapd_cli_cmd_disassoc_imminent },
        { "ess_disassoc", hostapd_cli_cmd_ess_disassoc },
+       { "bss_tm_req", hostapd_cli_cmd_bss_tm_req },
        { "get_config", hostapd_cli_cmd_get_config },
        { "help", hostapd_cli_cmd_help },
        { "interface", hostapd_cli_cmd_interface },
@@ -829,6 +1053,16 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = {
        { "quit", hostapd_cli_cmd_quit },
        { "set", hostapd_cli_cmd_set },
        { "get", hostapd_cli_cmd_get },
+       { "set_qos_map_set", hostapd_cli_cmd_set_qos_map_set },
+       { "send_qos_map_conf", hostapd_cli_cmd_send_qos_map_conf },
+       { "chan_switch", hostapd_cli_cmd_chan_switch },
+       { "hs20_wnm_notif", hostapd_cli_cmd_hs20_wnm_notif },
+       { "hs20_deauth_req", hostapd_cli_cmd_hs20_deauth_req },
+       { "vendor", hostapd_cli_cmd_vendor },
+       { "enable", hostapd_cli_cmd_enable },
+       { "reload", hostapd_cli_cmd_reload },
+       { "disable", hostapd_cli_cmd_disable },
+       { "erp_flush", hostapd_cli_cmd_erp_flush },
        { NULL, NULL }
 };
 
index 90e5966..4aab444 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / main()
- * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -14,6 +14,7 @@
 
 #include "utils/common.h"
 #include "utils/eloop.h"
+#include "utils/uuid.h"
 #include "crypto/random.h"
 #include "crypto/tls.h"
 #include "common/version.h"
 #include "ap/ap_drv_ops.h"
 #include "config_file.h"
 #include "eap_register.h"
-#include "dump_state.h"
 #include "ctrl_iface.h"
 
 
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-extern int wpa_debug_timestamp;
-
-extern struct wpa_driver_ops *wpa_drivers[];
-
-
 struct hapd_global {
        void **drv_priv;
        size_t drv_count;
@@ -99,22 +92,24 @@ static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
        if (hapd && hapd->conf && addr)
                os_snprintf(format, maxlen, "%s: STA " MACSTR "%s%s: %s",
                            hapd->conf->iface, MAC2STR(addr),
-                           module_str ? " " : "", module_str, txt);
+                           module_str ? " " : "", module_str ? module_str : "",
+                           txt);
        else if (hapd && hapd->conf)
                os_snprintf(format, maxlen, "%s:%s%s %s",
                            hapd->conf->iface, module_str ? " " : "",
-                           module_str, txt);
+                           module_str ? module_str : "", txt);
        else if (addr)
                os_snprintf(format, maxlen, "STA " MACSTR "%s%s: %s",
                            MAC2STR(addr), module_str ? " " : "",
-                           module_str, txt);
+                           module_str ? module_str : "", txt);
        else
                os_snprintf(format, maxlen, "%s%s%s",
-                           module_str, module_str ? ": " : "", txt);
+                           module_str ? module_str : "",
+                           module_str ? ": " : "", txt);
 
        if ((conf_stdout & module) && level >= conf_stdout_level) {
                wpa_debug_print_timestamp();
-               printf("%s\n", format);
+               wpa_printf(MSG_INFO, "%s", format);
        }
 
 #ifndef CONFIG_NATIVE_WINDOWS
@@ -148,65 +143,8 @@ static void hostapd_logger_cb(void *ctx, const u8 *addr, unsigned int module,
 
 
 /**
- * hostapd_init - Allocate and initialize per-interface data
- * @config_file: Path to the configuration file
- * Returns: Pointer to the allocated interface data or %NULL on failure
- *
- * This function is used to allocate main data structures for per-interface
- * data. The allocated data buffer will be freed by calling
- * hostapd_cleanup_iface().
+ * hostapd_driver_init - Preparate driver interface
  */
-static struct hostapd_iface * hostapd_init(const char *config_file)
-{
-       struct hostapd_iface *hapd_iface = NULL;
-       struct hostapd_config *conf = NULL;
-       struct hostapd_data *hapd;
-       size_t i;
-
-       hapd_iface = os_zalloc(sizeof(*hapd_iface));
-       if (hapd_iface == NULL)
-               goto fail;
-
-       hapd_iface->config_fname = os_strdup(config_file);
-       if (hapd_iface->config_fname == NULL)
-               goto fail;
-
-       conf = hostapd_config_read(hapd_iface->config_fname);
-       if (conf == NULL)
-               goto fail;
-       hapd_iface->conf = conf;
-
-       hapd_iface->num_bss = conf->num_bss;
-       hapd_iface->bss = os_calloc(conf->num_bss,
-                                   sizeof(struct hostapd_data *));
-       if (hapd_iface->bss == NULL)
-               goto fail;
-
-       for (i = 0; i < conf->num_bss; i++) {
-               hapd = hapd_iface->bss[i] =
-                       hostapd_alloc_bss_data(hapd_iface, conf,
-                                              &conf->bss[i]);
-               if (hapd == NULL)
-                       goto fail;
-               hapd->msg_ctx = hapd;
-       }
-
-       return hapd_iface;
-
-fail:
-       wpa_printf(MSG_ERROR, "Failed to set up interface with %s",
-                  config_file);
-       if (conf)
-               hostapd_config_free(conf);
-       if (hapd_iface) {
-               os_free(hapd_iface->config_fname);
-               os_free(hapd_iface->bss);
-               os_free(hapd_iface);
-       }
-       return NULL;
-}
-
-
 static int hostapd_driver_init(struct hostapd_iface *iface)
 {
        struct wpa_init_params params;
@@ -246,9 +184,7 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
        }
        params.bssid = b;
        params.ifname = hapd->conf->iface;
-       params.ssid = hapd->conf->ssid.ssid;
-       params.ssid_len = hapd->conf->ssid.ssid_len;
-       params.test_socket = hapd->conf->test_socket;
+       params.driver_params = hapd->iconf->driver_params;
        params.use_pae_group_addr = hapd->conf->use_pae_group_addr;
 
        params.num_bridge = hapd->iface->num_bss;
@@ -274,18 +210,38 @@ static int hostapd_driver_init(struct hostapd_iface *iface)
 
        if (hapd->driver->get_capa &&
            hapd->driver->get_capa(hapd->drv_priv, &capa) == 0) {
+               struct wowlan_triggers *triggs;
+
                iface->drv_flags = capa.flags;
+               iface->smps_modes = capa.smps_modes;
                iface->probe_resp_offloads = capa.probe_resp_offloads;
                iface->extended_capa = capa.extended_capa;
                iface->extended_capa_mask = capa.extended_capa_mask;
                iface->extended_capa_len = capa.extended_capa_len;
                iface->drv_max_acl_mac_addrs = capa.max_acl_mac_addrs;
+
+               triggs = wpa_get_wowlan_triggers(conf->wowlan_triggers, &capa);
+               if (triggs && hapd->driver->set_wowlan) {
+                       if (hapd->driver->set_wowlan(hapd->drv_priv, triggs))
+                               wpa_printf(MSG_ERROR, "set_wowlan failed");
+               }
+               os_free(triggs);
        }
+#if defined(TIZEN_WLAN_BOARD_SPRD)
+       hostapd_send_conf_to_driver(hapd);
+#endif
 
        return 0;
 }
 
 
+/**
+ * hostapd_interface_init - Read configuration file and init BSS data
+ *
+ * This function is used to parse configuration file for a full interface (one
+ * or more BSSes sharing the same radio) and allocate memory for the BSS
+ * interfaces. No actiual driver operations are started.
+ */
 static struct hostapd_iface *
 hostapd_interface_init(struct hapd_interfaces *interfaces,
                       const char *config_fname, int debug)
@@ -294,7 +250,7 @@ hostapd_interface_init(struct hapd_interfaces *interfaces,
        int k;
 
        wpa_printf(MSG_ERROR, "Configuration file: %s", config_fname);
-       iface = hostapd_init(config_fname);
+       iface = hostapd_init(interfaces, config_fname);
        if (!iface)
                return NULL;
        iface->interfaces = interfaces;
@@ -304,7 +260,7 @@ hostapd_interface_init(struct hapd_interfaces *interfaces,
                        iface->bss[0]->conf->logger_stdout_level--;
        }
 
-       if (iface->conf->bss[0].iface[0] == '\0' &&
+       if (iface->conf->bss[0]->iface[0] == '\0' &&
            !hostapd_drv_none(iface->bss[0])) {
                wpa_printf(MSG_ERROR, "Interface name not specified in %s",
                           config_fname);
@@ -312,12 +268,6 @@ hostapd_interface_init(struct hapd_interfaces *interfaces,
                return NULL;
        }
 
-       if (hostapd_driver_init(iface) ||
-           hostapd_setup_interface(iface)) {
-               hostapd_interface_deinit_free(iface);
-               return NULL;
-       }
-
        return iface;
 }
 
@@ -358,10 +308,7 @@ static void handle_reload(int sig, void *signal_ctx)
 
 static void handle_dump_state(int sig, void *signal_ctx)
 {
-#ifdef HOSTAPD_DUMP_STATE
-       struct hapd_interfaces *interfaces = signal_ctx;
-       hostapd_for_each_interface(interfaces, handle_dump_state_iface, NULL);
-#endif /* HOSTAPD_DUMP_STATE */
+       /* Not used anymore - ignore signal */
 }
 #endif /* CONFIG_NATIVE_WINDOWS */
 
@@ -464,7 +411,7 @@ static int hostapd_global_run(struct hapd_interfaces *ifaces, int daemonize,
 #endif /* EAP_SERVER_TNC */
 
        if (daemonize && os_daemonize(pid_file)) {
-               perror("daemon");
+               wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
                return -1;
        }
 
@@ -480,7 +427,7 @@ static void show_version(void)
                "hostapd v" VERSION_STR "\n"
                "User space daemon for IEEE 802.11 AP management,\n"
                "IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n"
-               "Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi> "
+               "Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi> "
                "and contributors\n");
 }
 
@@ -507,6 +454,10 @@ static void usage(void)
 #ifdef CONFIG_DEBUG_FILE
                "   -f   log output to debug file instead of stdout\n"
 #endif /* CONFIG_DEBUG_FILE */
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+               "   -T = record to Linux tracing in addition to logging\n"
+               "        (records all messages regardless of debug verbosity)\n"
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
                "   -t   include timestamps in some debug messages\n"
                "   -v   show hostapd version\n");
 
@@ -517,8 +468,9 @@ static void usage(void)
 static const char * hostapd_msg_ifname_cb(void *ctx)
 {
        struct hostapd_data *hapd = ctx;
-       if (hapd && hapd->iconf && hapd->iconf->bss)
-               return hapd->iconf->bss->iface;
+       if (hapd && hapd->iconf && hapd->iconf->bss &&
+           hapd->iconf->num_bss > 0 && hapd->iconf->bss[0])
+               return hapd->iconf->bss[0]->iface;
        return NULL;
 }
 
@@ -563,15 +515,41 @@ static int hostapd_get_ctrl_iface_group(struct hapd_interfaces *interfaces,
 }
 
 
+#ifdef CONFIG_WPS
+static int gen_uuid(const char *txt_addr)
+{
+       u8 addr[ETH_ALEN];
+       u8 uuid[UUID_LEN];
+       char buf[100];
+
+       if (hwaddr_aton(txt_addr, addr) < 0)
+               return -1;
+
+       uuid_gen_mac_addr(addr, uuid);
+       if (uuid_bin2str(uuid, buf, sizeof(buf)) < 0)
+               return -1;
+
+       printf("%s\n", buf);
+
+       return 0;
+}
+#endif /* CONFIG_WPS */
+
+
 int main(int argc, char *argv[])
 {
        struct hapd_interfaces interfaces;
        int ret = 1;
-       size_t i;
+       size_t i, j;
        int c, debug = 0, daemonize = 0;
        char *pid_file = NULL;
        const char *log_file = NULL;
        const char *entropy_file = NULL;
+       char **bss_config = NULL, **tmp_bss;
+       size_t num_bss_configs = 0;
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+       int enable_trace_dbg = 0;
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
 
        if (os_program_init())
                return -1;
@@ -588,7 +566,7 @@ int main(int argc, char *argv[])
        interfaces.global_ctrl_sock = -1;
 
        for (;;) {
-               c = getopt(argc, argv, "Bde:f:hKP:tvg:G:");
+               c = getopt(argc, argv, "b:Bde:f:hKP:Ttu:vg:G:");
                if (c < 0)
                        break;
                switch (c) {
@@ -619,6 +597,11 @@ int main(int argc, char *argv[])
                case 't':
                        wpa_debug_timestamp++;
                        break;
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+               case 'T':
+                       enable_trace_dbg = 1;
+                       break;
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
                case 'v':
                        show_version();
                        exit(1);
@@ -631,23 +614,48 @@ int main(int argc, char *argv[])
                        if (hostapd_get_ctrl_iface_group(&interfaces, optarg))
                                return -1;
                        break;
+               case 'b':
+                       tmp_bss = os_realloc_array(bss_config,
+                                                  num_bss_configs + 1,
+                                                  sizeof(char *));
+                       if (tmp_bss == NULL)
+                               goto out;
+                       bss_config = tmp_bss;
+                       bss_config[num_bss_configs++] = optarg;
+                       break;
+#ifdef CONFIG_WPS
+               case 'u':
+                       return gen_uuid(optarg);
+#endif /* CONFIG_WPS */
                default:
                        usage();
                        break;
                }
        }
 
-       if (optind == argc && interfaces.global_iface_path == NULL)
+       if (optind == argc && interfaces.global_iface_path == NULL &&
+           num_bss_configs == 0)
                usage();
 
        wpa_msg_register_ifname_cb(hostapd_msg_ifname_cb);
 
        if (log_file)
                wpa_debug_open_file(log_file);
+       else
+               wpa_debug_setup_stdout();
+#ifdef CONFIG_DEBUG_LINUX_TRACING
+       if (enable_trace_dbg) {
+               int tret = wpa_debug_open_linux_tracing();
+               if (tret) {
+                       wpa_printf(MSG_ERROR, "Failed to enable trace logging");
+                       return -1;
+               }
+       }
+#endif /* CONFIG_DEBUG_LINUX_TRACING */
 
        interfaces.count = argc - optind;
-       if (interfaces.count) {
-               interfaces.iface = os_calloc(interfaces.count,
+       if (interfaces.count || num_bss_configs) {
+               interfaces.iface = os_calloc(interfaces.count + num_bss_configs,
                                             sizeof(struct hostapd_iface *));
                if (interfaces.iface == NULL) {
                        wpa_printf(MSG_ERROR, "malloc failed");
@@ -660,7 +668,7 @@ int main(int argc, char *argv[])
                return -1;
        }
 
-       /* Initialize interfaces */
+       /* Allocate and parse configuration for full interface files */
        for (i = 0; i < interfaces.count; i++) {
                interfaces.iface[i] = hostapd_interface_init(&interfaces,
                                                             argv[optind + i],
@@ -671,6 +679,57 @@ int main(int argc, char *argv[])
                }
        }
 
+       /* Allocate and parse configuration for per-BSS files */
+       for (i = 0; i < num_bss_configs; i++) {
+               struct hostapd_iface *iface;
+               char *fname;
+
+               wpa_printf(MSG_INFO, "BSS config: %s", bss_config[i]);
+               fname = os_strchr(bss_config[i], ':');
+               if (fname == NULL) {
+                       wpa_printf(MSG_ERROR,
+                                  "Invalid BSS config identifier '%s'",
+                                  bss_config[i]);
+                       goto out;
+               }
+               *fname++ = '\0';
+               iface = hostapd_interface_init_bss(&interfaces, bss_config[i],
+                                                  fname, debug);
+               if (iface == NULL)
+                       goto out;
+               for (j = 0; j < interfaces.count; j++) {
+                       if (interfaces.iface[j] == iface)
+                               break;
+               }
+               if (j == interfaces.count) {
+                       struct hostapd_iface **tmp;
+                       tmp = os_realloc_array(interfaces.iface,
+                                              interfaces.count + 1,
+                                              sizeof(struct hostapd_iface *));
+                       if (tmp == NULL) {
+                               hostapd_interface_deinit_free(iface);
+                               goto out;
+                       }
+                       interfaces.iface = tmp;
+                       interfaces.iface[interfaces.count++] = iface;
+               }
+       }
+
+       /*
+        * Enable configured interfaces. Depending on channel configuration,
+        * this may complete full initialization before returning or use a
+        * callback mechanism to complete setup in case of operations like HT
+        * co-ex scans, ACS, or DFS are needed to determine channel parameters.
+        * In such case, the interface will be enabled from eloop context within
+        * hostapd_global_run().
+        */
+       interfaces.terminate_on_error = interfaces.count;
+       for (i = 0; i < interfaces.count; i++) {
+               if (hostapd_driver_init(interfaces.iface[i]) ||
+                   hostapd_setup_interface(interfaces.iface[i]))
+                       goto out;
+       }
+
        hostapd_global_ctrl_iface_init(&interfaces);
 
        if (hostapd_global_run(&interfaces, daemonize, pid_file)) {
@@ -683,8 +742,14 @@ int main(int argc, char *argv[])
  out:
        hostapd_global_ctrl_iface_deinit(&interfaces);
        /* Deinitialize all interfaces */
-       for (i = 0; i < interfaces.count; i++)
+       for (i = 0; i < interfaces.count; i++) {
+               if (!interfaces.iface[i])
+                       continue;
+               interfaces.iface[i]->driver_ap_teardown =
+                       !!(interfaces.iface[i]->drv_flags &
+                          WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
                hostapd_interface_deinit_free(interfaces.iface[i]);
+       }
        os_free(interfaces.iface);
 
        hostapd_global_deinit(pid_file);
@@ -692,6 +757,9 @@ int main(int argc, char *argv[])
 
        if (log_file)
                wpa_debug_close_file();
+       wpa_debug_close_linux_tracing();
+
+       os_free(bss_config);
 
        os_program_deinit();
 
index 61b5519..2fc3012 100755 (executable)
@@ -9,6 +9,7 @@
 import os
 import sys
 import time
+import argparse
 
 import nfc
 import nfc.ndef
@@ -16,11 +17,25 @@ import nfc.llcp
 import nfc.handover
 
 import logging
-logging.basicConfig()
 
 import wpaspy
 
 wpas_ctrl = '/var/run/hostapd'
+continue_loop = True
+summary_file = None
+success_file = None
+
+def summary(txt):
+    print txt
+    if summary_file:
+        with open(summary_file, 'a') as f:
+            f.write(txt + "\n")
+
+def success_report(txt):
+    summary(txt)
+    if success_file:
+        with open(success_file, 'a') as f:
+            f.write(txt + "\n")
 
 def wpas_connect():
     ifaces = []
@@ -47,29 +62,40 @@ def wpas_connect():
 def wpas_tag_read(message):
     wpas = wpas_connect()
     if (wpas == None):
-        return
-    print wpas.request("WPS_NFC_TAG_READ " + message.encode("hex"))
+        return False
+    if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")):
+        return False
+    return True
 
 
 def wpas_get_config_token():
     wpas = wpas_connect()
     if (wpas == None):
         return None
-    return wpas.request("WPS_NFC_CONFIG_TOKEN NDEF").rstrip().decode("hex")
+    ret = wpas.request("WPS_NFC_CONFIG_TOKEN NDEF")
+    if "FAIL" in ret:
+        return None
+    return ret.rstrip().decode("hex")
 
 
 def wpas_get_password_token():
     wpas = wpas_connect()
     if (wpas == None):
         return None
-    return wpas.request("WPS_NFC_TOKEN NDEF").rstrip().decode("hex")
+    ret = wpas.request("WPS_NFC_TOKEN NDEF")
+    if "FAIL" in ret:
+        return None
+    return ret.rstrip().decode("hex")
 
 
 def wpas_get_handover_sel():
     wpas = wpas_connect()
     if (wpas == None):
         return None
-    return wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip().decode("hex")
+    ret = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR")
+    if "FAIL" in ret:
+        return None
+    return ret.rstrip().decode("hex")
 
 
 def wpas_report_handover(req, sel):
@@ -82,185 +108,228 @@ def wpas_report_handover(req, sel):
 
 
 class HandoverServer(nfc.handover.HandoverServer):
-    def __init__(self):
-        super(HandoverServer, self).__init__()
+    def __init__(self, llc):
+        super(HandoverServer, self).__init__(llc)
+        self.ho_server_processing = False
+        self.success = False
+
+    # override to avoid parser error in request/response.pretty() in nfcpy
+    # due to new WSC handover format
+    def _process_request(self, request):
+        summary("received handover request {}".format(request.type))
+        response = nfc.ndef.Message("\xd1\x02\x01Hs\x12")
+        if not request.type == 'urn:nfc:wkt:Hr':
+            summary("not a handover request")
+        else:
+            try:
+                request = nfc.ndef.HandoverRequestMessage(request)
+            except nfc.ndef.DecodeError as e:
+                summary("error decoding 'Hr' message: {}".format(e))
+            else:
+                response = self.process_request(request)
+        summary("send handover response {}".format(response.type))
+        return response
 
     def process_request(self, request):
-        print "HandoverServer - request received"
-        print "Parsed handover request: " + request.pretty()
+        summary("HandoverServer - request received")
+        try:
+            print "Parsed handover request: " + request.pretty()
+        except Exception, e:
+            print e
+        print str(request).encode("hex")
 
         sel = nfc.ndef.HandoverSelectMessage(version="1.2")
 
         for carrier in request.carriers:
             print "Remote carrier type: " + carrier.type
             if carrier.type == "application/vnd.wfa.wsc":
-                print "WPS carrier type match - add WPS carrier record"
-                self.received_carrier = carrier.record
+                summary("WPS carrier type match - add WPS carrier record")
                 data = wpas_get_handover_sel()
                 if data is None:
-                    print "Could not get handover select carrier record from hostapd"
+                    summary("Could not get handover select carrier record from hostapd")
                     continue
                 print "Handover select carrier record from hostapd:"
                 print data.encode("hex")
-                self.sent_carrier = data
+                if "OK" in wpas_report_handover(carrier.record, data):
+                    success_report("Handover reported successfully")
+                else:
+                    summary("Handover report rejected")
 
                 message = nfc.ndef.Message(data);
                 sel.add_carrier(message[0], "active", message[1:])
 
         print "Handover select:"
-        print sel.pretty()
+        try:
+            print sel.pretty()
+        except Exception, e:
+            print e
         print str(sel).encode("hex")
 
-        print "Sending handover select"
+        summary("Sending handover select")
+        self.success = True
         return sel
 
 
-def wps_handover_resp(peer):
-    print "Trying to handle WPS handover"
-
-    srv = HandoverServer()
-    srv.sent_carrier = None
-
-    nfc.llcp.activate(peer);
-
-    try:
-        print "Trying handover";
-        srv.start()
-        print "Wait for disconnect"
-        while nfc.llcp.connected():
-            time.sleep(0.1)
-        print "Disconnected after handover"
-    except nfc.llcp.ConnectRefused:
-        print "Handover connection refused"
-        nfc.llcp.shutdown()
-        return
-
-    if srv.sent_carrier:
-        wpas_report_handover(srv.received_carrier, srv.sent_carrier)
-
-    print "Remove peer"
-    nfc.llcp.shutdown()
-    print "Done with handover"
-
-
 def wps_tag_read(tag):
+    success = False
     if len(tag.ndef.message):
-        message = nfc.ndef.Message(tag.ndef.message)
-        print "message type " + message.type
-
-        for record in message:
+        for record in tag.ndef.message:
             print "record type " + record.type
             if record.type == "application/vnd.wfa.wsc":
-                print "WPS tag - send to hostapd"
-                wpas_tag_read(tag.ndef.message)
+                summary("WPS tag - send to hostapd")
+                success = wpas_tag_read(tag.ndef.message)
                 break
     else:
-        print "Empty tag"
+        summary("Empty tag")
 
-    print "Remove tag"
-    while tag.is_present:
-        time.sleep(0.1)
+    if success:
+        success_report("Tag read succeeded")
 
+    return success
 
-def wps_write_config_tag(clf):
-    print "Write WPS config token"
-    data = wpas_get_config_token()
-    if (data == None):
-        print "Could not get WPS config token from hostapd"
-        return
 
-    print "Touch an NFC tag"
-    while True:
-        tag = clf.poll()
-        if tag == None:
-            time.sleep(0.1)
-            continue
-        break
-
-    print "Tag found - writing"
-    tag.ndef.message = data
+def rdwr_connected_write(tag):
+    summary("Tag found - writing - " + str(tag))
+    global write_data
+    tag.ndef.message = str(write_data)
+    success_report("Tag write succeeded")
     print "Done - remove tag"
-    while tag.is_present:
+    global only_one
+    if only_one:
+        global continue_loop
+        continue_loop = False
+    global write_wait_remove
+    while write_wait_remove and tag.is_present:
         time.sleep(0.1)
 
+def wps_write_config_tag(clf, wait_remove=True):
+    summary("Write WPS config token")
+    global write_data, write_wait_remove
+    write_wait_remove = wait_remove
+    write_data = wpas_get_config_token()
+    if write_data == None:
+        summary("Could not get WPS config token from hostapd")
+        return
+
+    print "Touch an NFC tag"
+    clf.connect(rdwr={'on-connect': rdwr_connected_write})
 
-def wps_write_password_tag(clf):
-    print "Write WPS password token"
-    data = wpas_get_password_token()
-    if (data == None):
-        print "Could not get WPS password token from hostapd"
+
+def wps_write_password_tag(clf, wait_remove=True):
+    summary("Write WPS password token")
+    global write_data, write_wait_remove
+    write_wait_remove = wait_remove
+    write_data = wpas_get_password_token()
+    if write_data == None:
+        summary("Could not get WPS password token from hostapd")
         return
 
     print "Touch an NFC tag"
-    while True:
-        tag = clf.poll()
-        if tag == None:
-            time.sleep(0.1)
-            continue
-        break
-
-    print "Tag found - writing"
-    tag.ndef.message = data
-    print "Done - remove tag"
-    while tag.is_present:
-        time.sleep(0.1)
+    clf.connect(rdwr={'on-connect': rdwr_connected_write})
+
+
+def rdwr_connected(tag):
+    global only_one, no_wait
+    summary("Tag connected: " + str(tag))
+
+    if tag.ndef:
+        print "NDEF tag: " + tag.type
+        try:
+            print tag.ndef.message.pretty()
+        except Exception, e:
+            print e
+        success = wps_tag_read(tag)
+        if only_one and success:
+            global continue_loop
+            continue_loop = False
+    else:
+        summary("Not an NDEF tag - remove tag")
+        return True
+
+    return not no_wait
 
 
-def find_peer(clf):
-    while True:
-        if nfc.llcp.connected():
-            print "LLCP connected"
-        general_bytes = nfc.llcp.startup({})
-        peer = clf.listen(ord(os.urandom(1)) + 250, general_bytes)
-        if isinstance(peer, nfc.DEP):
-            print "listen -> DEP";
-            if peer.general_bytes.startswith("Ffm"):
-                print "Found DEP"
-                return peer
-            print "mismatch in general_bytes"
-            print peer.general_bytes
-
-        peer = clf.poll(general_bytes)
-        if isinstance(peer, nfc.DEP):
-            print "poll -> DEP";
-            if peer.general_bytes.startswith("Ffm"):
-                print "Found DEP"
-                return peer
-            print "mismatch in general_bytes"
-            print peer.general_bytes
-
-        if peer:
-            print "Found tag"
-            return peer
+def llcp_startup(clf, llc):
+    print "Start LLCP server"
+    global srv
+    srv = HandoverServer(llc)
+    return llc
+
+def llcp_connected(llc):
+    print "P2P LLCP connected"
+    global wait_connection
+    wait_connection = False
+    global srv
+    srv.start()
+    return True
 
 
 def main():
     clf = nfc.ContactlessFrontend()
 
+    parser = argparse.ArgumentParser(description='nfcpy to hostapd integration for WPS NFC operations')
+    parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
+                        action='store_const', dest='loglevel',
+                        help='verbose debug output')
+    parser.add_argument('-q', const=logging.WARNING, action='store_const',
+                        dest='loglevel', help='be quiet')
+    parser.add_argument('--only-one', '-1', action='store_true',
+                        help='run only one operation and exit')
+    parser.add_argument('--no-wait', action='store_true',
+                        help='do not wait for tag to be removed before exiting')
+    parser.add_argument('--summary',
+                        help='summary file for writing status updates')
+    parser.add_argument('--success',
+                        help='success file for writing success update')
+    parser.add_argument('command', choices=['write-config',
+                                            'write-password'],
+                        nargs='?')
+    args = parser.parse_args()
+
+    global only_one
+    only_one = args.only_one
+
+    global no_wait
+    no_wait = args.no_wait
+
+    if args.summary:
+        global summary_file
+        summary_file = args.summary
+
+    if args.success:
+        global success_file
+        success_file = args.success
+
+    logging.basicConfig(level=args.loglevel)
+
     try:
-        if len(sys.argv) > 1 and sys.argv[1] == "write-config":
-            wps_write_config_tag(clf)
+        if not clf.open("usb"):
+            print "Could not open connection with an NFC device"
             raise SystemExit
 
-        if len(sys.argv) > 1 and sys.argv[1] == "write-password":
-            wps_write_password_tag(clf)
+        if args.command == "write-config":
+            wps_write_config_tag(clf, wait_remove=not args.no_wait)
             raise SystemExit
 
-        while True:
-            print "Waiting for a tag or peer to be touched"
-
-            tag = find_peer(clf)
-            if isinstance(tag, nfc.DEP):
-                wps_handover_resp(tag)
-                continue
-
-            if tag.ndef:
-                wps_tag_read(tag)
-                continue
+        if args.command == "write-password":
+            wps_write_password_tag(clf, wait_remove=not args.no_wait)
+            raise SystemExit
 
-            print "Not an NDEF tag - remove tag"
-            while tag.is_present:
-                time.sleep(0.1)
+        global continue_loop
+        while continue_loop:
+            print "Waiting for a tag or peer to be touched"
+            wait_connection = True
+            try:
+                if not clf.connect(rdwr={'on-connect': rdwr_connected},
+                                   llcp={'on-startup': llcp_startup,
+                                         'on-connect': llcp_connected}):
+                    break
+            except Exception, e:
+                print "clf.connect failed"
+
+            global srv
+            if only_one and srv and srv.success:
+                raise SystemExit
 
     except KeyboardInterrupt:
         raise SystemExit
diff --git a/hs20/client/Android.mk b/hs20/client/Android.mk
new file mode 100644 (file)
index 0000000..b23ac17
--- /dev/null
@@ -0,0 +1,81 @@
+LOCAL_PATH := $(call my-dir)
+
+INCLUDES = $(LOCAL_PATH)
+INCLUDES += $(LOCAL_PATH)/../../src/utils
+INCLUDES += $(LOCAL_PATH)/../../src/common
+INCLUDES += $(LOCAL_PATH)/../../src
+INCLUDES += external/openssl/include
+INCLUDES += external/libxml2/include
+INCLUDES += external/curl/include
+INCLUDES += external/webkit/Source/WebKit/gtk
+
+# We try to keep this compiling against older platform versions.
+# The new icu location (external/icu) exports its own headers, but
+# the older versions in external/icu4c don't, and we need to add those
+# headers to the include path by hand.
+ifeq ($(wildcard external/icu),)
+INCLUDES += external/icu4c/common
+else
+# The LOCAL_EXPORT_C_INCLUDE_DIRS from ICU did not seem to fully resolve the
+# build (e.g., "mm -B" failed to build, but following that with "mm" allowed
+# the build to complete). For now, add the include directory manually here for
+# Android 5.0.
+ver = $(filter 5.0%,$(PLATFORM_VERSION))
+ifneq (,$(strip $(ver)))
+INCLUDES += external/icu/icu4c/source/common
+endif
+endif
+
+
+L_CFLAGS += -DCONFIG_CTRL_IFACE
+L_CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
+L_CFLAGS += -DCONFIG_CTRL_IFACE_CLIENT_DIR=\"/data/misc/wifi/sockets\"
+
+OBJS = spp_client.c
+OBJS += oma_dm_client.c
+OBJS += osu_client.c
+OBJS += est.c
+OBJS += ../../src/common/wpa_ctrl.c
+OBJS += ../../src/common/wpa_helpers.c
+OBJS += ../../src/utils/xml-utils.c
+#OBJS += ../../src/utils/browser-android.c
+OBJS += ../../src/utils/browser-wpadebug.c
+OBJS += ../../src/utils/wpabuf.c
+OBJS += ../../src/utils/eloop.c
+OBJS += ../../src/wps/httpread.c
+OBJS += ../../src/wps/http_server.c
+OBJS += ../../src/utils/xml_libxml2.c
+OBJS += ../../src/utils/http_curl.c
+OBJS += ../../src/utils/base64.c
+OBJS += ../../src/utils/os_unix.c
+L_CFLAGS += -DCONFIG_DEBUG_FILE
+OBJS += ../../src/utils/wpa_debug.c
+OBJS += ../../src/utils/common.c
+OBJS += ../../src/crypto/crypto_internal.c
+OBJS += ../../src/crypto/md5-internal.c
+OBJS += ../../src/crypto/sha1-internal.c
+OBJS += ../../src/crypto/sha256-internal.c
+
+L_CFLAGS += -DEAP_TLS_OPENSSL
+
+L_CFLAGS += -Wno-unused-parameter
+
+
+########################
+include $(CLEAR_VARS)
+LOCAL_MODULE := hs20-osu-client
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SHARED_LIBRARIES := libc libcutils
+LOCAL_SHARED_LIBRARIES += libcrypto libssl
+#LOCAL_SHARED_LIBRARIES += libxml2
+LOCAL_STATIC_LIBRARIES += libxml2
+LOCAL_SHARED_LIBRARIES += libicuuc
+LOCAL_SHARED_LIBRARIES += libcurl
+
+LOCAL_CFLAGS := $(L_CFLAGS)
+LOCAL_SRC_FILES := $(OBJS)
+LOCAL_C_INCLUDES := $(INCLUDES)
+include $(BUILD_EXECUTABLE)
+
+########################
diff --git a/hs20/client/Makefile b/hs20/client/Makefile
new file mode 100644 (file)
index 0000000..ca67b54
--- /dev/null
@@ -0,0 +1,94 @@
+all: hs20-osu-client
+
+ifndef CC
+CC=gcc
+endif
+
+ifndef LDO
+LDO=$(CC)
+endif
+
+Q=@
+E=echo
+ifeq ($(V), 1)
+Q=
+E=true
+endif
+
+ifndef CFLAGS
+CFLAGS = -MMD -O2 -Wall -g
+endif
+
+CFLAGS += -I../../src/utils
+CFLAGS += -I../../src/common
+CFLAGS += -I../../src
+
+ifndef CONFIG_NO_BROWSER
+ifndef CONFIG_BROWSER_SYSTEM
+GTKCFLAGS := $(shell pkg-config --cflags gtk+-3.0 webkitgtk-3.0)
+GTKLIBS := $(shell pkg-config --libs gtk+-3.0 webkitgtk-3.0)
+CFLAGS += $(GTKCFLAGS)
+LIBS += $(GTKLIBS)
+endif
+endif
+
+OBJS=spp_client.o
+OBJS += oma_dm_client.o
+OBJS += osu_client.o
+OBJS += est.o
+OBJS += ../../src/utils/xml-utils.o
+CFLAGS += -DCONFIG_CTRL_IFACE
+CFLAGS += -DCONFIG_CTRL_IFACE_UNIX
+OBJS += ../../src/common/wpa_ctrl.o ../../src/common/wpa_helpers.o
+ifdef CONFIG_NO_BROWSER
+CFLAGS += -DCONFIG_NO_BROWSER
+else
+ifdef CONFIG_BROWSER_SYSTEM
+OBJS += ../../src/utils/eloop.o
+OBJS += ../../src/utils/wpabuf.o
+OBJS += ../../src/wps/httpread.o
+OBJS += ../../src/wps/http_server.o
+OBJS += ../../src/utils/browser-system.o
+else
+OBJS += ../../src/utils/browser.o
+endif
+endif
+OBJS += ../../src/utils/xml_libxml2.o
+OBJS += ../../src/utils/http_curl.o
+OBJS += ../../src/utils/base64.o
+OBJS += ../../src/utils/os_unix.o
+CFLAGS += -DCONFIG_DEBUG_FILE
+OBJS += ../../src/utils/wpa_debug.o
+OBJS += ../../src/utils/common.o
+OBJS += ../../src/crypto/crypto_internal.o
+OBJS += ../../src/crypto/md5-internal.o
+OBJS += ../../src/crypto/sha1-internal.o
+OBJS += ../../src/crypto/sha256-internal.o
+
+CFLAGS += $(shell xml2-config --cflags)
+LIBS += $(shell xml2-config --libs)
+LIBS += -lcurl
+
+CFLAGS += -DEAP_TLS_OPENSSL
+LIBS += -lssl -lcrypto
+
+hs20-osu-client: $(OBJS)
+       $(Q)$(LDO) $(LDFLAGS) -o hs20-osu-client $(OBJS) $(LIBS)
+       @$(E) "  LD " $@
+
+%.o: %.c
+       $(Q)$(CC) -c -o $@ $(CFLAGS) $<
+       @$(E) "  CC " $<
+
+clean:
+       rm -f core *~ *.o *.d hs20-osu-client
+       rm -f ../../src/utils/*.o
+       rm -f ../../src/utils/*.d
+       rm -f ../../src/common/*.o
+       rm -f ../../src/common/*.d
+       rm -f ../../src/crypto/*.o
+       rm -f ../../src/crypto/*.d
+       rm -f ../../src/wps/*.o
+       rm -f ../../src/wps/*.d
+
+-include $(OBJS:%.o=%.d)
diff --git a/hs20/client/devdetail.xml b/hs20/client/devdetail.xml
new file mode 100644 (file)
index 0000000..6d0389e
--- /dev/null
@@ -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/hs20/client/devinfo.xml b/hs20/client/devinfo.xml
new file mode 100644 (file)
index 0000000..d48a520
--- /dev/null
@@ -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/hs20/client/est.c b/hs20/client/est.c
new file mode 100644 (file)
index 0000000..ec05bc4
--- /dev/null
@@ -0,0 +1,715 @@
+/*
+ * Hotspot 2.0 OSU client - EST client
+ * Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs7.h>
+#include <openssl/rsa.h>
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/x509.h>
+#include <openssl/x509v3.h>
+
+#include "common.h"
+#include "utils/base64.h"
+#include "utils/xml-utils.h"
+#include "utils/http-utils.h"
+#include "osu_client.h"
+
+
+static int pkcs7_to_cert(struct hs20_osu_client *ctx, const u8 *pkcs7,
+                        size_t len, char *pem_file, char *der_file)
+{
+       PKCS7 *p7 = NULL;
+       const unsigned char *p = pkcs7;
+       STACK_OF(X509) *certs;
+       int i, num, ret = -1;
+       BIO *out = NULL;
+
+       p7 = d2i_PKCS7(NULL, &p, len);
+       if (p7 == NULL) {
+               wpa_printf(MSG_INFO, "Could not parse PKCS#7 object: %s",
+                          ERR_error_string(ERR_get_error(), NULL));
+               write_result(ctx, "Could not parse PKCS#7 object from EST");
+               goto fail;
+       }
+
+       switch (OBJ_obj2nid(p7->type)) {
+       case NID_pkcs7_signed:
+               certs = p7->d.sign->cert;
+               break;
+       case NID_pkcs7_signedAndEnveloped:
+               certs = p7->d.signed_and_enveloped->cert;
+               break;
+       default:
+               certs = NULL;
+               break;
+       }
+
+       if (!certs || ((num = sk_X509_num(certs)) == 0)) {
+               wpa_printf(MSG_INFO, "No certificates found in PKCS#7 object");
+               write_result(ctx, "No certificates found in PKCS#7 object");
+               goto fail;
+       }
+
+       if (der_file) {
+               FILE *f = fopen(der_file, "wb");
+               if (f == NULL)
+                       goto fail;
+               i2d_X509_fp(f, sk_X509_value(certs, 0));
+               fclose(f);
+       }
+
+       if (pem_file) {
+               out = BIO_new(BIO_s_file());
+               if (out == NULL ||
+                   BIO_write_filename(out, pem_file) <= 0)
+                       goto fail;
+
+               for (i = 0; i < num; i++) {
+                       X509 *cert = sk_X509_value(certs, i);
+                       X509_print(out, cert);
+                       PEM_write_bio_X509(out, cert);
+                       BIO_puts(out, "\n");
+               }
+       }
+
+       ret = 0;
+
+fail:
+       PKCS7_free(p7);
+       if (out)
+               BIO_free_all(out);
+
+       return ret;
+}
+
+
+int est_load_cacerts(struct hs20_osu_client *ctx, const char *url)
+{
+       char *buf, *resp;
+       size_t buflen;
+       unsigned char *pkcs7;
+       size_t pkcs7_len, resp_len;
+       int res;
+
+       buflen = os_strlen(url) + 100;
+       buf = os_malloc(buflen);
+       if (buf == NULL)
+               return -1;
+
+       os_snprintf(buf, buflen, "%s/cacerts", url);
+       wpa_printf(MSG_INFO, "Download EST cacerts from %s", buf);
+       write_summary(ctx, "Download EST cacerts from %s", buf);
+       ctx->no_osu_cert_validation = 1;
+       http_ocsp_set(ctx->http, 1);
+       res = http_download_file(ctx->http, buf, "Cert/est-cacerts.txt",
+                                ctx->ca_fname);
+       http_ocsp_set(ctx->http,
+                     (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
+       ctx->no_osu_cert_validation = 0;
+       if (res < 0) {
+               wpa_printf(MSG_INFO, "Failed to download EST cacerts from %s",
+                          buf);
+               write_result(ctx, "Failed to download EST cacerts from %s",
+                            buf);
+               os_free(buf);
+               return -1;
+       }
+       os_free(buf);
+
+       resp = os_readfile("Cert/est-cacerts.txt", &resp_len);
+       if (resp == NULL) {
+               wpa_printf(MSG_INFO, "Could not read Cert/est-cacerts.txt");
+               write_result(ctx, "Could not read EST cacerts");
+               return -1;
+       }
+
+       pkcs7 = base64_decode((unsigned char *) resp, resp_len, &pkcs7_len);
+       if (pkcs7 && pkcs7_len < resp_len / 2) {
+               wpa_printf(MSG_INFO, "Too short base64 decode (%u bytes; downloaded %u bytes) - assume this was binary",
+                          (unsigned int) pkcs7_len, (unsigned int) resp_len);
+               os_free(pkcs7);
+               pkcs7 = NULL;
+       }
+       if (pkcs7 == NULL) {
+               wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7");
+               pkcs7 = os_malloc(resp_len);
+               if (pkcs7) {
+                       os_memcpy(pkcs7, resp, resp_len);
+                       pkcs7_len = resp_len;
+               }
+       }
+       os_free(resp);
+
+       if (pkcs7 == NULL) {
+               wpa_printf(MSG_INFO, "Could not fetch PKCS7 cacerts");
+               write_result(ctx, "Could not fetch EST PKCS#7 cacerts");
+               return -1;
+       }
+
+       res = pkcs7_to_cert(ctx, pkcs7, pkcs7_len, "Cert/est-cacerts.pem",
+                           NULL);
+       os_free(pkcs7);
+       if (res < 0) {
+               wpa_printf(MSG_INFO, "Could not parse CA certs from PKCS#7 cacerts response");
+               write_result(ctx, "Could not parse CA certs from EST PKCS#7 cacerts response");
+               return -1;
+       }
+       unlink("Cert/est-cacerts.txt");
+
+       return 0;
+}
+
+
+/*
+ * CsrAttrs ::= SEQUENCE SIZE (0..MAX) OF AttrOrOID
+ *
+ * AttrOrOID ::= CHOICE {
+ *   oid OBJECT IDENTIFIER,
+ *   attribute Attribute }
+ *
+ * Attribute ::= SEQUENCE {
+ *   type OBJECT IDENTIFIER,
+ *   values SET SIZE(1..MAX) OF OBJECT IDENTIFIER }
+ */
+
+typedef struct {
+       ASN1_OBJECT *type;
+       STACK_OF(ASN1_OBJECT) *values;
+} Attribute;
+
+typedef struct {
+       int type;
+       union {
+               ASN1_OBJECT *oid;
+               Attribute *attribute;
+       } d;
+} AttrOrOID;
+
+typedef struct {
+       int type;
+       STACK_OF(AttrOrOID) *attrs;
+} CsrAttrs;
+
+ASN1_SEQUENCE(Attribute) = {
+       ASN1_SIMPLE(Attribute, type, ASN1_OBJECT),
+       ASN1_SET_OF(Attribute, values, ASN1_OBJECT)
+} ASN1_SEQUENCE_END(Attribute);
+
+ASN1_CHOICE(AttrOrOID) = {
+       ASN1_SIMPLE(AttrOrOID, d.oid, ASN1_OBJECT),
+       ASN1_SIMPLE(AttrOrOID, d.attribute, Attribute)
+} ASN1_CHOICE_END(AttrOrOID);
+
+ASN1_CHOICE(CsrAttrs) = {
+       ASN1_SEQUENCE_OF(CsrAttrs, attrs, AttrOrOID)
+} ASN1_CHOICE_END(CsrAttrs);
+
+IMPLEMENT_ASN1_FUNCTIONS(CsrAttrs);
+
+
+static void add_csrattrs_oid(struct hs20_osu_client *ctx, ASN1_OBJECT *oid,
+                            STACK_OF(X509_EXTENSION) *exts)
+{
+       char txt[100];
+       int res;
+
+       if (!oid)
+               return;
+
+       res = OBJ_obj2txt(txt, sizeof(txt), oid, 1);
+       if (res < 0 || res >= (int) sizeof(txt))
+               return;
+
+       if (os_strcmp(txt, "1.2.840.113549.1.9.7") == 0) {
+               wpa_printf(MSG_INFO, "TODO: csrattr challengePassword");
+       } else if (os_strcmp(txt, "1.2.840.113549.1.1.11") == 0) {
+               wpa_printf(MSG_INFO, "csrattr sha256WithRSAEncryption");
+       } else {
+               wpa_printf(MSG_INFO, "Ignore unsupported csrattr oid %s", txt);
+       }
+}
+
+
+static void add_csrattrs_ext_req(struct hs20_osu_client *ctx,
+                                STACK_OF(ASN1_OBJECT) *values,
+                                STACK_OF(X509_EXTENSION) *exts)
+{
+       char txt[100];
+       int i, num, res;
+
+       num = sk_ASN1_OBJECT_num(values);
+       for (i = 0; i < num; i++) {
+               ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(values, i);
+
+               res = OBJ_obj2txt(txt, sizeof(txt), oid, 1);
+               if (res < 0 || res >= (int) sizeof(txt))
+                       continue;
+
+               if (os_strcmp(txt, "1.3.6.1.1.1.1.22") == 0) {
+                       wpa_printf(MSG_INFO, "TODO: extReq macAddress");
+               } else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.3") == 0) {
+                       wpa_printf(MSG_INFO, "TODO: extReq imei");
+               } else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.4") == 0) {
+                       wpa_printf(MSG_INFO, "TODO: extReq meid");
+               } else if (os_strcmp(txt, "1.3.6.1.4.1.40808.1.1.5") == 0) {
+                       wpa_printf(MSG_INFO, "TODO: extReq DevId");
+               } else {
+                       wpa_printf(MSG_INFO, "Ignore unsupported cstattr extensionsRequest %s",
+                                  txt);
+               }
+       }
+}
+
+
+static void add_csrattrs_attr(struct hs20_osu_client *ctx, Attribute *attr,
+                             STACK_OF(X509_EXTENSION) *exts)
+{
+       char txt[100], txt2[100];
+       int i, num, res;
+
+       if (!attr || !attr->type || !attr->values)
+               return;
+
+       res = OBJ_obj2txt(txt, sizeof(txt), attr->type, 1);
+       if (res < 0 || res >= (int) sizeof(txt))
+               return;
+
+       if (os_strcmp(txt, "1.2.840.113549.1.9.14") == 0) {
+               add_csrattrs_ext_req(ctx, attr->values, exts);
+               return;
+       }
+
+       num = sk_ASN1_OBJECT_num(attr->values);
+       for (i = 0; i < num; i++) {
+               ASN1_OBJECT *oid = sk_ASN1_OBJECT_value(attr->values, i);
+
+               res = OBJ_obj2txt(txt2, sizeof(txt2), oid, 1);
+               if (res < 0 || res >= (int) sizeof(txt2))
+                       continue;
+
+               wpa_printf(MSG_INFO, "Ignore unsupported cstattr::attr %s oid %s",
+                          txt, txt2);
+       }
+}
+
+
+static void add_csrattrs(struct hs20_osu_client *ctx, CsrAttrs *csrattrs,
+                        STACK_OF(X509_EXTENSION) *exts)
+{
+       int i, num;
+
+       if (!csrattrs || ! csrattrs->attrs)
+               return;
+
+       num = SKM_sk_num(AttrOrOID, csrattrs->attrs);
+       for (i = 0; i < num; i++) {
+               AttrOrOID *ao = SKM_sk_value(AttrOrOID, csrattrs->attrs, i);
+               switch (ao->type) {
+               case 0:
+                       add_csrattrs_oid(ctx, ao->d.oid, exts);
+                       break;
+               case 1:
+                       add_csrattrs_attr(ctx, ao->d.attribute, exts);
+                       break;
+               }
+       }
+}
+
+
+static int generate_csr(struct hs20_osu_client *ctx, char *key_pem,
+                       char *csr_pem, char *est_req, char *old_cert,
+                       CsrAttrs *csrattrs)
+{
+       EVP_PKEY_CTX *pctx = NULL;
+       EVP_PKEY *pkey = NULL;
+       RSA *rsa;
+       X509_REQ *req = NULL;
+       int ret = -1;
+       unsigned int val;
+       X509_NAME *subj = NULL;
+       char name[100];
+       STACK_OF(X509_EXTENSION) *exts = NULL;
+       X509_EXTENSION *ex;
+       BIO *out;
+
+       wpa_printf(MSG_INFO, "Generate RSA private key");
+       write_summary(ctx, "Generate RSA private key");
+       pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
+       if (!pctx)
+               return -1;
+
+       if (EVP_PKEY_keygen_init(pctx) <= 0)
+               goto fail;
+
+       if (EVP_PKEY_CTX_set_rsa_keygen_bits(pctx, 2048) <= 0)
+               goto fail;
+
+       if (EVP_PKEY_keygen(pctx, &pkey) <= 0)
+               goto fail;
+       EVP_PKEY_CTX_free(pctx);
+       pctx = NULL;
+
+       rsa = EVP_PKEY_get1_RSA(pkey);
+       if (rsa == NULL)
+               goto fail;
+
+       if (key_pem) {
+               FILE *f = fopen(key_pem, "wb");
+               if (f == NULL)
+                       goto fail;
+               if (!PEM_write_RSAPrivateKey(f, rsa, NULL, NULL, 0, NULL,
+                                            NULL)) {
+                       wpa_printf(MSG_INFO, "Could not write private key: %s",
+                                  ERR_error_string(ERR_get_error(), NULL));
+                       fclose(f);
+                       goto fail;
+               }
+               fclose(f);
+       }
+
+       wpa_printf(MSG_INFO, "Generate CSR");
+       write_summary(ctx, "Generate CSR");
+       req = X509_REQ_new();
+       if (req == NULL)
+               goto fail;
+
+       if (old_cert) {
+               FILE *f;
+               X509 *cert;
+               int res;
+
+               f = fopen(old_cert, "r");
+               if (f == NULL)
+                       goto fail;
+               cert = PEM_read_X509(f, NULL, NULL, NULL);
+               fclose(f);
+
+               if (cert == NULL)
+                       goto fail;
+               res = X509_REQ_set_subject_name(req,
+                                               X509_get_subject_name(cert));
+               X509_free(cert);
+               if (!res)
+                       goto fail;
+       } else {
+               os_get_random((u8 *) &val, sizeof(val));
+               os_snprintf(name, sizeof(name), "cert-user-%u", val);
+               subj = X509_NAME_new();
+               if (subj == NULL ||
+                   !X509_NAME_add_entry_by_txt(subj, "CN", MBSTRING_ASC,
+                                               (unsigned char *) name,
+                                               -1, -1, 0) ||
+                   !X509_REQ_set_subject_name(req, subj))
+                       goto fail;
+               X509_NAME_free(subj);
+               subj = NULL;
+       }
+
+       if (!X509_REQ_set_pubkey(req, pkey))
+               goto fail;
+
+       exts = sk_X509_EXTENSION_new_null();
+       if (!exts)
+               goto fail;
+
+       ex = X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints,
+                                "CA:FALSE");
+       if (ex == NULL ||
+           !sk_X509_EXTENSION_push(exts, ex))
+               goto fail;
+
+       ex = X509V3_EXT_conf_nid(NULL, NULL, NID_key_usage,
+                                "nonRepudiation,digitalSignature,keyEncipherment");
+       if (ex == NULL ||
+           !sk_X509_EXTENSION_push(exts, ex))
+               goto fail;
+
+       ex = X509V3_EXT_conf_nid(NULL, NULL, NID_ext_key_usage,
+                                "1.3.6.1.4.1.40808.1.1.2");
+       if (ex == NULL ||
+           !sk_X509_EXTENSION_push(exts, ex))
+               goto fail;
+
+       add_csrattrs(ctx, csrattrs, exts);
+
+       if (!X509_REQ_add_extensions(req, exts))
+               goto fail;
+       sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
+       exts = NULL;
+
+       if (!X509_REQ_sign(req, pkey, EVP_sha256()))
+               goto fail;
+
+       out = BIO_new(BIO_s_mem());
+       if (out) {
+               char *txt;
+               size_t rlen;
+
+               X509_REQ_print(out, req);
+               rlen = BIO_ctrl_pending(out);
+               txt = os_malloc(rlen + 1);
+               if (txt) {
+                       int res = BIO_read(out, txt, rlen);
+                       if (res > 0) {
+                               txt[res] = '\0';
+                               wpa_printf(MSG_MSGDUMP, "OpenSSL: Certificate request:\n%s",
+                                          txt);
+                       }
+                       os_free(txt);
+               }
+               BIO_free(out);
+       }
+
+       if (csr_pem) {
+               FILE *f = fopen(csr_pem, "w");
+               if (f == NULL)
+                       goto fail;
+               X509_REQ_print_fp(f, req);
+               if (!PEM_write_X509_REQ(f, req)) {
+                       fclose(f);
+                       goto fail;
+               }
+               fclose(f);
+       }
+
+       if (est_req) {
+               BIO *mem = BIO_new(BIO_s_mem());
+               BUF_MEM *ptr;
+               char *pos, *end, *buf_end;
+               FILE *f;
+
+               if (mem == NULL)
+                       goto fail;
+               if (!PEM_write_bio_X509_REQ(mem, req)) {
+                       BIO_free(mem);
+                       goto fail;
+               }
+
+               BIO_get_mem_ptr(mem, &ptr);
+               pos = ptr->data;
+               buf_end = pos + ptr->length;
+
+               /* Remove START/END lines */
+               while (pos < buf_end && *pos != '\n')
+                       pos++;
+               if (pos == buf_end) {
+                       BIO_free(mem);
+                       goto fail;
+               }
+               pos++;
+
+               end = pos;
+               while (end < buf_end && *end != '-')
+                       end++;
+
+               f = fopen(est_req, "w");
+               if (f == NULL) {
+                       BIO_free(mem);
+                       goto fail;
+               }
+               fwrite(pos, end - pos, 1, f);
+               fclose(f);
+
+               BIO_free(mem);
+       }
+
+       ret = 0;
+fail:
+       if (exts)
+               sk_X509_EXTENSION_pop_free(exts, X509_EXTENSION_free);
+       if (subj)
+               X509_NAME_free(subj);
+       if (req)
+               X509_REQ_free(req);
+       if (pkey)
+               EVP_PKEY_free(pkey);
+       if (pctx)
+               EVP_PKEY_CTX_free(pctx);
+       return ret;
+}
+
+
+int est_build_csr(struct hs20_osu_client *ctx, const char *url)
+{
+       char *buf;
+       size_t buflen;
+       int res;
+       char old_cert_buf[200];
+       char *old_cert = NULL;
+       CsrAttrs *csrattrs = NULL;
+
+       buflen = os_strlen(url) + 100;
+       buf = os_malloc(buflen);
+       if (buf == NULL)
+               return -1;
+
+       os_snprintf(buf, buflen, "%s/csrattrs", url);
+       wpa_printf(MSG_INFO, "Download csrattrs from %s", buf);
+       write_summary(ctx, "Download EST csrattrs from %s", buf);
+       ctx->no_osu_cert_validation = 1;
+       http_ocsp_set(ctx->http, 1);
+       res = http_download_file(ctx->http, buf, "Cert/est-csrattrs.txt",
+                                ctx->ca_fname);
+       http_ocsp_set(ctx->http,
+                     (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
+       ctx->no_osu_cert_validation = 0;
+       os_free(buf);
+       if (res < 0) {
+               wpa_printf(MSG_INFO, "Failed to download EST csrattrs - assume no extra attributes are needed");
+       } else {
+               size_t resp_len;
+               char *resp;
+               unsigned char *attrs;
+               const unsigned char *pos;
+               size_t attrs_len;
+
+               resp = os_readfile("Cert/est-csrattrs.txt", &resp_len);
+               if (resp == NULL) {
+                       wpa_printf(MSG_INFO, "Could not read csrattrs");
+                       return -1;
+               }
+
+               attrs = base64_decode((unsigned char *) resp, resp_len,
+                                     &attrs_len);
+               os_free(resp);
+
+               if (attrs == NULL) {
+                       wpa_printf(MSG_INFO, "Could not base64 decode csrattrs");
+                       return -1;
+               }
+               unlink("Cert/est-csrattrs.txt");
+
+               pos = attrs;
+               csrattrs = d2i_CsrAttrs(NULL, &pos, attrs_len);
+               os_free(attrs);
+               if (csrattrs == NULL) {
+                       wpa_printf(MSG_INFO, "Failed to parse csrattrs ASN.1");
+                       /* Continue assuming no additional requirements */
+               }
+       }
+
+       if (ctx->client_cert_present) {
+               os_snprintf(old_cert_buf, sizeof(old_cert_buf),
+                           "SP/%s/client-cert.pem", ctx->fqdn);
+               old_cert = old_cert_buf;
+       }
+
+       res = generate_csr(ctx, "Cert/privkey-plain.pem", "Cert/est-req.pem",
+                          "Cert/est-req.b64", old_cert, csrattrs);
+       if (csrattrs)
+               CsrAttrs_free(csrattrs);
+
+       return res;
+}
+
+
+int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
+                     const char *user, const char *pw)
+{
+       char *buf, *resp, *req, *req2;
+       size_t buflen, resp_len, len, pkcs7_len;
+       unsigned char *pkcs7;
+       FILE *f;
+       char client_cert_buf[200];
+       char client_key_buf[200];
+       const char *client_cert = NULL, *client_key = NULL;
+       int res;
+
+       req = os_readfile("Cert/est-req.b64", &len);
+       if (req == NULL) {
+               wpa_printf(MSG_INFO, "Could not read Cert/req.b64");
+               return -1;
+       }
+       req2 = os_realloc(req, len + 1);
+       if (req2 == NULL) {
+               os_free(req);
+               return -1;
+       }
+       req2[len] = '\0';
+       req = req2;
+       wpa_printf(MSG_DEBUG, "EST simpleenroll request: %s", req);
+
+       buflen = os_strlen(url) + 100;
+       buf = os_malloc(buflen);
+       if (buf == NULL) {
+               os_free(req);
+               return -1;
+       }
+
+       if (ctx->client_cert_present) {
+               os_snprintf(buf, buflen, "%s/simplereenroll", url);
+               os_snprintf(client_cert_buf, sizeof(client_cert_buf),
+                           "SP/%s/client-cert.pem", ctx->fqdn);
+               client_cert = client_cert_buf;
+               os_snprintf(client_key_buf, sizeof(client_key_buf),
+                           "SP/%s/client-key.pem", ctx->fqdn);
+               client_key = client_key_buf;
+       } else
+               os_snprintf(buf, buflen, "%s/simpleenroll", url);
+       wpa_printf(MSG_INFO, "EST simpleenroll URL: %s", buf);
+       write_summary(ctx, "EST simpleenroll URL: %s", buf);
+       ctx->no_osu_cert_validation = 1;
+       http_ocsp_set(ctx->http, 1);
+       resp = http_post(ctx->http, buf, req, "application/pkcs10",
+                        "Content-Transfer-Encoding: base64",
+                        ctx->ca_fname, user, pw, client_cert, client_key,
+                        &resp_len);
+       http_ocsp_set(ctx->http,
+                     (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
+       ctx->no_osu_cert_validation = 0;
+       os_free(buf);
+       if (resp == NULL) {
+               wpa_printf(MSG_INFO, "EST certificate enrollment failed");
+               write_result(ctx, "EST certificate enrollment failed");
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG, "EST simpleenroll response: %s", resp);
+       f = fopen("Cert/est-resp.raw", "w");
+       if (f) {
+               fwrite(resp, resp_len, 1, f);
+               fclose(f);
+       }
+
+       pkcs7 = base64_decode((unsigned char *) resp, resp_len, &pkcs7_len);
+       if (pkcs7 == NULL) {
+               wpa_printf(MSG_INFO, "EST workaround - Could not decode base64, assume this is DER encoded PKCS7");
+               pkcs7 = os_malloc(resp_len);
+               if (pkcs7) {
+                       os_memcpy(pkcs7, resp, resp_len);
+                       pkcs7_len = resp_len;
+               }
+       }
+       os_free(resp);
+
+       if (pkcs7 == NULL) {
+               wpa_printf(MSG_INFO, "Failed to parse simpleenroll base64 response");
+               write_result(ctx, "Failed to parse EST simpleenroll base64 response");
+               return -1;
+       }
+
+       res = pkcs7_to_cert(ctx, pkcs7, pkcs7_len, "Cert/est_cert.pem",
+                           "Cert/est_cert.der");
+       os_free(pkcs7);
+
+       if (res < 0) {
+               wpa_printf(MSG_INFO, "EST: Failed to extract certificate from PKCS7 file");
+               write_result(ctx, "EST: Failed to extract certificate from EST PKCS7 file");
+               return -1;
+       }
+
+       wpa_printf(MSG_INFO, "EST simple%senroll completed successfully",
+                  ctx->client_cert_present ? "re" : "");
+       write_summary(ctx, "EST simple%senroll completed successfully",
+                     ctx->client_cert_present ? "re" : "");
+
+       return 0;
+}
diff --git a/hs20/client/oma_dm_client.c b/hs20/client/oma_dm_client.c
new file mode 100644 (file)
index 0000000..5854b72
--- /dev/null
@@ -0,0 +1,1392 @@
+/*
+ * Hotspot 2.0 - OMA DM client
+ * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "wpa_helpers.h"
+#include "xml-utils.h"
+#include "http-utils.h"
+#include "utils/browser.h"
+#include "osu_client.h"
+
+
+#define DM_SERVER_INITIATED_MGMT 1200
+#define DM_CLIENT_INITIATED_MGMT 1201
+#define DM_GENERIC_ALERT 1226
+
+/* OMA-TS-SyncML-RepPro-V1_2_2 - 10. Response Status Codes */
+#define DM_RESP_OK 200
+#define DM_RESP_AUTH_ACCEPTED 212
+#define DM_RESP_CHUNKED_ITEM_ACCEPTED 213
+#define DM_RESP_NOT_EXECUTED 215
+#define DM_RESP_ATOMIC_ROLL_BACK_OK 216
+#define DM_RESP_NOT_MODIFIED 304
+#define DM_RESP_BAD_REQUEST 400
+#define DM_RESP_UNAUTHORIZED 401
+#define DM_RESP_FORBIDDEN 403
+#define DM_RESP_NOT_FOUND 404
+#define DM_RESP_COMMAND_NOT_ALLOWED 405
+#define DM_RESP_OPTIONAL_FEATURE_NOT_SUPPORTED 406
+#define DM_RESP_MISSING_CREDENTIALS 407
+#define DM_RESP_CONFLICT 409
+#define DM_RESP_GONE 410
+#define DM_RESP_INCOMPLETE_COMMAND 412
+#define DM_RESP_REQ_ENTITY_TOO_LARGE 413
+#define DM_RESP_URI_TOO_LONG 414
+#define DM_RESP_UNSUPPORTED_MEDIA_TYPE_OR_FORMAT 415
+#define DM_RESP_REQ_TOO_BIG 416
+#define DM_RESP_ALREADY_EXISTS 418
+#define DM_RESP_DEVICE_FULL 420
+#define DM_RESP_SIZE_MISMATCH 424
+#define DM_RESP_PERMISSION_DENIED 425
+#define DM_RESP_COMMAND_FAILED 500
+#define DM_RESP_COMMAND_NOT_IMPLEMENTED 501
+#define DM_RESP_ATOMIC_ROLL_BACK_FAILED 516
+
+#define DM_HS20_SUBSCRIPTION_CREATION \
+       "org.wi-fi.hotspot2dot0.SubscriptionCreation"
+#define DM_HS20_SUBSCRIPTION_PROVISIONING \
+       "org.wi-fi.hotspot2dot0.SubscriptionProvisioning"
+#define DM_HS20_SUBSCRIPTION_REMEDIATION \
+       "org.wi-fi.hotspot2dot0.SubscriptionRemediation"
+#define DM_HS20_POLICY_UPDATE \
+       "org.wi-fi.hotspot2dot0.PolicyUpdate"
+
+#define DM_URI_PPS "./Wi-Fi/org.wi-fi/PerProviderSubscription"
+#define DM_URI_LAUNCH_BROWSER \
+       "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/launchBrowserToURI"
+
+
+static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent,
+                    const char *locuri, const char *data);
+
+
+static const char * int2str(int val)
+{
+       static char buf[20];
+       snprintf(buf, sizeof(buf), "%d", val);
+       return buf;
+}
+
+
+static char * oma_dm_get_target_locuri(struct hs20_osu_client *ctx,
+                                      xml_node_t *node)
+{
+       xml_node_t *locuri;
+       char *uri, *ret = NULL;
+
+       locuri = get_node(ctx->xml, node, "Item/Target/LocURI");
+       if (locuri == NULL)
+               return NULL;
+
+       uri = xml_node_get_text(ctx->xml, locuri);
+       if (uri)
+               ret = os_strdup(uri);
+       xml_node_get_text_free(ctx->xml, uri);
+       return ret;
+}
+
+
+static void oma_dm_add_locuri(struct hs20_osu_client *ctx, xml_node_t *parent,
+                             const char *element, const char *uri)
+{
+       xml_node_t *node;
+
+       node = xml_node_create(ctx->xml, parent, NULL, element);
+       if (node == NULL)
+               return;
+       xml_node_create_text(ctx->xml, node, NULL, "LocURI", uri);
+}
+
+
+static xml_node_t * oma_dm_build_hdr(struct hs20_osu_client *ctx,
+                                    const char *url, int msgid)
+{
+       xml_node_t *syncml, *synchdr;
+       xml_namespace_t *ns;
+
+       syncml = xml_node_create_root(ctx->xml, "SYNCML:SYNCML1.2", NULL, &ns,
+                                     "SyncML");
+
+       synchdr = xml_node_create(ctx->xml, syncml, NULL, "SyncHdr");
+       xml_node_create_text(ctx->xml, synchdr, NULL, "VerDTD", "1.2");
+       xml_node_create_text(ctx->xml, synchdr, NULL, "VerProto", "DM/1.2");
+       xml_node_create_text(ctx->xml, synchdr, NULL, "SessionID", "1");
+       xml_node_create_text(ctx->xml, synchdr, NULL, "MsgID", int2str(msgid));
+
+       oma_dm_add_locuri(ctx, synchdr, "Target", url);
+       oma_dm_add_locuri(ctx, synchdr, "Source", ctx->devid);
+
+       return syncml;
+}
+
+
+static void oma_dm_add_cmdid(struct hs20_osu_client *ctx, xml_node_t *parent,
+                            int cmdid)
+{
+       xml_node_create_text(ctx->xml, parent, NULL, "CmdID", int2str(cmdid));
+}
+
+
+static xml_node_t * add_alert(struct hs20_osu_client *ctx, xml_node_t *parent,
+                             int cmdid, int data)
+{
+       xml_node_t *node;
+
+       node = xml_node_create(ctx->xml, parent, NULL, "Alert");
+       if (node == NULL)
+               return NULL;
+       oma_dm_add_cmdid(ctx, node, cmdid);
+       xml_node_create_text(ctx->xml, node, NULL, "Data", int2str(data));
+
+       return node;
+}
+
+
+static xml_node_t * add_status(struct hs20_osu_client *ctx, xml_node_t *parent,
+                              int msgref, int cmdref, int cmdid,
+                              const char *cmd, int data, const char *targetref)
+{
+       xml_node_t *node;
+
+       node = xml_node_create(ctx->xml, parent, NULL, "Status");
+       if (node == NULL)
+               return NULL;
+       oma_dm_add_cmdid(ctx, node, cmdid);
+       xml_node_create_text(ctx->xml, node, NULL, "MsgRef", int2str(msgref));
+       if (cmdref)
+               xml_node_create_text(ctx->xml, node, NULL, "CmdRef",
+                                    int2str(cmdref));
+       xml_node_create_text(ctx->xml, node, NULL, "Cmd", cmd);
+       xml_node_create_text(ctx->xml, node, NULL, "Data", int2str(data));
+       if (targetref) {
+               xml_node_create_text(ctx->xml, node, NULL, "TargetRef",
+                                    targetref);
+       }
+
+       return node;
+}
+
+
+static xml_node_t * add_results(struct hs20_osu_client *ctx, xml_node_t *parent,
+                               int msgref, int cmdref, int cmdid,
+                               const char *locuri, const char *data)
+{
+       xml_node_t *node;
+
+       node = xml_node_create(ctx->xml, parent, NULL, "Results");
+       if (node == NULL)
+               return NULL;
+
+       oma_dm_add_cmdid(ctx, node, cmdid);
+       xml_node_create_text(ctx->xml, node, NULL, "MsgRef", int2str(msgref));
+       xml_node_create_text(ctx->xml, node, NULL, "CmdRef", int2str(cmdref));
+       add_item(ctx, node, locuri, data);
+
+       return node;
+}
+
+
+static char * mo_str(struct hs20_osu_client *ctx, const char *urn,
+                    const char *fname)
+{
+       xml_node_t *fnode, *tnds;
+       char *str;
+
+       fnode = node_from_file(ctx->xml, fname);
+       if (!fnode)
+               return NULL;
+       tnds = mo_to_tnds(ctx->xml, fnode, 0, urn, "syncml:dmddf1.2");
+       xml_node_free(ctx->xml, fnode);
+       if (!tnds)
+               return NULL;
+
+       str = xml_node_to_str(ctx->xml, tnds);
+       xml_node_free(ctx->xml, tnds);
+       if (str == NULL)
+               return NULL;
+       wpa_printf(MSG_INFO, "MgmtTree: %s", str);
+
+       return str;
+}
+
+
+static void add_item(struct hs20_osu_client *ctx, xml_node_t *parent,
+                    const char *locuri, const char *data)
+{
+       xml_node_t *item, *node;
+
+       item = xml_node_create(ctx->xml, parent, NULL, "Item");
+       oma_dm_add_locuri(ctx, item, "Source", locuri);
+       node = xml_node_create(ctx->xml, item, NULL, "Meta");
+       xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Format",
+                               "Chr");
+       xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Type",
+                               "text/plain");
+       xml_node_create_text(ctx->xml, item, NULL, "Data", data);
+}
+
+
+static void add_replace_devinfo(struct hs20_osu_client *ctx, xml_node_t *parent,
+                               int cmdid)
+{
+       xml_node_t *info, *child, *replace;
+       const char *name;
+       char locuri[200], *txt;
+
+       info = node_from_file(ctx->xml, "devinfo.xml");
+       if (info == NULL) {
+               wpa_printf(MSG_INFO, "Could not read devinfo.xml");
+               return;
+       }
+
+       replace = xml_node_create(ctx->xml, parent, NULL, "Replace");
+       if (replace == NULL) {
+               xml_node_free(ctx->xml, info);
+               return;
+       }
+       oma_dm_add_cmdid(ctx, replace, cmdid);
+
+       xml_node_for_each_child(ctx->xml, child, info) {
+               xml_node_for_each_check(ctx->xml, child);
+               name = xml_node_get_localname(ctx->xml, child);
+               os_snprintf(locuri, sizeof(locuri), "./DevInfo/%s", name);
+               txt = xml_node_get_text(ctx->xml, child);
+               if (txt) {
+                       add_item(ctx, replace, locuri, txt);
+                       xml_node_get_text_free(ctx->xml, txt);
+               }
+       }
+
+       xml_node_free(ctx->xml, info);
+}
+
+
+static void oma_dm_add_hs20_generic_alert(struct hs20_osu_client *ctx,
+                                         xml_node_t *syncbody,
+                                         int cmdid, const char *oper,
+                                         const char *data)
+{
+       xml_node_t *node, *item;
+       char buf[200];
+
+       node = add_alert(ctx, syncbody, cmdid, DM_GENERIC_ALERT);
+
+       item = xml_node_create(ctx->xml, node, NULL, "Item");
+       oma_dm_add_locuri(ctx, item, "Source", DM_URI_PPS);
+       node = xml_node_create(ctx->xml, item, NULL, "Meta");
+       snprintf(buf, sizeof(buf), "Reversed-Domain-Name: %s", oper);
+       xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Type", buf);
+       xml_node_create_text_ns(ctx->xml, node, "syncml:metinf", "Format",
+                               "xml");
+       xml_node_create_text(ctx->xml, item, NULL, "Data", data);
+}
+
+
+static xml_node_t * build_oma_dm_1(struct hs20_osu_client *ctx,
+                                  const char *url, int msgid, const char *oper)
+{
+       xml_node_t *syncml, *syncbody;
+       char *str;
+       int cmdid = 0;
+
+       syncml = oma_dm_build_hdr(ctx, url, msgid);
+       if (syncml == NULL)
+               return NULL;
+
+       syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody");
+       if (syncbody == NULL) {
+               xml_node_free(ctx->xml, syncml);
+               return NULL;
+       }
+
+       cmdid++;
+       add_alert(ctx, syncbody, cmdid, DM_CLIENT_INITIATED_MGMT);
+
+       str = mo_str(ctx, NULL, "devdetail.xml");
+       if (str == NULL) {
+               xml_node_free(ctx->xml, syncml);
+               return NULL;
+       }
+       cmdid++;
+       oma_dm_add_hs20_generic_alert(ctx, syncbody, cmdid, oper, str);
+       os_free(str);
+
+       cmdid++;
+       add_replace_devinfo(ctx, syncbody, cmdid);
+
+       xml_node_create(ctx->xml, syncbody, NULL, "Final");
+
+       return syncml;
+}
+
+
+static xml_node_t * build_oma_dm_1_sub_reg(struct hs20_osu_client *ctx,
+                                          const char *url, int msgid)
+{
+       xml_node_t *syncml;
+
+       syncml = build_oma_dm_1(ctx, url, msgid, DM_HS20_SUBSCRIPTION_CREATION);
+       if (syncml)
+               debug_dump_node(ctx, "OMA-DM Package 1 (sub reg)", syncml);
+
+       return syncml;
+}
+
+
+static xml_node_t * build_oma_dm_1_sub_prov(struct hs20_osu_client *ctx,
+                                           const char *url, int msgid)
+{
+       xml_node_t *syncml;
+
+       syncml = build_oma_dm_1(ctx, url, msgid,
+                               DM_HS20_SUBSCRIPTION_PROVISIONING);
+       if (syncml)
+               debug_dump_node(ctx, "OMA-DM Package 1 (sub prov)", syncml);
+
+       return syncml;
+}
+
+
+static xml_node_t * build_oma_dm_1_pol_upd(struct hs20_osu_client *ctx,
+                                          const char *url, int msgid)
+{
+       xml_node_t *syncml;
+
+       syncml = build_oma_dm_1(ctx, url, msgid, DM_HS20_POLICY_UPDATE);
+       if (syncml)
+               debug_dump_node(ctx, "OMA-DM Package 1 (pol upd)", syncml);
+
+       return syncml;
+}
+
+
+static xml_node_t * build_oma_dm_1_sub_rem(struct hs20_osu_client *ctx,
+                                          const char *url, int msgid)
+{
+       xml_node_t *syncml;
+
+       syncml = build_oma_dm_1(ctx, url, msgid,
+                               DM_HS20_SUBSCRIPTION_REMEDIATION);
+       if (syncml)
+               debug_dump_node(ctx, "OMA-DM Package 1 (sub rem)", syncml);
+
+       return syncml;
+}
+
+
+static int oma_dm_exec_browser(struct hs20_osu_client *ctx, xml_node_t *exec)
+{
+       xml_node_t *node;
+       char *data;
+       int res;
+
+       node = get_node(ctx->xml, exec, "Item/Data");
+       if (node == NULL) {
+               wpa_printf(MSG_INFO, "No Data node found");
+               return DM_RESP_BAD_REQUEST;
+       }
+
+       data = xml_node_get_text(ctx->xml, node);
+       if (data == NULL) {
+               wpa_printf(MSG_INFO, "Invalid data");
+               return DM_RESP_BAD_REQUEST;
+       }
+       wpa_printf(MSG_INFO, "Data: %s", data);
+       wpa_printf(MSG_INFO, "Launch browser to URI '%s'", data);
+       write_summary(ctx, "Launch browser to URI '%s'", data);
+       res = hs20_web_browser(data);
+       xml_node_get_text_free(ctx->xml, data);
+       if (res > 0) {
+               wpa_printf(MSG_INFO, "User response in browser completed successfully");
+               write_summary(ctx, "User response in browser completed successfully");
+               return DM_RESP_OK;
+       } else {
+               wpa_printf(MSG_INFO, "Failed to receive user response");
+               write_summary(ctx, "Failed to receive user response");
+               return DM_RESP_COMMAND_FAILED;
+       }
+}
+
+
+static int oma_dm_exec_get_cert(struct hs20_osu_client *ctx, xml_node_t *exec)
+{
+       xml_node_t *node, *getcert;
+       char *data;
+       const char *name;
+       int res;
+
+       wpa_printf(MSG_INFO, "Client certificate enrollment");
+       write_summary(ctx, "Client certificate enrollment");
+
+       node = get_node(ctx->xml, exec, "Item/Data");
+       if (node == NULL) {
+               wpa_printf(MSG_INFO, "No Data node found");
+               return DM_RESP_BAD_REQUEST;
+       }
+
+       data = xml_node_get_text(ctx->xml, node);
+       if (data == NULL) {
+               wpa_printf(MSG_INFO, "Invalid data");
+               return DM_RESP_BAD_REQUEST;
+       }
+       wpa_printf(MSG_INFO, "Data: %s", data);
+       getcert = xml_node_from_buf(ctx->xml, data);
+       xml_node_get_text_free(ctx->xml, data);
+
+       if (getcert == NULL) {
+               wpa_printf(MSG_INFO, "Could not parse Item/Data node contents");
+               return DM_RESP_BAD_REQUEST;
+       }
+
+       debug_dump_node(ctx, "OMA-DM getCertificate", getcert);
+
+       name = xml_node_get_localname(ctx->xml, getcert);
+       if (name == NULL || os_strcasecmp(name, "getCertificate") != 0) {
+               wpa_printf(MSG_INFO, "Unexpected getCertificate node name '%s'",
+                          name);
+               return DM_RESP_BAD_REQUEST;
+       }
+
+       res = osu_get_certificate(ctx, getcert);
+
+       xml_node_free(ctx->xml, getcert);
+
+       return res == 0 ? DM_RESP_OK : DM_RESP_COMMAND_FAILED;
+}
+
+
+static int oma_dm_exec(struct hs20_osu_client *ctx, xml_node_t *exec)
+{
+       char *locuri;
+       int ret;
+
+       locuri = oma_dm_get_target_locuri(ctx, exec);
+       if (locuri == NULL) {
+               wpa_printf(MSG_INFO, "No Target LocURI node found");
+               return DM_RESP_BAD_REQUEST;
+       }
+
+       wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
+
+       if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/"
+                         "launchBrowserToURI") == 0) {
+               ret = oma_dm_exec_browser(ctx, exec);
+       } else if (os_strcasecmp(locuri, "./DevDetail/Ext/org.wi-fi/Wi-Fi/Ops/"
+                         "getCertificate") == 0) {
+               ret = oma_dm_exec_get_cert(ctx, exec);
+       } else {
+               wpa_printf(MSG_INFO, "Unsupported exec Target LocURI");
+               ret = DM_RESP_NOT_FOUND;
+       }
+       os_free(locuri);
+
+       return ret;
+}
+
+
+static int oma_dm_run_add(struct hs20_osu_client *ctx, const char *locuri,
+                         xml_node_t *add, xml_node_t *pps,
+                         const char *pps_fname)
+{
+       const char *pos;
+       size_t fqdn_len;
+       xml_node_t *node, *tnds, *unode, *pps_node;
+       char *data, *uri, *upos, *end;
+       int use_tnds = 0;
+       size_t uri_len;
+
+       wpa_printf(MSG_INFO, "Add command target LocURI: %s", locuri);
+
+       if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
+               wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi");
+               return DM_RESP_PERMISSION_DENIED;
+       }
+       pos = locuri + 8;
+
+       if (ctx->fqdn == NULL)
+               return DM_RESP_COMMAND_FAILED;
+       fqdn_len = os_strlen(ctx->fqdn);
+       if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
+           pos[fqdn_len] != '/') {
+               wpa_printf(MSG_INFO, "Do not allow Add outside ./Wi-Fi/%s",
+                          ctx->fqdn);
+               return DM_RESP_PERMISSION_DENIED;
+       }
+       pos += fqdn_len + 1;
+
+       if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
+               wpa_printf(MSG_INFO,
+                          "Do not allow Add outside ./Wi-Fi/%s/PerProviderSubscription",
+                          ctx->fqdn);
+               return DM_RESP_PERMISSION_DENIED;
+       }
+       pos += 24;
+
+       wpa_printf(MSG_INFO, "Add command for PPS node %s", pos);
+
+       pps_node = get_node(ctx->xml, pps, pos);
+       if (pps_node) {
+               wpa_printf(MSG_INFO, "Specified PPS node exists already");
+               return DM_RESP_ALREADY_EXISTS;
+       }
+
+       uri = os_strdup(pos);
+       if (uri == NULL)
+               return DM_RESP_COMMAND_FAILED;
+       while (!pps_node) {
+               upos = os_strrchr(uri, '/');
+               if (!upos)
+                       break;
+               upos[0] = '\0';
+               pps_node = get_node(ctx->xml, pps, uri);
+               wpa_printf(MSG_INFO, "Node %s %s", uri,
+                          pps_node ? "exists" : "does not exist");
+       }
+
+       wpa_printf(MSG_INFO, "Parent URI: %s", uri);
+
+       if (!pps_node) {
+               /* Add at root of PPS MO */
+               pps_node = pps;
+       }
+
+       uri_len = os_strlen(uri);
+       os_strlcpy(uri, pos + uri_len, os_strlen(pos));
+       upos = uri;
+       while (*upos == '/')
+               upos++;
+       wpa_printf(MSG_INFO, "Nodes to add: %s", upos);
+
+       for (;;) {
+               end = os_strchr(upos, '/');
+               if (!end)
+                       break;
+               *end = '\0';
+               wpa_printf(MSG_INFO, "Adding interim node %s", upos);
+               pps_node = xml_node_create(ctx->xml, pps_node, NULL, upos);
+               if (pps_node == NULL) {
+                       os_free(uri);
+                       return DM_RESP_COMMAND_FAILED;
+               }
+               upos = end + 1;
+       }
+
+       wpa_printf(MSG_INFO, "Adding node %s", upos);
+
+       node = get_node(ctx->xml, add, "Item/Meta/Type");
+       if (node) {
+               char *type;
+               type = xml_node_get_text(ctx->xml, node);
+               if (type == NULL) {
+                       wpa_printf(MSG_ERROR, "Could not find type text");
+                       os_free(uri);
+                       return DM_RESP_BAD_REQUEST;
+               }
+               use_tnds = node &&
+                       os_strstr(type, "application/vnd.syncml.dmtnds+xml");
+       }
+
+       node = get_node(ctx->xml, add, "Item/Data");
+       if (node == NULL) {
+               wpa_printf(MSG_INFO, "No Add/Item/Data found");
+               os_free(uri);
+               return DM_RESP_BAD_REQUEST;
+       }
+
+       data = xml_node_get_text(ctx->xml, node);
+       if (data == NULL) {
+               wpa_printf(MSG_INFO, "Could not get Add/Item/Data text");
+               os_free(uri);
+               return DM_RESP_BAD_REQUEST;
+       }
+
+       wpa_printf(MSG_DEBUG, "Add/Item/Data: %s", data);
+
+       if (use_tnds) {
+               tnds = xml_node_from_buf(ctx->xml, data);
+               xml_node_get_text_free(ctx->xml, data);
+               if (tnds == NULL) {
+                       wpa_printf(MSG_INFO,
+                                  "Could not parse Add/Item/Data text");
+                       os_free(uri);
+                       return DM_RESP_BAD_REQUEST;
+               }
+
+               unode = tnds_to_mo(ctx->xml, tnds);
+               xml_node_free(ctx->xml, tnds);
+               if (unode == NULL) {
+                       wpa_printf(MSG_INFO, "Could not parse TNDS text");
+                       os_free(uri);
+                       return DM_RESP_BAD_REQUEST;
+               }
+
+               debug_dump_node(ctx, "Parsed TNDS", unode);
+
+               xml_node_add_child(ctx->xml, pps_node, unode);
+       } else {
+               /* TODO: What to do here? */
+               os_free(uri);
+               return DM_RESP_BAD_REQUEST;
+       }
+
+       os_free(uri);
+
+       if (update_pps_file(ctx, pps_fname, pps) < 0)
+               return DM_RESP_COMMAND_FAILED;
+
+       ctx->pps_updated = 1;
+
+       return DM_RESP_OK;
+}
+
+
+static int oma_dm_add(struct hs20_osu_client *ctx, xml_node_t *add,
+                     xml_node_t *pps, const char *pps_fname)
+{
+       xml_node_t *node;
+       char *locuri;
+       char fname[300];
+       int ret;
+
+       node = get_node(ctx->xml, add, "Item/Target/LocURI");
+       if (node == NULL) {
+               wpa_printf(MSG_INFO, "No Target LocURI node found");
+               return DM_RESP_BAD_REQUEST;
+       }
+       locuri = xml_node_get_text(ctx->xml, node);
+       if (locuri == NULL) {
+               wpa_printf(MSG_ERROR, "No LocURI node text found");
+               return DM_RESP_BAD_REQUEST;
+       }
+       wpa_printf(MSG_INFO, "Target LocURI: %s", locuri);
+       if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
+               wpa_printf(MSG_INFO, "Unsupported Add Target LocURI");
+               xml_node_get_text_free(ctx->xml, locuri);
+               return DM_RESP_PERMISSION_DENIED;
+       }
+
+       node = get_node(ctx->xml, add, "Item/Data");
+       if (node == NULL) {
+               wpa_printf(MSG_INFO, "No Data node found");
+               xml_node_get_text_free(ctx->xml, locuri);
+               return DM_RESP_BAD_REQUEST;
+       }
+
+       if (pps_fname && os_file_exists(pps_fname)) {
+               ret = oma_dm_run_add(ctx, locuri, add, pps, pps_fname);
+               if (ret != DM_RESP_OK) {
+                       xml_node_get_text_free(ctx->xml, locuri);
+                       return ret;
+               }
+               ret = 0;
+               os_strlcpy(fname, pps_fname, sizeof(fname));
+       } else
+               ret = hs20_add_pps_mo(ctx, locuri, node, fname, sizeof(fname));
+       xml_node_get_text_free(ctx->xml, locuri);
+       if (ret < 0)
+               return ret == -2 ? DM_RESP_ALREADY_EXISTS :
+                       DM_RESP_COMMAND_FAILED;
+
+       if (ctx->no_reconnect == 2) {
+               os_snprintf(ctx->pps_fname, sizeof(ctx->pps_fname), "%s",
+                           fname);
+               ctx->pps_cred_set = 1;
+               return DM_RESP_OK;
+       }
+
+       wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
+       cmd_set_pps(ctx, fname);
+
+       if (ctx->no_reconnect)
+               return DM_RESP_OK;
+
+       wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+       if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
+               wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
+
+       return DM_RESP_OK;
+}
+
+
+static int oma_dm_replace(struct hs20_osu_client *ctx, xml_node_t *replace,
+                         xml_node_t *pps, const char *pps_fname)
+{
+       char *locuri, *pos;
+       size_t fqdn_len;
+       xml_node_t *node, *tnds, *unode, *pps_node, *parent;
+       char *data;
+       int use_tnds = 0;
+
+       locuri = oma_dm_get_target_locuri(ctx, replace);
+       if (locuri == NULL)
+               return DM_RESP_BAD_REQUEST;
+
+       wpa_printf(MSG_INFO, "Replace command target LocURI: %s", locuri);
+       if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
+               wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi");
+               os_free(locuri);
+               return DM_RESP_PERMISSION_DENIED;
+       }
+       pos = locuri + 8;
+
+       if (ctx->fqdn == NULL) {
+               os_free(locuri);
+               return DM_RESP_COMMAND_FAILED;
+       }
+       fqdn_len = os_strlen(ctx->fqdn);
+       if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
+           pos[fqdn_len] != '/') {
+               wpa_printf(MSG_INFO, "Do not allow Replace outside ./Wi-Fi/%s",
+                          ctx->fqdn);
+               os_free(locuri);
+               return DM_RESP_PERMISSION_DENIED;
+       }
+       pos += fqdn_len + 1;
+
+       if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
+               wpa_printf(MSG_INFO,
+                          "Do not allow Replace outside ./Wi-Fi/%s/PerProviderSubscription",
+                          ctx->fqdn);
+               os_free(locuri);
+               return DM_RESP_PERMISSION_DENIED;
+       }
+       pos += 24;
+
+       wpa_printf(MSG_INFO, "Replace command for PPS node %s", pos);
+
+       pps_node = get_node(ctx->xml, pps, pos);
+       if (pps_node == NULL) {
+               wpa_printf(MSG_INFO, "Specified PPS node not found");
+               os_free(locuri);
+               return DM_RESP_NOT_FOUND;
+       }
+
+       node = get_node(ctx->xml, replace, "Item/Meta/Type");
+       if (node) {
+               char *type;
+               type = xml_node_get_text(ctx->xml, node);
+               if (type == NULL) {
+                       wpa_printf(MSG_INFO, "Could not find type text");
+                       os_free(locuri);
+                       return DM_RESP_BAD_REQUEST;
+               }
+               use_tnds = node &&
+                       os_strstr(type, "application/vnd.syncml.dmtnds+xml");
+       }
+
+       node = get_node(ctx->xml, replace, "Item/Data");
+       if (node == NULL) {
+               wpa_printf(MSG_INFO, "No Replace/Item/Data found");
+               os_free(locuri);
+               return DM_RESP_BAD_REQUEST;
+       }
+
+       data = xml_node_get_text(ctx->xml, node);
+       if (data == NULL) {
+               wpa_printf(MSG_INFO, "Could not get Replace/Item/Data text");
+               os_free(locuri);
+               return DM_RESP_BAD_REQUEST;
+       }
+
+       wpa_printf(MSG_DEBUG, "Replace/Item/Data: %s", data);
+
+       if (use_tnds) {
+               tnds = xml_node_from_buf(ctx->xml, data);
+               xml_node_get_text_free(ctx->xml, data);
+               if (tnds == NULL) {
+                       wpa_printf(MSG_INFO,
+                                  "Could not parse Replace/Item/Data text");
+                       os_free(locuri);
+                       return DM_RESP_BAD_REQUEST;
+               }
+
+               unode = tnds_to_mo(ctx->xml, tnds);
+               xml_node_free(ctx->xml, tnds);
+               if (unode == NULL) {
+                       wpa_printf(MSG_INFO, "Could not parse TNDS text");
+                       os_free(locuri);
+                       return DM_RESP_BAD_REQUEST;
+               }
+
+               debug_dump_node(ctx, "Parsed TNDS", unode);
+
+               parent = xml_node_get_parent(ctx->xml, pps_node);
+               xml_node_detach(ctx->xml, pps_node);
+               xml_node_add_child(ctx->xml, parent, unode);
+       } else {
+               xml_node_set_text(ctx->xml, pps_node, data);
+               xml_node_get_text_free(ctx->xml, data);
+       }
+
+       os_free(locuri);
+
+       if (update_pps_file(ctx, pps_fname, pps) < 0)
+               return DM_RESP_COMMAND_FAILED;
+
+       ctx->pps_updated = 1;
+
+       return DM_RESP_OK;
+}
+
+
+static int oma_dm_get(struct hs20_osu_client *ctx, xml_node_t *get,
+                     xml_node_t *pps, const char *pps_fname, char **value)
+{
+       char *locuri, *pos;
+       size_t fqdn_len;
+       xml_node_t *pps_node;
+       const char *name;
+
+       *value = NULL;
+
+       locuri = oma_dm_get_target_locuri(ctx, get);
+       if (locuri == NULL)
+               return DM_RESP_BAD_REQUEST;
+
+       wpa_printf(MSG_INFO, "Get command target LocURI: %s", locuri);
+       if (os_strncasecmp(locuri, "./Wi-Fi/", 8) != 0) {
+               wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi");
+               os_free(locuri);
+               return DM_RESP_PERMISSION_DENIED;
+       }
+       pos = locuri + 8;
+
+       if (ctx->fqdn == NULL)
+               return DM_RESP_COMMAND_FAILED;
+       fqdn_len = os_strlen(ctx->fqdn);
+       if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
+           pos[fqdn_len] != '/') {
+               wpa_printf(MSG_INFO, "Do not allow Get outside ./Wi-Fi/%s",
+                          ctx->fqdn);
+               os_free(locuri);
+               return DM_RESP_PERMISSION_DENIED;
+       }
+       pos += fqdn_len + 1;
+
+       if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
+               wpa_printf(MSG_INFO,
+                          "Do not allow Get outside ./Wi-Fi/%s/PerProviderSubscription",
+                          ctx->fqdn);
+               os_free(locuri);
+               return DM_RESP_PERMISSION_DENIED;
+       }
+       pos += 24;
+
+       wpa_printf(MSG_INFO, "Get command for PPS node %s", pos);
+
+       pps_node = get_node(ctx->xml, pps, pos);
+       if (pps_node == NULL) {
+               wpa_printf(MSG_INFO, "Specified PPS node not found");
+               os_free(locuri);
+               return DM_RESP_NOT_FOUND;
+       }
+
+       name = xml_node_get_localname(ctx->xml, pps_node);
+       wpa_printf(MSG_INFO, "Get command returned node with name '%s'", name);
+       if (os_strcasecmp(name, "Password") == 0) {
+               wpa_printf(MSG_INFO, "Do not allow Get for Password node");
+               os_free(locuri);
+               return DM_RESP_PERMISSION_DENIED;
+       }
+
+       /*
+        * TODO: No support for DMTNDS, so if interior node, reply with a
+        * list of children node names in Results element. The child list type is
+        * defined in [DMTND].
+        */
+
+       *value = xml_node_get_text(ctx->xml, pps_node);
+       if (*value == NULL)
+               return DM_RESP_COMMAND_FAILED;
+
+       return DM_RESP_OK;
+}
+
+
+static int oma_dm_get_cmdid(struct hs20_osu_client *ctx, xml_node_t *node)
+{
+       xml_node_t *cnode;
+       char *str;
+       int ret;
+
+       cnode = get_node(ctx->xml, node, "CmdID");
+       if (cnode == NULL)
+               return 0;
+
+       str = xml_node_get_text(ctx->xml, cnode);
+       if (str == NULL)
+               return 0;
+       ret = atoi(str);
+       xml_node_get_text_free(ctx->xml, str);
+       return ret;
+}
+
+
+static xml_node_t * oma_dm_send_recv(struct hs20_osu_client *ctx,
+                                    const char *url, xml_node_t *syncml,
+                                    const char *ext_hdr,
+                                    const char *username, const char *password,
+                                    const char *client_cert,
+                                    const char *client_key)
+{
+       xml_node_t *resp;
+       char *str, *res;
+       char *resp_uri = NULL;
+
+       str = xml_node_to_str(ctx->xml, syncml);
+       xml_node_free(ctx->xml, syncml);
+       if (str == NULL)
+               return NULL;
+
+       wpa_printf(MSG_INFO, "Send OMA DM Package");
+       write_summary(ctx, "Send OMA DM Package");
+       os_free(ctx->server_url);
+       ctx->server_url = os_strdup(url);
+       res = http_post(ctx->http, url, str, "application/vnd.syncml.dm+xml",
+                       ext_hdr, ctx->ca_fname, username, password,
+                       client_cert, client_key, NULL);
+       os_free(str);
+       os_free(resp_uri);
+       resp_uri = NULL;
+
+       if (res == NULL) {
+               const char *err = http_get_err(ctx->http);
+               if (err) {
+                       wpa_printf(MSG_INFO, "HTTP error: %s", err);
+                       write_result(ctx, "HTTP error: %s", err);
+               } else {
+                       write_summary(ctx, "Failed to send OMA DM Package");
+               }
+               return NULL;
+       }
+       wpa_printf(MSG_DEBUG, "Server response: %s", res);
+
+       wpa_printf(MSG_INFO, "Process OMA DM Package");
+       write_summary(ctx, "Process received OMA DM Package");
+       resp = xml_node_from_buf(ctx->xml, res);
+       os_free(res);
+       if (resp == NULL) {
+               wpa_printf(MSG_INFO, "Failed to parse OMA DM response");
+               return NULL;
+       }
+
+       debug_dump_node(ctx, "OMA DM Package", resp);
+
+       return resp;
+}
+
+
+static xml_node_t * oma_dm_process(struct hs20_osu_client *ctx, const char *url,
+                                  xml_node_t *resp, int msgid,
+                                  char **ret_resp_uri,
+                                  xml_node_t *pps, const char *pps_fname)
+{
+       xml_node_t *syncml, *syncbody, *hdr, *body, *child;
+       const char *name;
+       char *resp_uri = NULL;
+       int server_msgid = 0;
+       int cmdid = 0;
+       int server_cmdid;
+       int resp_needed = 0;
+       char *tmp;
+       int final = 0;
+       char *locuri;
+
+       *ret_resp_uri = NULL;
+
+       name = xml_node_get_localname(ctx->xml, resp);
+       if (name == NULL || os_strcasecmp(name, "SyncML") != 0) {
+               wpa_printf(MSG_INFO, "SyncML node not found");
+               return NULL;
+       }
+
+       hdr = get_node(ctx->xml, resp, "SyncHdr");
+       body = get_node(ctx->xml, resp, "SyncBody");
+       if (hdr == NULL || body == NULL) {
+               wpa_printf(MSG_INFO, "Could not find SyncHdr or SyncBody");
+               return NULL;
+       }
+
+       xml_node_for_each_child(ctx->xml, child, hdr) {
+               xml_node_for_each_check(ctx->xml, child);
+               name = xml_node_get_localname(ctx->xml, child);
+               wpa_printf(MSG_INFO, "SyncHdr %s", name);
+               if (os_strcasecmp(name, "RespURI") == 0) {
+                       tmp = xml_node_get_text(ctx->xml, child);
+                       if (tmp)
+                               resp_uri = os_strdup(tmp);
+                       xml_node_get_text_free(ctx->xml, tmp);
+               } else if (os_strcasecmp(name, "MsgID") == 0) {
+                       tmp = xml_node_get_text(ctx->xml, child);
+                       if (tmp)
+                               server_msgid = atoi(tmp);
+                       xml_node_get_text_free(ctx->xml, tmp);
+               }
+       }
+
+       wpa_printf(MSG_INFO, "Server MsgID: %d", server_msgid);
+       if (resp_uri)
+               wpa_printf(MSG_INFO, "RespURI: %s", resp_uri);
+
+       syncml = oma_dm_build_hdr(ctx, resp_uri ? resp_uri : url, msgid);
+       if (syncml == NULL) {
+               os_free(resp_uri);
+               return NULL;
+       }
+
+       syncbody = xml_node_create(ctx->xml, syncml, NULL, "SyncBody");
+       cmdid++;
+       add_status(ctx, syncbody, server_msgid, 0, cmdid, "SyncHdr",
+                  DM_RESP_AUTH_ACCEPTED, NULL);
+
+       xml_node_for_each_child(ctx->xml, child, body) {
+               xml_node_for_each_check(ctx->xml, child);
+               server_cmdid = oma_dm_get_cmdid(ctx, child);
+               name = xml_node_get_localname(ctx->xml, child);
+               wpa_printf(MSG_INFO, "SyncBody CmdID=%d - %s",
+                          server_cmdid, name);
+               if (os_strcasecmp(name, "Exec") == 0) {
+                       int res = oma_dm_exec(ctx, child);
+                       cmdid++;
+                       locuri = oma_dm_get_target_locuri(ctx, child);
+                       if (locuri == NULL)
+                               res = DM_RESP_BAD_REQUEST;
+                       add_status(ctx, syncbody, server_msgid, server_cmdid,
+                                  cmdid, name, res, locuri);
+                       os_free(locuri);
+                       resp_needed = 1;
+               } else if (os_strcasecmp(name, "Add") == 0) {
+                       int res = oma_dm_add(ctx, child, pps, pps_fname);
+                       cmdid++;
+                       locuri = oma_dm_get_target_locuri(ctx, child);
+                       if (locuri == NULL)
+                               res = DM_RESP_BAD_REQUEST;
+                       add_status(ctx, syncbody, server_msgid, server_cmdid,
+                                  cmdid, name, res, locuri);
+                       os_free(locuri);
+                       resp_needed = 1;
+               } else if (os_strcasecmp(name, "Replace") == 0) {
+                       int res;
+                       res = oma_dm_replace(ctx, child, pps, pps_fname);
+                       cmdid++;
+                       locuri = oma_dm_get_target_locuri(ctx, child);
+                       if (locuri == NULL)
+                               res = DM_RESP_BAD_REQUEST;
+                       add_status(ctx, syncbody, server_msgid, server_cmdid,
+                                  cmdid, name, res, locuri);
+                       os_free(locuri);
+                       resp_needed = 1;
+               } else if (os_strcasecmp(name, "Status") == 0) {
+                       /* TODO: Verify success */
+               } else if (os_strcasecmp(name, "Get") == 0) {
+                       int res;
+                       char *value;
+                       res = oma_dm_get(ctx, child, pps, pps_fname, &value);
+                       cmdid++;
+                       locuri = oma_dm_get_target_locuri(ctx, child);
+                       if (locuri == NULL)
+                               res = DM_RESP_BAD_REQUEST;
+                       add_status(ctx, syncbody, server_msgid, server_cmdid,
+                                  cmdid, name, res, locuri);
+                       if (res == DM_RESP_OK && value) {
+                               cmdid++;
+                               add_results(ctx, syncbody, server_msgid,
+                                           server_cmdid, cmdid, locuri, value);
+                       }
+                       os_free(locuri);
+                       xml_node_get_text_free(ctx->xml, value);
+                       resp_needed = 1;
+#if 0 /* TODO: MUST support */
+               } else if (os_strcasecmp(name, "Delete") == 0) {
+#endif
+#if 0 /* TODO: MUST support */
+               } else if (os_strcasecmp(name, "Sequence") == 0) {
+#endif
+               } else if (os_strcasecmp(name, "Final") == 0) {
+                       final = 1;
+                       break;
+               } else {
+                       locuri = oma_dm_get_target_locuri(ctx, child);
+                       add_status(ctx, syncbody, server_msgid, server_cmdid,
+                                  cmdid, name, DM_RESP_COMMAND_NOT_IMPLEMENTED,
+                                  locuri);
+                       os_free(locuri);
+                       resp_needed = 1;
+               }
+       }
+
+       if (!final) {
+               wpa_printf(MSG_INFO, "Final node not found");
+               xml_node_free(ctx->xml, syncml);
+               os_free(resp_uri);
+               return NULL;
+       }
+
+       if (!resp_needed) {
+               wpa_printf(MSG_INFO, "Exchange completed - no response needed");
+               xml_node_free(ctx->xml, syncml);
+               os_free(resp_uri);
+               return NULL;
+       }
+
+       xml_node_create(ctx->xml, syncbody, NULL, "Final");
+
+       debug_dump_node(ctx, "OMA-DM Package 3", syncml);
+
+       *ret_resp_uri = resp_uri;
+       return syncml;
+}
+
+
+int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url)
+{
+       xml_node_t *syncml, *resp;
+       char *resp_uri = NULL;
+       int msgid = 0;
+
+       if (url == NULL) {
+               wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
+               return -1;
+       }
+
+       wpa_printf(MSG_INFO, "OMA-DM credential provisioning requested");
+       write_summary(ctx, "OMA-DM credential provisioning");
+
+       msgid++;
+       syncml = build_oma_dm_1_sub_reg(ctx, url, msgid);
+       if (syncml == NULL)
+               return -1;
+
+       while (syncml) {
+               resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
+                                       syncml, NULL, NULL, NULL, NULL, NULL);
+               if (resp == NULL)
+                       return -1;
+
+               msgid++;
+               syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
+                                       NULL, NULL);
+               xml_node_free(ctx->xml, resp);
+       }
+
+       os_free(resp_uri);
+
+       return ctx->pps_cred_set ? 0 : -1;
+}
+
+
+int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url)
+{
+       xml_node_t *syncml, *resp;
+       char *resp_uri = NULL;
+       int msgid = 0;
+
+       if (url == NULL) {
+               wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
+               return -1;
+       }
+
+       wpa_printf(MSG_INFO, "OMA-DM SIM provisioning requested");
+       ctx->no_reconnect = 2;
+
+       wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
+       write_summary(ctx, "Wait for IP address before starting SIM provisioning");
+
+       if (wait_ip_addr(ctx->ifname, 15) < 0) {
+               wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
+       }
+       write_summary(ctx, "OMA-DM SIM provisioning");
+
+       msgid++;
+       syncml = build_oma_dm_1_sub_prov(ctx, url, msgid);
+       if (syncml == NULL)
+               return -1;
+
+       while (syncml) {
+               resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : url,
+                                       syncml, NULL, NULL, NULL, NULL, NULL);
+               if (resp == NULL)
+                       return -1;
+
+               msgid++;
+               syncml = oma_dm_process(ctx, url, resp, msgid, &resp_uri,
+                                       NULL, NULL);
+               xml_node_free(ctx->xml, resp);
+       }
+
+       os_free(resp_uri);
+
+       if (ctx->pps_cred_set) {
+               wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
+               cmd_set_pps(ctx, ctx->pps_fname);
+
+               wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+               write_summary(ctx, "Requesting reconnection with updated configuration");
+               if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
+                       wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
+                       write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
+                       return -1;
+               }
+       }
+
+       return ctx->pps_cred_set ? 0 : -1;
+}
+
+
+void oma_dm_pol_upd(struct hs20_osu_client *ctx, const char *address,
+                   const char *pps_fname,
+                   const char *client_cert, const char *client_key,
+                   const char *cred_username, const char *cred_password,
+                   xml_node_t *pps)
+{
+       xml_node_t *syncml, *resp;
+       char *resp_uri = NULL;
+       int msgid = 0;
+
+       wpa_printf(MSG_INFO, "OMA-DM policy update");
+       write_summary(ctx, "OMA-DM policy update");
+
+       msgid++;
+       syncml = build_oma_dm_1_pol_upd(ctx, address, msgid);
+       if (syncml == NULL)
+               return;
+
+       while (syncml) {
+               resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
+                                       syncml, NULL, cred_username,
+                                       cred_password, client_cert, client_key);
+               if (resp == NULL)
+                       return;
+
+               msgid++;
+               syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
+                                       pps, pps_fname);
+               xml_node_free(ctx->xml, resp);
+       }
+
+       os_free(resp_uri);
+
+       if (ctx->pps_updated) {
+               wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO");
+               write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request connection");
+               cmd_set_pps(ctx, pps_fname);
+               if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
+                       wpa_printf(MSG_INFO,
+                                  "Failed to request wpa_supplicant to reconnect");
+                       write_summary(ctx,
+                                     "Failed to request wpa_supplicant to reconnect");
+               }
+       }
+}
+
+
+void oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
+                   const char *pps_fname,
+                   const char *client_cert, const char *client_key,
+                   const char *cred_username, const char *cred_password,
+                   xml_node_t *pps)
+{
+       xml_node_t *syncml, *resp;
+       char *resp_uri = NULL;
+       int msgid = 0;
+
+       wpa_printf(MSG_INFO, "OMA-DM subscription remediation");
+       write_summary(ctx, "OMA-DM subscription remediation");
+
+       msgid++;
+       syncml = build_oma_dm_1_sub_rem(ctx, address, msgid);
+       if (syncml == NULL)
+               return;
+
+       while (syncml) {
+               resp = oma_dm_send_recv(ctx, resp_uri ? resp_uri : address,
+                                       syncml, NULL, cred_username,
+                                       cred_password, client_cert, client_key);
+               if (resp == NULL)
+                       return;
+
+               msgid++;
+               syncml = oma_dm_process(ctx, address, resp, msgid, &resp_uri,
+                                       pps, pps_fname);
+               xml_node_free(ctx->xml, resp);
+       }
+
+       os_free(resp_uri);
+
+       wpa_printf(MSG_INFO, "Update wpa_supplicant credential based on updated PPS MO and request reconnection");
+       write_summary(ctx, "Update wpa_supplicant credential based on updated PPS MO and request reconnection");
+       cmd_set_pps(ctx, pps_fname);
+       if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
+               wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
+               write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
+       }
+}
+
+
+void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname,
+                   const char *add_fname)
+{
+       xml_node_t *pps, *add;
+       int res;
+
+       ctx->fqdn = os_strdup("wi-fi.org");
+
+       pps = node_from_file(ctx->xml, pps_fname);
+       if (pps == NULL) {
+               wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
+                          pps_fname);
+               return;
+       }
+
+       add = node_from_file(ctx->xml, add_fname);
+       if (add == NULL) {
+               wpa_printf(MSG_INFO, "Add file %s could not be parsed",
+                          add_fname);
+               xml_node_free(ctx->xml, pps);
+               return;
+       }
+
+       res = oma_dm_add(ctx, add, pps, pps_fname);
+       wpa_printf(MSG_INFO, "oma_dm_add --> %d", res);
+
+       xml_node_free(ctx->xml, pps);
+       xml_node_free(ctx->xml, add);
+}
+
+
+void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname,
+                       const char *replace_fname)
+{
+       xml_node_t *pps, *replace;
+       int res;
+
+       ctx->fqdn = os_strdup("wi-fi.org");
+
+       pps = node_from_file(ctx->xml, pps_fname);
+       if (pps == NULL) {
+               wpa_printf(MSG_INFO, "PPS file %s could not be parsed",
+                          pps_fname);
+               return;
+       }
+
+       replace = node_from_file(ctx->xml, replace_fname);
+       if (replace == NULL) {
+               wpa_printf(MSG_INFO, "Replace file %s could not be parsed",
+                          replace_fname);
+               xml_node_free(ctx->xml, pps);
+               return;
+       }
+
+       res = oma_dm_replace(ctx, replace, pps, pps_fname);
+       wpa_printf(MSG_INFO, "oma_dm_replace --> %d", res);
+
+       xml_node_free(ctx->xml, pps);
+       xml_node_free(ctx->xml, replace);
+}
diff --git a/hs20/client/osu_client.c b/hs20/client/osu_client.c
new file mode 100644 (file)
index 0000000..de7f351
--- /dev/null
@@ -0,0 +1,3227 @@
+/*
+ * Hotspot 2.0 OSU client
+ * Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <time.h>
+#include <sys/stat.h>
+#ifdef ANDROID
+#include "private/android_filesystem_config.h"
+#endif /* ANDROID */
+
+#include "common.h"
+#include "utils/browser.h"
+#include "utils/base64.h"
+#include "utils/xml-utils.h"
+#include "utils/http-utils.h"
+#include "common/wpa_ctrl.h"
+#include "common/wpa_helpers.h"
+#include "eap_common/eap_defs.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "osu_client.h"
+
+
+void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
+{
+       va_list ap;
+       FILE *f;
+       char buf[500];
+
+       va_start(ap, fmt);
+       vsnprintf(buf, sizeof(buf), fmt, ap);
+       va_end(ap);
+       write_summary(ctx, "%s", buf);
+
+       if (!ctx->result_file)
+               return;
+
+       f = fopen(ctx->result_file, "w");
+       if (f == NULL)
+               return;
+
+       va_start(ap, fmt);
+       vfprintf(f, fmt, ap);
+       va_end(ap);
+       fprintf(f, "\n");
+       fclose(f);
+}
+
+
+void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...)
+{
+       va_list ap;
+       FILE *f;
+
+       if (!ctx->summary_file)
+               return;
+
+       f = fopen(ctx->summary_file, "a");
+       if (f == NULL)
+               return;
+
+       va_start(ap, fmt);
+       vfprintf(f, fmt, ap);
+       va_end(ap);
+       fprintf(f, "\n");
+       fclose(f);
+}
+
+
+void debug_dump_node(struct hs20_osu_client *ctx, const char *title,
+                    xml_node_t *node)
+{
+       char *str = xml_node_to_str(ctx->xml, node);
+       wpa_printf(MSG_DEBUG, "[hs20] %s: '%s'", title, str);
+       free(str);
+}
+
+
+static int valid_fqdn(const char *fqdn)
+{
+       const char *pos;
+
+       /* TODO: could make this more complete.. */
+       if (strchr(fqdn, '.') == 0 || strlen(fqdn) > 255)
+               return 0;
+       for (pos = fqdn; *pos; pos++) {
+               if (*pos >= 'a' && *pos <= 'z')
+                       continue;
+               if (*pos >= 'A' && *pos <= 'Z')
+                       continue;
+               if (*pos >= '0' && *pos <= '9')
+                       continue;
+               if (*pos == '-' || *pos == '.' || *pos == '_')
+                       continue;
+               return 0;
+       }
+       return 1;
+}
+
+
+int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert)
+{
+       xml_node_t *node;
+       char *url, *user = NULL, *pw = NULL;
+       char *proto;
+       int ret = -1;
+
+       proto = xml_node_get_attr_value(ctx->xml, getcert,
+                                       "enrollmentProtocol");
+       if (!proto)
+               return -1;
+       wpa_printf(MSG_INFO, "getCertificate - enrollmentProtocol=%s", proto);
+       write_summary(ctx, "getCertificate - enrollmentProtocol=%s", proto);
+       if (os_strcasecmp(proto, "EST") != 0) {
+               wpa_printf(MSG_INFO, "Unsupported enrollmentProtocol");
+               xml_node_get_attr_value_free(ctx->xml, proto);
+               return -1;
+       }
+       xml_node_get_attr_value_free(ctx->xml, proto);
+
+       node = get_node(ctx->xml, getcert, "enrollmentServerURI");
+       if (node == NULL) {
+               wpa_printf(MSG_INFO, "Could not find enrollmentServerURI node");
+               xml_node_get_attr_value_free(ctx->xml, proto);
+               return -1;
+       }
+       url = xml_node_get_text(ctx->xml, node);
+       if (url == NULL) {
+               wpa_printf(MSG_INFO, "Could not get URL text");
+               return -1;
+       }
+       wpa_printf(MSG_INFO, "enrollmentServerURI: %s", url);
+       write_summary(ctx, "enrollmentServerURI: %s", url);
+
+       node = get_node(ctx->xml, getcert, "estUserID");
+       if (node == NULL && !ctx->client_cert_present) {
+               wpa_printf(MSG_INFO, "Could not find estUserID node");
+               goto fail;
+       }
+       if (node) {
+               user = xml_node_get_text(ctx->xml, node);
+               if (user == NULL) {
+                       wpa_printf(MSG_INFO, "Could not get estUserID text");
+                       goto fail;
+               }
+               wpa_printf(MSG_INFO, "estUserID: %s", user);
+               write_summary(ctx, "estUserID: %s", user);
+       }
+
+       node = get_node(ctx->xml, getcert, "estPassword");
+       if (node == NULL && !ctx->client_cert_present) {
+               wpa_printf(MSG_INFO, "Could not find estPassword node");
+               goto fail;
+       }
+       if (node) {
+               pw = xml_node_get_base64_text(ctx->xml, node, NULL);
+               if (pw == NULL) {
+                       wpa_printf(MSG_INFO, "Could not get estPassword text");
+                       goto fail;
+               }
+               wpa_printf(MSG_INFO, "estPassword: %s", pw);
+       }
+
+       mkdir("Cert", S_IRWXU);
+       if (est_load_cacerts(ctx, url) < 0 ||
+           est_build_csr(ctx, url) < 0 ||
+           est_simple_enroll(ctx, url, user, pw) < 0)
+               goto fail;
+
+       ret = 0;
+fail:
+       xml_node_get_text_free(ctx->xml, url);
+       xml_node_get_text_free(ctx->xml, user);
+       xml_node_get_text_free(ctx->xml, pw);
+
+       return ret;
+}
+
+
+static int process_est_cert(struct hs20_osu_client *ctx, xml_node_t *cert,
+                           const char *fqdn)
+{
+       u8 digest1[SHA256_MAC_LEN], digest2[SHA256_MAC_LEN];
+       char *der, *pem;
+       size_t der_len, pem_len;
+       char *fingerprint;
+       char buf[200];
+
+       wpa_printf(MSG_INFO, "PPS for certificate credential - fqdn=%s", fqdn);
+
+       fingerprint = xml_node_get_text(ctx->xml, cert);
+       if (fingerprint == NULL)
+               return -1;
+       if (hexstr2bin(fingerprint, digest1, SHA256_MAC_LEN) < 0) {
+               wpa_printf(MSG_INFO, "Invalid SHA256 hash value");
+               write_result(ctx, "Invalid client certificate SHA256 hash value in PPS");
+               xml_node_get_text_free(ctx->xml, fingerprint);
+               return -1;
+       }
+       xml_node_get_text_free(ctx->xml, fingerprint);
+
+       der = os_readfile("Cert/est_cert.der", &der_len);
+       if (der == NULL) {
+               wpa_printf(MSG_INFO, "Could not find client certificate from EST");
+               write_result(ctx, "Could not find client certificate from EST");
+               return -1;
+       }
+
+       if (sha256_vector(1, (const u8 **) &der, &der_len, digest2) < 0) {
+               os_free(der);
+               return -1;
+       }
+       os_free(der);
+
+       if (os_memcmp(digest1, digest2, sizeof(digest1)) != 0) {
+               wpa_printf(MSG_INFO, "Client certificate from EST does not match fingerprint from PPS MO");
+               write_result(ctx, "Client certificate from EST does not match fingerprint from PPS MO");
+               return -1;
+       }
+
+       wpa_printf(MSG_INFO, "Client certificate from EST matches PPS MO");
+       unlink("Cert/est_cert.der");
+
+       os_snprintf(buf, sizeof(buf), "SP/%s/client-ca.pem", fqdn);
+       if (rename("Cert/est-cacerts.pem", buf) < 0) {
+               wpa_printf(MSG_INFO, "Could not move est-cacerts.pem to client-ca.pem: %s",
+                          strerror(errno));
+               return -1;
+       }
+       pem = os_readfile(buf, &pem_len);
+
+       os_snprintf(buf, sizeof(buf), "SP/%s/client-cert.pem", fqdn);
+       if (rename("Cert/est_cert.pem", buf) < 0) {
+               wpa_printf(MSG_INFO, "Could not move est_cert.pem to client-cert.pem: %s",
+                          strerror(errno));
+               os_free(pem);
+               return -1;
+       }
+
+       if (pem) {
+               FILE *f = fopen(buf, "a");
+               if (f) {
+                       fwrite(pem, pem_len, 1, f);
+                       fclose(f);
+               }
+               os_free(pem);
+       }
+
+       os_snprintf(buf, sizeof(buf), "SP/%s/client-key.pem", fqdn);
+       if (rename("Cert/privkey-plain.pem", buf) < 0) {
+               wpa_printf(MSG_INFO, "Could not move privkey-plain.pem to client-key.pem: %s",
+                          strerror(errno));
+               return -1;
+       }
+
+       unlink("Cert/est-req.b64");
+       unlink("Cert/est-req.pem");
+       unlink("Cert/est-resp.raw");
+       rmdir("Cert");
+
+       return 0;
+}
+
+
+#define TMP_CERT_DL_FILE "tmp-cert-download"
+
+static int download_cert(struct hs20_osu_client *ctx, xml_node_t *params,
+                        const char *fname)
+{
+       xml_node_t *url_node, *hash_node;
+       char *url, *hash;
+       char *cert;
+       size_t len;
+       u8 digest1[SHA256_MAC_LEN], digest2[SHA256_MAC_LEN];
+       int res;
+       unsigned char *b64;
+       FILE *f;
+
+       url_node = get_node(ctx->xml, params, "CertURL");
+       hash_node = get_node(ctx->xml, params, "CertSHA256Fingerprint");
+       if (url_node == NULL || hash_node == NULL)
+               return -1;
+       url = xml_node_get_text(ctx->xml, url_node);
+       hash = xml_node_get_text(ctx->xml, hash_node);
+       if (url == NULL || hash == NULL) {
+               xml_node_get_text_free(ctx->xml, url);
+               xml_node_get_text_free(ctx->xml, hash);
+               return -1;
+       }
+
+       wpa_printf(MSG_INFO, "CertURL: %s", url);
+       wpa_printf(MSG_INFO, "SHA256 hash: %s", hash);
+
+       if (hexstr2bin(hash, digest1, SHA256_MAC_LEN) < 0) {
+               wpa_printf(MSG_INFO, "Invalid SHA256 hash value");
+               write_result(ctx, "Invalid SHA256 hash value for downloaded certificate");
+               xml_node_get_text_free(ctx->xml, hash);
+               return -1;
+       }
+       xml_node_get_text_free(ctx->xml, hash);
+
+       write_summary(ctx, "Download certificate from %s", url);
+       ctx->no_osu_cert_validation = 1;
+       http_ocsp_set(ctx->http, 1);
+       res = http_download_file(ctx->http, url, TMP_CERT_DL_FILE, NULL);
+       http_ocsp_set(ctx->http,
+                     (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL) ? 1 : 2);
+       ctx->no_osu_cert_validation = 0;
+       xml_node_get_text_free(ctx->xml, url);
+       if (res < 0)
+               return -1;
+
+       cert = os_readfile(TMP_CERT_DL_FILE, &len);
+       remove(TMP_CERT_DL_FILE);
+       if (cert == NULL)
+               return -1;
+
+       if (sha256_vector(1, (const u8 **) &cert, &len, digest2) < 0) {
+               os_free(cert);
+               return -1;
+       }
+
+       if (os_memcmp(digest1, digest2, sizeof(digest1)) != 0) {
+               wpa_printf(MSG_INFO, "Downloaded certificate fingerprint did not match");
+               write_result(ctx, "Downloaded certificate fingerprint did not match");
+               os_free(cert);
+               return -1;
+       }
+
+       b64 = base64_encode((unsigned char *) cert, len, NULL);
+       os_free(cert);
+       if (b64 == NULL)
+               return -1;
+
+       f = fopen(fname, "wb");
+       if (f == NULL) {
+               os_free(b64);
+               return -1;
+       }
+
+       fprintf(f, "-----BEGIN CERTIFICATE-----\n"
+               "%s"
+               "-----END CERTIFICATE-----\n",
+               b64);
+
+       os_free(b64);
+       fclose(f);
+
+       wpa_printf(MSG_INFO, "Downloaded certificate into %s and validated fingerprint",
+                  fname);
+       write_summary(ctx, "Downloaded certificate into %s and validated fingerprint",
+                     fname);
+
+       return 0;
+}
+
+
+static int cmd_dl_osu_ca(struct hs20_osu_client *ctx, const char *pps_fname,
+                        const char *ca_fname)
+{
+       xml_node_t *pps, *node;
+       int ret;
+
+       pps = node_from_file(ctx->xml, pps_fname);
+       if (pps == NULL) {
+               wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
+               return -1;
+       }
+
+       node = get_child_node(ctx->xml, pps,
+                             "SubscriptionUpdate/TrustRoot");
+       if (node == NULL) {
+               wpa_printf(MSG_INFO, "No SubscriptionUpdate/TrustRoot/CertURL found from PPS");
+               xml_node_free(ctx->xml, pps);
+               return -1;
+       }
+
+       ret = download_cert(ctx, node, ca_fname);
+       xml_node_free(ctx->xml, pps);
+
+       return ret;
+}
+
+
+static int cmd_dl_polupd_ca(struct hs20_osu_client *ctx, const char *pps_fname,
+                           const char *ca_fname)
+{
+       xml_node_t *pps, *node;
+       int ret;
+
+       pps = node_from_file(ctx->xml, pps_fname);
+       if (pps == NULL) {
+               wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
+               return -1;
+       }
+
+       node = get_child_node(ctx->xml, pps,
+                             "Policy/PolicyUpdate/TrustRoot");
+       if (node == NULL) {
+               wpa_printf(MSG_INFO, "No Policy/PolicyUpdate/TrustRoot/CertURL found from PPS");
+               xml_node_free(ctx->xml, pps);
+               return -1;
+       }
+
+       ret = download_cert(ctx, node, ca_fname);
+       xml_node_free(ctx->xml, pps);
+
+       return ret;
+}
+
+
+static int cmd_dl_aaa_ca(struct hs20_osu_client *ctx, const char *pps_fname,
+                        const char *ca_fname)
+{
+       xml_node_t *pps, *node, *aaa;
+       int ret;
+
+       pps = node_from_file(ctx->xml, pps_fname);
+       if (pps == NULL) {
+               wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
+               return -1;
+       }
+
+       node = get_child_node(ctx->xml, pps,
+                             "AAAServerTrustRoot");
+       if (node == NULL) {
+               wpa_printf(MSG_INFO, "No AAAServerTrustRoot/CertURL found from PPS");
+               xml_node_free(ctx->xml, pps);
+               return -1;
+       }
+
+       aaa = xml_node_first_child(ctx->xml, node);
+       if (aaa == NULL) {
+               wpa_printf(MSG_INFO, "No AAAServerTrustRoot/CertURL found from PPS");
+               xml_node_free(ctx->xml, pps);
+               return -1;
+       }
+
+       ret = download_cert(ctx, aaa, ca_fname);
+       xml_node_free(ctx->xml, pps);
+
+       return ret;
+}
+
+
+static int download_trust_roots(struct hs20_osu_client *ctx,
+                               const char *pps_fname)
+{
+       char *dir, *pos;
+       char fname[300];
+       int ret;
+
+       dir = os_strdup(pps_fname);
+       if (dir == NULL)
+               return -1;
+       pos = os_strrchr(dir, '/');
+       if (pos == NULL) {
+               os_free(dir);
+               return -1;
+       }
+       *pos = '\0';
+
+       snprintf(fname, sizeof(fname), "%s/ca.pem", dir);
+       ret = cmd_dl_osu_ca(ctx, pps_fname, fname);
+       snprintf(fname, sizeof(fname), "%s/polupd-ca.pem", dir);
+       cmd_dl_polupd_ca(ctx, pps_fname, fname);
+       snprintf(fname, sizeof(fname), "%s/aaa-ca.pem", dir);
+       cmd_dl_aaa_ca(ctx, pps_fname, fname);
+
+       os_free(dir);
+
+       return ret;
+}
+
+
+static int server_dnsname_suffix_match(struct hs20_osu_client *ctx,
+                                      const char *fqdn)
+{
+       size_t match_len, len, i;
+       const char *val;
+
+       match_len = os_strlen(fqdn);
+
+       for (i = 0; i < ctx->server_dnsname_count; i++) {
+               wpa_printf(MSG_INFO,
+                          "Checking suffix match against server dNSName %s",
+                          ctx->server_dnsname[i]);
+               val = ctx->server_dnsname[i];
+               len = os_strlen(val);
+
+               if (match_len > len)
+                       continue;
+
+               if (os_strncasecmp(val + len - match_len, fqdn, match_len) != 0)
+                       continue; /* no match */
+
+               if (match_len == len)
+                       return 1; /* exact match */
+
+               if (val[len - match_len - 1] == '.')
+                       return 1; /* full label match completes suffix match */
+
+               /* Reject due to incomplete label match */
+       }
+
+       /* None of the dNSName(s) matched */
+       return 0;
+}
+
+
+int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri,
+                   xml_node_t *add_mo, char *fname, size_t fname_len)
+{
+       char *str;
+       char *fqdn, *pos;
+       xml_node_t *tnds, *mo, *cert;
+       const char *name;
+       int ret;
+
+       if (strncmp(uri, "./Wi-Fi/", 8) != 0) {
+               wpa_printf(MSG_INFO, "Unsupported location for addMO to add PPS MO: '%s'",
+                          uri);
+               write_result(ctx, "Unsupported location for addMO to add PPS MO: '%s'",
+                            uri);
+               return -1;
+       }
+
+       fqdn = strdup(uri + 8);
+       if (fqdn == NULL)
+               return -1;
+       pos = strchr(fqdn, '/');
+       if (pos) {
+               if (os_strcasecmp(pos, "/PerProviderSubscription") != 0) {
+                       wpa_printf(MSG_INFO, "Unsupported location for addMO to add PPS MO (extra directory): '%s'",
+                                  uri);
+                       write_result(ctx, "Unsupported location for addMO to "
+                                    "add PPS MO (extra directory): '%s'", uri);
+                       return -1;
+               }
+               *pos = '\0'; /* remove trailing slash and PPS node name */
+       }
+       wpa_printf(MSG_INFO, "SP FQDN: %s", fqdn);
+
+       if (!server_dnsname_suffix_match(ctx, fqdn)) {
+               wpa_printf(MSG_INFO, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values",
+                          fqdn);
+               write_result(ctx, "FQDN '%s' for new PPS MO did not have suffix match with server's dNSName values",
+                            fqdn);
+               free(fqdn);
+               return -1;
+       }
+
+       if (!valid_fqdn(fqdn)) {
+               wpa_printf(MSG_INFO, "Invalid FQDN '%s'", fqdn);
+               write_result(ctx, "Invalid FQDN '%s'", fqdn);
+               free(fqdn);
+               return -1;
+       }
+
+       mkdir("SP", S_IRWXU);
+       snprintf(fname, fname_len, "SP/%s", fqdn);
+       if (mkdir(fname, S_IRWXU) < 0) {
+               if (errno != EEXIST) {
+                       int err = errno;
+                       wpa_printf(MSG_INFO, "mkdir(%s) failed: %s",
+                                  fname, strerror(err));
+                       free(fqdn);
+                       return -1;
+               }
+       }
+
+#ifdef ANDROID
+       /* Allow processes running with Group ID as AID_WIFI,
+        * to read files from SP/<fqdn> directory */
+       if (chown(fname, -1, AID_WIFI)) {
+               wpa_printf(MSG_INFO, "CTRL: Could not chown directory: %s",
+                          strerror(errno));
+               /* Try to continue anyway */
+       }
+       if (chmod(fname, S_IRWXU | S_IRGRP | S_IXGRP) < 0) {
+               wpa_printf(MSG_INFO, "CTRL: Could not chmod directory: %s",
+                          strerror(errno));
+               /* Try to continue anyway */
+       }
+#endif /* ANDROID */
+
+       snprintf(fname, fname_len, "SP/%s/pps.xml", fqdn);
+
+       if (os_file_exists(fname)) {
+               wpa_printf(MSG_INFO, "PPS file '%s' exists - reject addMO",
+                          fname);
+               write_result(ctx, "PPS file '%s' exists - reject addMO",
+                            fname);
+               free(fqdn);
+               return -2;
+       }
+       wpa_printf(MSG_INFO, "Using PPS file: %s", fname);
+
+       str = xml_node_get_text(ctx->xml, add_mo);
+       if (str == NULL) {
+               wpa_printf(MSG_INFO, "Could not extract MO text");
+               free(fqdn);
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG, "[hs20] addMO text: '%s'", str);
+
+       tnds = xml_node_from_buf(ctx->xml, str);
+       xml_node_get_text_free(ctx->xml, str);
+       if (tnds == NULL) {
+               wpa_printf(MSG_INFO, "[hs20] Could not parse addMO text");
+               free(fqdn);
+               return -1;
+       }
+
+       mo = tnds_to_mo(ctx->xml, tnds);
+       if (mo == NULL) {
+               wpa_printf(MSG_INFO, "[hs20] Could not parse addMO TNDS text");
+               free(fqdn);
+               return -1;
+       }
+
+       debug_dump_node(ctx, "Parsed TNDS", mo);
+
+       name = xml_node_get_localname(ctx->xml, mo);
+       if (os_strcasecmp(name, "PerProviderSubscription") != 0) {
+               wpa_printf(MSG_INFO, "[hs20] Unexpected PPS MO root node name '%s'",
+                          name);
+               free(fqdn);
+               return -1;
+       }
+
+       cert = get_child_node(ctx->xml, mo,
+                             "Credential/DigitalCertificate/"
+                             "CertSHA256Fingerprint");
+       if (cert && process_est_cert(ctx, cert, fqdn) < 0) {
+               xml_node_free(ctx->xml, mo);
+               free(fqdn);
+               return -1;
+       }
+       free(fqdn);
+
+       if (node_to_file(ctx->xml, fname, mo) < 0) {
+               wpa_printf(MSG_INFO, "Could not write MO to file");
+               xml_node_free(ctx->xml, mo);
+               return -1;
+       }
+       xml_node_free(ctx->xml, mo);
+
+       wpa_printf(MSG_INFO, "A new PPS MO added as '%s'", fname);
+       write_summary(ctx, "A new PPS MO added as '%s'", fname);
+
+       ret = download_trust_roots(ctx, fname);
+       if (ret < 0) {
+               wpa_printf(MSG_INFO, "Remove invalid PPS MO file");
+               write_summary(ctx, "Remove invalid PPS MO file");
+               unlink(fname);
+       }
+
+       return ret;
+}
+
+
+int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname,
+                   xml_node_t *pps)
+{
+       char *str;
+       FILE *f;
+       char backup[300];
+
+       if (ctx->client_cert_present) {
+               xml_node_t *cert;
+               cert = get_child_node(ctx->xml, pps,
+                                     "Credential/DigitalCertificate/"
+                                     "CertSHA256Fingerprint");
+               if (cert && os_file_exists("Cert/est_cert.der") &&
+                   process_est_cert(ctx, cert, ctx->fqdn) < 0) {
+                       wpa_printf(MSG_INFO, "EST certificate update processing failed on PPS MO update");
+                       return -1;
+               }
+       }
+
+       wpa_printf(MSG_INFO, "Updating PPS MO %s", pps_fname);
+
+       str = xml_node_to_str(ctx->xml, pps);
+       if (str == NULL) {
+               wpa_printf(MSG_ERROR, "No node found");
+               return -1;
+       }
+       wpa_printf(MSG_MSGDUMP, "[hs20] Updated PPS: '%s'", str);
+
+       snprintf(backup, sizeof(backup), "%s.bak", pps_fname);
+       rename(pps_fname, backup);
+       f = fopen(pps_fname, "w");
+       if (f == NULL) {
+               wpa_printf(MSG_INFO, "Could not write PPS");
+               rename(backup, pps_fname);
+               free(str);
+               return -1;
+       }
+       fprintf(f, "%s\n", str);
+       fclose(f);
+
+       free(str);
+
+       return 0;
+}
+
+
+void get_user_pw(struct hs20_osu_client *ctx, xml_node_t *pps,
+                const char *alt_loc, char **user, char **pw)
+{
+       xml_node_t *node;
+
+       node = get_child_node(ctx->xml, pps,
+                             "Credential/UsernamePassword/Username");
+       if (node)
+               *user = xml_node_get_text(ctx->xml, node);
+
+       node = get_child_node(ctx->xml, pps,
+                             "Credential/UsernamePassword/Password");
+       if (node)
+               *pw = xml_node_get_base64_text(ctx->xml, node, NULL);
+
+       node = get_child_node(ctx->xml, pps, alt_loc);
+       if (node) {
+               xml_node_t *a;
+               a = get_node(ctx->xml, node, "Username");
+               if (a) {
+                       xml_node_get_text_free(ctx->xml, *user);
+                       *user = xml_node_get_text(ctx->xml, a);
+                       wpa_printf(MSG_INFO, "Use OSU username '%s'", *user);
+               }
+
+               a = get_node(ctx->xml, node, "Password");
+               if (a) {
+                       free(*pw);
+                       *pw = xml_node_get_base64_text(ctx->xml, a, NULL);
+                       wpa_printf(MSG_INFO, "Use OSU password");
+               }
+       }
+}
+
+
+/* Remove old credentials based on HomeSP/FQDN */
+static void remove_sp_creds(struct hs20_osu_client *ctx, const char *fqdn)
+{
+       char cmd[300];
+       os_snprintf(cmd, sizeof(cmd), "REMOVE_CRED provisioning_sp=%s", fqdn);
+       if (wpa_command(ctx->ifname, cmd) < 0)
+               wpa_printf(MSG_INFO, "Failed to remove old credential(s)");
+}
+
+
+static void set_pps_cred_policy_spe(struct hs20_osu_client *ctx, int id,
+                                   xml_node_t *spe)
+{
+       xml_node_t *ssid;
+       char *txt;
+
+       ssid = get_node(ctx->xml, spe, "SSID");
+       if (ssid == NULL)
+               return;
+       txt = xml_node_get_text(ctx->xml, ssid);
+       if (txt == NULL)
+               return;
+       wpa_printf(MSG_DEBUG, "- Policy/SPExclusionList/<X+>/SSID = %s", txt);
+       if (set_cred_quoted(ctx->ifname, id, "excluded_ssid", txt) < 0)
+               wpa_printf(MSG_INFO, "Failed to set cred excluded_ssid");
+       xml_node_get_text_free(ctx->xml, txt);
+}
+
+
+static void set_pps_cred_policy_spel(struct hs20_osu_client *ctx, int id,
+                                    xml_node_t *spel)
+{
+       xml_node_t *child;
+
+       xml_node_for_each_child(ctx->xml, child, spel) {
+               xml_node_for_each_check(ctx->xml, child);
+               set_pps_cred_policy_spe(ctx, id, child);
+       }
+}
+
+
+static void set_pps_cred_policy_prp(struct hs20_osu_client *ctx, int id,
+                                   xml_node_t *prp)
+{
+       xml_node_t *node;
+       char *txt = NULL, *pos;
+       char *prio, *country_buf = NULL;
+       const char *country;
+       char val[200];
+       int priority;
+
+       node = get_node(ctx->xml, prp, "Priority");
+       if (node == NULL)
+               return;
+       prio = xml_node_get_text(ctx->xml, node);
+       if (prio == NULL)
+               return;
+       wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/Priority = %s",
+                  prio);
+       priority = atoi(prio);
+       xml_node_get_text_free(ctx->xml, prio);
+
+       node = get_node(ctx->xml, prp, "Country");
+       if (node) {
+               country_buf = xml_node_get_text(ctx->xml, node);
+               if (country_buf == NULL)
+                       return;
+               country = country_buf;
+               wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/Country = %s",
+                          country);
+       } else {
+               country = "*";
+       }
+
+       node = get_node(ctx->xml, prp, "FQDN_Match");
+       if (node == NULL)
+               goto out;
+       txt = xml_node_get_text(ctx->xml, node);
+       if (txt == NULL)
+               goto out;
+       wpa_printf(MSG_INFO, "- Policy/PreferredRoamingPartnerList/<X+>/FQDN_Match = %s",
+                  txt);
+       pos = strrchr(txt, ',');
+       if (pos == NULL)
+               goto out;
+       *pos++ = '\0';
+
+       snprintf(val, sizeof(val), "%s,%d,%d,%s", txt,
+                strcmp(pos, "includeSubdomains") != 0, priority, country);
+       if (set_cred_quoted(ctx->ifname, id, "roaming_partner", val) < 0)
+               wpa_printf(MSG_INFO, "Failed to set cred roaming_partner");
+out:
+       xml_node_get_text_free(ctx->xml, country_buf);
+       xml_node_get_text_free(ctx->xml, txt);
+}
+
+
+static void set_pps_cred_policy_prpl(struct hs20_osu_client *ctx, int id,
+                                    xml_node_t *prpl)
+{
+       xml_node_t *child;
+
+       xml_node_for_each_child(ctx->xml, child, prpl) {
+               xml_node_for_each_check(ctx->xml, child);
+               set_pps_cred_policy_prp(ctx, id, child);
+       }
+}
+
+
+static void set_pps_cred_policy_min_backhaul(struct hs20_osu_client *ctx, int id,
+                                            xml_node_t *min_backhaul)
+{
+       xml_node_t *node;
+       char *type, *dl = NULL, *ul = NULL;
+       int home;
+
+       node = get_node(ctx->xml, min_backhaul, "NetworkType");
+       if (node == NULL) {
+               wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold without mandatory NetworkType node");
+               return;
+       }
+
+       type = xml_node_get_text(ctx->xml, node);
+       if (type == NULL)
+               return;
+       wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/NetworkType = %s",
+                  type);
+       if (os_strcasecmp(type, "home") == 0)
+               home = 1;
+       else if (os_strcasecmp(type, "roaming") == 0)
+               home = 0;
+       else {
+               wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold with invalid NetworkType");
+               xml_node_get_text_free(ctx->xml, type);
+               return;
+       }
+       xml_node_get_text_free(ctx->xml, type);
+
+       node = get_node(ctx->xml, min_backhaul, "DLBandwidth");
+       if (node)
+               dl = xml_node_get_text(ctx->xml, node);
+
+       node = get_node(ctx->xml, min_backhaul, "ULBandwidth");
+       if (node)
+               ul = xml_node_get_text(ctx->xml, node);
+
+       if (dl == NULL && ul == NULL) {
+               wpa_printf(MSG_INFO, "Ignore MinBackhaulThreshold without either DLBandwidth or ULBandwidth nodes");
+               return;
+       }
+
+       if (dl)
+               wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/DLBandwidth = %s",
+                          dl);
+       if (ul)
+               wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold/<X+>/ULBandwidth = %s",
+                          ul);
+
+       if (home) {
+               if (dl &&
+                   set_cred(ctx->ifname, id, "min_dl_bandwidth_home", dl) < 0)
+                       wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
+               if (ul &&
+                   set_cred(ctx->ifname, id, "min_ul_bandwidth_home", ul) < 0)
+                       wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
+       } else {
+               if (dl &&
+                   set_cred(ctx->ifname, id, "min_dl_bandwidth_roaming", dl) <
+                   0)
+                       wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
+               if (ul &&
+                   set_cred(ctx->ifname, id, "min_ul_bandwidth_roaming", ul) <
+                   0)
+                       wpa_printf(MSG_INFO, "Failed to set cred bandwidth limit");
+       }
+
+       xml_node_get_text_free(ctx->xml, dl);
+       xml_node_get_text_free(ctx->xml, ul);
+}
+
+
+static void set_pps_cred_policy_min_backhaul_list(struct hs20_osu_client *ctx,
+                                                 int id, xml_node_t *node)
+{
+       xml_node_t *child;
+
+       wpa_printf(MSG_INFO, "- Policy/MinBackhaulThreshold");
+
+       xml_node_for_each_child(ctx->xml, child, node) {
+               xml_node_for_each_check(ctx->xml, child);
+               set_pps_cred_policy_min_backhaul(ctx, id, child);
+       }
+}
+
+
+static void set_pps_cred_policy_update(struct hs20_osu_client *ctx, int id,
+                                      xml_node_t *node)
+{
+       wpa_printf(MSG_INFO, "- Policy/PolicyUpdate");
+       /* Not used in wpa_supplicant */
+}
+
+
+static void set_pps_cred_policy_required_proto_port(struct hs20_osu_client *ctx,
+                                                   int id, xml_node_t *tuple)
+{
+       xml_node_t *node;
+       char *proto, *port;
+       char *buf;
+       size_t buflen;
+
+       node = get_node(ctx->xml, tuple, "IPProtocol");
+       if (node == NULL) {
+               wpa_printf(MSG_INFO, "Ignore RequiredProtoPortTuple without mandatory IPProtocol node");
+               return;
+       }
+
+       proto = xml_node_get_text(ctx->xml, node);
+       if (proto == NULL)
+               return;
+
+       wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple/<X+>/IPProtocol = %s",
+                  proto);
+
+       node = get_node(ctx->xml, tuple, "PortNumber");
+       port = node ? xml_node_get_text(ctx->xml, node) : NULL;
+       if (port) {
+               wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple/<X+>/PortNumber = %s",
+                          port);
+               buflen = os_strlen(proto) + os_strlen(port) + 10;
+               buf = os_malloc(buflen);
+               if (buf)
+                       os_snprintf(buf, buflen, "%s:%s", proto, port);
+               xml_node_get_text_free(ctx->xml, port);
+       } else {
+               buflen = os_strlen(proto) + 10;
+               buf = os_malloc(buflen);
+               if (buf)
+                       os_snprintf(buf, buflen, "%s", proto);
+       }
+
+       xml_node_get_text_free(ctx->xml, proto);
+
+       if (buf == NULL)
+               return;
+
+       if (set_cred(ctx->ifname, id, "req_conn_capab", buf) < 0)
+               wpa_printf(MSG_INFO, "Could not set req_conn_capab");
+
+       os_free(buf);
+}
+
+
+static void set_pps_cred_policy_required_proto_ports(struct hs20_osu_client *ctx,
+                                                    int id, xml_node_t *node)
+{
+       xml_node_t *child;
+
+       wpa_printf(MSG_INFO, "- Policy/RequiredProtoPortTuple");
+
+       xml_node_for_each_child(ctx->xml, child, node) {
+               xml_node_for_each_check(ctx->xml, child);
+               set_pps_cred_policy_required_proto_port(ctx, id, child);
+       }
+}
+
+
+static void set_pps_cred_policy_max_bss_load(struct hs20_osu_client *ctx, int id,
+                                            xml_node_t *node)
+{
+       char *str = xml_node_get_text(ctx->xml, node);
+       if (str == NULL)
+               return;
+       wpa_printf(MSG_INFO, "- Policy/MaximumBSSLoadValue - %s", str);
+       if (set_cred(ctx->ifname, id, "max_bss_load", str) < 0)
+               wpa_printf(MSG_INFO, "Failed to set cred max_bss_load limit");
+       xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_policy(struct hs20_osu_client *ctx, int id,
+                               xml_node_t *node)
+{
+       xml_node_t *child;
+       const char *name;
+
+       wpa_printf(MSG_INFO, "- Policy");
+
+       xml_node_for_each_child(ctx->xml, child, node) {
+               xml_node_for_each_check(ctx->xml, child);
+               name = xml_node_get_localname(ctx->xml, child);
+               if (os_strcasecmp(name, "PreferredRoamingPartnerList") == 0)
+                       set_pps_cred_policy_prpl(ctx, id, child);
+               else if (os_strcasecmp(name, "MinBackhaulThreshold") == 0)
+                       set_pps_cred_policy_min_backhaul_list(ctx, id, child);
+               else if (os_strcasecmp(name, "PolicyUpdate") == 0)
+                       set_pps_cred_policy_update(ctx, id, child);
+               else if (os_strcasecmp(name, "SPExclusionList") == 0)
+                       set_pps_cred_policy_spel(ctx, id, child);
+               else if (os_strcasecmp(name, "RequiredProtoPortTuple") == 0)
+                       set_pps_cred_policy_required_proto_ports(ctx, id, child);
+               else if (os_strcasecmp(name, "MaximumBSSLoadValue") == 0)
+                       set_pps_cred_policy_max_bss_load(ctx, id, child);
+               else
+                       wpa_printf(MSG_INFO, "Unknown Policy node '%s'", name);
+       }
+}
+
+
+static void set_pps_cred_priority(struct hs20_osu_client *ctx, int id,
+                                 xml_node_t *node)
+{
+       char *str = xml_node_get_text(ctx->xml, node);
+       if (str == NULL)
+               return;
+       wpa_printf(MSG_INFO, "- CredentialPriority = %s", str);
+       if (set_cred(ctx->ifname, id, "sp_priority", str) < 0)
+               wpa_printf(MSG_INFO, "Failed to set cred sp_priority");
+       xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_aaa_server_trust_root(struct hs20_osu_client *ctx,
+                                              int id, xml_node_t *node)
+{
+       wpa_printf(MSG_INFO, "- AAAServerTrustRoot - TODO");
+}
+
+
+static void set_pps_cred_sub_update(struct hs20_osu_client *ctx, int id,
+                                   xml_node_t *node)
+{
+       wpa_printf(MSG_INFO, "- SubscriptionUpdate");
+       /* not used within wpa_supplicant */
+}
+
+
+static void set_pps_cred_home_sp_network_id(struct hs20_osu_client *ctx,
+                                           int id, xml_node_t *node)
+{
+       xml_node_t *ssid_node, *hessid_node;
+       char *ssid, *hessid;
+
+       ssid_node = get_node(ctx->xml, node, "SSID");
+       if (ssid_node == NULL) {
+               wpa_printf(MSG_INFO, "Ignore HomeSP/NetworkID without mandatory SSID node");
+               return;
+       }
+
+       hessid_node = get_node(ctx->xml, node, "HESSID");
+
+       ssid = xml_node_get_text(ctx->xml, ssid_node);
+       if (ssid == NULL)
+               return;
+       hessid = hessid_node ? xml_node_get_text(ctx->xml, hessid_node) : NULL;
+
+       wpa_printf(MSG_INFO, "- HomeSP/NetworkID/<X+>/SSID = %s", ssid);
+       if (hessid)
+               wpa_printf(MSG_INFO, "- HomeSP/NetworkID/<X+>/HESSID = %s",
+                          hessid);
+
+       /* TODO: Configure to wpa_supplicant */
+
+       xml_node_get_text_free(ctx->xml, ssid);
+       xml_node_get_text_free(ctx->xml, hessid);
+}
+
+
+static void set_pps_cred_home_sp_network_ids(struct hs20_osu_client *ctx,
+                                            int id, xml_node_t *node)
+{
+       xml_node_t *child;
+
+       wpa_printf(MSG_INFO, "- HomeSP/NetworkID");
+
+       xml_node_for_each_child(ctx->xml, child, node) {
+               xml_node_for_each_check(ctx->xml, child);
+               set_pps_cred_home_sp_network_id(ctx, id, child);
+       }
+}
+
+
+static void set_pps_cred_home_sp_friendly_name(struct hs20_osu_client *ctx,
+                                              int id, xml_node_t *node)
+{
+       char *str = xml_node_get_text(ctx->xml, node);
+       if (str == NULL)
+               return;
+       wpa_printf(MSG_INFO, "- HomeSP/FriendlyName = %s", str);
+       /* not used within wpa_supplicant(?) */
+       xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_home_sp_icon_url(struct hs20_osu_client *ctx,
+                                         int id, xml_node_t *node)
+{
+       char *str = xml_node_get_text(ctx->xml, node);
+       if (str == NULL)
+               return;
+       wpa_printf(MSG_INFO, "- HomeSP/IconURL = %s", str);
+       /* not used within wpa_supplicant */
+       xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_home_sp_fqdn(struct hs20_osu_client *ctx, int id,
+                                     xml_node_t *node)
+{
+       char *str = xml_node_get_text(ctx->xml, node);
+       if (str == NULL)
+               return;
+       wpa_printf(MSG_INFO, "- HomeSP/FQDN = %s", str);
+       if (set_cred_quoted(ctx->ifname, id, "domain", str) < 0)
+               wpa_printf(MSG_INFO, "Failed to set cred domain");
+       if (set_cred_quoted(ctx->ifname, id, "domain_suffix_match", str) < 0)
+               wpa_printf(MSG_INFO, "Failed to set cred domain_suffix_match");
+       xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_home_sp_oi(struct hs20_osu_client *ctx, int id,
+                                   xml_node_t *node)
+{
+       xml_node_t *child;
+       const char *name;
+       char *homeoi = NULL;
+       int required = 0;
+       char *str;
+
+       xml_node_for_each_child(ctx->xml, child, node) {
+               xml_node_for_each_check(ctx->xml, child);
+               name = xml_node_get_localname(ctx->xml, child);
+               if (strcasecmp(name, "HomeOI") == 0 && !homeoi) {
+                       homeoi = xml_node_get_text(ctx->xml, child);
+                       wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+>/HomeOI = %s",
+                                  homeoi);
+               } else if (strcasecmp(name, "HomeOIRequired") == 0) {
+                       str = xml_node_get_text(ctx->xml, child);
+                       wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+>/HomeOIRequired = '%s'",
+                                  str);
+                       if (str == NULL)
+                               continue;
+                       required = strcasecmp(str, "true") == 0;
+                       xml_node_get_text_free(ctx->xml, str);
+               } else
+                       wpa_printf(MSG_INFO, "Unknown HomeOIList node '%s'",
+                                  name);
+       }
+
+       if (homeoi == NULL) {
+               wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+> without HomeOI ignored");
+               return;
+       }
+
+       wpa_printf(MSG_INFO, "- HomeSP/HomeOIList/<X+> '%s' required=%d",
+                  homeoi, required);
+
+       if (required) {
+               if (set_cred(ctx->ifname, id, "required_roaming_consortium",
+                            homeoi) < 0)
+                       wpa_printf(MSG_INFO, "Failed to set cred required_roaming_consortium");
+       } else {
+               if (set_cred_quoted(ctx->ifname, id, "roaming_consortium",
+                                   homeoi) < 0)
+                       wpa_printf(MSG_INFO, "Failed to set cred roaming_consortium");
+       }
+
+       xml_node_get_text_free(ctx->xml, homeoi);
+}
+
+
+static void set_pps_cred_home_sp_oi_list(struct hs20_osu_client *ctx, int id,
+                                        xml_node_t *node)
+{
+       xml_node_t *child;
+
+       wpa_printf(MSG_INFO, "- HomeSP/HomeOIList");
+
+       xml_node_for_each_child(ctx->xml, child, node) {
+               xml_node_for_each_check(ctx->xml, child);
+               set_pps_cred_home_sp_oi(ctx, id, child);
+       }
+}
+
+
+static void set_pps_cred_home_sp_other_partner(struct hs20_osu_client *ctx,
+                                              int id, xml_node_t *node)
+{
+       xml_node_t *child;
+       const char *name;
+       char *fqdn = NULL;
+
+       xml_node_for_each_child(ctx->xml, child, node) {
+               xml_node_for_each_check(ctx->xml, child);
+               name = xml_node_get_localname(ctx->xml, child);
+               if (os_strcasecmp(name, "FQDN") == 0 && !fqdn) {
+                       fqdn = xml_node_get_text(ctx->xml, child);
+                       wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners/<X+>/FQDN = %s",
+                                  fqdn);
+               } else
+                       wpa_printf(MSG_INFO, "Unknown OtherHomePartners node '%s'",
+                                  name);
+       }
+
+       if (fqdn == NULL) {
+               wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners/<X+> without FQDN ignored");
+               return;
+       }
+
+       if (set_cred_quoted(ctx->ifname, id, "domain", fqdn) < 0)
+               wpa_printf(MSG_INFO, "Failed to set cred domain for OtherHomePartners node");
+
+       xml_node_get_text_free(ctx->xml, fqdn);
+}
+
+
+static void set_pps_cred_home_sp_other_partners(struct hs20_osu_client *ctx,
+                                               int id,
+                                               xml_node_t *node)
+{
+       xml_node_t *child;
+
+       wpa_printf(MSG_INFO, "- HomeSP/OtherHomePartners");
+
+       xml_node_for_each_child(ctx->xml, child, node) {
+               xml_node_for_each_check(ctx->xml, child);
+               set_pps_cred_home_sp_other_partner(ctx, id, child);
+       }
+}
+
+
+static void set_pps_cred_home_sp_roaming_consortium_oi(
+       struct hs20_osu_client *ctx, int id, xml_node_t *node)
+{
+       char *str = xml_node_get_text(ctx->xml, node);
+       if (str == NULL)
+               return;
+       wpa_printf(MSG_INFO, "- HomeSP/RoamingConsortiumOI = %s", str);
+       /* TODO: Set to wpa_supplicant */
+       xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_home_sp(struct hs20_osu_client *ctx, int id,
+                                xml_node_t *node)
+{
+       xml_node_t *child;
+       const char *name;
+
+       wpa_printf(MSG_INFO, "- HomeSP");
+
+       xml_node_for_each_child(ctx->xml, child, node) {
+               xml_node_for_each_check(ctx->xml, child);
+               name = xml_node_get_localname(ctx->xml, child);
+               if (os_strcasecmp(name, "NetworkID") == 0)
+                       set_pps_cred_home_sp_network_ids(ctx, id, child);
+               else if (os_strcasecmp(name, "FriendlyName") == 0)
+                       set_pps_cred_home_sp_friendly_name(ctx, id, child);
+               else if (os_strcasecmp(name, "IconURL") == 0)
+                       set_pps_cred_home_sp_icon_url(ctx, id, child);
+               else if (os_strcasecmp(name, "FQDN") == 0)
+                       set_pps_cred_home_sp_fqdn(ctx, id, child);
+               else if (os_strcasecmp(name, "HomeOIList") == 0)
+                       set_pps_cred_home_sp_oi_list(ctx, id, child);
+               else if (os_strcasecmp(name, "OtherHomePartners") == 0)
+                       set_pps_cred_home_sp_other_partners(ctx, id, child);
+               else if (os_strcasecmp(name, "RoamingConsortiumOI") == 0)
+                       set_pps_cred_home_sp_roaming_consortium_oi(ctx, id,
+                                                                  child);
+               else
+                       wpa_printf(MSG_INFO, "Unknown HomeSP node '%s'", name);
+       }
+}
+
+
+static void set_pps_cred_sub_params(struct hs20_osu_client *ctx, int id,
+                                   xml_node_t *node)
+{
+       wpa_printf(MSG_INFO, "- SubscriptionParameters");
+       /* not used within wpa_supplicant */
+}
+
+
+static void set_pps_cred_creation_date(struct hs20_osu_client *ctx, int id,
+                                      xml_node_t *node)
+{
+       char *str = xml_node_get_text(ctx->xml, node);
+       if (str == NULL)
+               return;
+       wpa_printf(MSG_INFO, "- Credential/CreationDate = %s", str);
+       /* not used within wpa_supplicant */
+       xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_expiration_date(struct hs20_osu_client *ctx, int id,
+                                        xml_node_t *node)
+{
+       char *str = xml_node_get_text(ctx->xml, node);
+       if (str == NULL)
+               return;
+       wpa_printf(MSG_INFO, "- Credential/ExpirationDate = %s", str);
+       /* not used within wpa_supplicant */
+       xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_username(struct hs20_osu_client *ctx, int id,
+                                 xml_node_t *node)
+{
+       char *str = xml_node_get_text(ctx->xml, node);
+       if (str == NULL)
+               return;
+       wpa_printf(MSG_INFO, "- Credential/UsernamePassword/Username = %s",
+                  str);
+       if (set_cred_quoted(ctx->ifname, id, "username", str) < 0)
+               wpa_printf(MSG_INFO, "Failed to set cred username");
+       xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_password(struct hs20_osu_client *ctx, int id,
+                                 xml_node_t *node)
+{
+       int len, i;
+       char *pw, *hex, *pos, *end;
+
+       pw = xml_node_get_base64_text(ctx->xml, node, &len);
+       if (pw == NULL)
+               return;
+
+       wpa_printf(MSG_INFO, "- Credential/UsernamePassword/Password = %s", pw);
+
+       hex = malloc(len * 2 + 1);
+       if (hex == NULL) {
+               free(pw);
+               return;
+       }
+       end = hex + len * 2 + 1;
+       pos = hex;
+       for (i = 0; i < len; i++) {
+               snprintf(pos, end - pos, "%02x", pw[i]);
+               pos += 2;
+       }
+       free(pw);
+
+       if (set_cred(ctx->ifname, id, "password", hex) < 0)
+               wpa_printf(MSG_INFO, "Failed to set cred password");
+       free(hex);
+}
+
+
+static void set_pps_cred_machine_managed(struct hs20_osu_client *ctx, int id,
+                                        xml_node_t *node)
+{
+       char *str = xml_node_get_text(ctx->xml, node);
+       if (str == NULL)
+               return;
+       wpa_printf(MSG_INFO, "- Credential/UsernamePassword/MachineManaged = %s",
+                  str);
+       /* not used within wpa_supplicant */
+       xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_soft_token_app(struct hs20_osu_client *ctx, int id,
+                                       xml_node_t *node)
+{
+       char *str = xml_node_get_text(ctx->xml, node);
+       if (str == NULL)
+               return;
+       wpa_printf(MSG_INFO, "- Credential/UsernamePassword/SoftTokenApp = %s",
+                  str);
+       /* not used within wpa_supplicant */
+       xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_able_to_share(struct hs20_osu_client *ctx, int id,
+                                      xml_node_t *node)
+{
+       char *str = xml_node_get_text(ctx->xml, node);
+       if (str == NULL)
+               return;
+       wpa_printf(MSG_INFO, "- Credential/UsernamePassword/AbleToShare = %s",
+                  str);
+       /* not used within wpa_supplicant */
+       xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_eap_method(struct hs20_osu_client *ctx, int id,
+                                   xml_node_t *node)
+{
+       wpa_printf(MSG_INFO, "- Credential/UsernamePassword/EAPMethod - TODO");
+}
+
+
+static void set_pps_cred_username_password(struct hs20_osu_client *ctx, int id,
+                                          xml_node_t *node)
+{
+       xml_node_t *child;
+       const char *name;
+
+       wpa_printf(MSG_INFO, "- Credential/UsernamePassword");
+
+       xml_node_for_each_child(ctx->xml, child, node) {
+               xml_node_for_each_check(ctx->xml, child);
+               name = xml_node_get_localname(ctx->xml, child);
+               if (os_strcasecmp(name, "Username") == 0)
+                       set_pps_cred_username(ctx, id, child);
+               else if (os_strcasecmp(name, "Password") == 0)
+                       set_pps_cred_password(ctx, id, child);
+               else if (os_strcasecmp(name, "MachineManaged") == 0)
+                       set_pps_cred_machine_managed(ctx, id, child);
+               else if (os_strcasecmp(name, "SoftTokenApp") == 0)
+                       set_pps_cred_soft_token_app(ctx, id, child);
+               else if (os_strcasecmp(name, "AbleToShare") == 0)
+                       set_pps_cred_able_to_share(ctx, id, child);
+               else if (os_strcasecmp(name, "EAPMethod") == 0)
+                       set_pps_cred_eap_method(ctx, id, child);
+               else
+                       wpa_printf(MSG_INFO, "Unknown Credential/UsernamePassword node '%s'",
+                                  name);
+       }
+}
+
+
+static void set_pps_cred_digital_cert(struct hs20_osu_client *ctx, int id,
+                                     xml_node_t *node, const char *fqdn)
+{
+       char buf[200], dir[200];
+
+       wpa_printf(MSG_INFO, "- Credential/DigitalCertificate");
+
+       if (getcwd(dir, sizeof(dir)) == NULL)
+               return;
+
+       /* TODO: could build username from Subject of Subject AltName */
+       if (set_cred_quoted(ctx->ifname, id, "username", "cert") < 0) {
+               wpa_printf(MSG_INFO, "Failed to set username");
+       }
+
+       snprintf(buf, sizeof(buf), "%s/SP/%s/client-cert.pem", dir, fqdn);
+       if (os_file_exists(buf)) {
+               if (set_cred_quoted(ctx->ifname, id, "client_cert", buf) < 0) {
+                       wpa_printf(MSG_INFO, "Failed to set client_cert");
+               }
+       }
+
+       snprintf(buf, sizeof(buf), "%s/SP/%s/client-key.pem", dir, fqdn);
+       if (os_file_exists(buf)) {
+               if (set_cred_quoted(ctx->ifname, id, "private_key", buf) < 0) {
+                       wpa_printf(MSG_INFO, "Failed to set private_key");
+               }
+       }
+}
+
+
+static void set_pps_cred_realm(struct hs20_osu_client *ctx, int id,
+                              xml_node_t *node, const char *fqdn, int sim)
+{
+       char *str = xml_node_get_text(ctx->xml, node);
+       char buf[200], dir[200];
+
+       if (str == NULL)
+               return;
+
+       wpa_printf(MSG_INFO, "- Credential/Realm = %s", str);
+       if (set_cred_quoted(ctx->ifname, id, "realm", str) < 0)
+               wpa_printf(MSG_INFO, "Failed to set cred realm");
+       xml_node_get_text_free(ctx->xml, str);
+
+       if (sim)
+               return;
+
+       if (getcwd(dir, sizeof(dir)) == NULL)
+               return;
+       snprintf(buf, sizeof(buf), "%s/SP/%s/aaa-ca.pem", dir, fqdn);
+       if (os_file_exists(buf)) {
+               if (set_cred_quoted(ctx->ifname, id, "ca_cert", buf) < 0) {
+                       wpa_printf(MSG_INFO, "Failed to set CA cert");
+               }
+       }
+}
+
+
+static void set_pps_cred_check_aaa_cert_status(struct hs20_osu_client *ctx,
+                                              int id, xml_node_t *node)
+{
+       char *str = xml_node_get_text(ctx->xml, node);
+
+       if (str == NULL)
+               return;
+
+       wpa_printf(MSG_INFO, "- Credential/CheckAAAServerCertStatus = %s", str);
+       if (os_strcasecmp(str, "true") == 0 &&
+           set_cred(ctx->ifname, id, "ocsp", "2") < 0)
+               wpa_printf(MSG_INFO, "Failed to set cred ocsp");
+       xml_node_get_text_free(ctx->xml, str);
+}
+
+
+static void set_pps_cred_sim(struct hs20_osu_client *ctx, int id,
+                            xml_node_t *sim, xml_node_t *realm)
+{
+       xml_node_t *node;
+       char *imsi, *eaptype, *str, buf[20];
+       int type;
+       int mnc_len = 3;
+       size_t imsi_len;
+
+       node = get_node(ctx->xml, sim, "EAPType");
+       if (node == NULL) {
+               wpa_printf(MSG_INFO, "No SIM/EAPType node in credential");
+               return;
+       }
+       eaptype = xml_node_get_text(ctx->xml, node);
+       if (eaptype == NULL) {
+               wpa_printf(MSG_INFO, "Could not extract SIM/EAPType");
+               return;
+       }
+       wpa_printf(MSG_INFO, " - Credential/SIM/EAPType = %s", eaptype);
+       type = atoi(eaptype);
+       xml_node_get_text_free(ctx->xml, eaptype);
+
+       switch (type) {
+       case EAP_TYPE_SIM:
+               if (set_cred(ctx->ifname, id, "eap", "SIM") < 0)
+                       wpa_printf(MSG_INFO, "Could not set eap=SIM");
+               break;
+       case EAP_TYPE_AKA:
+               if (set_cred(ctx->ifname, id, "eap", "AKA") < 0)
+                       wpa_printf(MSG_INFO, "Could not set eap=SIM");
+               break;
+       case EAP_TYPE_AKA_PRIME:
+               if (set_cred(ctx->ifname, id, "eap", "AKA'") < 0)
+                       wpa_printf(MSG_INFO, "Could not set eap=SIM");
+               break;
+       default:
+               wpa_printf(MSG_INFO, "Unsupported SIM/EAPType %d", type);
+               return;
+       }
+
+       node = get_node(ctx->xml, sim, "IMSI");
+       if (node == NULL) {
+               wpa_printf(MSG_INFO, "No SIM/IMSI node in credential");
+               return;
+       }
+       imsi = xml_node_get_text(ctx->xml, node);
+       if (imsi == NULL) {
+               wpa_printf(MSG_INFO, "Could not extract SIM/IMSI");
+               return;
+       }
+       wpa_printf(MSG_INFO, " - Credential/SIM/IMSI = %s", imsi);
+       imsi_len = os_strlen(imsi);
+       if (imsi_len < 7 || imsi_len + 2 > sizeof(buf)) {
+               wpa_printf(MSG_INFO, "Invalid IMSI length");
+               xml_node_get_text_free(ctx->xml, imsi);
+               return;
+       }
+
+       str = xml_node_get_text(ctx->xml, node);
+       if (str) {
+               char *pos;
+               pos = os_strstr(str, "mnc");
+               if (pos && os_strlen(pos) >= 6) {
+                       if (os_strncmp(imsi + 3, pos + 3, 3) == 0)
+                               mnc_len = 3;
+                       else if (os_strncmp(imsi + 3, pos + 4, 2) == 0)
+                               mnc_len = 2;
+               }
+               xml_node_get_text_free(ctx->xml, str);
+       }
+
+       os_memcpy(buf, imsi, 3 + mnc_len);
+       buf[3 + mnc_len] = '-';
+       os_strlcpy(buf + 3 + mnc_len + 1, imsi + 3 + mnc_len,
+                  sizeof(buf) - 3 - mnc_len - 1);
+
+       xml_node_get_text_free(ctx->xml, imsi);
+
+       if (set_cred_quoted(ctx->ifname, id, "imsi", buf) < 0)
+               wpa_printf(MSG_INFO, "Could not set IMSI");
+
+       if (set_cred_quoted(ctx->ifname, id, "milenage",
+                           "90dca4eda45b53cf0f12d7c9c3bc6a89:"
+                           "cb9cccc4b9258e6dca4760379fb82581:000000000123") <
+           0)
+               wpa_printf(MSG_INFO, "Could not set Milenage parameters");
+}
+
+
+static void set_pps_cred_credential(struct hs20_osu_client *ctx, int id,
+                                   xml_node_t *node, const char *fqdn)
+{
+       xml_node_t *child, *sim, *realm;
+       const char *name;
+
+       wpa_printf(MSG_INFO, "- Credential");
+
+       sim = get_node(ctx->xml, node, "SIM");
+       realm = get_node(ctx->xml, node, "Realm");
+
+       xml_node_for_each_child(ctx->xml, child, node) {
+               xml_node_for_each_check(ctx->xml, child);
+               name = xml_node_get_localname(ctx->xml, child);
+               if (os_strcasecmp(name, "CreationDate") == 0)
+                       set_pps_cred_creation_date(ctx, id, child);
+               else if (os_strcasecmp(name, "ExpirationDate") == 0)
+                       set_pps_cred_expiration_date(ctx, id, child);
+               else if (os_strcasecmp(name, "UsernamePassword") == 0)
+                       set_pps_cred_username_password(ctx, id, child);
+               else if (os_strcasecmp(name, "DigitalCertificate") == 0)
+                       set_pps_cred_digital_cert(ctx, id, child, fqdn);
+               else if (os_strcasecmp(name, "Realm") == 0)
+                       set_pps_cred_realm(ctx, id, child, fqdn, sim != NULL);
+               else if (os_strcasecmp(name, "CheckAAAServerCertStatus") == 0)
+                       set_pps_cred_check_aaa_cert_status(ctx, id, child);
+               else if (os_strcasecmp(name, "SIM") == 0)
+                       set_pps_cred_sim(ctx, id, child, realm);
+               else
+                       wpa_printf(MSG_INFO, "Unknown Credential node '%s'",
+                                  name);
+       }
+}
+
+
+static void set_pps_credential(struct hs20_osu_client *ctx, int id,
+                              xml_node_t *cred, const char *fqdn)
+{
+       xml_node_t *child;
+       const char *name;
+
+       xml_node_for_each_child(ctx->xml, child, cred) {
+               xml_node_for_each_check(ctx->xml, child);
+               name = xml_node_get_localname(ctx->xml, child);
+               if (os_strcasecmp(name, "Policy") == 0)
+                       set_pps_cred_policy(ctx, id, child);
+               else if (os_strcasecmp(name, "CredentialPriority") == 0)
+                       set_pps_cred_priority(ctx, id, child);
+               else if (os_strcasecmp(name, "AAAServerTrustRoot") == 0)
+                       set_pps_cred_aaa_server_trust_root(ctx, id, child);
+               else if (os_strcasecmp(name, "SubscriptionUpdate") == 0)
+                       set_pps_cred_sub_update(ctx, id, child);
+               else if (os_strcasecmp(name, "HomeSP") == 0)
+                       set_pps_cred_home_sp(ctx, id, child);
+               else if (os_strcasecmp(name, "SubscriptionParameters") == 0)
+                       set_pps_cred_sub_params(ctx, id, child);
+               else if (os_strcasecmp(name, "Credential") == 0)
+                       set_pps_cred_credential(ctx, id, child, fqdn);
+               else
+                       wpa_printf(MSG_INFO, "Unknown credential node '%s'",
+                                  name);
+       }
+}
+
+
+static void set_pps(struct hs20_osu_client *ctx, xml_node_t *pps,
+                   const char *fqdn)
+{
+       xml_node_t *child;
+       const char *name;
+       int id;
+       char *update_identifier = NULL;
+
+       /*
+        * TODO: Could consider more complex mechanism that would remove
+        * credentials only if there are changes in the information sent to
+        * wpa_supplicant.
+        */
+       remove_sp_creds(ctx, fqdn);
+
+       xml_node_for_each_child(ctx->xml, child, pps) {
+               xml_node_for_each_check(ctx->xml, child);
+               name = xml_node_get_localname(ctx->xml, child);
+               if (os_strcasecmp(name, "UpdateIdentifier") == 0) {
+                       update_identifier = xml_node_get_text(ctx->xml, child);
+                       if (update_identifier) {
+                               wpa_printf(MSG_INFO, "- UpdateIdentifier = %s",
+                                          update_identifier);
+                               break;
+                       }
+               }
+       }
+
+       xml_node_for_each_child(ctx->xml, child, pps) {
+               xml_node_for_each_check(ctx->xml, child);
+               name = xml_node_get_localname(ctx->xml, child);
+               if (os_strcasecmp(name, "UpdateIdentifier") == 0)
+                       continue;
+               id = add_cred(ctx->ifname);
+               if (id < 0) {
+                       wpa_printf(MSG_INFO, "Failed to add credential to wpa_supplicant");
+                       write_summary(ctx, "Failed to add credential to wpa_supplicant");
+                       break;
+               }
+               write_summary(ctx, "Add a credential to wpa_supplicant");
+               if (update_identifier &&
+                   set_cred(ctx->ifname, id, "update_identifier",
+                            update_identifier) < 0)
+                       wpa_printf(MSG_INFO, "Failed to set update_identifier");
+               if (set_cred_quoted(ctx->ifname, id, "provisioning_sp", fqdn) <
+                   0)
+                       wpa_printf(MSG_INFO, "Failed to set provisioning_sp");
+               wpa_printf(MSG_INFO, "credential localname: '%s'", name);
+               set_pps_credential(ctx, id, child, fqdn);
+               ctx->pps_cred_set = 1;
+       }
+
+       xml_node_get_text_free(ctx->xml, update_identifier);
+}
+
+
+void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname)
+{
+       xml_node_t *pps;
+       const char *fqdn;
+       char *fqdn_buf = NULL, *pos;
+
+       pps = node_from_file(ctx->xml, pps_fname);
+       if (pps == NULL) {
+               wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
+               return;
+       }
+
+       fqdn = os_strstr(pps_fname, "SP/");
+       if (fqdn) {
+               fqdn_buf = os_strdup(fqdn + 3);
+               if (fqdn_buf == NULL)
+                       return;
+               pos = os_strchr(fqdn_buf, '/');
+               if (pos)
+                       *pos = '\0';
+               fqdn = fqdn_buf;
+       } else
+               fqdn = "wi-fi.org";
+
+       wpa_printf(MSG_INFO, "Set PPS MO info to wpa_supplicant - SP FQDN %s",
+                  fqdn);
+       set_pps(ctx, pps, fqdn);
+
+       os_free(fqdn_buf);
+       xml_node_free(ctx->xml, pps);
+}
+
+
+static int cmd_get_fqdn(struct hs20_osu_client *ctx, const char *pps_fname)
+{
+       xml_node_t *pps, *node;
+       char *fqdn = NULL;
+
+       pps = node_from_file(ctx->xml, pps_fname);
+       if (pps == NULL) {
+               wpa_printf(MSG_INFO, "Could not read or parse '%s'", pps_fname);
+               return -1;
+       }
+
+       node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
+       if (node)
+               fqdn = xml_node_get_text(ctx->xml, node);
+
+       xml_node_free(ctx->xml, pps);
+
+       if (fqdn) {
+               FILE *f = fopen("pps-fqdn", "w");
+               if (f) {
+                       fprintf(f, "%s", fqdn);
+                       fclose(f);
+               }
+               xml_node_get_text_free(ctx->xml, fqdn);
+               return 0;
+       }
+
+       xml_node_get_text_free(ctx->xml, fqdn);
+       return -1;
+}
+
+
+static void cmd_to_tnds(struct hs20_osu_client *ctx, const char *in_fname,
+                       const char *out_fname, const char *urn, int use_path)
+{
+       xml_node_t *mo, *node;
+
+       mo = node_from_file(ctx->xml, in_fname);
+       if (mo == NULL) {
+               wpa_printf(MSG_INFO, "Could not read or parse '%s'", in_fname);
+               return;
+       }
+
+       node = mo_to_tnds(ctx->xml, mo, use_path, urn, NULL);
+       if (node) {
+               node_to_file(ctx->xml, out_fname, node);
+               xml_node_free(ctx->xml, node);
+       }
+
+       xml_node_free(ctx->xml, mo);
+}
+
+
+static void cmd_from_tnds(struct hs20_osu_client *ctx, const char *in_fname,
+                         const char *out_fname)
+{
+       xml_node_t *tnds, *mo;
+
+       tnds = node_from_file(ctx->xml, in_fname);
+       if (tnds == NULL) {
+               wpa_printf(MSG_INFO, "Could not read or parse '%s'", in_fname);
+               return;
+       }
+
+       mo = tnds_to_mo(ctx->xml, tnds);
+       if (mo) {
+               node_to_file(ctx->xml, out_fname, mo);
+               xml_node_free(ctx->xml, mo);
+       }
+
+       xml_node_free(ctx->xml, tnds);
+}
+
+
+struct osu_icon {
+       int id;
+       char lang[4];
+       char mime_type[256];
+       char filename[256];
+};
+
+struct osu_data {
+       char bssid[20];
+       char url[256];
+       unsigned int methods;
+       char osu_ssid[33];
+       char osu_nai[256];
+       struct osu_lang_text friendly_name[MAX_OSU_VALS];
+       size_t friendly_name_count;
+       struct osu_lang_text serv_desc[MAX_OSU_VALS];
+       size_t serv_desc_count;
+       struct osu_icon icon[MAX_OSU_VALS];
+       size_t icon_count;
+};
+
+
+static struct osu_data * parse_osu_providers(const char *fname, size_t *count)
+{
+       FILE *f;
+       char buf[1000];
+       struct osu_data *osu = NULL, *last = NULL;
+       size_t osu_count = 0;
+       char *pos, *end;
+
+       f = fopen(fname, "r");
+       if (f == NULL) {
+               wpa_printf(MSG_ERROR, "Could not open %s", fname);
+               return NULL;
+       }
+
+       while (fgets(buf, sizeof(buf), f)) {
+               pos = strchr(buf, '\n');
+               if (pos)
+                       *pos = '\0';
+
+               if (strncmp(buf, "OSU-PROVIDER ", 13) == 0) {
+                       last = realloc(osu, (osu_count + 1) * sizeof(*osu));
+                       if (last == NULL)
+                               break;
+                       osu = last;
+                       last = &osu[osu_count++];
+                       memset(last, 0, sizeof(*last));
+                       snprintf(last->bssid, sizeof(last->bssid), "%s",
+                                buf + 13);
+                       continue;
+               }
+               if (!last)
+                       continue;
+
+               if (strncmp(buf, "uri=", 4) == 0) {
+                       snprintf(last->url, sizeof(last->url), "%s", buf + 4);
+                       continue;
+               }
+
+               if (strncmp(buf, "methods=", 8) == 0) {
+                       last->methods = strtol(buf + 8, NULL, 16);
+                       continue;
+               }
+
+               if (strncmp(buf, "osu_ssid=", 9) == 0) {
+                       snprintf(last->osu_ssid, sizeof(last->osu_ssid),
+                                "%s", buf + 9);
+                       continue;
+               }
+
+               if (os_strncmp(buf, "osu_nai=", 8) == 0) {
+                       os_snprintf(last->osu_nai, sizeof(last->osu_nai),
+                                   "%s", buf + 8);
+                       continue;
+               }
+
+               if (strncmp(buf, "friendly_name=", 14) == 0) {
+                       struct osu_lang_text *txt;
+                       if (last->friendly_name_count == MAX_OSU_VALS)
+                               continue;
+                       pos = strchr(buf + 14, ':');
+                       if (pos == NULL)
+                               continue;
+                       *pos++ = '\0';
+                       txt = &last->friendly_name[last->friendly_name_count++];
+                       snprintf(txt->lang, sizeof(txt->lang), "%s", buf + 14);
+                       snprintf(txt->text, sizeof(txt->text), "%s", pos);
+               }
+
+               if (strncmp(buf, "desc=", 5) == 0) {
+                       struct osu_lang_text *txt;
+                       if (last->serv_desc_count == MAX_OSU_VALS)
+                               continue;
+                       pos = strchr(buf + 5, ':');
+                       if (pos == NULL)
+                               continue;
+                       *pos++ = '\0';
+                       txt = &last->serv_desc[last->serv_desc_count++];
+                       snprintf(txt->lang, sizeof(txt->lang), "%s", buf + 5);
+                       snprintf(txt->text, sizeof(txt->text), "%s", pos);
+               }
+
+               if (strncmp(buf, "icon=", 5) == 0) {
+                       struct osu_icon *icon;
+                       if (last->icon_count == MAX_OSU_VALS)
+                               continue;
+                       icon = &last->icon[last->icon_count++];
+                       icon->id = atoi(buf + 5);
+                       pos = strchr(buf, ':');
+                       if (pos == NULL)
+                               continue;
+                       pos = strchr(pos + 1, ':');
+                       if (pos == NULL)
+                               continue;
+                       pos = strchr(pos + 1, ':');
+                       if (pos == NULL)
+                               continue;
+                       pos++;
+                       end = strchr(pos, ':');
+                       if (!end)
+                               continue;
+                       *end = '\0';
+                       snprintf(icon->lang, sizeof(icon->lang), "%s", pos);
+                       pos = end + 1;
+
+                       end = strchr(pos, ':');
+                       if (end)
+                               *end = '\0';
+                       snprintf(icon->mime_type, sizeof(icon->mime_type),
+                                "%s", pos);
+                       if (!pos)
+                               continue;
+                       pos = end + 1;
+
+                       end = strchr(pos, ':');
+                       if (end)
+                               *end = '\0';
+                       snprintf(icon->filename, sizeof(icon->filename),
+                                "%s", pos);
+                       continue;
+               }
+       }
+
+       fclose(f);
+
+       *count = osu_count;
+       return osu;
+}
+
+
+static int osu_connect(struct hs20_osu_client *ctx, const char *bssid,
+                      const char *ssid, const char *url,
+                      unsigned int methods, int no_prod_assoc,
+                      const char *osu_nai)
+{
+       int id;
+       const char *ifname = ctx->ifname;
+       char buf[200];
+       struct wpa_ctrl *mon;
+       int res;
+
+       id = add_network(ifname);
+       if (id < 0)
+               return -1;
+       if (set_network_quoted(ifname, id, "ssid", ssid) < 0)
+               return -1;
+       if (osu_nai && os_strlen(osu_nai) > 0) {
+               char dir[255], fname[300];
+               if (getcwd(dir, sizeof(dir)) == NULL)
+                       return -1;
+               os_snprintf(fname, sizeof(fname), "%s/osu-ca.pem", dir);
+
+               if (set_network(ifname, id, "proto", "OSEN") < 0 ||
+                   set_network(ifname, id, "key_mgmt", "OSEN") < 0 ||
+                   set_network(ifname, id, "pairwise", "CCMP") < 0 ||
+                   set_network(ifname, id, "group", "GTK_NOT_USED") < 0 ||
+                   set_network(ifname, id, "eap", "WFA-UNAUTH-TLS") < 0 ||
+                   set_network(ifname, id, "ocsp", "2") < 0 ||
+                   set_network_quoted(ifname, id, "identity", osu_nai) < 0 ||
+                   set_network_quoted(ifname, id, "ca_cert", fname) < 0)
+                       return -1;
+       } else {
+               if (set_network(ifname, id, "key_mgmt", "NONE") < 0)
+                       return -1;
+       }
+
+       mon = open_wpa_mon(ifname);
+       if (mon == NULL)
+               return -1;
+
+       wpa_printf(MSG_INFO, "Associate with OSU SSID");
+       write_summary(ctx, "Associate with OSU SSID");
+       snprintf(buf, sizeof(buf), "SELECT_NETWORK %d", id);
+       if (wpa_command(ifname, buf) < 0)
+               return -1;
+
+       res = get_wpa_cli_event(mon, "CTRL-EVENT-CONNECTED",
+                               buf, sizeof(buf));
+
+       wpa_ctrl_detach(mon);
+       wpa_ctrl_close(mon);
+
+       if (res < 0) {
+               wpa_printf(MSG_INFO, "Could not connect");
+               write_summary(ctx, "Could not connect to OSU network");
+               wpa_printf(MSG_INFO, "Remove OSU network connection");
+               snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id);
+               wpa_command(ifname, buf);
+               return -1;
+       }
+
+       write_summary(ctx, "Waiting for IP address for subscription registration");
+       if (wait_ip_addr(ifname, 15) < 0) {
+               wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
+       }
+
+       if (no_prod_assoc) {
+               if (res < 0)
+                       return -1;
+               wpa_printf(MSG_INFO, "No production connection used for testing purposes");
+               write_summary(ctx, "No production connection used for testing purposes");
+               return 0;
+       }
+
+       ctx->no_reconnect = 1;
+       if (methods & 0x02)
+               res = cmd_prov(ctx, url);
+       else if (methods & 0x01)
+               res = cmd_oma_dm_prov(ctx, url);
+
+       wpa_printf(MSG_INFO, "Remove OSU network connection");
+       write_summary(ctx, "Remove OSU network connection");
+       snprintf(buf, sizeof(buf), "REMOVE_NETWORK %d", id);
+       wpa_command(ifname, buf);
+
+       if (res < 0)
+               return -1;
+
+       wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+       write_summary(ctx, "Requesting reconnection with updated configuration");
+       if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0) {
+               wpa_printf(MSG_INFO, "Failed to request wpa_supplicant to reconnect");
+               write_summary(ctx, "Failed to request wpa_supplicant to reconnect");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int cmd_osu_select(struct hs20_osu_client *ctx, const char *dir,
+                         int connect, int no_prod_assoc,
+                         const char *friendly_name)
+{
+       char fname[255];
+       FILE *f;
+       struct osu_data *osu = NULL, *last = NULL;
+       size_t osu_count, i, j;
+       int ret;
+
+       write_summary(ctx, "OSU provider selection");
+
+       if (dir == NULL) {
+               wpa_printf(MSG_INFO, "Missing dir parameter to osu_select");
+               return -1;
+       }
+
+       snprintf(fname, sizeof(fname), "%s/osu-providers.txt", dir);
+       osu = parse_osu_providers(fname, &osu_count);
+       if (osu == NULL) {
+               wpa_printf(MSG_INFO, "Could not any OSU providers from %s",
+                          fname);
+               write_result(ctx, "No OSU providers available");
+               return -1;
+       }
+
+       if (friendly_name) {
+               for (i = 0; i < osu_count; i++) {
+                       last = &osu[i];
+                       for (j = 0; j < last->friendly_name_count; j++) {
+                               if (os_strcmp(last->friendly_name[j].text,
+                                             friendly_name) == 0)
+                                       break;
+                       }
+                       if (j < last->friendly_name_count)
+                               break;
+               }
+               if (i == osu_count) {
+                       wpa_printf(MSG_INFO, "Requested operator friendly name '%s' not found in the list of available providers",
+                                  friendly_name);
+                       write_summary(ctx, "Requested operator friendly name '%s' not found in the list of available providers",
+                                     friendly_name);
+                       free(osu);
+                       return -1;
+               }
+
+               wpa_printf(MSG_INFO, "OSU Provider selected based on requested operator friendly name '%s'",
+                          friendly_name);
+               write_summary(ctx, "OSU Provider selected based on requested operator friendly name '%s'",
+                             friendly_name);
+               ret = i + 1;
+               goto selected;
+       }
+
+       snprintf(fname, sizeof(fname), "%s/osu-providers.html", dir);
+       f = fopen(fname, "w");
+       if (f == NULL) {
+               wpa_printf(MSG_INFO, "Could not open %s", fname);
+               free(osu);
+               return -1;
+       }
+
+       fprintf(f, "<html><head>"
+               "<meta http-equiv=\"Content-type\" content=\"text/html; "
+               "charset=utf-8\"<title>Select service operator</title>"
+               "</head><body><h1>Select service operator</h1>\n");
+
+       if (osu_count == 0)
+               fprintf(f, "No online signup available\n");
+
+       for (i = 0; i < osu_count; i++) {
+               last = &osu[i];
+#ifdef ANDROID
+               fprintf(f, "<p>\n"
+                       "<a href=\"http://localhost:12345/osu/%d\">"
+                       "<table><tr><td>", (int) i + 1);
+#else /* ANDROID */
+               fprintf(f, "<p>\n"
+                       "<a href=\"osu://%d\">"
+                       "<table><tr><td>", (int) i + 1);
+#endif /* ANDROID */
+               for (j = 0; j < last->icon_count; j++) {
+                       fprintf(f, "<img src=\"osu-icon-%d.%s\">\n",
+                               last->icon[j].id,
+                               strcasecmp(last->icon[j].mime_type,
+                                          "image/png") == 0 ? "png" : "icon");
+               }
+               fprintf(f, "<td>");
+               for (j = 0; j < last->friendly_name_count; j++) {
+                       fprintf(f, "<small>[%s]</small> %s<br>\n",
+                               last->friendly_name[j].lang,
+                               last->friendly_name[j].text);
+               }
+               fprintf(f, "<tr><td colspan=2>");
+               for (j = 0; j < last->serv_desc_count; j++) {
+                       fprintf(f, "<small>[%s]</small> %s<br>\n",
+                               last->serv_desc[j].lang,
+                               last->serv_desc[j].text);
+               }
+               fprintf(f, "</table></a><br><small>BSSID: %s<br>\n"
+                       "SSID: %s<br>\n",
+                       last->bssid, last->osu_ssid);
+               if (last->osu_nai)
+                       fprintf(f, "NAI: %s<br>\n", last->osu_nai);
+               fprintf(f, "URL: %s<br>\n"
+                       "methods:%s%s<br>\n"
+                       "</small></p>\n",
+                       last->url,
+                       last->methods & 0x01 ? " OMA-DM" : "",
+                       last->methods & 0x02 ? " SOAP-XML-SPP" : "");
+       }
+
+       fprintf(f, "</body></html>\n");
+
+       fclose(f);
+
+       snprintf(fname, sizeof(fname), "file://%s/osu-providers.html", dir);
+       write_summary(ctx, "Start web browser with OSU provider selection page");
+       ret = hs20_web_browser(fname);
+
+selected:
+       if (ret > 0 && (size_t) ret <= osu_count) {
+               char *data;
+               size_t data_len;
+
+               wpa_printf(MSG_INFO, "Selected OSU id=%d", ret);
+               last = &osu[ret - 1];
+               ret = 0;
+               wpa_printf(MSG_INFO, "BSSID: %s", last->bssid);
+               wpa_printf(MSG_INFO, "SSID: %s", last->osu_ssid);
+               wpa_printf(MSG_INFO, "URL: %s", last->url);
+               write_summary(ctx, "Selected OSU provider id=%d BSSID=%s SSID=%s URL=%s",
+                             ret, last->bssid, last->osu_ssid, last->url);
+
+               ctx->friendly_name_count = last->friendly_name_count;
+               for (j = 0; j < last->friendly_name_count; j++) {
+                       wpa_printf(MSG_INFO, "FRIENDLY_NAME: [%s]%s",
+                                  last->friendly_name[j].lang,
+                                  last->friendly_name[j].text);
+                       os_strlcpy(ctx->friendly_name[j].lang,
+                                  last->friendly_name[j].lang,
+                                  sizeof(ctx->friendly_name[j].lang));
+                       os_strlcpy(ctx->friendly_name[j].text,
+                                  last->friendly_name[j].text,
+                                  sizeof(ctx->friendly_name[j].text));
+               }
+
+               ctx->icon_count = last->icon_count;
+               for (j = 0; j < last->icon_count; j++) {
+                       char fname[256];
+
+                       os_snprintf(fname, sizeof(fname), "%s/osu-icon-%d.%s",
+                                   dir, last->icon[j].id,
+                                   strcasecmp(last->icon[j].mime_type,
+                                              "image/png") == 0 ?
+                                   "png" : "icon");
+                       wpa_printf(MSG_INFO, "ICON: %s (%s)",
+                                  fname, last->icon[j].filename);
+                       os_strlcpy(ctx->icon_filename[j],
+                                  last->icon[j].filename,
+                                  sizeof(ctx->icon_filename[j]));
+
+                       data = os_readfile(fname, &data_len);
+                       if (data) {
+                               sha256_vector(1, (const u8 **) &data, &data_len,
+                                             ctx->icon_hash[j]);
+                               os_free(data);
+                       }
+               }
+
+               if (connect == 2) {
+                       if (last->methods & 0x02)
+                               ret = cmd_prov(ctx, last->url);
+                       else if (last->methods & 0x01)
+                               ret = cmd_oma_dm_prov(ctx, last->url);
+                       else
+                               ret = -1;
+               } else if (connect)
+                       ret = osu_connect(ctx, last->bssid, last->osu_ssid,
+                                         last->url, last->methods,
+                                         no_prod_assoc, last->osu_nai);
+       } else
+               ret = -1;
+
+       free(osu);
+
+       return ret;
+}
+
+
+static int cmd_signup(struct hs20_osu_client *ctx, int no_prod_assoc,
+                     const char *friendly_name)
+{
+       char dir[255];
+       char fname[300], buf[400];
+       struct wpa_ctrl *mon;
+       const char *ifname;
+       int res;
+
+       ifname = ctx->ifname;
+
+       if (getcwd(dir, sizeof(dir)) == NULL)
+               return -1;
+
+       snprintf(fname, sizeof(fname), "%s/osu-info", dir);
+       if (mkdir(fname, S_IRWXU | S_IRWXG) < 0 && errno != EEXIST) {
+               wpa_printf(MSG_INFO, "mkdir(%s) failed: %s",
+                          fname, strerror(errno));
+               return -1;
+       }
+
+       snprintf(buf, sizeof(buf), "SET osu_dir %s", fname);
+       if (wpa_command(ifname, buf) < 0) {
+               wpa_printf(MSG_INFO, "Failed to configure osu_dir to wpa_supplicant");
+               return -1;
+       }
+
+       mon = open_wpa_mon(ifname);
+       if (mon == NULL)
+               return -1;
+
+       wpa_printf(MSG_INFO, "Starting OSU fetch");
+       write_summary(ctx, "Starting OSU provider information fetch");
+       if (wpa_command(ifname, "FETCH_OSU") < 0) {
+               wpa_printf(MSG_INFO, "Could not start OSU fetch");
+               wpa_ctrl_detach(mon);
+               wpa_ctrl_close(mon);
+               return -1;
+       }
+       res = get_wpa_cli_event(mon, "OSU provider fetch completed",
+                               buf, sizeof(buf));
+
+       wpa_ctrl_detach(mon);
+       wpa_ctrl_close(mon);
+
+       if (res < 0) {
+               wpa_printf(MSG_INFO, "OSU fetch did not complete");
+               write_summary(ctx, "OSU fetch did not complete");
+               return -1;
+       }
+       wpa_printf(MSG_INFO, "OSU provider fetch completed");
+
+       return cmd_osu_select(ctx, fname, 1, no_prod_assoc, friendly_name);
+}
+
+
+static int cmd_sub_rem(struct hs20_osu_client *ctx, const char *address,
+                      const char *pps_fname, const char *ca_fname)
+{
+       xml_node_t *pps, *node;
+       char pps_fname_buf[300];
+       char ca_fname_buf[200];
+       char *cred_username = NULL;
+       char *cred_password = NULL;
+       char *sub_rem_uri = NULL;
+       char client_cert_buf[200];
+       char *client_cert = NULL;
+       char client_key_buf[200];
+       char *client_key = NULL;
+       int spp;
+
+       wpa_printf(MSG_INFO, "Subscription remediation requested with Server URL: %s",
+                  address);
+
+       if (!pps_fname) {
+               char buf[256];
+               wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information");
+               if (os_strncmp(address, "fqdn=", 5) == 0) {
+                       wpa_printf(MSG_INFO, "Use requested FQDN from command line");
+                       os_snprintf(buf, sizeof(buf), "%s", address + 5);
+                       address = NULL;
+               } else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf,
+                                         sizeof(buf)) < 0) {
+                       wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant");
+                       return -1;
+               }
+               os_free(ctx->fqdn);
+               ctx->fqdn = os_strdup(buf);
+               if (ctx->fqdn == NULL)
+                       return -1;
+               wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s",
+                          buf);
+               os_snprintf(pps_fname_buf, sizeof(pps_fname_buf),
+                           "SP/%s/pps.xml", ctx->fqdn);
+               pps_fname = pps_fname_buf;
+
+               os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), "SP/%s/ca.pem",
+                           ctx->fqdn);
+               ca_fname = ca_fname_buf;
+       }
+
+       if (!os_file_exists(pps_fname)) {
+               wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible",
+                          pps_fname);
+               return -1;
+       }
+       wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname);
+
+       if (ca_fname && !os_file_exists(ca_fname)) {
+               wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible",
+                          ca_fname);
+               return -1;
+       }
+       wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname);
+       ctx->ca_fname = ca_fname;
+
+       pps = node_from_file(ctx->xml, pps_fname);
+       if (pps == NULL) {
+               wpa_printf(MSG_INFO, "Could not read PPS MO");
+               return -1;
+       }
+
+       if (!ctx->fqdn) {
+               char *tmp;
+               node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
+               if (node == NULL) {
+                       wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS");
+                       return -1;
+               }
+               tmp = xml_node_get_text(ctx->xml, node);
+               if (tmp == NULL) {
+                       wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS");
+                       return -1;
+               }
+               ctx->fqdn = os_strdup(tmp);
+               xml_node_get_text_free(ctx->xml, tmp);
+               if (!ctx->fqdn) {
+                       wpa_printf(MSG_INFO, "No FQDN known");
+                       return -1;
+               }
+       }
+
+       node = get_child_node(ctx->xml, pps,
+                             "SubscriptionUpdate/UpdateMethod");
+       if (node) {
+               char *tmp;
+               tmp = xml_node_get_text(ctx->xml, node);
+               if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0)
+                       spp = 0;
+               else
+                       spp = 1;
+       } else {
+               wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP");
+               spp = 1;
+       }
+
+       get_user_pw(ctx, pps, "SubscriptionUpdate/UsernamePassword",
+                   &cred_username, &cred_password);
+       if (cred_username)
+               wpa_printf(MSG_INFO, "Using username: %s", cred_username);
+       if (cred_password)
+               wpa_printf(MSG_DEBUG, "Using password: %s", cred_password);
+
+       if (cred_username == NULL && cred_password == NULL &&
+           get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) {
+               wpa_printf(MSG_INFO, "Using client certificate");
+               os_snprintf(client_cert_buf, sizeof(client_cert_buf),
+                           "SP/%s/client-cert.pem", ctx->fqdn);
+               client_cert = client_cert_buf;
+               os_snprintf(client_key_buf, sizeof(client_key_buf),
+                           "SP/%s/client-key.pem", ctx->fqdn);
+               client_key = client_key_buf;
+               ctx->client_cert_present = 1;
+       }
+
+       node = get_child_node(ctx->xml, pps, "SubscriptionUpdate/URI");
+       if (node) {
+               sub_rem_uri = xml_node_get_text(ctx->xml, node);
+               if (sub_rem_uri &&
+                   (!address || os_strcmp(address, sub_rem_uri) != 0)) {
+                       wpa_printf(MSG_INFO, "Override sub rem URI based on PPS: %s",
+                                  sub_rem_uri);
+                       address = sub_rem_uri;
+               }
+       }
+       if (!address) {
+               wpa_printf(MSG_INFO, "Server URL not known");
+               return -1;
+       }
+
+       write_summary(ctx, "Wait for IP address for subscriptiom remediation");
+       wpa_printf(MSG_INFO, "Wait for IP address before starting subscription remediation");
+
+       if (wait_ip_addr(ctx->ifname, 15) < 0) {
+               wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
+       }
+
+       if (spp)
+               spp_sub_rem(ctx, address, pps_fname,
+                           client_cert, client_key,
+                           cred_username, cred_password, pps);
+       else
+               oma_dm_sub_rem(ctx, address, pps_fname,
+                              client_cert, client_key,
+                              cred_username, cred_password, pps);
+
+       xml_node_get_text_free(ctx->xml, sub_rem_uri);
+       xml_node_get_text_free(ctx->xml, cred_username);
+       str_clear_free(cred_password);
+       xml_node_free(ctx->xml, pps);
+       return 0;
+}
+
+
+static int cmd_pol_upd(struct hs20_osu_client *ctx, const char *address,
+                      const char *pps_fname, const char *ca_fname)
+{
+       xml_node_t *pps;
+       xml_node_t *node;
+       char pps_fname_buf[300];
+       char ca_fname_buf[200];
+       char *uri = NULL;
+       char *cred_username = NULL;
+       char *cred_password = NULL;
+       char client_cert_buf[200];
+       char *client_cert = NULL;
+       char client_key_buf[200];
+       char *client_key = NULL;
+       int spp;
+
+       wpa_printf(MSG_INFO, "Policy update requested");
+
+       if (!pps_fname) {
+               char buf[256];
+               wpa_printf(MSG_INFO, "Determining PPS file based on Home SP information");
+               if (os_strncmp(address, "fqdn=", 5) == 0) {
+                       wpa_printf(MSG_INFO, "Use requested FQDN from command line");
+                       os_snprintf(buf, sizeof(buf), "%s", address + 5);
+                       address = NULL;
+               } else if (get_wpa_status(ctx->ifname, "provisioning_sp", buf,
+                                         sizeof(buf)) < 0) {
+                       wpa_printf(MSG_INFO, "Could not get provisioning Home SP FQDN from wpa_supplicant");
+                       return -1;
+               }
+               os_free(ctx->fqdn);
+               ctx->fqdn = os_strdup(buf);
+               if (ctx->fqdn == NULL)
+                       return -1;
+               wpa_printf(MSG_INFO, "Home SP FQDN for current credential: %s",
+                          buf);
+               os_snprintf(pps_fname_buf, sizeof(pps_fname_buf),
+                           "SP/%s/pps.xml", ctx->fqdn);
+               pps_fname = pps_fname_buf;
+
+               os_snprintf(ca_fname_buf, sizeof(ca_fname_buf), "SP/%s/ca.pem",
+                           buf);
+               ca_fname = ca_fname_buf;
+       }
+
+       if (!os_file_exists(pps_fname)) {
+               wpa_printf(MSG_INFO, "PPS file '%s' does not exist or is not accessible",
+                          pps_fname);
+               return -1;
+       }
+       wpa_printf(MSG_INFO, "Using PPS file: %s", pps_fname);
+
+       if (ca_fname && !os_file_exists(ca_fname)) {
+               wpa_printf(MSG_INFO, "CA file '%s' does not exist or is not accessible",
+                          ca_fname);
+               return -1;
+       }
+       wpa_printf(MSG_INFO, "Using server trust root: %s", ca_fname);
+       ctx->ca_fname = ca_fname;
+
+       pps = node_from_file(ctx->xml, pps_fname);
+       if (pps == NULL) {
+               wpa_printf(MSG_INFO, "Could not read PPS MO");
+               return -1;
+       }
+
+       if (!ctx->fqdn) {
+               char *tmp;
+               node = get_child_node(ctx->xml, pps, "HomeSP/FQDN");
+               if (node == NULL) {
+                       wpa_printf(MSG_INFO, "No HomeSP/FQDN found from PPS");
+                       return -1;
+               }
+               tmp = xml_node_get_text(ctx->xml, node);
+               if (tmp == NULL) {
+                       wpa_printf(MSG_INFO, "No HomeSP/FQDN text found from PPS");
+                       return -1;
+               }
+               ctx->fqdn = os_strdup(tmp);
+               xml_node_get_text_free(ctx->xml, tmp);
+               if (!ctx->fqdn) {
+                       wpa_printf(MSG_INFO, "No FQDN known");
+                       return -1;
+               }
+       }
+
+       node = get_child_node(ctx->xml, pps,
+                             "Policy/PolicyUpdate/UpdateMethod");
+       if (node) {
+               char *tmp;
+               tmp = xml_node_get_text(ctx->xml, node);
+               if (tmp && os_strcasecmp(tmp, "OMA-DM-ClientInitiated") == 0)
+                       spp = 0;
+               else
+                       spp = 1;
+       } else {
+               wpa_printf(MSG_INFO, "No UpdateMethod specified - assume SPP");
+               spp = 1;
+       }
+
+       get_user_pw(ctx, pps, "Policy/PolicyUpdate/UsernamePassword",
+                   &cred_username, &cred_password);
+       if (cred_username)
+               wpa_printf(MSG_INFO, "Using username: %s", cred_username);
+       if (cred_password)
+               wpa_printf(MSG_DEBUG, "Using password: %s", cred_password);
+
+       if (cred_username == NULL && cred_password == NULL &&
+           get_child_node(ctx->xml, pps, "Credential/DigitalCertificate")) {
+               wpa_printf(MSG_INFO, "Using client certificate");
+               os_snprintf(client_cert_buf, sizeof(client_cert_buf),
+                           "SP/%s/client-cert.pem", ctx->fqdn);
+               client_cert = client_cert_buf;
+               os_snprintf(client_key_buf, sizeof(client_key_buf),
+                           "SP/%s/client-key.pem", ctx->fqdn);
+               client_key = client_key_buf;
+       }
+
+       if (!address) {
+               node = get_child_node(ctx->xml, pps, "Policy/PolicyUpdate/URI");
+               if (node) {
+                       uri = xml_node_get_text(ctx->xml, node);
+                       wpa_printf(MSG_INFO, "URI based on PPS: %s", uri);
+                       address = uri;
+               }
+       }
+       if (!address) {
+               wpa_printf(MSG_INFO, "Server URL not known");
+               return -1;
+       }
+
+       if (spp)
+               spp_pol_upd(ctx, address, pps_fname,
+                           client_cert, client_key,
+                           cred_username, cred_password, pps);
+       else
+               oma_dm_pol_upd(ctx, address, pps_fname,
+                              client_cert, client_key,
+                              cred_username, cred_password, pps);
+
+       xml_node_get_text_free(ctx->xml, uri);
+       xml_node_get_text_free(ctx->xml, cred_username);
+       str_clear_free(cred_password);
+       xml_node_free(ctx->xml, pps);
+
+       return 0;
+}
+
+
+static char * get_hostname(const char *url)
+{
+       const char *pos, *end, *end2;
+       char *ret;
+
+       if (url == NULL)
+               return NULL;
+
+       pos = os_strchr(url, '/');
+       if (pos == NULL)
+               return NULL;
+       pos++;
+       if (*pos != '/')
+               return NULL;
+       pos++;
+
+       end = os_strchr(pos, '/');
+       end2 = os_strchr(pos, ':');
+       if (end && end2 && end2 < end)
+               end = end2;
+       if (end)
+               end--;
+       else {
+               end = pos;
+               while (*end)
+                       end++;
+               if (end > pos)
+                       end--;
+       }
+
+       ret = os_malloc(end - pos + 2);
+       if (ret == NULL)
+               return NULL;
+
+       os_memcpy(ret, pos, end - pos + 1);
+       ret[end - pos + 1] = '\0';
+
+       return ret;
+}
+
+
+static int osu_cert_cb(void *_ctx, struct http_cert *cert)
+{
+       struct hs20_osu_client *ctx = _ctx;
+       unsigned int i, j;
+       int found;
+       char *host = NULL;
+
+       wpa_printf(MSG_INFO, "osu_cert_cb(osu_cert_validation=%d)",
+                  !ctx->no_osu_cert_validation);
+
+       host = get_hostname(ctx->server_url);
+
+       for (i = 0; i < ctx->server_dnsname_count; i++)
+               os_free(ctx->server_dnsname[i]);
+       os_free(ctx->server_dnsname);
+       ctx->server_dnsname = os_calloc(cert->num_dnsname, sizeof(char *));
+       ctx->server_dnsname_count = 0;
+
+       found = 0;
+       for (i = 0; i < cert->num_dnsname; i++) {
+               if (ctx->server_dnsname) {
+                       ctx->server_dnsname[ctx->server_dnsname_count] =
+                               os_strdup(cert->dnsname[i]);
+                       if (ctx->server_dnsname[ctx->server_dnsname_count])
+                               ctx->server_dnsname_count++;
+               }
+               if (host && os_strcasecmp(host, cert->dnsname[i]) == 0)
+                       found = 1;
+               wpa_printf(MSG_INFO, "dNSName '%s'", cert->dnsname[i]);
+       }
+
+       if (host && !found) {
+               wpa_printf(MSG_INFO, "Server name from URL (%s) did not match any dNSName - abort connection",
+                          host);
+               write_result(ctx, "Server name from URL (%s) did not match any dNSName - abort connection",
+                            host);
+               os_free(host);
+               return -1;
+       }
+
+       os_free(host);
+
+       for (i = 0; i < cert->num_othername; i++) {
+               if (os_strcmp(cert->othername[i].oid,
+                             "1.3.6.1.4.1.40808.1.1.1") == 0) {
+                       wpa_hexdump_ascii(MSG_INFO,
+                                         "id-wfa-hotspot-friendlyName",
+                                         cert->othername[i].data,
+                                         cert->othername[i].len);
+               }
+       }
+
+       for (j = 0; !ctx->no_osu_cert_validation &&
+                    j < ctx->friendly_name_count; j++) {
+               int found = 0;
+               for (i = 0; i < cert->num_othername; i++) {
+                       if (os_strcmp(cert->othername[i].oid,
+                                     "1.3.6.1.4.1.40808.1.1.1") != 0)
+                               continue;
+                       if (cert->othername[i].len < 3)
+                               continue;
+                       if (os_strncasecmp((char *) cert->othername[i].data,
+                                          ctx->friendly_name[j].lang, 3) != 0)
+                               continue;
+                       if (os_strncmp((char *) cert->othername[i].data + 3,
+                                      ctx->friendly_name[j].text,
+                                      cert->othername[i].len - 3) == 0) {
+                               found = 1;
+                               break;
+                       }
+               }
+
+               if (!found) {
+                       wpa_printf(MSG_INFO, "No friendly name match found for '[%s]%s'",
+                                  ctx->friendly_name[j].lang,
+                                  ctx->friendly_name[j].text);
+                       write_result(ctx, "No friendly name match found for '[%s]%s'",
+                                    ctx->friendly_name[j].lang,
+                                    ctx->friendly_name[j].text);
+                       return -1;
+               }
+       }
+
+       for (i = 0; i < cert->num_logo; i++) {
+               struct http_logo *logo = &cert->logo[i];
+
+               wpa_printf(MSG_INFO, "logo hash alg %s uri '%s'",
+                          logo->alg_oid, logo->uri);
+               wpa_hexdump_ascii(MSG_INFO, "hashValue",
+                                 logo->hash, logo->hash_len);
+       }
+
+       for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) {
+               int found = 0;
+               char *name = ctx->icon_filename[j];
+               size_t name_len = os_strlen(name);
+
+               wpa_printf(MSG_INFO, "Looking for icon file name '%s' match",
+                          name);
+               for (i = 0; i < cert->num_logo; i++) {
+                       struct http_logo *logo = &cert->logo[i];
+                       size_t uri_len = os_strlen(logo->uri);
+                       char *pos;
+
+                       wpa_printf(MSG_INFO, "Comparing to '%s' uri_len=%d name_len=%d",
+                                  logo->uri, (int) uri_len, (int) name_len);
+                       if (uri_len < 1 + name_len)
+                               continue;
+                       pos = &logo->uri[uri_len - name_len - 1];
+                       if (*pos != '/')
+                               continue;
+                       pos++;
+                       if (os_strcmp(pos, name) == 0) {
+                               found = 1;
+                               break;
+                       }
+               }
+
+               if (!found) {
+                       wpa_printf(MSG_INFO, "No icon filename match found for '%s'",
+                                  name);
+                       write_result(ctx,
+                                    "No icon filename match found for '%s'",
+                                    name);
+                       return -1;
+               }
+       }
+
+       for (j = 0; !ctx->no_osu_cert_validation && j < ctx->icon_count; j++) {
+               int found = 0;
+
+               for (i = 0; i < cert->num_logo; i++) {
+                       struct http_logo *logo = &cert->logo[i];
+
+                       if (logo->hash_len != 32)
+                               continue;
+                       if (os_memcmp(logo->hash, ctx->icon_hash[j], 32) == 0) {
+                               found = 1;
+                               break;
+                       }
+               }
+
+               if (!found) {
+                       wpa_printf(MSG_INFO, "No icon hash match found");
+                       write_result(ctx, "No icon hash match found");
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+
+static int init_ctx(struct hs20_osu_client *ctx)
+{
+       xml_node_t *devinfo, *devid;
+
+       os_memset(ctx, 0, sizeof(*ctx));
+       ctx->ifname = "wlan0";
+       ctx->xml = xml_node_init_ctx(ctx, NULL);
+       if (ctx->xml == NULL)
+               return -1;
+
+       devinfo = node_from_file(ctx->xml, "devinfo.xml");
+       if (!devinfo) {
+               wpa_printf(MSG_ERROR, "devinfo.xml not found");
+               return -1;
+       }
+
+       devid = get_node(ctx->xml, devinfo, "DevId");
+       if (devid) {
+               char *tmp = xml_node_get_text(ctx->xml, devid);
+               if (tmp) {
+                       ctx->devid = os_strdup(tmp);
+                       xml_node_get_text_free(ctx->xml, tmp);
+               }
+       }
+       xml_node_free(ctx->xml, devinfo);
+
+       if (ctx->devid == NULL) {
+               wpa_printf(MSG_ERROR, "Could not fetch DevId from devinfo.xml");
+               return -1;
+       }
+
+       ctx->http = http_init_ctx(ctx, ctx->xml);
+       if (ctx->http == NULL) {
+               xml_node_deinit_ctx(ctx->xml);
+               return -1;
+       }
+       http_ocsp_set(ctx->http, 2);
+       http_set_cert_cb(ctx->http, osu_cert_cb, ctx);
+
+       return 0;
+}
+
+
+static void deinit_ctx(struct hs20_osu_client *ctx)
+{
+       size_t i;
+
+       http_deinit_ctx(ctx->http);
+       xml_node_deinit_ctx(ctx->xml);
+       os_free(ctx->fqdn);
+       os_free(ctx->server_url);
+       os_free(ctx->devid);
+
+       for (i = 0; i < ctx->server_dnsname_count; i++)
+               os_free(ctx->server_dnsname[i]);
+       os_free(ctx->server_dnsname);
+}
+
+
+static void check_workarounds(struct hs20_osu_client *ctx)
+{
+       FILE *f;
+       char buf[100];
+       unsigned long int val = 0;
+
+       f = fopen("hs20-osu-client.workarounds", "r");
+       if (f == NULL)
+               return;
+
+       if (fgets(buf, sizeof(buf), f))
+               val = strtoul(buf, NULL, 16);
+
+       fclose(f);
+
+       if (val) {
+               wpa_printf(MSG_INFO, "Workarounds enabled: 0x%lx", val);
+               ctx->workarounds = val;
+               if (ctx->workarounds & WORKAROUND_OCSP_OPTIONAL)
+                       http_ocsp_set(ctx->http, 1);
+       }
+}
+
+
+static void usage(void)
+{
+       printf("usage: hs20-osu-client [-dddqqKt] [-S<station ifname>] \\\n"
+              "    [-w<wpa_supplicant ctrl_iface dir>] "
+              "[-r<result file>] [-f<debug file>] \\\n"
+              "    [-s<summary file>] \\\n"
+              "    <command> [arguments..]\n"
+              "commands:\n"
+              "- to_tnds <XML MO> <XML MO in TNDS format> [URN]\n"
+              "- to_tnds2 <XML MO> <XML MO in TNDS format (Path) "
+              "[URN]>\n"
+              "- from_tnds <XML MO in TNDS format> <XML MO>\n"
+              "- set_pps <PerProviderSubscription XML file name>\n"
+              "- get_fqdn <PerProviderSubscription XML file name>\n"
+              "- pol_upd [Server URL] [PPS] [CA cert]\n"
+              "- sub_rem <Server URL> [PPS] [CA cert]\n"
+              "- prov <Server URL> [CA cert]\n"
+              "- oma_dm_prov <Server URL> [CA cert]\n"
+              "- sim_prov <Server URL> [CA cert]\n"
+              "- oma_dm_sim_prov <Server URL> [CA cert]\n"
+              "- signup [CA cert]\n"
+              "- dl_osu_ca <PPS> <CA file>\n"
+              "- dl_polupd_ca <PPS> <CA file>\n"
+              "- dl_aaa_ca <PPS> <CA file>\n"
+              "- browser <URL>\n"
+              "- parse_cert <X.509 certificate (DER)>\n"
+              "- osu_select <OSU info directory> [CA cert]\n");
+}
+
+
+int main(int argc, char *argv[])
+{
+       struct hs20_osu_client ctx;
+       int c;
+       int ret = 0;
+       int no_prod_assoc = 0;
+       const char *friendly_name = NULL;
+       const char *wpa_debug_file_path = NULL;
+       extern char *wpas_ctrl_path;
+       extern int wpa_debug_level;
+       extern int wpa_debug_show_keys;
+       extern int wpa_debug_timestamp;
+
+       if (init_ctx(&ctx) < 0)
+               return -1;
+
+       for (;;) {
+               c = getopt(argc, argv, "df:hi:KNO:qr:s:S:tw:");
+               if (c < 0)
+                       break;
+               switch (c) {
+               case 'd':
+                       if (wpa_debug_level > 0)
+                               wpa_debug_level--;
+                       break;
+               case 'f':
+                       wpa_debug_file_path = optarg;
+                       break;
+               case 'K':
+                       wpa_debug_show_keys++;
+                       break;
+               case 'N':
+                       no_prod_assoc = 1;
+                       break;
+               case 'O':
+                       friendly_name = optarg;
+                       break;
+               case 'q':
+                       wpa_debug_level++;
+                       break;
+               case 'r':
+                       ctx.result_file = optarg;
+                       break;
+               case 's':
+                       ctx.summary_file = optarg;
+                       break;
+               case 'S':
+                       ctx.ifname = optarg;
+                       break;
+               case 't':
+                       wpa_debug_timestamp++;
+                       break;
+               case 'w':
+                       wpas_ctrl_path = optarg;
+                       break;
+               case 'h':
+               default:
+                       usage();
+                       exit(0);
+                       break;
+               }
+       }
+
+       if (argc - optind < 1) {
+               usage();
+               exit(0);
+       }
+
+       wpa_debug_open_file(wpa_debug_file_path);
+
+#ifdef __linux__
+       setlinebuf(stdout);
+#endif /* __linux__ */
+
+       if (ctx.result_file)
+               unlink(ctx.result_file);
+       wpa_printf(MSG_DEBUG, "===[hs20-osu-client START - command: %s ]======"
+                  "================", argv[optind]);
+       check_workarounds(&ctx);
+
+       if (strcmp(argv[optind], "to_tnds") == 0) {
+               if (argc - optind < 2) {
+                       usage();
+                       exit(0);
+               }
+               cmd_to_tnds(&ctx, argv[optind + 1], argv[optind + 2],
+                           argc > optind + 3 ? argv[optind + 3] : NULL,
+                           0);
+       } else if (strcmp(argv[optind], "to_tnds2") == 0) {
+               if (argc - optind < 2) {
+                       usage();
+                       exit(0);
+               }
+               cmd_to_tnds(&ctx, argv[optind + 1], argv[optind + 2],
+                           argc > optind + 3 ? argv[optind + 3] : NULL,
+                           1);
+       } else if (strcmp(argv[optind], "from_tnds") == 0) {
+               if (argc - optind < 2) {
+                       usage();
+                       exit(0);
+               }
+               cmd_from_tnds(&ctx, argv[optind + 1], argv[optind + 2]);
+       } else if (strcmp(argv[optind], "sub_rem") == 0) {
+               if (argc - optind < 2) {
+                       usage();
+                       exit(0);
+               }
+               if (argc - optind < 2)
+                       wpa_printf(MSG_ERROR, "Server URL missing from command line");
+               else
+                       ret = cmd_sub_rem(&ctx, argv[optind + 1],
+                                         argc > optind + 2 ?
+                                         argv[optind + 2] : NULL,
+                                         argc > optind + 3 ?
+                                         argv[optind + 3] : NULL);
+       } else if (strcmp(argv[optind], "pol_upd") == 0) {
+               if (argc - optind < 2) {
+                       usage();
+                       exit(0);
+               }
+               ret = cmd_pol_upd(&ctx, argc > 2 ? argv[optind + 1] : NULL,
+                                 argc > optind + 2 ? argv[optind + 2] : NULL,
+                                 argc > optind + 3 ? argv[optind + 3] : NULL);
+       } else if (strcmp(argv[optind], "prov") == 0) {
+               if (argc - optind < 2) {
+                       usage();
+                       exit(0);
+               }
+               ctx.ca_fname = argv[optind + 2];
+               cmd_prov(&ctx, argv[optind + 1]);
+       } else if (strcmp(argv[optind], "sim_prov") == 0) {
+               if (argc - optind < 2) {
+                       usage();
+                       exit(0);
+               }
+               ctx.ca_fname = argv[optind + 2];
+               cmd_sim_prov(&ctx, argv[optind + 1]);
+       } else if (strcmp(argv[optind], "dl_osu_ca") == 0) {
+               if (argc - optind < 2) {
+                       usage();
+                       exit(0);
+               }
+               cmd_dl_osu_ca(&ctx, argv[optind + 1], argv[optind + 2]);
+       } else if (strcmp(argv[optind], "dl_polupd_ca") == 0) {
+               if (argc - optind < 2) {
+                       usage();
+                       exit(0);
+               }
+               cmd_dl_polupd_ca(&ctx, argv[optind + 1], argv[optind + 2]);
+       } else if (strcmp(argv[optind], "dl_aaa_ca") == 0) {
+               if (argc - optind < 2) {
+                       usage();
+                       exit(0);
+               }
+               cmd_dl_aaa_ca(&ctx, argv[optind + 1], argv[optind + 2]);
+       } else if (strcmp(argv[optind], "osu_select") == 0) {
+               if (argc - optind < 2) {
+                       usage();
+                       exit(0);
+               }
+               ctx.ca_fname = argc > optind + 2 ? argv[optind + 2] : NULL;
+               cmd_osu_select(&ctx, argv[optind + 1], 2, 1, NULL);
+       } else if (strcmp(argv[optind], "signup") == 0) {
+               ctx.ca_fname = argc > optind + 1 ? argv[optind + 1] : NULL;
+               ret = cmd_signup(&ctx, no_prod_assoc, friendly_name);
+       } else if (strcmp(argv[optind], "set_pps") == 0) {
+               if (argc - optind < 2) {
+                       usage();
+                       exit(0);
+               }
+               cmd_set_pps(&ctx, argv[optind + 1]);
+       } else if (strcmp(argv[optind], "get_fqdn") == 0) {
+               if (argc - optind < 1) {
+                       usage();
+                       exit(0);
+               }
+               ret = cmd_get_fqdn(&ctx, argv[optind + 1]);
+       } else if (strcmp(argv[optind], "oma_dm_prov") == 0) {
+               if (argc - optind < 2) {
+                       usage();
+                       exit(0);
+               }
+               ctx.ca_fname = argv[optind + 2];
+               cmd_oma_dm_prov(&ctx, argv[optind + 1]);
+       } else if (strcmp(argv[optind], "oma_dm_sim_prov") == 0) {
+               if (argc - optind < 2) {
+                       usage();
+                       exit(0);
+               }
+               ctx.ca_fname = argv[optind + 2];
+               if (cmd_oma_dm_sim_prov(&ctx, argv[optind + 1]) < 0) {
+                       write_summary(&ctx, "Failed to complete OMA DM SIM provisioning");
+                       return -1;
+               }
+       } else if (strcmp(argv[optind], "oma_dm_add") == 0) {
+               if (argc - optind < 2) {
+                       usage();
+                       exit(0);
+               }
+               cmd_oma_dm_add(&ctx, argv[optind + 1], argv[optind + 2]);
+       } else if (strcmp(argv[optind], "oma_dm_replace") == 0) {
+               if (argc - optind < 2) {
+                       usage();
+                       exit(0);
+               }
+               cmd_oma_dm_replace(&ctx, argv[optind + 1], argv[optind + 2]);
+       } else if (strcmp(argv[optind], "est_csr") == 0) {
+               if (argc - optind < 2) {
+                       usage();
+                       exit(0);
+               }
+               mkdir("Cert", S_IRWXU);
+               est_build_csr(&ctx, argv[optind + 1]);
+       } else if (strcmp(argv[optind], "browser") == 0) {
+               int ret;
+
+               if (argc - optind < 2) {
+                       usage();
+                       exit(0);
+               }
+
+               wpa_printf(MSG_INFO, "Launch web browser to URL %s",
+                          argv[optind + 1]);
+               ret = hs20_web_browser(argv[optind + 1]);
+               wpa_printf(MSG_INFO, "Web browser result: %d", ret);
+       } else if (strcmp(argv[optind], "parse_cert") == 0) {
+               if (argc - optind < 2) {
+                       usage();
+                       exit(0);
+               }
+
+               wpa_debug_level = MSG_MSGDUMP;
+               http_parse_x509_certificate(ctx.http, argv[optind + 1]);
+               wpa_debug_level = MSG_INFO;
+       } else {
+               wpa_printf(MSG_INFO, "Unknown command '%s'", argv[optind]);
+       }
+
+       deinit_ctx(&ctx);
+       wpa_printf(MSG_DEBUG,
+                  "===[hs20-osu-client END ]======================");
+
+       wpa_debug_close_file();
+
+       return ret;
+}
diff --git a/hs20/client/osu_client.h b/hs20/client/osu_client.h
new file mode 100644 (file)
index 0000000..9a7059e
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Hotspot 2.0 - OSU client
+ * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef OSU_CLIENT_H
+#define OSU_CLIENT_H
+
+#define SPP_NS_URI "http://www.wi-fi.org/specifications/hotspot2dot0/v1.0/spp"
+
+#define URN_OMA_DM_DEVINFO "urn:oma:mo:oma-dm-devinfo:1.0"
+#define URN_OMA_DM_DEVDETAIL "urn:oma:mo:oma-dm-devdetail:1.0"
+#define URN_HS20_DEVDETAIL_EXT "urn:wfa:mo-ext:hotspot2dot0-devdetail-ext:1.0"
+#define URN_HS20_PPS "urn:wfa:mo:hotspot2dot0-perprovidersubscription:1.0"
+
+
+#define MAX_OSU_VALS 10
+
+struct osu_lang_text {
+       char lang[4];
+       char text[253];
+};
+
+struct hs20_osu_client {
+       struct xml_node_ctx *xml;
+       struct http_ctx *http;
+       int no_reconnect;
+       char pps_fname[300];
+       char *devid;
+       const char *result_file;
+       const char *summary_file;
+       const char *ifname;
+       const char *ca_fname;
+       int no_osu_cert_validation; /* for EST operations */
+       char *fqdn;
+       char *server_url;
+       struct osu_lang_text friendly_name[MAX_OSU_VALS];
+       size_t friendly_name_count;
+       size_t icon_count;
+       char icon_filename[MAX_OSU_VALS][256];
+       u8 icon_hash[MAX_OSU_VALS][32];
+       int pps_cred_set;
+       int pps_updated;
+       int client_cert_present;
+       char **server_dnsname;
+       size_t server_dnsname_count;
+#define WORKAROUND_OCSP_OPTIONAL 0x00000001
+       unsigned long int workarounds;
+};
+
+
+/* osu_client.c */
+
+void write_result(struct hs20_osu_client *ctx, const char *fmt, ...)
+       __attribute__ ((format (printf, 2, 3)));
+void write_summary(struct hs20_osu_client *ctx, const char *fmt, ...)
+       __attribute__ ((format (printf, 2, 3)));
+
+void debug_dump_node(struct hs20_osu_client *ctx, const char *title,
+                    xml_node_t *node);
+int osu_get_certificate(struct hs20_osu_client *ctx, xml_node_t *getcert);
+int hs20_add_pps_mo(struct hs20_osu_client *ctx, const char *uri,
+                   xml_node_t *add_mo, char *fname, size_t fname_len);
+void get_user_pw(struct hs20_osu_client *ctx, xml_node_t *pps,
+                const char *alt_loc, char **user, char **pw);
+int update_pps_file(struct hs20_osu_client *ctx, const char *pps_fname,
+                   xml_node_t *pps);
+void cmd_set_pps(struct hs20_osu_client *ctx, const char *pps_fname);
+
+
+/* spp_client.c */
+
+void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
+                const char *pps_fname,
+                const char *client_cert, const char *client_key,
+                const char *cred_username, const char *cred_password,
+                xml_node_t *pps);
+void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
+                const char *pps_fname,
+                const char *client_cert, const char *client_key,
+                const char *cred_username, const char *cred_password,
+                xml_node_t *pps);
+int cmd_prov(struct hs20_osu_client *ctx, const char *url);
+int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url);
+
+
+/* oma_dm_client.c */
+
+int cmd_oma_dm_prov(struct hs20_osu_client *ctx, const char *url);
+int cmd_oma_dm_sim_prov(struct hs20_osu_client *ctx, const char *url);
+void oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
+                   const char *pps_fname,
+                   const char *client_cert, const char *client_key,
+                   const char *cred_username, const char *cred_password,
+                   xml_node_t *pps);
+void oma_dm_pol_upd(struct hs20_osu_client *ctx, const char *address,
+                   const char *pps_fname,
+                   const char *client_cert, const char *client_key,
+                   const char *cred_username, const char *cred_password,
+                   xml_node_t *pps);
+void cmd_oma_dm_sub_rem(struct hs20_osu_client *ctx, const char *address,
+                       const char *pps_fname);
+void cmd_oma_dm_add(struct hs20_osu_client *ctx, const char *pps_fname,
+                   const char *add_fname);
+void cmd_oma_dm_replace(struct hs20_osu_client *ctx, const char *pps_fname,
+                       const char *replace_fname);
+
+/* est.c */
+
+int est_load_cacerts(struct hs20_osu_client *ctx, const char *url);
+int est_build_csr(struct hs20_osu_client *ctx, const char *url);
+int est_simple_enroll(struct hs20_osu_client *ctx, const char *url,
+                     const char *user, const char *pw);
+
+#endif /* OSU_CLIENT_H */
diff --git a/hs20/client/spp_client.c b/hs20/client/spp_client.c
new file mode 100644 (file)
index 0000000..302a050
--- /dev/null
@@ -0,0 +1,995 @@
+/*
+ * Hotspot 2.0 SPP client
+ * Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/stat.h>
+
+#include "common.h"
+#include "browser.h"
+#include "wpa_ctrl.h"
+#include "wpa_helpers.h"
+#include "xml-utils.h"
+#include "http-utils.h"
+#include "utils/base64.h"
+#include "crypto/crypto.h"
+#include "crypto/sha256.h"
+#include "osu_client.h"
+
+
+static int hs20_spp_update_response(struct hs20_osu_client *ctx,
+                                   const char *session_id,
+                                   const char *spp_status,
+                                   const char *error_code);
+static void hs20_policy_update_complete(
+       struct hs20_osu_client *ctx, const char *pps_fname);
+
+
+static char * get_spp_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
+                                char *attr_name)
+{
+       return xml_node_get_attr_value_ns(ctx, node, SPP_NS_URI, attr_name);
+}
+
+
+static int hs20_spp_validate(struct hs20_osu_client *ctx, xml_node_t *node,
+                            const char *expected_name)
+{
+       struct xml_node_ctx *xctx = ctx->xml;
+       const char *name;
+       char *err;
+       int ret;
+
+       if (!xml_node_is_element(xctx, node))
+               return -1;
+
+       name = xml_node_get_localname(xctx, node);
+       if (name == NULL)
+               return -1;
+
+       if (strcmp(expected_name, name) != 0) {
+               wpa_printf(MSG_INFO, "Unexpected SOAP method name '%s' (expected '%s')",
+                          name, expected_name);
+               write_summary(ctx, "Unexpected SOAP method name '%s' (expected '%s')",
+                             name, expected_name);
+               return -1;
+       }
+
+       ret = xml_validate(xctx, node, "spp.xsd", &err);
+       if (ret < 0) {
+               wpa_printf(MSG_INFO, "XML schema validation error(s)\n%s", err);
+               write_summary(ctx, "SPP XML schema validation failed");
+               os_free(err);
+       }
+       return ret;
+}
+
+
+static void add_mo_container(struct xml_node_ctx *ctx, xml_namespace_t *ns,
+                            xml_node_t *parent, const char *urn,
+                            const char *fname)
+{
+       xml_node_t *node;
+       xml_node_t *fnode, *tnds;
+       char *str;
+
+       fnode = node_from_file(ctx, fname);
+       if (!fnode)
+               return;
+       tnds = mo_to_tnds(ctx, fnode, 0, urn, "syncml:dmddf1.2");
+       xml_node_free(ctx, fnode);
+       if (!tnds)
+               return;
+
+       str = xml_node_to_str(ctx, tnds);
+       xml_node_free(ctx, tnds);
+       if (str == NULL)
+               return;
+
+       node = xml_node_create_text(ctx, parent, ns, "moContainer", str);
+       if (node)
+               xml_node_add_attr(ctx, node, ns, "moURN", urn);
+       os_free(str);
+}
+
+
+static xml_node_t * build_spp_post_dev_data(struct hs20_osu_client *ctx,
+                                           xml_namespace_t **ret_ns,
+                                           const char *session_id,
+                                           const char *reason)
+{
+       xml_namespace_t *ns;
+       xml_node_t *spp_node;
+
+       write_summary(ctx, "Building sppPostDevData requestReason='%s'",
+                     reason);
+       spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
+                                       "sppPostDevData");
+       if (spp_node == NULL)
+               return NULL;
+       if (ret_ns)
+               *ret_ns = ns;
+
+       xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
+       xml_node_add_attr(ctx->xml, spp_node, NULL, "requestReason", reason);
+       if (session_id)
+               xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID",
+                                 session_id);
+       xml_node_add_attr(ctx->xml, spp_node, NULL, "redirectURI",
+                         "http://localhost:12345/");
+
+       xml_node_create_text(ctx->xml, spp_node, ns, "supportedSPPVersions",
+                            "1.0");
+       xml_node_create_text(ctx->xml, spp_node, ns, "supportedMOList",
+                            URN_HS20_PPS " " URN_OMA_DM_DEVINFO " "
+                            URN_OMA_DM_DEVDETAIL " " URN_HS20_DEVDETAIL_EXT);
+
+       add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVINFO,
+                        "devinfo.xml");
+       add_mo_container(ctx->xml, ns, spp_node, URN_OMA_DM_DEVDETAIL,
+                        "devdetail.xml");
+
+       return spp_node;
+}
+
+
+static int process_update_node(struct hs20_osu_client *ctx, xml_node_t *pps,
+                              xml_node_t *update)
+{
+       xml_node_t *node, *parent, *tnds, *unode;
+       char *str;
+       const char *name;
+       char *uri, *pos;
+       char *cdata, *cdata_end;
+       size_t fqdn_len;
+
+       wpa_printf(MSG_INFO, "Processing updateNode");
+       debug_dump_node(ctx, "updateNode", update);
+
+       uri = get_spp_attr_value(ctx->xml, update, "managementTreeURI");
+       if (uri == NULL) {
+               wpa_printf(MSG_INFO, "No managementTreeURI present");
+               return -1;
+       }
+       wpa_printf(MSG_INFO, "managementTreeUri: '%s'", uri);
+
+       name = os_strrchr(uri, '/');
+       if (name == NULL) {
+               wpa_printf(MSG_INFO, "Unexpected URI");
+               xml_node_get_attr_value_free(ctx->xml, uri);
+               return -1;
+       }
+       name++;
+       wpa_printf(MSG_INFO, "Update interior node: '%s'", name);
+
+       str = xml_node_get_text(ctx->xml, update);
+       if (str == NULL) {
+               wpa_printf(MSG_INFO, "Could not extract MO text");
+               xml_node_get_attr_value_free(ctx->xml, uri);
+               return -1;
+       }
+       wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text: '%s'", str);
+       cdata = strstr(str, "<![CDATA[");
+       cdata_end = strstr(str, "]]>");
+       if (cdata && cdata_end && cdata_end > cdata &&
+           cdata < strstr(str, "MgmtTree") &&
+           cdata_end > strstr(str, "/MgmtTree")) {
+               char *tmp;
+               wpa_printf(MSG_DEBUG, "[hs20] Removing extra CDATA container");
+               tmp = strdup(cdata + 9);
+               if (tmp) {
+                       cdata_end = strstr(tmp, "]]>");
+                       if (cdata_end)
+                               *cdata_end = '\0';
+                       wpa_printf(MSG_DEBUG, "[hs20] nodeContainer text with CDATA container removed: '%s'",
+                                  tmp);
+                       tnds = xml_node_from_buf(ctx->xml, tmp);
+                       free(tmp);
+               } else
+                       tnds = NULL;
+       } else
+               tnds = xml_node_from_buf(ctx->xml, str);
+       xml_node_get_text_free(ctx->xml, str);
+       if (tnds == NULL) {
+               wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer text");
+               xml_node_get_attr_value_free(ctx->xml, uri);
+               return -1;
+       }
+
+       unode = tnds_to_mo(ctx->xml, tnds);
+       xml_node_free(ctx->xml, tnds);
+       if (unode == NULL) {
+               wpa_printf(MSG_INFO, "[hs20] Could not parse nodeContainer TNDS text");
+               xml_node_get_attr_value_free(ctx->xml, uri);
+               return -1;
+       }
+
+       debug_dump_node(ctx, "Parsed TNDS", unode);
+
+       if (get_node_uri(ctx->xml, unode, name) == NULL) {
+               wpa_printf(MSG_INFO, "[hs20] %s node not found", name);
+               xml_node_free(ctx->xml, unode);
+               xml_node_get_attr_value_free(ctx->xml, uri);
+               return -1;
+       }
+
+       if (os_strncasecmp(uri, "./Wi-Fi/", 8) != 0) {
+               wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi");
+               xml_node_free(ctx->xml, unode);
+               xml_node_get_attr_value_free(ctx->xml, uri);
+               return -1;
+       }
+       pos = uri + 8;
+
+       if (ctx->fqdn == NULL) {
+               wpa_printf(MSG_INFO, "FQDN not known");
+               xml_node_free(ctx->xml, unode);
+               xml_node_get_attr_value_free(ctx->xml, uri);
+               return -1;
+       }
+       fqdn_len = os_strlen(ctx->fqdn);
+       if (os_strncasecmp(pos, ctx->fqdn, fqdn_len) != 0 ||
+           pos[fqdn_len] != '/') {
+               wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s",
+                          ctx->fqdn);
+               xml_node_free(ctx->xml, unode);
+               xml_node_get_attr_value_free(ctx->xml, uri);
+               return -1;
+       }
+       pos += fqdn_len + 1;
+
+       if (os_strncasecmp(pos, "PerProviderSubscription/", 24) != 0) {
+               wpa_printf(MSG_INFO, "Do not allow update outside ./Wi-Fi/%s/PerProviderSubscription",
+                          ctx->fqdn);
+               xml_node_free(ctx->xml, unode);
+               xml_node_get_attr_value_free(ctx->xml, uri);
+               return -1;
+       }
+       pos += 24;
+
+       wpa_printf(MSG_INFO, "Update command for PPS node %s", pos);
+
+       node = get_node(ctx->xml, pps, pos);
+       if (node) {
+               parent = xml_node_get_parent(ctx->xml, node);
+               xml_node_detach(ctx->xml, node);
+               wpa_printf(MSG_INFO, "Replace '%s' node", name);
+       } else {
+               char *pos2;
+               pos2 = os_strrchr(pos, '/');
+               if (pos2 == NULL) {
+                       parent = pps;
+               } else {
+                       *pos2 = '\0';
+                       parent = get_node(ctx->xml, pps, pos);
+               }
+               if (parent == NULL) {
+                       wpa_printf(MSG_INFO, "Could not find parent %s", pos);
+                       xml_node_free(ctx->xml, unode);
+                       xml_node_get_attr_value_free(ctx->xml, uri);
+                       return -1;
+               }
+               wpa_printf(MSG_INFO, "Add '%s' node", name);
+       }
+       xml_node_add_child(ctx->xml, parent, unode);
+
+       xml_node_get_attr_value_free(ctx->xml, uri);
+
+       return 0;
+}
+
+
+static int update_pps(struct hs20_osu_client *ctx, xml_node_t *update,
+                     const char *pps_fname, xml_node_t *pps)
+{
+       wpa_printf(MSG_INFO, "Updating PPS based on updateNode element(s)");
+       xml_node_for_each_sibling(ctx->xml, update) {
+               xml_node_for_each_check(ctx->xml, update);
+               if (process_update_node(ctx, pps, update) < 0)
+                       return -1;
+       }
+
+       return update_pps_file(ctx, pps_fname, pps);
+}
+
+
+static void hs20_sub_rem_complete(struct hs20_osu_client *ctx,
+                                 const char *pps_fname)
+{
+       /*
+        * Update wpa_supplicant credentials and reconnect using updated
+        * information.
+        */
+       wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
+       cmd_set_pps(ctx, pps_fname);
+
+       if (ctx->no_reconnect)
+               return;
+
+       wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+       if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
+               wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
+}
+
+
+static xml_node_t * hs20_spp_upload_mo(struct hs20_osu_client *ctx,
+                                      xml_node_t *cmd,
+                                      const char *session_id,
+                                      const char *pps_fname)
+{
+       xml_namespace_t *ns;
+       xml_node_t *node, *ret_node;
+       char *urn;
+
+       urn = get_spp_attr_value(ctx->xml, cmd, "moURN");
+       if (!urn) {
+               wpa_printf(MSG_INFO, "No URN included");
+               return NULL;
+       }
+       wpa_printf(MSG_INFO, "Upload MO request - URN=%s", urn);
+       if (strcasecmp(urn, URN_HS20_PPS) != 0) {
+               wpa_printf(MSG_INFO, "Unsupported moURN");
+               xml_node_get_attr_value_free(ctx->xml, urn);
+               return NULL;
+       }
+       xml_node_get_attr_value_free(ctx->xml, urn);
+
+       if (!pps_fname) {
+               wpa_printf(MSG_INFO, "PPS file name no known");
+               return NULL;
+       }
+
+       node = build_spp_post_dev_data(ctx, &ns, session_id,
+                                      "MO upload");
+       if (node == NULL)
+               return NULL;
+       add_mo_container(ctx->xml, ns, node, URN_HS20_PPS, pps_fname);
+
+       ret_node = soap_send_receive(ctx->http, node);
+       if (ret_node == NULL)
+               return NULL;
+
+       debug_dump_node(ctx, "Received response to MO upload", ret_node);
+
+       if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
+               wpa_printf(MSG_INFO, "SPP validation failed");
+               xml_node_free(ctx->xml, ret_node);
+               return NULL;
+       }
+
+       return ret_node;
+}
+
+
+static int hs20_add_mo(struct hs20_osu_client *ctx, xml_node_t *add_mo,
+                      char *fname, size_t fname_len)
+{
+       char *uri, *urn;
+       int ret;
+
+       debug_dump_node(ctx, "Received addMO", add_mo);
+
+       urn = get_spp_attr_value(ctx->xml, add_mo, "moURN");
+       if (urn == NULL) {
+               wpa_printf(MSG_INFO, "[hs20] No moURN in addMO");
+               return -1;
+       }
+       wpa_printf(MSG_INFO, "addMO - moURN: '%s'", urn);
+       if (strcasecmp(urn, URN_HS20_PPS) != 0) {
+               wpa_printf(MSG_INFO, "[hs20] Unsupported MO in addMO");
+               xml_node_get_attr_value_free(ctx->xml, urn);
+               return -1;
+       }
+       xml_node_get_attr_value_free(ctx->xml, urn);
+
+       uri = get_spp_attr_value(ctx->xml, add_mo, "managementTreeURI");
+       if (uri == NULL) {
+               wpa_printf(MSG_INFO, "[hs20] No managementTreeURI in addMO");
+               return -1;
+       }
+       wpa_printf(MSG_INFO, "addMO - managementTreeURI: '%s'", uri);
+
+       ret = hs20_add_pps_mo(ctx, uri, add_mo, fname, fname_len);
+       xml_node_get_attr_value_free(ctx->xml, uri);
+       return ret;
+}
+
+
+static int process_spp_user_input_response(struct hs20_osu_client *ctx,
+                                          const char *session_id,
+                                          xml_node_t *add_mo)
+{
+       int ret;
+       char fname[300];
+
+       debug_dump_node(ctx, "addMO", add_mo);
+
+       wpa_printf(MSG_INFO, "Subscription registration completed");
+
+       if (hs20_add_mo(ctx, add_mo, fname, sizeof(fname)) < 0) {
+               wpa_printf(MSG_INFO, "Could not add MO");
+               ret = hs20_spp_update_response(
+                       ctx, session_id,
+                       "Error occurred",
+                       "MO addition or update failed");
+               return 0;
+       }
+
+       ret = hs20_spp_update_response(ctx, session_id, "OK", NULL);
+       if (ret == 0)
+               hs20_sub_rem_complete(ctx, fname);
+
+       return 0;
+}
+
+
+static xml_node_t * hs20_spp_user_input_completed(struct hs20_osu_client *ctx,
+                                                   const char *session_id)
+{
+       xml_node_t *node, *ret_node;
+
+       node = build_spp_post_dev_data(ctx, NULL, session_id,
+                                      "User input completed");
+       if (node == NULL)
+               return NULL;
+
+       ret_node = soap_send_receive(ctx->http, node);
+       if (!ret_node) {
+               if (soap_reinit_client(ctx->http) < 0)
+                       return NULL;
+               wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
+               node = build_spp_post_dev_data(ctx, NULL, session_id,
+                                              "User input completed");
+               if (node == NULL)
+                       return NULL;
+               ret_node = soap_send_receive(ctx->http, node);
+               if (ret_node == NULL)
+                       return NULL;
+               wpa_printf(MSG_INFO, "Continue with new connection");
+       }
+
+       if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
+               wpa_printf(MSG_INFO, "SPP validation failed");
+               xml_node_free(ctx->xml, ret_node);
+               return NULL;
+       }
+
+       return ret_node;
+}
+
+
+static xml_node_t * hs20_spp_get_certificate(struct hs20_osu_client *ctx,
+                                            xml_node_t *cmd,
+                                            const char *session_id,
+                                            const char *pps_fname)
+{
+       xml_namespace_t *ns;
+       xml_node_t *node, *ret_node;
+       int res;
+
+       wpa_printf(MSG_INFO, "Client certificate enrollment");
+
+       res = osu_get_certificate(ctx, cmd);
+       if (res < 0)
+               wpa_printf(MSG_INFO, "EST simpleEnroll failed");
+
+       node = build_spp_post_dev_data(ctx, &ns, session_id,
+                                      res == 0 ?
+                                      "Certificate enrollment completed" :
+                                      "Certificate enrollment failed");
+       if (node == NULL)
+               return NULL;
+
+       ret_node = soap_send_receive(ctx->http, node);
+       if (ret_node == NULL)
+               return NULL;
+
+       debug_dump_node(ctx, "Received response to certificate enrollment "
+                       "completed", ret_node);
+
+       if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
+               wpa_printf(MSG_INFO, "SPP validation failed");
+               xml_node_free(ctx->xml, ret_node);
+               return NULL;
+       }
+
+       return ret_node;
+}
+
+
+static int hs20_spp_exec(struct hs20_osu_client *ctx, xml_node_t *exec,
+                        const char *session_id, const char *pps_fname,
+                        xml_node_t *pps, xml_node_t **ret_node)
+{
+       xml_node_t *cmd;
+       const char *name;
+       char *uri;
+       char *id = strdup(session_id);
+
+       if (id == NULL)
+               return -1;
+
+       *ret_node = NULL;
+
+       debug_dump_node(ctx, "exec", exec);
+
+       xml_node_for_each_child(ctx->xml, cmd, exec) {
+               xml_node_for_each_check(ctx->xml, cmd);
+               break;
+       }
+       if (!cmd) {
+               wpa_printf(MSG_INFO, "exec command element not found (cmd=%p)",
+                          cmd);
+               free(id);
+               return -1;
+       }
+
+       name = xml_node_get_localname(ctx->xml, cmd);
+
+       if (strcasecmp(name, "launchBrowserToURI") == 0) {
+               int res;
+               uri = xml_node_get_text(ctx->xml, cmd);
+               if (!uri) {
+                       wpa_printf(MSG_INFO, "No URI found");
+                       free(id);
+                       return -1;
+               }
+               wpa_printf(MSG_INFO, "Launch browser to URI '%s'", uri);
+               write_summary(ctx, "Launch browser to URI '%s'", uri);
+               res = hs20_web_browser(uri);
+               xml_node_get_text_free(ctx->xml, uri);
+               if (res > 0) {
+                       wpa_printf(MSG_INFO, "User response in browser completed successfully - sessionid='%s'",
+                                  id);
+                       write_summary(ctx, "User response in browser completed successfully");
+                       *ret_node = hs20_spp_user_input_completed(ctx, id);
+                       free(id);
+                       return *ret_node ? 0 : -1;
+               } else {
+                       wpa_printf(MSG_INFO, "Failed to receive user response");
+                       write_summary(ctx, "Failed to receive user response");
+                       hs20_spp_update_response(
+                               ctx, id, "Error occurred", "Other");
+                       free(id);
+                       return -1;
+               }
+               return 0;
+       }
+
+       if (strcasecmp(name, "uploadMO") == 0) {
+               if (pps_fname == NULL)
+                       return -1;
+               *ret_node = hs20_spp_upload_mo(ctx, cmd, id,
+                                              pps_fname);
+               free(id);
+               return *ret_node ? 0 : -1;
+       }
+
+       if (strcasecmp(name, "getCertificate") == 0) {
+               *ret_node = hs20_spp_get_certificate(ctx, cmd, id,
+                                                    pps_fname);
+               free(id);
+               return *ret_node ? 0 : -1;
+       }
+
+       wpa_printf(MSG_INFO, "Unsupported exec command: '%s'", name);
+       free(id);
+       return -1;
+}
+
+
+enum spp_post_dev_data_use {
+       SPP_SUBSCRIPTION_REMEDIATION,
+       SPP_POLICY_UPDATE,
+       SPP_SUBSCRIPTION_REGISTRATION,
+};
+
+static void process_spp_post_dev_data_response(
+       struct hs20_osu_client *ctx,
+       enum spp_post_dev_data_use use, xml_node_t *node,
+       const char *pps_fname, xml_node_t *pps)
+{
+       xml_node_t *child;
+       char *status = NULL;
+       xml_node_t *update = NULL, *exec = NULL, *add_mo = NULL, *no_mo = NULL;
+       char *session_id = NULL;
+
+       debug_dump_node(ctx, "sppPostDevDataResponse node", node);
+
+       status = get_spp_attr_value(ctx->xml, node, "sppStatus");
+       if (status == NULL) {
+               wpa_printf(MSG_INFO, "No sppStatus attribute");
+               goto out;
+       }
+       write_summary(ctx, "Received sppPostDevDataResponse sppStatus='%s'",
+                     status);
+
+       session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
+       if (session_id == NULL) {
+               wpa_printf(MSG_INFO, "No sessionID attribute");
+               goto out;
+       }
+
+       wpa_printf(MSG_INFO, "[hs20] sppPostDevDataResponse - sppStatus: '%s'  sessionID: '%s'",
+                  status, session_id);
+
+       xml_node_for_each_child(ctx->xml, child, node) {
+               const char *name;
+               xml_node_for_each_check(ctx->xml, child);
+               debug_dump_node(ctx, "child", child);
+               name = xml_node_get_localname(ctx->xml, child);
+               wpa_printf(MSG_INFO, "localname: '%s'", name);
+               if (!update && strcasecmp(name, "updateNode") == 0)
+                       update = child;
+               if (!exec && strcasecmp(name, "exec") == 0)
+                       exec = child;
+               if (!add_mo && strcasecmp(name, "addMO") == 0)
+                       add_mo = child;
+               if (!no_mo && strcasecmp(name, "noMOUpdate") == 0)
+                       no_mo = child;
+       }
+
+       if (use == SPP_SUBSCRIPTION_REMEDIATION &&
+           strcasecmp(status,
+                      "Remediation complete, request sppUpdateResponse") == 0)
+       {
+               int res, ret;
+               if (!update && !no_mo) {
+                       wpa_printf(MSG_INFO, "No updateNode or noMOUpdate element");
+                       goto out;
+               }
+               wpa_printf(MSG_INFO, "Subscription remediation completed");
+               res = update_pps(ctx, update, pps_fname, pps);
+               if (res < 0)
+                       wpa_printf(MSG_INFO, "Failed to update PPS MO");
+               ret = hs20_spp_update_response(
+                       ctx, session_id,
+                       res < 0 ? "Error occurred" : "OK",
+                       res < 0 ? "MO addition or update failed" : NULL);
+               if (res == 0 && ret == 0)
+                       hs20_sub_rem_complete(ctx, pps_fname);
+               goto out;
+       }
+
+       if (use == SPP_SUBSCRIPTION_REMEDIATION &&
+           strcasecmp(status, "Exchange complete, release TLS connection") ==
+           0) {
+               if (!no_mo) {
+                       wpa_printf(MSG_INFO, "No noMOUpdate element");
+                       goto out;
+               }
+               wpa_printf(MSG_INFO, "Subscription remediation completed (no MO update)");
+               goto out;
+       }
+
+       if (use == SPP_POLICY_UPDATE &&
+           strcasecmp(status, "Update complete, request sppUpdateResponse") ==
+           0) {
+               int res, ret;
+               wpa_printf(MSG_INFO, "Policy update received - update PPS");
+               res = update_pps(ctx, update, pps_fname, pps);
+               ret = hs20_spp_update_response(
+                       ctx, session_id,
+                       res < 0 ? "Error occurred" : "OK",
+                       res < 0 ? "MO addition or update failed" : NULL);
+               if (res == 0 && ret == 0)
+                       hs20_policy_update_complete(ctx, pps_fname);
+               goto out;
+       }
+
+       if (use == SPP_SUBSCRIPTION_REGISTRATION &&
+           strcasecmp(status, "Provisioning complete, request "
+                      "sppUpdateResponse")  == 0) {
+               if (!add_mo) {
+                       wpa_printf(MSG_INFO, "No addMO element - not sure what to do next");
+                       goto out;
+               }
+               process_spp_user_input_response(ctx, session_id, add_mo);
+               node = NULL;
+               goto out;
+       }
+
+       if (strcasecmp(status, "No update available at this time") == 0) {
+               wpa_printf(MSG_INFO, "No update available at this time");
+               goto out;
+       }
+
+       if (strcasecmp(status, "OK") == 0) {
+               int res;
+               xml_node_t *ret;
+
+               if (!exec) {
+                       wpa_printf(MSG_INFO, "No exec element - not sure what to do next");
+                       goto out;
+               }
+               res = hs20_spp_exec(ctx, exec, session_id,
+                                   pps_fname, pps, &ret);
+               /* xml_node_free(ctx->xml, node); */
+               node = NULL;
+               if (res == 0 && ret)
+                       process_spp_post_dev_data_response(ctx, use,
+                                                          ret, pps_fname, pps);
+               goto out;
+       }
+
+       if (strcasecmp(status, "Error occurred") == 0) {
+               xml_node_t *err;
+               char *code = NULL;
+               err = get_node(ctx->xml, node, "sppError");
+               if (err)
+                       code = xml_node_get_attr_value(ctx->xml, err,
+                                                      "errorCode");
+               wpa_printf(MSG_INFO, "Error occurred - errorCode=%s",
+                          code ? code : "N/A");
+               xml_node_get_attr_value_free(ctx->xml, code);
+               goto out;
+       }
+
+       wpa_printf(MSG_INFO,
+                  "[hs20] Unsupported sppPostDevDataResponse sppStatus '%s'",
+                  status);
+out:
+       xml_node_get_attr_value_free(ctx->xml, status);
+       xml_node_get_attr_value_free(ctx->xml, session_id);
+       xml_node_free(ctx->xml, node);
+}
+
+
+static int spp_post_dev_data(struct hs20_osu_client *ctx,
+                            enum spp_post_dev_data_use use,
+                            const char *reason,
+                            const char *pps_fname, xml_node_t *pps)
+{
+       xml_node_t *payload;
+       xml_node_t *ret_node;
+
+       payload = build_spp_post_dev_data(ctx, NULL, NULL, reason);
+       if (payload == NULL)
+               return -1;
+
+       ret_node = soap_send_receive(ctx->http, payload);
+       if (!ret_node) {
+               const char *err = http_get_err(ctx->http);
+               if (err) {
+                       wpa_printf(MSG_INFO, "HTTP error: %s", err);
+                       write_result(ctx, "HTTP error: %s", err);
+               } else {
+                       write_summary(ctx, "Failed to send SOAP message");
+               }
+               return -1;
+       }
+
+       if (hs20_spp_validate(ctx, ret_node, "sppPostDevDataResponse") < 0) {
+               wpa_printf(MSG_INFO, "SPP validation failed");
+               xml_node_free(ctx->xml, ret_node);
+               return -1;
+       }
+
+       process_spp_post_dev_data_response(ctx, use, ret_node,
+                                          pps_fname, pps);
+       return 0;
+}
+
+
+void spp_sub_rem(struct hs20_osu_client *ctx, const char *address,
+                const char *pps_fname,
+                const char *client_cert, const char *client_key,
+                const char *cred_username, const char *cred_password,
+                xml_node_t *pps)
+{
+       wpa_printf(MSG_INFO, "SPP subscription remediation");
+       write_summary(ctx, "SPP subscription remediation");
+
+       os_free(ctx->server_url);
+       ctx->server_url = os_strdup(address);
+
+       if (soap_init_client(ctx->http, address, ctx->ca_fname,
+                            cred_username, cred_password, client_cert,
+                            client_key) == 0) {
+               spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REMEDIATION,
+                                 "Subscription remediation", pps_fname, pps);
+       }
+}
+
+
+static void hs20_policy_update_complete(struct hs20_osu_client *ctx,
+                                       const char *pps_fname)
+{
+       wpa_printf(MSG_INFO, "Policy update completed");
+
+       /*
+        * Update wpa_supplicant credentials and reconnect using updated
+        * information.
+        */
+       wpa_printf(MSG_INFO, "Updating wpa_supplicant credentials");
+       cmd_set_pps(ctx, pps_fname);
+
+       wpa_printf(MSG_INFO, "Requesting reconnection with updated configuration");
+       if (wpa_command(ctx->ifname, "INTERWORKING_SELECT auto") < 0)
+               wpa_printf(MSG_ERROR, "Failed to request wpa_supplicant to reconnect");
+}
+
+
+static int process_spp_exchange_complete(struct hs20_osu_client *ctx,
+                                        xml_node_t *node)
+{
+       char *status, *session_id;
+
+       debug_dump_node(ctx, "sppExchangeComplete", node);
+
+       status = get_spp_attr_value(ctx->xml, node, "sppStatus");
+       if (status == NULL) {
+               wpa_printf(MSG_INFO, "No sppStatus attribute");
+               return -1;
+       }
+       write_summary(ctx, "Received sppExchangeComplete sppStatus='%s'",
+                     status);
+
+       session_id = get_spp_attr_value(ctx->xml, node, "sessionID");
+       if (session_id == NULL) {
+               wpa_printf(MSG_INFO, "No sessionID attribute");
+               xml_node_get_attr_value_free(ctx->xml, status);
+               return -1;
+       }
+
+       wpa_printf(MSG_INFO, "[hs20] sppStatus: '%s'  sessionID: '%s'",
+                  status, session_id);
+       xml_node_get_attr_value_free(ctx->xml, session_id);
+
+       if (strcasecmp(status, "Exchange complete, release TLS connection") ==
+           0) {
+               xml_node_get_attr_value_free(ctx->xml, status);
+               return 0;
+       }
+
+       wpa_printf(MSG_INFO, "Unexpected sppStatus '%s'", status);
+       write_summary(ctx, "Unexpected sppStatus '%s'", status);
+       xml_node_get_attr_value_free(ctx->xml, status);
+       return -1;
+}
+
+
+static xml_node_t * build_spp_update_response(struct hs20_osu_client *ctx,
+                                             const char *session_id,
+                                             const char *spp_status,
+                                             const char *error_code)
+{
+       xml_namespace_t *ns;
+       xml_node_t *spp_node, *node;
+
+       spp_node = xml_node_create_root(ctx->xml, SPP_NS_URI, "spp", &ns,
+                                       "sppUpdateResponse");
+       if (spp_node == NULL)
+               return NULL;
+
+       xml_node_add_attr(ctx->xml, spp_node, ns, "sppVersion", "1.0");
+       xml_node_add_attr(ctx->xml, spp_node, ns, "sessionID", session_id);
+       xml_node_add_attr(ctx->xml, spp_node, ns, "sppStatus", spp_status);
+
+       if (error_code) {
+               node = xml_node_create(ctx->xml, spp_node, ns, "sppError");
+               if (node)
+                       xml_node_add_attr(ctx->xml, node, NULL, "errorCode",
+                                         error_code);
+       }
+
+       return spp_node;
+}
+
+
+static int hs20_spp_update_response(struct hs20_osu_client *ctx,
+                                   const char *session_id,
+                                   const char *spp_status,
+                                   const char *error_code)
+{
+       xml_node_t *node, *ret_node;
+       int ret;
+
+       write_summary(ctx, "Building sppUpdateResponse sppStatus='%s' error_code='%s'",
+                     spp_status, error_code);
+       node = build_spp_update_response(ctx, session_id, spp_status,
+                                        error_code);
+       if (node == NULL)
+               return -1;
+       ret_node = soap_send_receive(ctx->http, node);
+       if (!ret_node) {
+               if (soap_reinit_client(ctx->http) < 0)
+                       return -1;
+               wpa_printf(MSG_INFO, "Try to finish with re-opened connection");
+               node = build_spp_update_response(ctx, session_id, spp_status,
+                                                error_code);
+               if (node == NULL)
+                       return -1;
+               ret_node = soap_send_receive(ctx->http, node);
+               if (ret_node == NULL)
+                       return -1;
+               wpa_printf(MSG_INFO, "Continue with new connection");
+       }
+
+       if (hs20_spp_validate(ctx, ret_node, "sppExchangeComplete") < 0) {
+               wpa_printf(MSG_INFO, "SPP validation failed");
+               xml_node_free(ctx->xml, ret_node);
+               return -1;
+       }
+
+       ret = process_spp_exchange_complete(ctx, ret_node);
+       xml_node_free(ctx->xml, ret_node);
+       return ret;
+}
+
+
+void spp_pol_upd(struct hs20_osu_client *ctx, const char *address,
+                const char *pps_fname,
+                const char *client_cert, const char *client_key,
+                const char *cred_username, const char *cred_password,
+                xml_node_t *pps)
+{
+       wpa_printf(MSG_INFO, "SPP policy update");
+       write_summary(ctx, "SPP policy update");
+
+       os_free(ctx->server_url);
+       ctx->server_url = os_strdup(address);
+
+       if (soap_init_client(ctx->http, address, ctx->ca_fname, cred_username,
+                            cred_password, client_cert, client_key) == 0) {
+               spp_post_dev_data(ctx, SPP_POLICY_UPDATE, "Policy update",
+                                 pps_fname, pps);
+       }
+}
+
+
+int cmd_prov(struct hs20_osu_client *ctx, const char *url)
+{
+       unlink("Cert/est_cert.der");
+       unlink("Cert/est_cert.pem");
+
+       if (url == NULL) {
+               wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
+               return -1;
+       }
+
+       wpa_printf(MSG_INFO, "Credential provisioning requested");
+
+       os_free(ctx->server_url);
+       ctx->server_url = os_strdup(url);
+
+       if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
+                            NULL) < 0)
+               return -1;
+       spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
+                         "Subscription registration", NULL, NULL);
+
+       return ctx->pps_cred_set ? 0 : -1;
+}
+
+
+int cmd_sim_prov(struct hs20_osu_client *ctx, const char *url)
+{
+       if (url == NULL) {
+               wpa_printf(MSG_INFO, "Invalid prov command (missing URL)");
+               return -1;
+       }
+
+       wpa_printf(MSG_INFO, "SIM provisioning requested");
+
+       os_free(ctx->server_url);
+       ctx->server_url = os_strdup(url);
+
+       wpa_printf(MSG_INFO, "Wait for IP address before starting SIM provisioning");
+
+       if (wait_ip_addr(ctx->ifname, 15) < 0) {
+               wpa_printf(MSG_INFO, "Could not get IP address for WLAN - try connection anyway");
+       }
+
+       if (soap_init_client(ctx->http, url, ctx->ca_fname, NULL, NULL, NULL,
+                            NULL) < 0)
+               return -1;
+       spp_post_dev_data(ctx, SPP_SUBSCRIPTION_REGISTRATION,
+                         "Subscription provisioning", NULL, NULL);
+
+       return ctx->pps_cred_set ? 0 : -1;
+}
index ec0d2dc..fb29d8d 100644 (file)
@@ -9,3 +9,7 @@ CFLAGS = -O2 -Wall -g
 endif
 
 hwsim_test: hwsim_test.o
+
+clean:
+       rm -rf *.o
+       rm -rf hwsim_test
index 75e5984..c22a33f 100644 (file)
@@ -17,6 +17,7 @@
 #include <net/ethernet.h>
 #include <net/if.h>
 #include <arpa/inet.h>
+#include <netinet/ip.h>
 
 #define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5]
 #define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x"
 
 static unsigned char addr1[ETH_ALEN], addr2[ETH_ALEN], bcast[ETH_ALEN];
 
+static u_int16_t checksum(const void *buf, size_t len)
+{
+       size_t i;
+       u_int32_t sum = 0;
+       const u_int16_t *pos = buf;
+
+       for (i = 0; i < len / 2; i++)
+               sum += *pos++;
+
+       while (sum >> 16)
+               sum = (sum & 0xffff) + (sum >> 16);
+
+       return sum ^ 0xffff;
+}
+
+
 static void tx(int s, const char *ifname, int ifindex,
-              const unsigned char *src, const unsigned char *dst)
+              const unsigned char *src, const unsigned char *dst,
+              u_int8_t tos)
 {
        char buf[HWSIM_PACKETLEN], *pos;
        struct ether_header *eth;
+       struct iphdr *ip;
        int i;
 
        printf("TX: %s(ifindex=%d) " MACSTR " -> " MACSTR "\n",
@@ -40,8 +59,19 @@ static void tx(int s, const char *ifname, int ifindex,
        memcpy(eth->ether_dhost, dst, ETH_ALEN);
        memcpy(eth->ether_shost, src, ETH_ALEN);
        eth->ether_type = htons(HWSIM_ETHERTYPE);
-       pos = (char *) (eth + 1);
-       for (i = 0; i < sizeof(buf) - sizeof(*eth); i++)
+       ip = (struct iphdr *) (eth + 1);
+       memset(ip, 0, sizeof(*ip));
+       ip->ihl = 5;
+       ip->version = 4;
+       ip->ttl = 64;
+       ip->tos = tos;
+       ip->tot_len = htons(HWSIM_PACKETLEN - sizeof(*eth));
+       ip->protocol = 1;
+       ip->saddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 1);
+       ip->daddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 2);
+       ip->check = checksum(ip, sizeof(*ip));
+       pos = (char *) (ip + 1);
+       for (i = 0; i < sizeof(buf) - sizeof(*eth) - sizeof(*ip); i++)
                *pos++ = i;
 
        if (send(s, buf, sizeof(buf), 0) < 0)
@@ -50,10 +80,10 @@ static void tx(int s, const char *ifname, int ifindex,
 
 
 struct rx_result {
-       int rx_unicast1:1;
-       int rx_broadcast1:1;
-       int rx_unicast2:1;
-       int rx_broadcast2:1;
+       unsigned int rx_unicast1:1;
+       unsigned int rx_broadcast1:1;
+       unsigned int rx_unicast2:1;
+       unsigned int rx_broadcast2:1;
 };
 
 
@@ -62,6 +92,7 @@ static void rx(int s, int iface, const char *ifname, int ifindex,
 {
        char buf[HWSIM_PACKETLEN + 1], *pos;
        struct ether_header *eth;
+       struct iphdr *ip;
        int len, i;
 
        len = recv(s, buf, sizeof(buf), 0);
@@ -76,12 +107,13 @@ static void rx(int s, int iface, const char *ifname, int ifindex,
               MAC2STR(eth->ether_shost), MAC2STR(eth->ether_dhost), len);
 
        if (len != HWSIM_PACKETLEN) {
-               printf("Ignore frame with unexpected RX length\n");
+               printf("Ignore frame with unexpected RX length (%d)\n", len);
                return;
        }
 
-       pos = (char *) (eth + 1);
-       for (i = 0; i < sizeof(buf) - 1 - sizeof(*eth); i++) {
+       ip = (struct iphdr *) (eth + 1);
+       pos = (char *) (ip + 1);
+       for (i = 0; i < sizeof(buf) - 1 - sizeof(*eth) - sizeof(*ip); i++) {
                if ((unsigned char) *pos != (unsigned char) i) {
                        printf("Ignore frame with unexpected contents\n");
                        printf("i=%d received=0x%x expected=0x%x\n",
@@ -110,21 +142,57 @@ static void rx(int s, int iface, const char *ifname, int ifindex,
 }
 
 
+static void usage(void)
+{
+       fprintf(stderr, "usage: hwsim_test [-D<DSCP>] [-t<tos>] <ifname1> <ifname2>\n");
+}
+
+
 int main(int argc, char *argv[])
 {
-       int s1 = -1, s2 = -1, ret = -1;
+       int s1 = -1, s2 = -1, ret = -1, c;
        struct ifreq ifr;
        int ifindex1, ifindex2;
        struct sockaddr_ll ll;
        fd_set rfds;
        struct timeval tv;
        struct rx_result res;
+       char *s_ifname, *d_ifname, *end;
+       int tos = 0;
 
-       if (argc != 3) {
-               fprintf(stderr, "usage: hwsim_test <ifname1> <ifname2>\n");
+       for (;;) {
+               c = getopt(argc, argv, "D:t:");
+               if (c < 0)
+                       break;
+               switch (c) {
+               case 'D':
+                       tos = strtol(optarg, &end, 0) << 2;
+                       if (*end) {
+                               usage();
+                               return -1;
+                       }
+                       break;
+               case 't':
+                       tos = strtol(optarg, &end, 0);
+                       if (*end) {
+                               usage();
+                               return -1;
+                       }
+                       break;
+               default:
+                       usage();
+                       return -1;
+               }
+       }
+
+       if (optind != argc - 2) {
+               usage();
                return -1;
        }
 
+       s_ifname = argv[optind];
+       d_ifname = argv[optind + 1];
+
        memset(bcast, 0xff, ETH_ALEN);
 
        s1 = socket(PF_PACKET, SOCK_RAW, htons(HWSIM_ETHERTYPE));
@@ -140,7 +208,7 @@ int main(int argc, char *argv[])
        }
 
        memset(&ifr, 0, sizeof(ifr));
-       strncpy(ifr.ifr_name, argv[1], sizeof(ifr.ifr_name));
+       strncpy(ifr.ifr_name, s_ifname, sizeof(ifr.ifr_name));
        if (ioctl(s1, SIOCGIFINDEX, &ifr) < 0) {
                perror("ioctl[SIOCGIFINDEX]");
                goto fail;
@@ -153,7 +221,7 @@ int main(int argc, char *argv[])
        memcpy(addr1, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
 
        memset(&ifr, 0, sizeof(ifr));
-       strncpy(ifr.ifr_name, argv[2], sizeof(ifr.ifr_name));
+       strncpy(ifr.ifr_name, d_ifname, sizeof(ifr.ifr_name));
        if (ioctl(s2, SIOCGIFINDEX, &ifr) < 0) {
                perror("ioctl[SIOCGIFINDEX]");
                goto fail;
@@ -183,10 +251,10 @@ int main(int argc, char *argv[])
                goto fail;
        }
 
-       tx(s1, argv[1], ifindex1, addr1, addr2);
-       tx(s1, argv[1], ifindex1, addr1, bcast);
-       tx(s2, argv[2], ifindex2, addr2, addr1);
-       tx(s2, argv[2], ifindex2, addr2, bcast);
+       tx(s1, s_ifname, ifindex1, addr1, addr2, tos);
+       tx(s1, s_ifname, ifindex1, addr1, bcast, tos);
+       tx(s2, d_ifname, ifindex2, addr2, addr1, tos);
+       tx(s2, d_ifname, ifindex2, addr2, bcast, tos);
 
        tv.tv_sec = 1;
        tv.tv_usec = 0;
@@ -208,9 +276,9 @@ int main(int argc, char *argv[])
                        break; /* timeout */
 
                if (FD_ISSET(s1, &rfds))
-                       rx(s1, 1, argv[1], ifindex1, &res);
+                       rx(s1, 1, s_ifname, ifindex1, &res);
                if (FD_ISSET(s2, &rfds))
-                       rx(s2, 2, argv[2], ifindex2, &res);
+                       rx(s2, 2, d_ifname, ifindex2, &res);
 
                if (res.rx_unicast1 && res.rx_broadcast1 &&
                    res.rx_unicast2 && res.rx_broadcast2) {
@@ -221,8 +289,8 @@ int main(int argc, char *argv[])
 
        if (ret) {
                printf("Did not receive all expected frames:\n"
-                      "rx_unicast1=%d rx_broadcast1=%d "
-                      "rx_unicast2=%d rx_broadcast2=%d\n",
+                      "rx_unicast1=%u rx_broadcast1=%u "
+                      "rx_unicast2=%u rx_broadcast2=%u\n",
                       res.rx_unicast1, res.rx_broadcast1,
                       res.rx_unicast2, res.rx_broadcast2);
        } else {
index 8b43ace..9554030 100644 (file)
@@ -3,8 +3,17 @@
                <domain name="wpasupplicant"/>
                <request>
                        <smack request="dbus" type="rwx"/>
+                       <smack request="wifi-direct" type="rwx"/>
+                       <smack request="sys-assert::core" type="rwxat"/>
+                       <smack request="ca-certificates::ssl-certs" type="rx"/>
+                       <smack request="net-config::logging" type="rwx"/>
+                       <smack request="mobileap-agent" type="rw"/>
+                       <smack request="system::sys_logging" type="wx"/>
+                       <smack request="syslogd" type="w"/>
+                       <smack request="_" type="wx"/>
                </request>
                <permit>
+                       <smack permit="^" type="w"/>
                        <smack permit="dbus" type="rwx"/>
                </permit>
        </define>
@@ -12,8 +21,6 @@
                <filesystem path="/etc/dbus-1/system.d/wpa_supplicant.conf" label="_"/>
                <filesystem path="/usr/lib/systemd/system/network.target.wants/wpa_supplicant.service" label="_"/>
                <filesystem path="/usr/lib/systemd/system/wpa_supplicant.service" label="_"/>
-               <filesystem path="/usr/share/dbus-1/services/fi.w1.wpa_supplicant1.service" label="_"/>
-               <filesystem path="/usr/share/dbus-1/services/fi.epitest.hostap.WPASupplicant.service" label="_"/>
                <filesystem path="/usr/share/doc/wpasupplicant/README.wpa_supplicant.conf.gz" label="_"/>
                <filesystem path="/etc/rc.d/init.d/wpa_supplicant" label="_" exec_label="none"/>
                <filesystem path="/etc/rc.d/rc3.d/S62wpasupplicant" label="_" exec_label="none"/>
@@ -22,4 +29,4 @@
        <request>
                <domain name="wpasupplicant"/>
        </request>
-</manifest>
\ No newline at end of file
+</manifest>
index 9fcd39d..8b66549 100644 (file)
@@ -1,6 +1,6 @@
 Name:      wpasupplicant
 Summary:    Support for WPA and WPA2 (IEEE 802.11i / RSN)
-Version:    2.1.6
+Version:    2.4.36
 Release:    1
 Group:      System/Network
 License:    BSD-2.0
@@ -26,6 +26,22 @@ association with IEEE 802.11i networks.
 %setup -q
 
 %build
+
+%if "%{?tizen_profile_name}" == "mobile"
+%if "%{?tizen_target_name}" == "Z130H"
+CONFIG_TIZEN_MOBILE=y; export CONFIG_TIZEN_MOBILE
+%else
+%if "%{?tizen_target_name}" == "Z300H"
+CONFIG_TIZEN_WLAN_BOARD_SPRD=y; export CONFIG_TIZEN_WLAN_BOARD_SPRD
+%else
+CONFIG_BCM_DRIVER_V115=y; export CONFIG_BCM_DRIVER_V115
+%endif
+%endif
+%else
+%if "%{?tizen_profile_name}" == "tv"
+%endif
+%endif
+
 cp %{SOURCE1001} .
 cp -v configurations/tizen.config wpa_supplicant/.config
 cp -v configurations/tizen_hostapd.config hostapd/.config
@@ -35,7 +51,7 @@ make %{?_smp_mflags} -C hostapd all
 
 %install
 mkdir -p %{buildroot}%{_sbindir}/systemd/
-mkdir -p %{buildroot}%{_sbindir}/dbus/
+#mkdir -p %{buildroot}%{_sbindir}/dbus/
 
 cp -v wpa_supplicant/wpa_supplicant %{buildroot}%{_sbindir}/
 cp -v wpa_supplicant/wpa_cli %{buildroot}%{_sbindir}/
@@ -51,16 +67,11 @@ cp -v hostapd/hostapd.conf %{buildroot}%{_sysconfdir}/wpa_supplicant/hostapd.con
 # D-Bus
 #mkdir -p %{buildroot}%{_sysconfdir}/dbus-1/system.d/
 #cp wpa_supplicant/dbus/dbus-wpa_supplicant.conf %{buildroot}%{_sysconfdir}/dbus-1/system.d/wpa_supplicant.conf
-mkdir -p %{buildroot}%{_datadir}/dbus-1/services/
-cp wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service %{buildroot}%{_datadir}/dbus-1/services/
-cp wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service %{buildroot}%{_datadir}/dbus-1/services/
+#mkdir -p %{buildroot}%{_datadir}/dbus-1/services/
+#mkdir -p %{buildroot}%{_datadir}/dbus-1/system-services/
+#cp wpa_supplicant/dbus/fi.epitest.hostap.WPASupplicant.service %{buildroot}%{_datadir}/dbus-1/services/
+#cp wpa_supplicant/dbus/fi.w1.wpa_supplicant1.service %{buildroot}%{_datadir}/dbus-1/system-services/
 
-mkdir -p %{buildroot}%{_sysconfdir}/rc.d/init.d
-cp etc/rc.d/init.d/wpa_supplicant %{buildroot}%{_sysconfdir}/rc.d/init.d/wpa_supplicant
-mkdir -p %{buildroot}%{_sysconfdir}/rc.d/rc3.d/
-ln -s ../init.d/wpa_supplicant %{buildroot}%{_sysconfdir}/rc.d/rc3.d/S62wpasupplicant
-mkdir -p %{buildroot}%{_sysconfdir}/rc.d/rc5.d/
-ln -s ../init.d/wpa_supplicant %{buildroot}%{_sysconfdir}/rc.d/rc5.d/S62wpasupplicant
 
 # sanitise the example configuration
 mkdir -p %{buildroot}%{_defaultdocdir}/wpasupplicant
@@ -73,7 +84,7 @@ sed 's/^\([^#]\+=.*\|}\)/#\1/' < ./wpa_supplicant/wpa_supplicant.conf | gzip > %
 #ln -s ../wpa_supplicant.service %{buildroot}%{_libdir}/systemd/system/network.target.wants/wpa_supplicant.service
 
 rm -rf %{buildroot}%{_sbindir}/systemd/
-rm -rf %{buildroot}%{_sbindir}/dbus/
+#rm -rf %{buildroot}%{_sbindir}/dbus/
 rm -rf %{buildroot}%{_sbindir}/wpa_passphrase
 
 %post -p /sbin/ldconfig
@@ -89,11 +100,10 @@ rm -rf %{buildroot}%{_sbindir}/wpa_passphrase
 %{_sbindir}/hostapd_cli
 %attr(500,root,root) %{_sbindir}/wpa_supp.sh
 #%attr(644,-,-) %{_sysconfdir}/dbus-1/system.d/*.conf
-%attr(644,-,-) %{_datadir}/dbus-1/services/*.service
+#%attr(644,-,-) %{_datadir}/dbus-1/services/*.service
+#%attr(644,-,-) %{_datadir}/dbus-1/system-services/*.service
 %attr(644,-,-) %{_sysconfdir}/wpa_supplicant/*.conf
 %{_defaultdocdir}/wpasupplicant/README.wpa_supplicant.*
-%{_sysconfdir}/rc.d/init.d/wpa_supplicant
-%{_sysconfdir}/rc.d/rc3.d/S62wpasupplicant
-%{_sysconfdir}/rc.d/rc5.d/S62wpasupplicant
 #%{_libdir}/systemd/system/wpa_supplicant.service
 #%{_libdir}/systemd/system/network.target.wants/wpa_supplicant.service
+%doc COPYING.mbsd
index 92e992c..883e2f2 100644 (file)
@@ -21,6 +21,7 @@ CFLAGS += -I../src/utils
 LIBS = ../src/radius/libradius.a
 LIBS += ../src/crypto/libcrypto.a
 LIBS += ../src/utils/libutils.a
+LLIBS = -lrt
 
 ../src/utils/libutils.a:
        $(MAKE) -C ../src/utils
@@ -36,7 +37,7 @@ LIBS += ../src/utils/libutils.a
 OBJS_ex = radius_example.o
 
 radius_example: $(OBJS_ex) $(LIBS)
-       $(LDO) $(LDFLAGS) -o radius_example $(OBJS_ex) $(LIBS)
+       $(LDO) $(LDFLAGS) -o radius_example $(OBJS_ex) $(LIBS) $(LLIBS)
 
 clean:
        $(MAKE) -C ../src clean
index cb0e154..e4b3678 100644 (file)
@@ -13,8 +13,6 @@
 #include "radius/radius.h"
 #include "radius/radius_client.h"
 
-extern int wpa_debug_level;
-
 struct radius_ctx {
        struct radius_client_data *radius;
        struct hostapd_radius_servers conf;
index d73a175..10e0171 100644 (file)
@@ -1,4 +1,4 @@
-SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p radius rsn_supp tls utils wps
+SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet p2p pae radius rsn_supp tls utils wps
 
 all:
        for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done
index 9c41962..adfd3df 100644 (file)
@@ -2,7 +2,7 @@ all:
        @echo Nothing to be made.
 
 clean:
-       rm -f *~ *.o *.d
+       rm -f *~ *.o *.d *.gcno *.gcda *.gcov
 
 install:
        @echo Nothing to be made.
index 9540531..7c55146 100644 (file)
@@ -10,7 +10,8 @@
 
 #include "utils/common.h"
 #include "utils/eloop.h"
-#include "drivers/driver.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
 #include "radius/radius.h"
 #include "radius/radius_client.h"
 #include "hostapd.h"
@@ -44,19 +45,26 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
        msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST,
                             radius_client_get_id(hapd->radius));
        if (msg == NULL) {
-               printf("Could not create net RADIUS packet\n");
+               wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
                return NULL;
        }
 
        if (sta) {
                radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta));
 
-               os_snprintf(buf, sizeof(buf), "%08X-%08X",
-                           sta->acct_session_id_hi, sta->acct_session_id_lo);
-               if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID,
-                                        (u8 *) buf, os_strlen(buf))) {
-                       printf("Could not add Acct-Session-Id\n");
-                       goto fail;
+               if ((hapd->conf->wpa & 2) &&
+                   !hapd->conf->disable_pmksa_caching &&
+                   sta->eapol_sm && sta->eapol_sm->acct_multi_session_id_hi) {
+                       os_snprintf(buf, sizeof(buf), "%08X+%08X",
+                                   sta->eapol_sm->acct_multi_session_id_hi,
+                                   sta->eapol_sm->acct_multi_session_id_lo);
+                       if (!radius_msg_add_attr(
+                                   msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
+                                   (u8 *) buf, os_strlen(buf))) {
+                               wpa_printf(MSG_INFO,
+                                          "Could not add Acct-Multi-Session-Id");
+                               goto fail;
+                       }
                }
        } else {
                radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd));
@@ -64,7 +72,7 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
 
        if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE,
                                       status_type)) {
-               printf("Could not add Acct-Status-Type\n");
+               wpa_printf(MSG_INFO, "Could not add Acct-Status-Type");
                goto fail;
        }
 
@@ -74,7 +82,7 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
                                       hapd->conf->ieee802_1x ?
                                       RADIUS_ACCT_AUTHENTIC_RADIUS :
                                       RADIUS_ACCT_AUTHENTIC_LOCAL)) {
-               printf("Could not add Acct-Authentic\n");
+               wpa_printf(MSG_INFO, "Could not add Acct-Authentic");
                goto fail;
        }
 
@@ -99,7 +107,7 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
 
                if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val,
                                         len)) {
-                       printf("Could not add User-Name\n");
+                       wpa_printf(MSG_INFO, "Could not add User-Name");
                        goto fail;
                }
        }
@@ -117,7 +125,7 @@ static struct radius_msg * accounting_msg(struct hostapd_data *hapd,
 
                        if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS,
                                                 val, len)) {
-                               printf("Could not add Class\n");
+                               wpa_printf(MSG_INFO, "Could not add Class");
                                goto fail;
                        }
                }
@@ -202,7 +210,6 @@ static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx)
 void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
 {
        struct radius_msg *msg;
-       struct os_time t;
        int interval;
 
        if (sta->acct_session_started)
@@ -213,8 +220,7 @@ void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta)
                       "starting accounting session %08X-%08X",
                       sta->acct_session_id_hi, sta->acct_session_id_lo);
 
-       os_get_time(&t);
-       sta->acct_session_start = t.sec;
+       os_get_reltime(&sta->acct_session_start);
        sta->last_rx_bytes = sta->last_tx_bytes = 0;
        sta->acct_input_gigawords = sta->acct_output_gigawords = 0;
        hostapd_drv_sta_clear_stats(hapd, sta->addr);
@@ -244,6 +250,7 @@ static void accounting_sta_report(struct hostapd_data *hapd,
        struct radius_msg *msg;
        int cause = sta->acct_terminate_cause;
        struct hostap_sta_driver_data data;
+       struct os_reltime now_r, diff;
        struct os_time now;
        u32 gigawords;
 
@@ -254,14 +261,16 @@ static void accounting_sta_report(struct hostapd_data *hapd,
                             stop ? RADIUS_ACCT_STATUS_TYPE_STOP :
                             RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE);
        if (!msg) {
-               printf("Could not create RADIUS Accounting message\n");
+               wpa_printf(MSG_INFO, "Could not create RADIUS Accounting message");
                return;
        }
 
+       os_get_reltime(&now_r);
        os_get_time(&now);
+       os_reltime_sub(&now_r, &sta->acct_session_start, &diff);
        if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME,
-                                      now.sec - sta->acct_session_start)) {
-               printf("Could not add Acct-Session-Time\n");
+                                      diff.sec)) {
+               wpa_printf(MSG_INFO, "Could not add Acct-Session-Time");
                goto fail;
        }
 
@@ -269,19 +278,19 @@ static void accounting_sta_report(struct hostapd_data *hapd,
                if (!radius_msg_add_attr_int32(msg,
                                               RADIUS_ATTR_ACCT_INPUT_PACKETS,
                                               data.rx_packets)) {
-                       printf("Could not add Acct-Input-Packets\n");
+                       wpa_printf(MSG_INFO, "Could not add Acct-Input-Packets");
                        goto fail;
                }
                if (!radius_msg_add_attr_int32(msg,
                                               RADIUS_ATTR_ACCT_OUTPUT_PACKETS,
                                               data.tx_packets)) {
-                       printf("Could not add Acct-Output-Packets\n");
+                       wpa_printf(MSG_INFO, "Could not add Acct-Output-Packets");
                        goto fail;
                }
                if (!radius_msg_add_attr_int32(msg,
                                               RADIUS_ATTR_ACCT_INPUT_OCTETS,
                                               data.rx_bytes)) {
-                       printf("Could not add Acct-Input-Octets\n");
+                       wpa_printf(MSG_INFO, "Could not add Acct-Input-Octets");
                        goto fail;
                }
                gigawords = sta->acct_input_gigawords;
@@ -292,13 +301,13 @@ static void accounting_sta_report(struct hostapd_data *hapd,
                    !radius_msg_add_attr_int32(
                            msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS,
                            gigawords)) {
-                       printf("Could not add Acct-Input-Gigawords\n");
+                       wpa_printf(MSG_INFO, "Could not add Acct-Input-Gigawords");
                        goto fail;
                }
                if (!radius_msg_add_attr_int32(msg,
                                               RADIUS_ATTR_ACCT_OUTPUT_OCTETS,
                                               data.tx_bytes)) {
-                       printf("Could not add Acct-Output-Octets\n");
+                       wpa_printf(MSG_INFO, "Could not add Acct-Output-Octets");
                        goto fail;
                }
                gigawords = sta->acct_output_gigawords;
@@ -309,14 +318,14 @@ static void accounting_sta_report(struct hostapd_data *hapd,
                    !radius_msg_add_attr_int32(
                            msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS,
                            gigawords)) {
-                       printf("Could not add Acct-Output-Gigawords\n");
+                       wpa_printf(MSG_INFO, "Could not add Acct-Output-Gigawords");
                        goto fail;
                }
        }
 
        if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP,
                                       now.sec)) {
-               printf("Could not add Event-Timestamp\n");
+               wpa_printf(MSG_INFO, "Could not add Event-Timestamp");
                goto fail;
        }
 
@@ -326,7 +335,7 @@ static void accounting_sta_report(struct hostapd_data *hapd,
        if (stop && cause &&
            !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
                                       cause)) {
-               printf("Could not add Acct-Terminate-Cause\n");
+               wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
                goto fail;
        }
 
@@ -400,13 +409,12 @@ accounting_receive(struct radius_msg *msg, struct radius_msg *req,
                   void *data)
 {
        if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) {
-               printf("Unknown RADIUS message code\n");
+               wpa_printf(MSG_INFO, "Unknown RADIUS message code");
                return RADIUS_RX_UNKNOWN;
        }
 
        if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) {
-               printf("Incoming RADIUS packet did not have correct "
-                      "Authenticator - dropped\n");
+               wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Authenticator - dropped");
                return RADIUS_RX_INVALID_AUTHENTICATOR;
        }
 
@@ -432,7 +440,7 @@ static void accounting_report_state(struct hostapd_data *hapd, int on)
        if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE,
                                       RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT))
        {
-               printf("Could not add Acct-Terminate-Cause\n");
+               wpa_printf(MSG_INFO, "Could not add Acct-Terminate-Cause");
                radius_msg_free(msg);
                return;
        }
diff --git a/src/ap/acs.c b/src/ap/acs.c
new file mode 100644 (file)
index 0000000..ae7f6c3
--- /dev/null
@@ -0,0 +1,949 @@
+/*
+ * ACS - Automatic Channel Selection module
+ * Copyright (c) 2011, Atheros Communications
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <math.h>
+
+#include "utils/common.h"
+#include "utils/list.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "drivers/driver.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "ap_config.h"
+#include "hw_features.h"
+#include "acs.h"
+
+/*
+ * Automatic Channel Selection
+ * ===========================
+ *
+ * More info at
+ * ------------
+ * http://wireless.kernel.org/en/users/Documentation/acs
+ *
+ * How to use
+ * ----------
+ * - make sure you have CONFIG_ACS=y in hostapd's .config
+ * - use channel=0 or channel=acs to enable ACS
+ *
+ * How does it work
+ * ----------------
+ * 1. passive scans are used to collect survey data
+ *    (it is assumed that scan trigger collection of survey data in driver)
+ * 2. interference factor is calculated for each channel
+ * 3. ideal channel is picked depending on channel width by using adjacent
+ *    channel interference factors
+ *
+ * Known limitations
+ * -----------------
+ * - Current implementation depends heavily on the amount of time willing to
+ *   spend gathering survey data during hostapd startup. Short traffic bursts
+ *   may be missed and a suboptimal channel may be picked.
+ * - Ideal channel may end up overlapping a channel with 40 MHz intolerant BSS
+ *
+ * Todo / Ideas
+ * ------------
+ * - implement other interference computation methods
+ *   - BSS/RSSI based
+ *   - spectral scan based
+ *   (should be possibly to hook this up with current ACS scans)
+ * - add wpa_supplicant support (for P2P)
+ * - collect a histogram of interference over time allowing more educated
+ *   guess about an ideal channel (perhaps CSA could be used to migrate AP to a
+ *   new "better" channel while running)
+ * - include neighboring BSS scan to avoid conflicts with 40 MHz intolerant BSSs
+ *   when choosing the ideal channel
+ *
+ * Survey interference factor implementation details
+ * -------------------------------------------------
+ * Generic interference_factor in struct hostapd_channel_data is used.
+ *
+ * The survey interference factor is defined as the ratio of the
+ * observed busy time over the time we spent on the channel,
+ * this value is then amplified by the observed noise floor on
+ * the channel in comparison to the lowest noise floor observed
+ * on the entire band.
+ *
+ * This corresponds to:
+ * ---
+ * (busy time - tx time) / (active time - tx time) * 2^(chan_nf + band_min_nf)
+ * ---
+ *
+ * The coefficient of 2 reflects the way power in "far-field"
+ * radiation decreases as the square of distance from the antenna [1].
+ * What this does is it decreases the observed busy time ratio if the
+ * noise observed was low but increases it if the noise was high,
+ * proportionally to the way "far field" radiation changes over
+ * distance.
+ *
+ * If channel busy time is not available the fallback is to use channel RX time.
+ *
+ * Since noise floor is in dBm it is necessary to convert it into Watts so that
+ * combined channel interference (e.g., HT40, which uses two channels) can be
+ * calculated easily.
+ * ---
+ * (busy time - tx time) / (active time - tx time) *
+ *    2^(10^(chan_nf/10) + 10^(band_min_nf/10))
+ * ---
+ *
+ * However to account for cases where busy/rx time is 0 (channel load is then
+ * 0%) channel noise floor signal power is combined into the equation so a
+ * channel with lower noise floor is preferred. The equation becomes:
+ * ---
+ * 10^(chan_nf/5) + (busy time - tx time) / (active time - tx time) *
+ *    2^(10^(chan_nf/10) + 10^(band_min_nf/10))
+ * ---
+ *
+ * All this "interference factor" is purely subjective and only time
+ * will tell how usable this is. By using the minimum noise floor we
+ * remove any possible issues due to card calibration. The computation
+ * of the interference factor then is dependent on what the card itself
+ * picks up as the minimum noise, not an actual real possible card
+ * noise value.
+ *
+ * Total interference computation details
+ * --------------------------------------
+ * The above channel interference factor is calculated with no respect to
+ * target operational bandwidth.
+ *
+ * To find an ideal channel the above data is combined by taking into account
+ * the target operational bandwidth and selected band. E.g., on 2.4 GHz channels
+ * overlap with 20 MHz bandwidth, but there is no overlap for 20 MHz bandwidth
+ * on 5 GHz.
+ *
+ * Each valid and possible channel spec (i.e., channel + width) is taken and its
+ * interference factor is computed by summing up interferences of each channel
+ * it overlaps. The one with least total interference is picked up.
+ *
+ * Note: This implies base channel interference factor must be non-negative
+ * allowing easy summing up.
+ *
+ * Example ACS analysis printout
+ * -----------------------------
+ *
+ * ACS: Trying survey-based ACS
+ * ACS: Survey analysis for channel 1 (2412 MHz)
+ * ACS:  1: min_nf=-113 interference_factor=0.0802469 nf=-113 time=162 busy=0 rx=13
+ * ACS:  2: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12
+ * ACS:  3: min_nf=-113 interference_factor=0.0679012 nf=-113 time=162 busy=0 rx=11
+ * ACS:  4: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5
+ * ACS:  5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4
+ * ACS:  * interference factor average: 0.0557166
+ * ACS: Survey analysis for channel 2 (2417 MHz)
+ * ACS:  1: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3
+ * ACS:  2: min_nf=-113 interference_factor=0.0246914 nf=-113 time=162 busy=0 rx=4
+ * ACS:  3: min_nf=-113 interference_factor=0.037037 nf=-113 time=162 busy=0 rx=6
+ * ACS:  4: min_nf=-113 interference_factor=0.149068 nf=-113 time=161 busy=0 rx=24
+ * ACS:  5: min_nf=-113 interference_factor=0.0248447 nf=-113 time=161 busy=0 rx=4
+ * ACS:  * interference factor average: 0.050832
+ * ACS: Survey analysis for channel 3 (2422 MHz)
+ * ACS:  1: min_nf=-113 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
+ * ACS:  2: min_nf=-113 interference_factor=0.0185185 nf=-113 time=162 busy=0 rx=3
+ * ACS:  3: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
+ * ACS:  4: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
+ * ACS:  5: min_nf=-113 interference_factor=0.0186335 nf=-113 time=161 busy=0 rx=3
+ * ACS:  * interference factor average: 0.0148838
+ * ACS: Survey analysis for channel 4 (2427 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS:  2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9
+ * ACS:  3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
+ * ACS:  4: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3
+ * ACS:  5: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS:  * interference factor average: 0.0160801
+ * ACS: Survey analysis for channel 5 (2432 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=0.409938 nf=-113 time=161 busy=0 rx=66
+ * ACS:  2: min_nf=-114 interference_factor=0.0432099 nf=-113 time=162 busy=0 rx=7
+ * ACS:  3: min_nf=-114 interference_factor=0.0124224 nf=-113 time=161 busy=0 rx=2
+ * ACS:  4: min_nf=-114 interference_factor=0.677019 nf=-113 time=161 busy=0 rx=109
+ * ACS:  5: min_nf=-114 interference_factor=0.0186335 nf=-114 time=161 busy=0 rx=3
+ * ACS:  * interference factor average: 0.232244
+ * ACS: Survey analysis for channel 6 (2437 MHz)
+ * ACS:  1: min_nf=-113 interference_factor=0.552795 nf=-113 time=161 busy=0 rx=89
+ * ACS:  2: min_nf=-113 interference_factor=0.0807453 nf=-112 time=161 busy=0 rx=13
+ * ACS:  3: min_nf=-113 interference_factor=0.0310559 nf=-113 time=161 busy=0 rx=5
+ * ACS:  4: min_nf=-113 interference_factor=0.434783 nf=-112 time=161 busy=0 rx=70
+ * ACS:  5: min_nf=-113 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10
+ * ACS:  * interference factor average: 0.232298
+ * ACS: Survey analysis for channel 7 (2442 MHz)
+ * ACS:  1: min_nf=-113 interference_factor=0.440994 nf=-112 time=161 busy=0 rx=71
+ * ACS:  2: min_nf=-113 interference_factor=0.385093 nf=-113 time=161 busy=0 rx=62
+ * ACS:  3: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
+ * ACS:  4: min_nf=-113 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
+ * ACS:  5: min_nf=-113 interference_factor=0.0745342 nf=-113 time=161 busy=0 rx=12
+ * ACS:  * interference factor average: 0.195031
+ * ACS: Survey analysis for channel 8 (2447 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=0.0496894 nf=-112 time=161 busy=0 rx=8
+ * ACS:  2: min_nf=-114 interference_factor=0.0496894 nf=-114 time=161 busy=0 rx=8
+ * ACS:  3: min_nf=-114 interference_factor=0.0372671 nf=-113 time=161 busy=0 rx=6
+ * ACS:  4: min_nf=-114 interference_factor=0.12963 nf=-113 time=162 busy=0 rx=21
+ * ACS:  5: min_nf=-114 interference_factor=0.166667 nf=-114 time=162 busy=0 rx=27
+ * ACS:  * interference factor average: 0.0865885
+ * ACS: Survey analysis for channel 9 (2452 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=0.0124224 nf=-114 time=161 busy=0 rx=2
+ * ACS:  2: min_nf=-114 interference_factor=0.0310559 nf=-114 time=161 busy=0 rx=5
+ * ACS:  3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
+ * ACS:  4: min_nf=-114 interference_factor=0.00617284 nf=-114 time=162 busy=0 rx=1
+ * ACS:  5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS:  * interference factor average: 0.00993022
+ * ACS: Survey analysis for channel 10 (2457 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS:  2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS:  3: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS:  4: min_nf=-114 interference_factor=0.0493827 nf=-114 time=162 busy=0 rx=8
+ * ACS:  5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS:  * interference factor average: 0.0136033
+ * ACS: Survey analysis for channel 11 (2462 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=161 busy=0 rx=0
+ * ACS:  2: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0
+ * ACS:  3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=161 busy=0 rx=0
+ * ACS:  4: min_nf=-114 interference_factor=0.0432099 nf=-114 time=162 busy=0 rx=7
+ * ACS:  5: min_nf=-114 interference_factor=0.0925926 nf=-114 time=162 busy=0 rx=15
+ * ACS:  * interference factor average: 0.0271605
+ * ACS: Survey analysis for channel 12 (2467 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=0.0621118 nf=-113 time=161 busy=0 rx=10
+ * ACS:  2: min_nf=-114 interference_factor=0.00621118 nf=-114 time=161 busy=0 rx=1
+ * ACS:  3: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
+ * ACS:  4: min_nf=-114 interference_factor=2.51189e-23 nf=-113 time=162 busy=0 rx=0
+ * ACS:  5: min_nf=-114 interference_factor=0.00617284 nf=-113 time=162 busy=0 rx=1
+ * ACS:  * interference factor average: 0.0148992
+ * ACS: Survey analysis for channel 13 (2472 MHz)
+ * ACS:  1: min_nf=-114 interference_factor=0.0745342 nf=-114 time=161 busy=0 rx=12
+ * ACS:  2: min_nf=-114 interference_factor=0.0555556 nf=-114 time=162 busy=0 rx=9
+ * ACS:  3: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS:  4: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS:  5: min_nf=-114 interference_factor=1.58489e-23 nf=-114 time=162 busy=0 rx=0
+ * ACS:  * interference factor average: 0.0260179
+ * ACS: Survey analysis for selected bandwidth 20MHz
+ * ACS:  * channel 1: total interference = 0.121432
+ * ACS:  * channel 2: total interference = 0.137512
+ * ACS:  * channel 3: total interference = 0.369757
+ * ACS:  * channel 4: total interference = 0.546338
+ * ACS:  * channel 5: total interference = 0.690538
+ * ACS:  * channel 6: total interference = 0.762242
+ * ACS:  * channel 7: total interference = 0.756092
+ * ACS:  * channel 8: total interference = 0.537451
+ * ACS:  * channel 9: total interference = 0.332313
+ * ACS:  * channel 10: total interference = 0.152182
+ * ACS:  * channel 11: total interference = 0.0916111
+ * ACS:  * channel 12: total interference = 0.0816809
+ * ACS:  * channel 13: total interference = 0.0680776
+ * ACS: Ideal channel is 13 (2472 MHz) with total interference factor of 0.0680776
+ *
+ * [1] http://en.wikipedia.org/wiki/Near_and_far_field
+ */
+
+
+static int acs_request_scan(struct hostapd_iface *iface);
+static int acs_survey_is_sufficient(struct freq_survey *survey);
+
+
+static void acs_clean_chan_surveys(struct hostapd_channel_data *chan)
+{
+       struct freq_survey *survey, *tmp;
+
+       if (dl_list_empty(&chan->survey_list))
+               return;
+
+       dl_list_for_each_safe(survey, tmp, &chan->survey_list,
+                             struct freq_survey, list) {
+               dl_list_del(&survey->list);
+               os_free(survey);
+       }
+}
+
+
+static void acs_cleanup(struct hostapd_iface *iface)
+{
+       int i;
+       struct hostapd_channel_data *chan;
+
+       for (i = 0; i < iface->current_mode->num_channels; i++) {
+               chan = &iface->current_mode->channels[i];
+
+               if (chan->flag & HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED)
+                       acs_clean_chan_surveys(chan);
+
+               dl_list_init(&chan->survey_list);
+               chan->flag |= HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED;
+               chan->min_nf = 0;
+       }
+
+       iface->chans_surveyed = 0;
+       iface->acs_num_completed_scans = 0;
+}
+
+
+static void acs_fail(struct hostapd_iface *iface)
+{
+       wpa_printf(MSG_ERROR, "ACS: Failed to start");
+       acs_cleanup(iface);
+       hostapd_disable_iface(iface);
+}
+
+
+static long double
+acs_survey_interference_factor(struct freq_survey *survey, s8 min_nf)
+{
+       long double factor, busy, total;
+
+       if (survey->filled & SURVEY_HAS_CHAN_TIME_BUSY)
+               busy = survey->channel_time_busy;
+       else if (survey->filled & SURVEY_HAS_CHAN_TIME_RX)
+               busy = survey->channel_time_rx;
+       else {
+               /* This shouldn't really happen as survey data is checked in
+                * acs_sanity_check() */
+               wpa_printf(MSG_ERROR, "ACS: Survey data missing");
+               return 0;
+       }
+
+       total = survey->channel_time;
+
+       if (survey->filled & SURVEY_HAS_CHAN_TIME_TX) {
+               busy -= survey->channel_time_tx;
+               total -= survey->channel_time_tx;
+       }
+
+       /* TODO: figure out the best multiplier for noise floor base */
+       factor = pow(10, survey->nf / 5.0L) +
+               (busy / total) *
+               pow(2, pow(10, (long double) survey->nf / 10.0L) -
+                   pow(10, (long double) min_nf / 10.0L));
+
+       return factor;
+}
+
+
+static void
+acs_survey_chan_interference_factor(struct hostapd_iface *iface,
+                                   struct hostapd_channel_data *chan)
+{
+       struct freq_survey *survey;
+       unsigned int i = 0;
+       long double int_factor = 0;
+       unsigned count = 0;
+
+       if (dl_list_empty(&chan->survey_list))
+               return;
+
+       if (chan->flag & HOSTAPD_CHAN_DISABLED)
+               return;
+
+       chan->interference_factor = 0;
+
+       dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
+       {
+               i++;
+
+               if (!acs_survey_is_sufficient(survey)) {
+                       wpa_printf(MSG_DEBUG, "ACS: %d: insufficient data", i);
+                       continue;
+               }
+
+               count++;
+               int_factor = acs_survey_interference_factor(survey,
+                                                           iface->lowest_nf);
+               chan->interference_factor += int_factor;
+               wpa_printf(MSG_DEBUG, "ACS: %d: min_nf=%d interference_factor=%Lg nf=%d time=%lu busy=%lu rx=%lu",
+                          i, chan->min_nf, int_factor,
+                          survey->nf, (unsigned long) survey->channel_time,
+                          (unsigned long) survey->channel_time_busy,
+                          (unsigned long) survey->channel_time_rx);
+       }
+
+       if (!count)
+               return;
+       chan->interference_factor /= count;
+}
+
+
+static int acs_usable_ht40_chan(struct hostapd_channel_data *chan)
+{
+       const int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149,
+                               157, 184, 192 };
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(allowed); i++)
+               if (chan->chan == allowed[i])
+                       return 1;
+
+       return 0;
+}
+
+
+static int acs_usable_vht80_chan(struct hostapd_channel_data *chan)
+{
+       const int allowed[] = { 36, 52, 100, 116, 132, 149 };
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(allowed); i++)
+               if (chan->chan == allowed[i])
+                       return 1;
+
+       return 0;
+}
+
+
+static int acs_survey_is_sufficient(struct freq_survey *survey)
+{
+       if (!(survey->filled & SURVEY_HAS_NF)) {
+               wpa_printf(MSG_INFO, "ACS: Survey is missing noise floor");
+               return 0;
+       }
+
+       if (!(survey->filled & SURVEY_HAS_CHAN_TIME)) {
+               wpa_printf(MSG_INFO, "ACS: Survey is missing channel time");
+               return 0;
+       }
+
+       if (!(survey->filled & SURVEY_HAS_CHAN_TIME_BUSY) &&
+           !(survey->filled & SURVEY_HAS_CHAN_TIME_RX)) {
+               wpa_printf(MSG_INFO,
+                          "ACS: Survey is missing RX and busy time (at least one is required)");
+               return 0;
+       }
+
+       return 1;
+}
+
+
+static int acs_survey_list_is_sufficient(struct hostapd_channel_data *chan)
+{
+       struct freq_survey *survey;
+       int ret = -1;
+
+       dl_list_for_each(survey, &chan->survey_list, struct freq_survey, list)
+       {
+               if (acs_survey_is_sufficient(survey)) {
+                       ret = 1;
+                       break;
+               }
+               ret = 0;
+       }
+
+       if (ret == -1)
+               ret = 1; /* no survey list entries */
+
+       if (!ret) {
+               wpa_printf(MSG_INFO,
+                          "ACS: Channel %d has insufficient survey data",
+                          chan->chan);
+       }
+
+       return ret;
+}
+
+
+static int acs_surveys_are_sufficient(struct hostapd_iface *iface)
+{
+       int i;
+       struct hostapd_channel_data *chan;
+       int valid = 0;
+
+       for (i = 0; i < iface->current_mode->num_channels; i++) {
+               chan = &iface->current_mode->channels[i];
+               if (chan->flag & HOSTAPD_CHAN_DISABLED)
+                       continue;
+
+               if (!acs_survey_list_is_sufficient(chan))
+                       continue;
+
+               valid++;
+       }
+
+       /* We need at least survey data for one channel */
+       return !!valid;
+}
+
+
+static int acs_usable_chan(struct hostapd_channel_data *chan)
+{
+       if (dl_list_empty(&chan->survey_list))
+               return 0;
+       if (chan->flag & HOSTAPD_CHAN_DISABLED)
+               return 0;
+       if (!acs_survey_list_is_sufficient(chan))
+               return 0;
+       return 1;
+}
+
+
+static int is_in_chanlist(struct hostapd_iface *iface,
+                         struct hostapd_channel_data *chan)
+{
+       int *entry;
+
+       if (!iface->conf->chanlist)
+               return 1;
+
+       for (entry = iface->conf->chanlist; *entry != -1; entry++) {
+               if (*entry == chan->chan)
+                       return 1;
+       }
+       return 0;
+}
+
+
+static void acs_survey_all_chans_intereference_factor(
+       struct hostapd_iface *iface)
+{
+       int i;
+       struct hostapd_channel_data *chan;
+
+       for (i = 0; i < iface->current_mode->num_channels; i++) {
+               chan = &iface->current_mode->channels[i];
+
+               if (!acs_usable_chan(chan))
+                       continue;
+
+               if (!is_in_chanlist(iface, chan))
+                       continue;
+
+               wpa_printf(MSG_DEBUG, "ACS: Survey analysis for channel %d (%d MHz)",
+                          chan->chan, chan->freq);
+
+               acs_survey_chan_interference_factor(iface, chan);
+
+               wpa_printf(MSG_DEBUG, "ACS:  * interference factor average: %Lg",
+                          chan->interference_factor);
+       }
+}
+
+
+static struct hostapd_channel_data *acs_find_chan(struct hostapd_iface *iface,
+                                                 int freq)
+{
+       struct hostapd_channel_data *chan;
+       int i;
+
+       for (i = 0; i < iface->current_mode->num_channels; i++) {
+               chan = &iface->current_mode->channels[i];
+
+               if (chan->flag & HOSTAPD_CHAN_DISABLED)
+                       continue;
+
+               if (chan->freq == freq)
+                       return chan;
+       }
+
+       return NULL;
+}
+
+
+static int is_24ghz_mode(enum hostapd_hw_mode mode)
+{
+       return mode == HOSTAPD_MODE_IEEE80211B ||
+               mode == HOSTAPD_MODE_IEEE80211G;
+}
+
+
+static int is_common_24ghz_chan(int chan)
+{
+       return chan == 1 || chan == 6 || chan == 11;
+}
+
+
+#ifndef ACS_ADJ_WEIGHT
+#define ACS_ADJ_WEIGHT 0.85
+#endif /* ACS_ADJ_WEIGHT */
+
+#ifndef ACS_NEXT_ADJ_WEIGHT
+#define ACS_NEXT_ADJ_WEIGHT 0.55
+#endif /* ACS_NEXT_ADJ_WEIGHT */
+
+#ifndef ACS_24GHZ_PREFER_1_6_11
+/*
+ * Select commonly used channels 1, 6, 11 by default even if a neighboring
+ * channel has a smaller interference factor as long as it is not better by more
+ * than this multiplier.
+ */
+#define ACS_24GHZ_PREFER_1_6_11 0.8
+#endif /* ACS_24GHZ_PREFER_1_6_11 */
+
+/*
+ * At this point it's assumed chan->interface_factor has been computed.
+ * This function should be reusable regardless of interference computation
+ * option (survey, BSS, spectral, ...). chan->interference factor must be
+ * summable (i.e., must be always greater than zero).
+ */
+static struct hostapd_channel_data *
+acs_find_ideal_chan(struct hostapd_iface *iface)
+{
+       struct hostapd_channel_data *chan, *adj_chan, *ideal_chan = NULL,
+               *rand_chan = NULL;
+       long double factor, ideal_factor = 0;
+       int i, j;
+       int n_chans = 1;
+       unsigned int k;
+
+       /* TODO: HT40- support */
+
+       if (iface->conf->ieee80211n &&
+           iface->conf->secondary_channel == -1) {
+               wpa_printf(MSG_ERROR, "ACS: HT40- is not supported yet. Please try HT40+");
+               return NULL;
+       }
+
+       if (iface->conf->ieee80211n &&
+           iface->conf->secondary_channel)
+               n_chans = 2;
+
+       if (iface->conf->ieee80211ac &&
+           iface->conf->vht_oper_chwidth == 1)
+               n_chans = 4;
+
+       /* TODO: VHT80+80, VHT160. Update acs_adjust_vht_center_freq() too. */
+
+       wpa_printf(MSG_DEBUG, "ACS: Survey analysis for selected bandwidth %d MHz",
+                  n_chans == 1 ? 20 :
+                  n_chans == 2 ? 40 :
+                  n_chans == 4 ? 80 :
+                  -1);
+
+       for (i = 0; i < iface->current_mode->num_channels; i++) {
+               double total_weight;
+               struct acs_bias *bias, tmp_bias;
+
+               chan = &iface->current_mode->channels[i];
+
+               if (chan->flag & HOSTAPD_CHAN_DISABLED)
+                       continue;
+
+               if (!is_in_chanlist(iface, chan))
+                       continue;
+
+               /* HT40 on 5 GHz has a limited set of primary channels as per
+                * 11n Annex J */
+               if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+                   iface->conf->ieee80211n &&
+                   iface->conf->secondary_channel &&
+                   !acs_usable_ht40_chan(chan)) {
+                       wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for HT40",
+                                  chan->chan);
+                       continue;
+               }
+
+               if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+                   iface->conf->ieee80211ac &&
+                   iface->conf->vht_oper_chwidth == 1 &&
+                   !acs_usable_vht80_chan(chan)) {
+                       wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for VHT80",
+                                  chan->chan);
+                       continue;
+               }
+
+               factor = 0;
+               if (acs_usable_chan(chan))
+                       factor = chan->interference_factor;
+               total_weight = 1;
+
+               for (j = 1; j < n_chans; j++) {
+                       adj_chan = acs_find_chan(iface, chan->freq + (j * 20));
+                       if (!adj_chan)
+                               break;
+
+                       if (acs_usable_chan(adj_chan)) {
+                               factor += adj_chan->interference_factor;
+                               total_weight += 1;
+                       }
+               }
+
+               if (j != n_chans) {
+                       wpa_printf(MSG_DEBUG, "ACS: Channel %d: not enough bandwidth",
+                                  chan->chan);
+                       continue;
+               }
+
+               /* 2.4 GHz has overlapping 20 MHz channels. Include adjacent
+                * channel interference factor. */
+               if (is_24ghz_mode(iface->current_mode->mode)) {
+                       for (j = 0; j < n_chans; j++) {
+                               adj_chan = acs_find_chan(iface, chan->freq +
+                                                        (j * 20) - 5);
+                               if (adj_chan && acs_usable_chan(adj_chan)) {
+                                       factor += ACS_ADJ_WEIGHT *
+                                               adj_chan->interference_factor;
+                                       total_weight += ACS_ADJ_WEIGHT;
+                               }
+
+                               adj_chan = acs_find_chan(iface, chan->freq +
+                                                        (j * 20) - 10);
+                               if (adj_chan && acs_usable_chan(adj_chan)) {
+                                       factor += ACS_NEXT_ADJ_WEIGHT *
+                                               adj_chan->interference_factor;
+                                       total_weight += ACS_NEXT_ADJ_WEIGHT;
+                               }
+
+                               adj_chan = acs_find_chan(iface, chan->freq +
+                                                        (j * 20) + 5);
+                               if (adj_chan && acs_usable_chan(adj_chan)) {
+                                       factor += ACS_ADJ_WEIGHT *
+                                               adj_chan->interference_factor;
+                                       total_weight += ACS_ADJ_WEIGHT;
+                               }
+
+                               adj_chan = acs_find_chan(iface, chan->freq +
+                                                        (j * 20) + 10);
+                               if (adj_chan && acs_usable_chan(adj_chan)) {
+                                       factor += ACS_NEXT_ADJ_WEIGHT *
+                                               adj_chan->interference_factor;
+                                       total_weight += ACS_NEXT_ADJ_WEIGHT;
+                               }
+                       }
+               }
+
+               factor /= total_weight;
+
+               bias = NULL;
+               if (iface->conf->acs_chan_bias) {
+                       for (k = 0; k < iface->conf->num_acs_chan_bias; k++) {
+                               bias = &iface->conf->acs_chan_bias[k];
+                               if (bias->channel == chan->chan)
+                                       break;
+                               bias = NULL;
+                       }
+               } else if (is_24ghz_mode(iface->current_mode->mode) &&
+                          is_common_24ghz_chan(chan->chan)) {
+                       tmp_bias.channel = chan->chan;
+                       tmp_bias.bias = ACS_24GHZ_PREFER_1_6_11;
+                       bias = &tmp_bias;
+               }
+
+               if (bias) {
+                       factor *= bias->bias;
+                       wpa_printf(MSG_DEBUG,
+                                  "ACS:  * channel %d: total interference = %Lg (%f bias)",
+                                  chan->chan, factor, bias->bias);
+               } else {
+                       wpa_printf(MSG_DEBUG,
+                                  "ACS:  * channel %d: total interference = %Lg",
+                                  chan->chan, factor);
+               }
+
+               if (acs_usable_chan(chan) &&
+                   (!ideal_chan || factor < ideal_factor)) {
+                       ideal_factor = factor;
+                       ideal_chan = chan;
+               }
+
+               /* This channel would at least be usable */
+               if (!rand_chan)
+                       rand_chan = chan;
+       }
+
+       if (ideal_chan) {
+               wpa_printf(MSG_DEBUG, "ACS: Ideal channel is %d (%d MHz) with total interference factor of %Lg",
+                          ideal_chan->chan, ideal_chan->freq, ideal_factor);
+               return ideal_chan;
+       }
+
+       return rand_chan;
+}
+
+
+static void acs_adjust_vht_center_freq(struct hostapd_iface *iface)
+{
+       int offset;
+
+       wpa_printf(MSG_DEBUG, "ACS: Adjusting VHT center frequency");
+
+       switch (iface->conf->vht_oper_chwidth) {
+       case VHT_CHANWIDTH_USE_HT:
+               offset = 2 * iface->conf->secondary_channel;
+               break;
+       case VHT_CHANWIDTH_80MHZ:
+               offset = 6;
+               break;
+       default:
+               /* TODO: How can this be calculated? Adjust
+                * acs_find_ideal_chan() */
+               wpa_printf(MSG_INFO, "ACS: Only VHT20/40/80 is supported now");
+               return;
+       }
+
+       iface->conf->vht_oper_centr_freq_seg0_idx =
+               iface->conf->channel + offset;
+}
+
+
+static int acs_study_survey_based(struct hostapd_iface *iface)
+{
+       wpa_printf(MSG_DEBUG, "ACS: Trying survey-based ACS");
+
+       if (!iface->chans_surveyed) {
+               wpa_printf(MSG_ERROR, "ACS: Unable to collect survey data");
+               return -1;
+       }
+
+       if (!acs_surveys_are_sufficient(iface)) {
+               wpa_printf(MSG_ERROR, "ACS: Surveys have insufficient data");
+               return -1;
+       }
+
+       acs_survey_all_chans_intereference_factor(iface);
+       return 0;
+}
+
+
+static int acs_study_options(struct hostapd_iface *iface)
+{
+       int err;
+
+       err = acs_study_survey_based(iface);
+       if (err == 0)
+               return 0;
+
+       /* TODO: If no surveys are available/sufficient this is a good
+        * place to fallback to BSS-based ACS */
+
+       return -1;
+}
+
+
+static void acs_study(struct hostapd_iface *iface)
+{
+       struct hostapd_channel_data *ideal_chan;
+       int err;
+
+       err = acs_study_options(iface);
+       if (err < 0) {
+               wpa_printf(MSG_ERROR, "ACS: All study options have failed");
+               goto fail;
+       }
+
+       ideal_chan = acs_find_ideal_chan(iface);
+       if (!ideal_chan) {
+               wpa_printf(MSG_ERROR, "ACS: Failed to compute ideal channel");
+               err = -1;
+               goto fail;
+       }
+
+       iface->conf->channel = ideal_chan->chan;
+
+       if (iface->conf->ieee80211ac)
+               acs_adjust_vht_center_freq(iface);
+
+       err = 0;
+fail:
+       /*
+        * hostapd_setup_interface_complete() will return -1 on failure,
+        * 0 on success and 0 is HOSTAPD_CHAN_VALID :)
+        */
+       if (hostapd_acs_completed(iface, err) == HOSTAPD_CHAN_VALID) {
+               acs_cleanup(iface);
+               return;
+       }
+
+       /* This can possibly happen if channel parameters (secondary
+        * channel, center frequencies) are misconfigured */
+       wpa_printf(MSG_ERROR, "ACS: Possibly channel configuration is invalid, please report this along with your config file.");
+       acs_fail(iface);
+}
+
+
+static void acs_scan_complete(struct hostapd_iface *iface)
+{
+       int err;
+
+       iface->scan_cb = NULL;
+
+       wpa_printf(MSG_DEBUG, "ACS: Using survey based algorithm (acs_num_scans=%d)",
+                  iface->conf->acs_num_scans);
+
+       err = hostapd_drv_get_survey(iface->bss[0], 0);
+       if (err) {
+               wpa_printf(MSG_ERROR, "ACS: Failed to get survey data");
+               goto fail;
+       }
+
+       if (++iface->acs_num_completed_scans < iface->conf->acs_num_scans) {
+               err = acs_request_scan(iface);
+               if (err) {
+                       wpa_printf(MSG_ERROR, "ACS: Failed to request scan");
+                       goto fail;
+               }
+
+               return;
+       }
+
+       acs_study(iface);
+       return;
+fail:
+       hostapd_acs_completed(iface, 1);
+       acs_fail(iface);
+}
+
+
+static int acs_request_scan(struct hostapd_iface *iface)
+{
+       struct wpa_driver_scan_params params;
+       struct hostapd_channel_data *chan;
+       int i, *freq;
+
+       os_memset(&params, 0, sizeof(params));
+       params.freqs = os_calloc(iface->current_mode->num_channels + 1,
+                                sizeof(params.freqs[0]));
+       if (params.freqs == NULL)
+               return -1;
+
+       freq = params.freqs;
+       for (i = 0; i < iface->current_mode->num_channels; i++) {
+               chan = &iface->current_mode->channels[i];
+               if (chan->flag & HOSTAPD_CHAN_DISABLED)
+                       continue;
+
+               *freq++ = chan->freq;
+       }
+       *freq = 0;
+
+       iface->scan_cb = acs_scan_complete;
+
+       wpa_printf(MSG_DEBUG, "ACS: Scanning %d / %d",
+                  iface->acs_num_completed_scans + 1,
+                  iface->conf->acs_num_scans);
+
+       if (hostapd_driver_scan(iface->bss[0], &params) < 0) {
+               wpa_printf(MSG_ERROR, "ACS: Failed to request initial scan");
+               acs_cleanup(iface);
+               os_free(params.freqs);
+               return -1;
+       }
+
+       os_free(params.freqs);
+       return 0;
+}
+
+
+enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
+{
+       int err;
+
+       wpa_printf(MSG_INFO, "ACS: Automatic channel selection started, this may take a bit");
+
+       if (iface->drv_flags & WPA_DRIVER_FLAGS_ACS_OFFLOAD) {
+               wpa_printf(MSG_INFO, "ACS: Offloading to driver");
+               err = hostapd_drv_do_acs(iface->bss[0]);
+               if (err)
+                       return HOSTAPD_CHAN_INVALID;
+               return HOSTAPD_CHAN_ACS;
+       }
+
+       acs_cleanup(iface);
+
+       err = acs_request_scan(iface);
+       if (err < 0)
+               return HOSTAPD_CHAN_INVALID;
+
+       hostapd_set_state(iface, HAPD_IFACE_ACS);
+       wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_STARTED);
+
+       return HOSTAPD_CHAN_ACS;
+}
diff --git a/src/ap/acs.h b/src/ap/acs.h
new file mode 100644 (file)
index 0000000..fc85259
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * ACS - Automatic Channel Selection module
+ * Copyright (c) 2011, Atheros Communications
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef ACS_H
+#define ACS_H
+
+#ifdef CONFIG_ACS
+
+enum hostapd_chan_status acs_init(struct hostapd_iface *iface);
+
+#else /* CONFIG_ACS */
+
+static inline enum hostapd_chan_status acs_init(struct hostapd_iface *iface)
+{
+       wpa_printf(MSG_ERROR, "ACS was disabled on your build, rebuild hostapd with CONFIG_ACS=y or set channel");
+       return HOSTAPD_CHAN_INVALID;
+}
+
+#endif /* CONFIG_ACS */
+
+#endif /* ACS_H */
index c7748da..76011dc 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / Configuration helper functions
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -73,6 +73,7 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
 #ifdef CONFIG_IEEE80211W
        bss->assoc_sa_query_max_timeout = 1000;
        bss->assoc_sa_query_retry_timeout = 201;
+       bss->group_mgmt_cipher = WPA_CIPHER_AES_128_CMAC;
 #endif /* CONFIG_IEEE80211W */
 #ifdef EAP_SERVER_FAST
         /* both anonymous and authenticated provisioning */
@@ -106,9 +107,9 @@ struct hostapd_config * hostapd_config_defaults(void)
        const struct hostapd_wmm_ac_params ac_be =
                { aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */
        const struct hostapd_wmm_ac_params ac_vi = /* video traffic */
-               { aCWmin - 1, aCWmin, 2, 3000 / 32, 0 };
+               { aCWmin - 1, aCWmin, 2, 3008 / 32, 0 };
        const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */
-               { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 0 };
+               { aCWmin - 2, aCWmin - 1, 2, 1504 / 32, 0 };
        const struct hostapd_tx_queue_params txq_bk =
                { 7, ecw2cw(aCWmin), ecw2cw(aCWmax), 0 };
        const struct hostapd_tx_queue_params txq_be =
@@ -130,9 +131,17 @@ struct hostapd_config * hostapd_config_defaults(void)
                os_free(bss);
                return NULL;
        }
+       conf->bss = os_calloc(1, sizeof(struct hostapd_bss_config *));
+       if (conf->bss == NULL) {
+               os_free(conf);
+               os_free(bss);
+               return NULL;
+       }
+       conf->bss[0] = bss;
 
        bss->radius = os_zalloc(sizeof(*bss->radius));
        if (bss->radius == NULL) {
+               os_free(conf->bss);
                os_free(conf);
                os_free(bss);
                return NULL;
@@ -141,12 +150,13 @@ struct hostapd_config * hostapd_config_defaults(void)
        hostapd_config_defaults_bss(bss);
 
        conf->num_bss = 1;
-       conf->bss = bss;
 
        conf->beacon_int = 100;
        conf->rts_threshold = -1; /* use driver default: 2347 */
        conf->fragm_threshold = -1; /* user driver default: 2346 */
        conf->send_probe_response = 1;
+       /* Set to invalid value means do not add Power Constraint IE */
+       conf->local_pwr_constraint = -1;
 
        conf->wmm_ac_params[0] = ac_be;
        conf->wmm_ac_params[1] = ac_bk;
@@ -164,13 +174,17 @@ struct hostapd_config * hostapd_config_defaults(void)
        conf->ap_table_expiration_time = 60;
 
 #ifdef CONFIG_TESTING_OPTIONS
-       conf->ignore_probe_probability = 0.0d;
-       conf->ignore_auth_probability = 0.0d;
-       conf->ignore_assoc_probability = 0.0d;
-       conf->ignore_reassoc_probability = 0.0d;
-       conf->corrupt_gtk_rekey_mic_probability = 0.0d;
+       conf->ignore_probe_probability = 0.0;
+       conf->ignore_auth_probability = 0.0;
+       conf->ignore_assoc_probability = 0.0;
+       conf->ignore_reassoc_probability = 0.0;
+       conf->corrupt_gtk_rekey_mic_probability = 0.0;
 #endif /* CONFIG_TESTING_OPTIONS */
 
+#ifdef CONFIG_ACS
+       conf->acs_num_scans = 5;
+#endif /* CONFIG_ACS */
+
        return conf;
 }
 
@@ -325,20 +339,6 @@ int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf)
 }
 
 
-int hostapd_wep_key_cmp(struct hostapd_wep_keys *a, struct hostapd_wep_keys *b)
-{
-       int i;
-
-       if (a->idx != b->idx || a->default_len != b->default_len)
-               return 1;
-       for (i = 0; i < NUM_WEP_KEYS; i++)
-               if (a->len[i] != b->len[i] ||
-                   os_memcmp(a->key[i], b->key[i], a->len[i]) != 0)
-                       return 1;
-       return 0;
-}
-
-
 static void hostapd_config_free_radius(struct hostapd_radius_server *servers,
                                       int num_servers)
 {
@@ -375,10 +375,11 @@ static void hostapd_config_free_radius_attr(struct hostapd_radius_attr *attr)
 }
 
 
-static void hostapd_config_free_eap_user(struct hostapd_eap_user *user)
+void hostapd_config_free_eap_user(struct hostapd_eap_user *user)
 {
+       hostapd_config_free_radius_attr(user->accept_attr);
        os_free(user->identity);
-       os_free(user->password);
+       bin_clear_free(user->password, user->password_len);
        os_free(user);
 }
 
@@ -387,28 +388,35 @@ static void hostapd_config_free_wep(struct hostapd_wep_keys *keys)
 {
        int i;
        for (i = 0; i < NUM_WEP_KEYS; i++) {
-               os_free(keys->key[i]);
+               bin_clear_free(keys->key[i], keys->len[i]);
                keys->key[i] = NULL;
        }
 }
 
 
-static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
+void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **l)
+{
+       struct hostapd_wpa_psk *psk, *tmp;
+
+       for (psk = *l; psk;) {
+               tmp = psk;
+               psk = psk->next;
+               bin_clear_free(tmp, sizeof(*tmp));
+       }
+       *l = NULL;
+}
+
+
+void hostapd_config_free_bss(struct hostapd_bss_config *conf)
 {
-       struct hostapd_wpa_psk *psk, *prev;
        struct hostapd_eap_user *user, *prev_user;
 
        if (conf == NULL)
                return;
 
-       psk = conf->ssid.wpa_psk;
-       while (psk) {
-               prev = psk;
-               psk = psk->next;
-               os_free(prev);
-       }
+       hostapd_config_clear_wpa_psk(&conf->ssid.wpa_psk);
 
-       os_free(conf->ssid.wpa_passphrase);
+       str_clear_free(conf->ssid.wpa_passphrase);
        os_free(conf->ssid.wpa_psk_file);
        hostapd_config_free_wep(&conf->ssid.wep);
 #ifdef CONFIG_FULL_DYNAMIC_VLAN
@@ -423,15 +431,17 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
        }
        os_free(conf->eap_user_sqlite);
 
-       os_free(conf->dump_log_name);
        os_free(conf->eap_req_id_text);
+       os_free(conf->erp_domain);
        os_free(conf->accept_mac);
        os_free(conf->deny_mac);
        os_free(conf->nas_identifier);
-       hostapd_config_free_radius(conf->radius->auth_servers,
-                                  conf->radius->num_auth_servers);
-       hostapd_config_free_radius(conf->radius->acct_servers,
-                                  conf->radius->num_acct_servers);
+       if (conf->radius) {
+               hostapd_config_free_radius(conf->radius->auth_servers,
+                                          conf->radius->num_auth_servers);
+               hostapd_config_free_radius(conf->radius->acct_servers,
+                                          conf->radius->num_acct_servers);
+       }
        hostapd_config_free_radius_attr(conf->radius_auth_req_attr);
        hostapd_config_free_radius_attr(conf->radius_acct_req_attr);
        os_free(conf->rsn_preauth_interfaces);
@@ -442,28 +452,15 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
        os_free(conf->private_key_passwd);
        os_free(conf->ocsp_stapling_response);
        os_free(conf->dh_file);
+       os_free(conf->openssl_ciphers);
        os_free(conf->pac_opaque_encr_key);
        os_free(conf->eap_fast_a_id);
        os_free(conf->eap_fast_a_id_info);
        os_free(conf->eap_sim_db);
        os_free(conf->radius_server_clients);
-       os_free(conf->test_socket);
        os_free(conf->radius);
        os_free(conf->radius_das_shared_secret);
        hostapd_config_free_vlan(conf);
-       if (conf->ssid.dyn_vlan_keys) {
-               struct hostapd_ssid *ssid = &conf->ssid;
-               size_t i;
-               for (i = 0; i <= ssid->max_dyn_vlan_keys; i++) {
-                       if (ssid->dyn_vlan_keys[i] == NULL)
-                               continue;
-                       hostapd_config_free_wep(ssid->dyn_vlan_keys[i]);
-                       os_free(ssid->dyn_vlan_keys[i]);
-               }
-               os_free(ssid->dyn_vlan_keys);
-               ssid->dyn_vlan_keys = NULL;
-       }
-
        os_free(conf->time_zone);
 
 #ifdef CONFIG_IEEE80211R
@@ -506,6 +503,12 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
        os_free(conf->model_description);
        os_free(conf->model_url);
        os_free(conf->upc);
+       {
+               unsigned int i;
+
+               for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
+                       wpabuf_free(conf->wps_vendor_ext[i]);
+       }
        wpabuf_free(conf->wps_nfc_dh_pubkey);
        wpabuf_free(conf->wps_nfc_dh_privkey);
        wpabuf_free(conf->wps_nfc_dev_pw);
@@ -527,11 +530,36 @@ static void hostapd_config_free_bss(struct hostapd_bss_config *conf)
        os_free(conf->hs20_wan_metrics);
        os_free(conf->hs20_connection_capability);
        os_free(conf->hs20_operating_class);
+       os_free(conf->hs20_icons);
+       if (conf->hs20_osu_providers) {
+               size_t i;
+               for (i = 0; i < conf->hs20_osu_providers_count; i++) {
+                       struct hs20_osu_provider *p;
+                       size_t j;
+                       p = &conf->hs20_osu_providers[i];
+                       os_free(p->friendly_name);
+                       os_free(p->server_uri);
+                       os_free(p->method_list);
+                       for (j = 0; j < p->icons_count; j++)
+                               os_free(p->icons[j]);
+                       os_free(p->icons);
+                       os_free(p->osu_nai);
+                       os_free(p->service_desc);
+               }
+               os_free(conf->hs20_osu_providers);
+       }
+       os_free(conf->subscr_remediation_url);
 #endif /* CONFIG_HS20 */
 
        wpabuf_free(conf->vendor_elements);
 
        os_free(conf->sae_groups);
+
+       os_free(conf->wowlan_triggers);
+
+       os_free(conf->server_id);
+
+       os_free(conf);
 }
 
 
@@ -547,10 +575,15 @@ void hostapd_config_free(struct hostapd_config *conf)
                return;
 
        for (i = 0; i < conf->num_bss; i++)
-               hostapd_config_free_bss(&conf->bss[i]);
+               hostapd_config_free_bss(conf->bss[i]);
        os_free(conf->bss);
        os_free(conf->supported_rates);
        os_free(conf->basic_rates);
+       os_free(conf->chanlist);
+       os_free(conf->driver_params);
+#ifdef CONFIG_ACS
+       os_free(conf->acs_chan_bias);
+#endif /* CONFIG_ACS */
 
        os_free(conf);
 }
@@ -632,14 +665,30 @@ const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id)
 
 
 const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
-                          const u8 *addr, const u8 *prev_psk)
+                          const u8 *addr, const u8 *p2p_dev_addr,
+                          const u8 *prev_psk)
 {
        struct hostapd_wpa_psk *psk;
        int next_ok = prev_psk == NULL;
 
+       if (p2p_dev_addr && !is_zero_ether_addr(p2p_dev_addr)) {
+               wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR
+                          " p2p_dev_addr=" MACSTR " prev_psk=%p",
+                          MAC2STR(addr), MAC2STR(p2p_dev_addr), prev_psk);
+               addr = NULL; /* Use P2P Device Address for matching */
+       } else {
+               wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR
+                          " prev_psk=%p",
+                          MAC2STR(addr), prev_psk);
+       }
+
        for (psk = conf->ssid.wpa_psk; psk != NULL; psk = psk->next) {
                if (next_ok &&
-                   (psk->group || os_memcmp(psk->addr, addr, ETH_ALEN) == 0))
+                   (psk->group ||
+                    (addr && os_memcmp(psk->addr, addr, ETH_ALEN) == 0) ||
+                    (!addr && p2p_dev_addr &&
+                     os_memcmp(psk->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
+                     0)))
                        return psk->psk;
 
                if (psk->psk == prev_psk)
@@ -648,3 +697,250 @@ const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
 
        return NULL;
 }
+
+
+static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
+                                   struct hostapd_config *conf,
+                                   int full_config)
+{
+       if (full_config && bss->ieee802_1x && !bss->eap_server &&
+           !bss->radius->auth_servers) {
+               wpa_printf(MSG_ERROR, "Invalid IEEE 802.1X configuration (no "
+                          "EAP authenticator configured).");
+               return -1;
+       }
+
+       if (bss->wpa) {
+               int wep, i;
+
+               wep = bss->default_wep_key_len > 0 ||
+                      bss->individual_wep_key_len > 0;
+               for (i = 0; i < NUM_WEP_KEYS; i++) {
+                       if (bss->ssid.wep.keys_set) {
+                               wep = 1;
+                               break;
+                       }
+               }
+
+               if (wep) {
+                       wpa_printf(MSG_ERROR, "WEP configuration in a WPA network is not supported");
+                       return -1;
+               }
+       }
+
+       if (full_config && bss->wpa &&
+           bss->wpa_psk_radius != PSK_RADIUS_IGNORED &&
+           bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) {
+               wpa_printf(MSG_ERROR, "WPA-PSK using RADIUS enabled, but no "
+                          "RADIUS checking (macaddr_acl=2) enabled.");
+               return -1;
+       }
+
+       if (full_config && bss->wpa && (bss->wpa_key_mgmt & WPA_KEY_MGMT_PSK) &&
+           bss->ssid.wpa_psk == NULL && bss->ssid.wpa_passphrase == NULL &&
+           bss->ssid.wpa_psk_file == NULL &&
+           (bss->wpa_psk_radius != PSK_RADIUS_REQUIRED ||
+            bss->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH)) {
+               wpa_printf(MSG_ERROR, "WPA-PSK enabled, but PSK or passphrase "
+                          "is not configured.");
+               return -1;
+       }
+
+       if (full_config && hostapd_mac_comp_empty(bss->bssid) != 0) {
+               size_t i;
+
+               for (i = 0; i < conf->num_bss; i++) {
+                       if (conf->bss[i] != bss &&
+                           (hostapd_mac_comp(conf->bss[i]->bssid,
+                                             bss->bssid) == 0)) {
+                               wpa_printf(MSG_ERROR, "Duplicate BSSID " MACSTR
+                                          " on interface '%s' and '%s'.",
+                                          MAC2STR(bss->bssid),
+                                          conf->bss[i]->iface, bss->iface);
+                               return -1;
+                       }
+               }
+       }
+
+#ifdef CONFIG_IEEE80211R
+       if (full_config && wpa_key_mgmt_ft(bss->wpa_key_mgmt) &&
+           (bss->nas_identifier == NULL ||
+            os_strlen(bss->nas_identifier) < 1 ||
+            os_strlen(bss->nas_identifier) > FT_R0KH_ID_MAX_LEN)) {
+               wpa_printf(MSG_ERROR, "FT (IEEE 802.11r) requires "
+                          "nas_identifier to be configured as a 1..48 octet "
+                          "string");
+               return -1;
+       }
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_IEEE80211N
+       if (full_config && conf->ieee80211n &&
+           conf->hw_mode == HOSTAPD_MODE_IEEE80211B) {
+               bss->disable_11n = 1;
+               wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) in 11b mode is not "
+                          "allowed, disabling HT capabilities");
+       }
+
+       if (full_config && conf->ieee80211n &&
+           bss->ssid.security_policy == SECURITY_STATIC_WEP) {
+               bss->disable_11n = 1;
+               wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WEP is not "
+                          "allowed, disabling HT capabilities");
+       }
+
+       if (full_config && conf->ieee80211n && bss->wpa &&
+           !(bss->wpa_pairwise & WPA_CIPHER_CCMP) &&
+           !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+                                  WPA_CIPHER_CCMP_256 | WPA_CIPHER_GCMP_256)))
+       {
+               bss->disable_11n = 1;
+               wpa_printf(MSG_ERROR, "HT (IEEE 802.11n) with WPA/WPA2 "
+                          "requires CCMP/GCMP to be enabled, disabling HT "
+                          "capabilities");
+       }
+#endif /* CONFIG_IEEE80211N */
+
+#ifdef CONFIG_WPS
+       if (full_config && bss->wps_state && bss->ignore_broadcast_ssid) {
+               wpa_printf(MSG_INFO, "WPS: ignore_broadcast_ssid "
+                          "configuration forced WPS to be disabled");
+               bss->wps_state = 0;
+       }
+
+       if (full_config && bss->wps_state &&
+           bss->ssid.wep.keys_set && bss->wpa == 0) {
+               wpa_printf(MSG_INFO, "WPS: WEP configuration forced WPS to be "
+                          "disabled");
+               bss->wps_state = 0;
+       }
+
+       if (full_config && bss->wps_state && bss->wpa &&
+           (!(bss->wpa & 2) ||
+            !(bss->rsn_pairwise & WPA_CIPHER_CCMP))) {
+               wpa_printf(MSG_INFO, "WPS: WPA/TKIP configuration without "
+                          "WPA2/CCMP forced WPS to be disabled");
+               bss->wps_state = 0;
+       }
+#endif /* CONFIG_WPS */
+
+#ifdef CONFIG_HS20
+       if (full_config && bss->hs20 &&
+           (!(bss->wpa & 2) ||
+            !(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP |
+                                   WPA_CIPHER_CCMP_256 |
+                                   WPA_CIPHER_GCMP_256)))) {
+               wpa_printf(MSG_ERROR, "HS 2.0: WPA2-Enterprise/CCMP "
+                          "configuration is required for Hotspot 2.0 "
+                          "functionality");
+               return -1;
+       }
+#endif /* CONFIG_HS20 */
+
+       return 0;
+}
+
+
+int hostapd_config_check(struct hostapd_config *conf, int full_config)
+{
+       size_t i;
+
+       if (full_config && conf->ieee80211d &&
+           (!conf->country[0] || !conf->country[1])) {
+               wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11d without "
+                          "setting the country_code");
+               return -1;
+       }
+
+       if (full_config && conf->ieee80211h && !conf->ieee80211d) {
+               wpa_printf(MSG_ERROR, "Cannot enable IEEE 802.11h without "
+                          "IEEE 802.11d enabled");
+               return -1;
+       }
+
+       if (full_config && conf->local_pwr_constraint != -1 &&
+           !conf->ieee80211d) {
+               wpa_printf(MSG_ERROR, "Cannot add Power Constraint element without Country element");
+               return -1;
+       }
+
+       if (full_config && conf->spectrum_mgmt_required &&
+           conf->local_pwr_constraint == -1) {
+               wpa_printf(MSG_ERROR, "Cannot set Spectrum Management bit without Country and Power Constraint elements");
+               return -1;
+       }
+
+       for (i = 0; i < conf->num_bss; i++) {
+               if (hostapd_config_check_bss(conf->bss[i], conf, full_config))
+                       return -1;
+       }
+
+       return 0;
+}
+
+
+void hostapd_set_security_params(struct hostapd_bss_config *bss,
+                                int full_config)
+{
+       if (bss->individual_wep_key_len == 0) {
+               /* individual keys are not use; can use key idx0 for
+                * broadcast keys */
+               bss->broadcast_key_idx_min = 0;
+       }
+
+       if ((bss->wpa & 2) && bss->rsn_pairwise == 0)
+               bss->rsn_pairwise = bss->wpa_pairwise;
+       bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise,
+                                                   bss->rsn_pairwise);
+
+       if (full_config) {
+               bss->radius->auth_server = bss->radius->auth_servers;
+               bss->radius->acct_server = bss->radius->acct_servers;
+       }
+
+       if (bss->wpa && bss->ieee802_1x) {
+               bss->ssid.security_policy = SECURITY_WPA;
+       } else if (bss->wpa) {
+               bss->ssid.security_policy = SECURITY_WPA_PSK;
+       } else if (bss->ieee802_1x) {
+               int cipher = WPA_CIPHER_NONE;
+               bss->ssid.security_policy = SECURITY_IEEE_802_1X;
+               bss->ssid.wep.default_len = bss->default_wep_key_len;
+               if (full_config && bss->default_wep_key_len) {
+                       cipher = bss->default_wep_key_len >= 13 ?
+                               WPA_CIPHER_WEP104 : WPA_CIPHER_WEP40;
+               } else if (full_config && bss->ssid.wep.keys_set) {
+                       if (bss->ssid.wep.len[0] >= 13)
+                               cipher = WPA_CIPHER_WEP104;
+                       else
+                               cipher = WPA_CIPHER_WEP40;
+               }
+               bss->wpa_group = cipher;
+               bss->wpa_pairwise = cipher;
+               bss->rsn_pairwise = cipher;
+               if (full_config)
+                       bss->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_NO_WPA;
+       } else if (bss->ssid.wep.keys_set) {
+               int cipher = WPA_CIPHER_WEP40;
+               if (bss->ssid.wep.len[0] >= 13)
+                       cipher = WPA_CIPHER_WEP104;
+               bss->ssid.security_policy = SECURITY_STATIC_WEP;
+               bss->wpa_group = cipher;
+               bss->wpa_pairwise = cipher;
+               bss->rsn_pairwise = cipher;
+               if (full_config)
+                       bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE;
+       } else if (bss->osen) {
+               bss->ssid.security_policy = SECURITY_OSEN;
+               bss->wpa_group = WPA_CIPHER_CCMP;
+               bss->wpa_pairwise = 0;
+               bss->rsn_pairwise = WPA_CIPHER_CCMP;
+       } else {
+               bss->ssid.security_policy = SECURITY_PLAINTEXT;
+               bss->wpa_group = WPA_CIPHER_NONE;
+               bss->wpa_pairwise = WPA_CIPHER_NONE;
+               bss->rsn_pairwise = WPA_CIPHER_NONE;
+               if (full_config)
+                       bss->wpa_key_mgmt = WPA_KEY_MGMT_NONE;
+       }
+}
index 1124920..961d2dd 100644 (file)
 #include "common/ieee802_11_common.h"
 #include "wps/wps.h"
 
+/**
+ * mesh_conf - local MBSS state and settings
+ */
+struct mesh_conf {
+       u8 meshid[32];
+       u8 meshid_len;
+       /* Active Path Selection Protocol Identifier */
+       u8 mesh_pp_id;
+       /* Active Path Selection Metric Identifier */
+       u8 mesh_pm_id;
+       /* Congestion Control Mode Identifier */
+       u8 mesh_cc_id;
+       /* Synchronization Protocol Identifier */
+       u8 mesh_sp_id;
+       /* Authentication Protocol Identifier */
+       u8 mesh_auth_id;
+       u8 *ies;
+       int ie_len;
+#define MESH_CONF_SEC_NONE BIT(0)
+#define MESH_CONF_SEC_AUTH BIT(1)
+#define MESH_CONF_SEC_AMPE BIT(2)
+       unsigned int security;
+       int dot11MeshMaxRetries;
+       int dot11MeshRetryTimeout; /* msec */
+       int dot11MeshConfirmTimeout; /* msec */
+       int dot11MeshHoldingTimeout; /* msec */
+};
+
 #define MAX_STA_COUNT 2007
 #define MAX_VLAN_ID 4094
 
@@ -45,7 +73,8 @@ typedef enum hostap_security_policy {
        SECURITY_STATIC_WEP = 1,
        SECURITY_IEEE_802_1X = 2,
        SECURITY_WPA_PSK = 3,
-       SECURITY_WPA = 4
+       SECURITY_WPA = 4,
+       SECURITY_OSEN = 5
 } secpolicy;
 
 struct hostapd_ssid {
@@ -53,6 +82,8 @@ struct hostapd_ssid {
        size_t ssid_len;
        unsigned int ssid_set:1;
        unsigned int utf8_ssid:1;
+       unsigned int wpa_passphrase_set:1;
+       unsigned int wpa_psk_set:1;
 
        char vlan[IFNAMSIZ + 1];
        secpolicy security_policy;
@@ -74,8 +105,6 @@ struct hostapd_ssid {
 #ifdef CONFIG_FULL_DYNAMIC_VLAN
        char *vlan_tagged_interface;
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
-       struct hostapd_wep_keys **dyn_vlan_keys;
-       size_t max_dyn_vlan_keys;
 };
 
 
@@ -107,6 +136,7 @@ struct hostapd_wpa_psk {
        int group;
        u8 psk[PMK_LEN];
        u8 addr[ETH_ALEN];
+       u8 p2p_dev_addr[ETH_ALEN];
 };
 
 struct hostapd_eap_user {
@@ -124,7 +154,10 @@ struct hostapd_eap_user {
        unsigned int wildcard_prefix:1;
        unsigned int password_hash:1; /* whether password is hashed with
                                       * nt_password_hash() */
+       unsigned int remediation:1;
+       unsigned int macacl:1;
        int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */
+       struct hostapd_radius_attr *accept_attr;
 };
 
 struct hostapd_radius_attr {
@@ -188,11 +221,10 @@ struct hostapd_bss_config {
        unsigned int logger_syslog; /* module bitfield */
        unsigned int logger_stdout; /* module bitfield */
 
-       char *dump_log_name; /* file name for state dump (SIGUSR1) */
-
        int max_num_sta; /* maximum number of STAs in station table */
 
        int dtim_period;
+       int bss_load_update_period;
 
        int ieee802_1x; /* use IEEE 802.1X */
        int eapol_version;
@@ -201,6 +233,7 @@ struct hostapd_bss_config {
        struct hostapd_eap_user *eap_user;
        char *eap_user_sqlite;
        char *eap_sim_db;
+       int eap_server_erp; /* Whether ERP is enabled on internal EAP server */
        struct hostapd_ip_addr own_ip_addr;
        char *nas_identifier;
        struct hostapd_radius_servers *radius;
@@ -227,6 +260,8 @@ struct hostapd_bss_config {
        int wep_rekeying_period;
        int broadcast_key_idx_min, broadcast_key_idx_max;
        int eap_reauth_period;
+       int erp_send_reauth_start;
+       char *erp_domain;
 
        int ieee802_11f; /* use IEEE 802.11f (IAPP) */
        char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast
@@ -243,6 +278,7 @@ struct hostapd_bss_config {
        int num_deny_mac;
        int wds_sta;
        int isolate;
+       int start_disabled;
 
        int auth_algs; /* bitfield of allowed IEEE 802.11 authentication
                        * algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */
@@ -251,6 +287,7 @@ struct hostapd_bss_config {
        int wpa_key_mgmt;
 #ifdef CONFIG_IEEE80211W
        enum mfp_options ieee80211w;
+       int group_mgmt_cipher;
        /* dot11AssociationSAQueryMaximumTimeout (in TUs) */
        unsigned int assoc_sa_query_max_timeout;
        /* dot11AssociationSAQueryRetryTimeout (in TUs) */
@@ -297,6 +334,7 @@ struct hostapd_bss_config {
        int check_crl;
        char *ocsp_stapling_response;
        char *dh_file;
+       char *openssl_ciphers;
        u8 *pac_opaque_encr_key;
        u8 *eap_fast_a_id;
        size_t eap_fast_a_id_len;
@@ -311,10 +349,9 @@ struct hostapd_bss_config {
 
        char *radius_server_clients;
        int radius_server_auth_port;
+       int radius_server_acct_port;
        int radius_server_ipv6;
 
-       char *test_socket; /* UNIX domain socket path for driver_test */
-
        int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group
                                 * address instead of individual address
                                 * (for driver_wired.c).
@@ -326,7 +363,7 @@ struct hostapd_bss_config {
        int wmm_enabled;
        int wmm_uapsd;
 
-       struct hostapd_vlan *vlan, *vlan_tail;
+       struct hostapd_vlan *vlan;
 
        macaddr bssid;
 
@@ -359,6 +396,7 @@ struct hostapd_bss_config {
        u8 *extra_cred;
        size_t extra_cred_len;
        int wps_cred_processing;
+       int force_per_enrollee_psk;
        u8 *ap_settings;
        size_t ap_settings_len;
        char *upnp_iface;
@@ -375,6 +413,7 @@ struct hostapd_bss_config {
        struct wpabuf *wps_nfc_dev_pw;
 #endif /* CONFIG_WPS */
        int pbc_in_m1;
+       char *server_id;
 
 #define P2P_ENABLED BIT(0)
 #define P2P_GROUP_OWNER BIT(1)
@@ -382,6 +421,12 @@ struct hostapd_bss_config {
 #define P2P_MANAGE BIT(3)
 #define P2P_ALLOW_CROSS_CONNECTION BIT(4)
        int p2p;
+#ifdef CONFIG_P2P
+       u8 ip_addr_go[4];
+       u8 ip_addr_mask[4];
+       u8 ip_addr_start[4];
+       u8 ip_addr_end[4];
+#endif /* CONFIG_P2P */
 
        int disassoc_low_ack;
        int skip_inactivity_poll;
@@ -440,9 +485,15 @@ struct hostapd_bss_config {
        u16 gas_comeback_delay;
        int gas_frag_limit;
 
+       u8 qos_map_set[16 + 2 * 21];
+       unsigned int qos_map_set_len;
+
+       int osen;
+       int proxy_arp;
 #ifdef CONFIG_HS20
        int hs20;
        int disable_dgaf;
+       u16 anqp_domain_id;
        unsigned int hs20_oper_friendly_name_count;
        struct hostapd_lang_string *hs20_oper_friendly_name;
        u8 *hs20_wan_metrics;
@@ -450,6 +501,32 @@ struct hostapd_bss_config {
        size_t hs20_connection_capability_len;
        u8 *hs20_operating_class;
        u8 hs20_operating_class_len;
+       struct hs20_icon {
+               u16 width;
+               u16 height;
+               char language[3];
+               char type[256];
+               char name[256];
+               char file[256];
+       } *hs20_icons;
+       size_t hs20_icons_count;
+       u8 osu_ssid[HOSTAPD_MAX_SSID_LEN];
+       size_t osu_ssid_len;
+       struct hs20_osu_provider {
+               unsigned int friendly_name_count;
+               struct hostapd_lang_string *friendly_name;
+               char *server_uri;
+               int *method_list;
+               char **icons;
+               size_t icons_count;
+               char *osu_nai;
+               unsigned int service_desc_count;
+               struct hostapd_lang_string *service_desc;
+       } *hs20_osu_providers, *last_osu;
+       size_t hs20_osu_providers_count;
+       unsigned int hs20_deauth_req_timeout;
+       char *subscr_remediation_url;
+       u8 subscr_remediation_method;
 #endif /* CONFIG_HS20 */
 
        u8 wps_rf_bands; /* RF bands for WPS (WPS_RF_*) */
@@ -462,6 +539,20 @@ struct hostapd_bss_config {
 
        unsigned int sae_anti_clogging_threshold;
        int *sae_groups;
+
+       char *wowlan_triggers; /* Wake-on-WLAN triggers */
+
+#ifdef CONFIG_TESTING_OPTIONS
+       u8 bss_load_test[5];
+       u8 bss_load_test_set;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+#define MESH_ENABLED BIT(0)
+       int mesh;
+
+       int radio_measurements;
+
+       int vendor_vht;
 };
 
 
@@ -469,7 +560,7 @@ struct hostapd_bss_config {
  * struct hostapd_config - Per-radio interface configuration
  */
 struct hostapd_config {
-       struct hostapd_bss_config *bss, *last_bss;
+       struct hostapd_bss_config **bss, *last_bss;
        size_t num_bss;
 
        u16 beacon_int;
@@ -477,6 +568,7 @@ struct hostapd_config {
        int fragm_threshold;
        u8 send_probe_response;
        u8 channel;
+       int *chanlist;
        enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */
        enum {
                LONG_PREAMBLE = 0,
@@ -487,6 +579,7 @@ struct hostapd_config {
        int *basic_rates;
 
        const struct wpa_driver_ops *driver;
+       char *driver_params;
 
        int ap_table_max_size;
        int ap_table_expiration_time;
@@ -502,6 +595,16 @@ struct hostapd_config {
 
        int ieee80211h; /* DFS */
 
+       /*
+        * Local power constraint is an octet encoded as an unsigned integer in
+        * units of decibels. Invalid value -1 indicates that Power Constraint
+        * element will not be added.
+        */
+       int local_pwr_constraint;
+
+       /* Control Spectrum Management bit */
+       int spectrum_mgmt_required;
+
        struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES];
 
        /*
@@ -518,6 +621,7 @@ struct hostapd_config {
        int ieee80211n;
        int secondary_channel;
        int require_ht;
+       int obss_interval;
        u32 vht_capab;
        int ieee80211ac;
        int require_vht;
@@ -525,6 +629,10 @@ struct hostapd_config {
        u8 vht_oper_centr_freq_seg0_idx;
        u8 vht_oper_centr_freq_seg1_idx;
 
+#ifdef CONFIG_P2P
+       u8 p2p_go_ctwindow;
+#endif /* CONFIG_P2P */
+
 #ifdef CONFIG_TESTING_OPTIONS
        double ignore_probe_probability;
        double ignore_auth_probability;
@@ -532,6 +640,15 @@ struct hostapd_config {
        double ignore_reassoc_probability;
        double corrupt_gtk_rekey_mic_probability;
 #endif /* CONFIG_TESTING_OPTIONS */
+
+#ifdef CONFIG_ACS
+       unsigned int acs_num_scans;
+       struct acs_bias {
+               int channel;
+               double bias;
+       } *acs_chan_bias;
+       unsigned int num_acs_chan_bias;
+#endif /* CONFIG_ACS */
 };
 
 
@@ -539,19 +656,24 @@ int hostapd_mac_comp(const void *a, const void *b);
 int hostapd_mac_comp_empty(const void *a);
 struct hostapd_config * hostapd_config_defaults(void);
 void hostapd_config_defaults_bss(struct hostapd_bss_config *bss);
+void hostapd_config_free_eap_user(struct hostapd_eap_user *user);
+void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p);
+void hostapd_config_free_bss(struct hostapd_bss_config *conf);
 void hostapd_config_free(struct hostapd_config *conf);
 int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
                          const u8 *addr, int *vlan_id);
 int hostapd_rate_found(int *list, int rate);
-int hostapd_wep_key_cmp(struct hostapd_wep_keys *a,
-                       struct hostapd_wep_keys *b);
 const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
-                          const u8 *addr, const u8 *prev_psk);
+                          const u8 *addr, const u8 *p2p_dev_addr,
+                          const u8 *prev_psk);
 int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf);
 int hostapd_vlan_id_valid(struct hostapd_vlan *vlan, int vlan_id);
 const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan,
                                        int vlan_id);
 struct hostapd_radius_attr *
 hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type);
+int hostapd_config_check(struct hostapd_config *conf, int full_config);
+void hostapd_set_security_params(struct hostapd_bss_config *bss,
+                                int full_config);
 
 #endif /* HOSTAPD_CONFIG_H */
index 8205d13..e16306c 100644 (file)
@@ -9,8 +9,8 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
-#include "drivers/driver.h"
 #include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
 #include "wps/wps.h"
 #include "p2p/p2p.h"
 #include "hostapd.h"
@@ -129,14 +129,14 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
        }
 #endif /* CONFIG_P2P_MANAGER */
 
-#ifdef CONFIG_WPS2
+#ifdef CONFIG_WPS
        if (hapd->conf->wps_state) {
                struct wpabuf *a = wps_build_assoc_resp_ie();
                if (a && wpabuf_resize(&assocresp, wpabuf_len(a)) == 0)
                        wpabuf_put_buf(assocresp, a);
                wpabuf_free(a);
        }
-#endif /* CONFIG_WPS2 */
+#endif /* CONFIG_WPS */
 
 #ifdef CONFIG_P2P_MANAGER
        if (hapd->conf->p2p & P2P_MANAGE) {
@@ -171,6 +171,17 @@ int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
                        goto fail;
                wpabuf_put_data(proberesp, buf, pos - buf);
        }
+
+       pos = hostapd_eid_osen(hapd, buf);
+       if (pos != buf) {
+               if (wpabuf_resize(&beacon, pos - buf) != 0)
+                       goto fail;
+               wpabuf_put_data(beacon, buf, pos - buf);
+
+               if (wpabuf_resize(&proberesp, pos - buf) != 0)
+                       goto fail;
+               wpabuf_put_data(proberesp, buf, pos - buf);
+       }
 #endif /* CONFIG_HS20 */
 
        if (hapd->conf->vendor_elements) {
@@ -270,7 +281,8 @@ int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
                params.wpa = hapd->conf->wpa;
                params.ieee802_1x = hapd->conf->ieee802_1x;
                params.wpa_group = hapd->conf->wpa_group;
-               params.wpa_pairwise = hapd->conf->wpa_pairwise;
+               params.wpa_pairwise = hapd->conf->wpa_pairwise |
+                       hapd->conf->rsn_pairwise;
                params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt;
                params.rsn_preauth = hapd->conf->rsn_preauth;
 #ifdef CONFIG_IEEE80211W
@@ -286,7 +298,7 @@ int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname)
        char force_ifname[IFNAMSIZ];
        u8 if_addr[ETH_ALEN];
        return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, hapd->own_addr,
-                             NULL, NULL, force_ifname, if_addr, NULL);
+                             NULL, NULL, force_ifname, if_addr, NULL, 0);
 }
 
 
@@ -296,19 +308,19 @@ int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname)
 }
 
 
-int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, int aid,
-                       int val)
+int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
+                       const u8 *addr, int aid, int val)
 {
        const char *bridge = NULL;
 
        if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL)
-               return 0;
+               return -1;
        if (hapd->conf->wds_bridge[0])
                bridge = hapd->conf->wds_bridge;
        else if (hapd->conf->bridge[0])
                bridge = hapd->conf->bridge;
        return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val,
-                                        bridge);
+                                        bridge, ifname_wds);
 }
 
 
@@ -347,7 +359,7 @@ int hostapd_sta_add(struct hostapd_data *hapd,
                    u16 listen_interval,
                    const struct ieee80211_ht_capabilities *ht_capab,
                    const struct ieee80211_vht_capabilities *vht_capab,
-                   u32 flags, u8 qosinfo)
+                   u32 flags, u8 qosinfo, u8 vht_opmode)
 {
        struct hostapd_sta_add_params params;
 
@@ -365,6 +377,8 @@ int hostapd_sta_add(struct hostapd_data *hapd,
        params.listen_interval = listen_interval;
        params.ht_capabilities = ht_capab;
        params.vht_capabilities = vht_capab;
+       params.vht_opmode_enabled = !!(flags & WLAN_STA_VHT_OPMODE_ENABLED);
+       params.vht_opmode = vht_opmode;
        params.flags = hostapd_sta_flags_to_drv(flags);
        params.qosinfo = qosinfo;
        return hapd->driver->sta_add(hapd->drv_priv, &params);
@@ -417,20 +431,21 @@ int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len)
 int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
                   const char *ifname, const u8 *addr, void *bss_ctx,
                   void **drv_priv, char *force_ifname, u8 *if_addr,
-                  const char *bridge)
+                  const char *bridge, int use_existing)
 {
        if (hapd->driver == NULL || hapd->driver->if_add == NULL)
                return -1;
        return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr,
                                    bss_ctx, drv_priv, force_ifname, if_addr,
-                                   bridge);
+                                   bridge, use_existing);
 }
 
 
 int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
                      const char *ifname)
 {
-       if (hapd->driver == NULL || hapd->driver->if_remove == NULL)
+       if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+           hapd->driver->if_remove == NULL)
                return -1;
        return hapd->driver->if_remove(hapd->drv_priv, type, ifname);
 }
@@ -463,73 +478,21 @@ int hostapd_flush(struct hostapd_data *hapd)
 }
 
 
-int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq,
-                    int channel, int ht_enabled, int vht_enabled,
+int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
+                    int freq, int channel, int ht_enabled, int vht_enabled,
                     int sec_channel_offset, int vht_oper_chwidth,
                     int center_segment0, int center_segment1)
 {
        struct hostapd_freq_params data;
-       int tmp;
-
-       os_memset(&data, 0, sizeof(data));
-       data.mode = mode;
-       data.freq = freq;
-       data.channel = channel;
-       data.ht_enabled = ht_enabled;
-       data.vht_enabled = vht_enabled;
-       data.sec_channel_offset = sec_channel_offset;
-       data.center_freq1 = freq + sec_channel_offset * 10;
-       data.center_freq2 = 0;
-       data.bandwidth = sec_channel_offset ? 40 : 20;
-
-       /*
-        * This validation code is probably misplaced, maybe it should be
-        * in src/ap/hw_features.c and check the hardware support as well.
-        */
-       if (data.vht_enabled) switch (vht_oper_chwidth) {
-       case VHT_CHANWIDTH_USE_HT:
-               if (center_segment1)
-                       return -1;
-               if (5000 + center_segment0 * 5 != data.center_freq1)
-                       return -1;
-               break;
-       case VHT_CHANWIDTH_80P80MHZ:
-               if (center_segment1 == center_segment0 + 4 ||
-                   center_segment1 == center_segment0 - 4)
-                       return -1;
-               data.center_freq2 = 5000 + center_segment1 * 5;
-               /* fall through */
-       case VHT_CHANWIDTH_80MHZ:
-               data.bandwidth = 80;
-               if (vht_oper_chwidth == 1 && center_segment1)
-                       return -1;
-               if (vht_oper_chwidth == 3 && !center_segment1)
-                       return -1;
-               if (!sec_channel_offset)
-                       return -1;
-               /* primary 40 part must match the HT configuration */
-               tmp = (30 + freq - 5000 - center_segment0 * 5)/20;
-               tmp /= 2;
-               if (data.center_freq1 != 5000 +
-                                        center_segment0 * 5 - 20 + 40 * tmp)
-                       return -1;
-               data.center_freq1 = 5000 + center_segment0 * 5;
-               break;
-       case VHT_CHANWIDTH_160MHZ:
-               data.bandwidth = 160;
-               if (center_segment1)
-                       return -1;
-               if (!sec_channel_offset)
-                       return -1;
-               /* primary 40 part must match the HT configuration */
-               tmp = (70 + freq - 5000 - center_segment0 * 5)/20;
-               tmp /= 2;
-               if (data.center_freq1 != 5000 +
-                                        center_segment0 * 5 - 60 + 40 * tmp)
-                       return -1;
-               data.center_freq1 = 5000 + center_segment0 * 5;
-               break;
-       }
+
+       if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
+                                   vht_enabled, sec_channel_offset,
+                                   vht_oper_chwidth,
+                                   center_segment0, center_segment1,
+                                   hapd->iface->current_mode ?
+                                   hapd->iface->current_mode->vht_capab : 0))
+               return -1;
+
        if (hapd->driver == NULL)
                return 0;
        if (hapd->driver->set_freq == NULL)
@@ -683,7 +646,7 @@ int hostapd_drv_wnm_oper(struct hostapd_data *hapd, enum wnm_oper oper,
                         const u8 *peer, u8 *buf, u16 *buf_len)
 {
        if (hapd->driver == NULL || hapd->driver->wnm_oper == NULL)
-               return 0;
+               return -1;
        return hapd->driver->wnm_oper(hapd->drv_priv, oper, peer, buf,
                                      buf_len);
 }
@@ -699,3 +662,66 @@ int hostapd_drv_send_action(struct hostapd_data *hapd, unsigned int freq,
                                         hapd->own_addr, hapd->own_addr, data,
                                         len, 0);
 }
+
+
+int hostapd_start_dfs_cac(struct hostapd_iface *iface,
+                         enum hostapd_hw_mode mode, int freq,
+                         int channel, int ht_enabled, int vht_enabled,
+                         int sec_channel_offset, int vht_oper_chwidth,
+                         int center_segment0, int center_segment1)
+{
+       struct hostapd_data *hapd = iface->bss[0];
+       struct hostapd_freq_params data;
+       int res;
+
+       if (!hapd->driver || !hapd->driver->start_dfs_cac)
+               return 0;
+
+       if (!iface->conf->ieee80211h) {
+               wpa_printf(MSG_ERROR, "Can't start DFS CAC, DFS functionality "
+                          "is not enabled");
+               return -1;
+       }
+
+       if (hostapd_set_freq_params(&data, mode, freq, channel, ht_enabled,
+                                   vht_enabled, sec_channel_offset,
+                                   vht_oper_chwidth, center_segment0,
+                                   center_segment1,
+                                   iface->current_mode->vht_capab)) {
+               wpa_printf(MSG_ERROR, "Can't set freq params");
+               return -1;
+       }
+
+       res = hapd->driver->start_dfs_cac(hapd->drv_priv, &data);
+       if (!res) {
+               iface->cac_started = 1;
+               os_get_reltime(&iface->dfs_cac_start);
+       }
+
+       return res;
+}
+
+
+int hostapd_drv_set_qos_map(struct hostapd_data *hapd,
+                           const u8 *qos_map_set, u8 qos_map_set_len)
+{
+       if (hapd->driver == NULL || hapd->driver->set_qos_map == NULL)
+               return 0;
+       return hapd->driver->set_qos_map(hapd->drv_priv, qos_map_set,
+                                        qos_map_set_len);
+}
+
+
+int hostapd_drv_do_acs(struct hostapd_data *hapd)
+{
+       struct drv_acs_params params;
+
+       if (hapd->driver == NULL || hapd->driver->do_acs == NULL)
+               return 0;
+       os_memset(&params, 0, sizeof(params));
+       params.hw_mode = hapd->iface->conf->hw_mode;
+       params.ht_enabled = !!(hapd->iface->conf->ieee80211n);
+       params.ht40_enabled = !!(hapd->iface->conf->ht_capab &
+                                HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
+       return hapd->driver->do_acs(hapd->drv_priv, &params);
+}
index 70fab55..5d07e71 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd - Driver operations
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -14,6 +14,7 @@ struct wpa_bss_params;
 struct wpa_driver_scan_params;
 struct ieee80211_ht_capabilities;
 struct ieee80211_vht_capabilities;
+struct hostapd_freq_params;
 
 u32 hostapd_sta_flags_to_drv(u32 flags);
 int hostapd_build_ap_extra_ies(struct hostapd_data *hapd,
@@ -31,15 +32,15 @@ int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, const char *ifname,
                              int enabled);
 int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname);
 int hostapd_vlan_if_remove(struct hostapd_data *hapd, const char *ifname);
-int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, int aid,
-                       int val);
+int hostapd_set_wds_sta(struct hostapd_data *hapd, char *ifname_wds,
+                       const u8 *addr, int aid, int val);
 int hostapd_sta_add(struct hostapd_data *hapd,
                    const u8 *addr, u16 aid, u16 capability,
                    const u8 *supp_rates, size_t supp_rates_len,
                    u16 listen_interval,
                    const struct ieee80211_ht_capabilities *ht_capab,
                    const struct ieee80211_vht_capabilities *vht_capab,
-                   u32 flags, u8 qosinfo);
+                   u32 flags, u8 qosinfo, u8 vht_opmode);
 int hostapd_set_privacy(struct hostapd_data *hapd, int enabled);
 int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem,
                             size_t elem_len);
@@ -48,7 +49,7 @@ int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len);
 int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type,
                   const char *ifname, const u8 *addr, void *bss_ctx,
                   void **drv_priv, char *force_ifname, u8 *if_addr,
-                  const char *bridge);
+                  const char *bridge, int use_existing);
 int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type,
                      const char *ifname);
 int hostapd_set_ieee8021x(struct hostapd_data *hapd,
@@ -56,8 +57,8 @@ int hostapd_set_ieee8021x(struct hostapd_data *hapd,
 int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd,
                       const u8 *addr, int idx, u8 *seq);
 int hostapd_flush(struct hostapd_data *hapd);
-int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq,
-                    int channel, int ht_enabled, int vht_enabled,
+int hostapd_set_freq(struct hostapd_data *hapd, enum hostapd_hw_mode mode,
+                    int freq, int channel, int ht_enabled, int vht_enabled,
                     int sec_channel_offset, int vht_oper_chwidth,
                     int center_segment0, int center_segment1);
 int hostapd_set_rts(struct hostapd_data *hapd, int rts);
@@ -101,6 +102,12 @@ int hostapd_sta_assoc(struct hostapd_data *hapd, const u8 *addr,
                      int reassoc, u16 status, const u8 *ie, size_t len);
 int hostapd_add_tspec(struct hostapd_data *hapd, const u8 *addr,
                      u8 *tspec_ie, size_t tspec_ielen);
+int hostapd_start_dfs_cac(struct hostapd_iface *iface,
+                         enum hostapd_hw_mode mode, int freq,
+                         int channel, int ht_enabled, int vht_enabled,
+                         int sec_channel_offset, int vht_oper_chwidth,
+                         int center_segment0, int center_segment1);
+int hostapd_drv_do_acs(struct hostapd_data *hapd);
 
 
 #include "drivers/driver.h"
@@ -109,6 +116,9 @@ int hostapd_drv_wnm_oper(struct hostapd_data *hapd,
                         enum wnm_oper oper, const u8 *peer,
                         u8 *buf, u16 *buf_len);
 
+int hostapd_drv_set_qos_map(struct hostapd_data *hapd, const u8 *qos_map_set,
+                           u8 qos_map_set_len);
+
 static inline int hostapd_drv_set_countermeasures(struct hostapd_data *hapd,
                                                  int enabled)
 {
@@ -225,4 +235,105 @@ static inline void hostapd_drv_poll_client(struct hostapd_data *hapd,
        hapd->driver->poll_client(hapd->drv_priv, own_addr, addr, qos);
 }
 
+static inline int hostapd_drv_get_survey(struct hostapd_data *hapd,
+                                        unsigned int freq)
+{
+       if (hapd->driver == NULL)
+               return -1;
+       if (!hapd->driver->get_survey)
+               return -1;
+       return hapd->driver->get_survey(hapd->drv_priv, freq);
+}
+
+static inline int hostapd_get_country(struct hostapd_data *hapd, char *alpha2)
+{
+       if (hapd->driver == NULL || hapd->driver->get_country == NULL)
+               return -1;
+       return hapd->driver->get_country(hapd->drv_priv, alpha2);
+}
+
+static inline const char * hostapd_drv_get_radio_name(struct hostapd_data *hapd)
+{
+       if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+           hapd->driver->get_radio_name == NULL)
+               return NULL;
+       return hapd->driver->get_radio_name(hapd->drv_priv);
+}
+
+static inline int hostapd_drv_switch_channel(struct hostapd_data *hapd,
+                                            struct csa_settings *settings)
+{
+       if (hapd->driver == NULL || hapd->driver->switch_channel == NULL)
+               return -ENOTSUP;
+
+       return hapd->driver->switch_channel(hapd->drv_priv, settings);
+}
+
+static inline int hostapd_drv_status(struct hostapd_data *hapd, char *buf,
+                                    size_t buflen)
+{
+       if (hapd->driver == NULL || hapd->driver->status == NULL)
+               return -1;
+       return hapd->driver->status(hapd->drv_priv, buf, buflen);
+}
+
+static inline int hostapd_drv_br_add_ip_neigh(struct hostapd_data *hapd,
+                                             int version, const u8 *ipaddr,
+                                             int prefixlen, const u8 *addr)
+{
+       if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+           hapd->driver->br_add_ip_neigh == NULL)
+               return -1;
+       return hapd->driver->br_add_ip_neigh(hapd->drv_priv, version, ipaddr,
+                                            prefixlen, addr);
+}
+
+static inline int hostapd_drv_br_delete_ip_neigh(struct hostapd_data *hapd,
+                                                u8 version, const u8 *ipaddr)
+{
+       if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+           hapd->driver->br_delete_ip_neigh == NULL)
+               return -1;
+       return hapd->driver->br_delete_ip_neigh(hapd->drv_priv, version,
+                                               ipaddr);
+}
+
+static inline int hostapd_drv_br_port_set_attr(struct hostapd_data *hapd,
+                                              enum drv_br_port_attr attr,
+                                              unsigned int val)
+{
+       if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+           hapd->driver->br_port_set_attr == NULL)
+               return -1;
+       return hapd->driver->br_port_set_attr(hapd->drv_priv, attr, val);
+}
+
+static inline int hostapd_drv_br_set_net_param(struct hostapd_data *hapd,
+                                              enum drv_br_net_param param,
+                                              unsigned int val)
+{
+       if (hapd->driver == NULL || hapd->drv_priv == NULL ||
+           hapd->driver->br_set_net_param == NULL)
+               return -1;
+       return hapd->driver->br_set_net_param(hapd->drv_priv, param, val);
+}
+
+static inline int hostapd_drv_vendor_cmd(struct hostapd_data *hapd,
+                                        int vendor_id, int subcmd,
+                                        const u8 *data, size_t data_len,
+                                        struct wpabuf *buf)
+{
+       if (hapd->driver == NULL || hapd->driver->vendor_cmd == NULL)
+               return -1;
+       return hapd->driver->vendor_cmd(hapd->drv_priv, vendor_id, subcmd, data,
+                                       data_len, buf);
+}
+
+static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
+{
+       if (hapd->driver == NULL || hapd->driver->stop_ap == NULL)
+               return 0;
+       return hapd->driver->stop_ap(hapd->drv_priv);
+}
+
 #endif /* AP_DRV_OPS */
index 9f02151..04a56a9 100644 (file)
@@ -14,7 +14,6 @@
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
-#include "drivers/driver.h"
 #include "hostapd.h"
 #include "ap_config.h"
 #include "ieee802_11.h"
@@ -33,7 +32,8 @@ static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap)
 {
        int i;
 
-       if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G ||
+       if (iface->current_mode == NULL ||
+           iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G ||
            iface->conf->channel != ap->channel)
                return 0;
 
@@ -111,8 +111,8 @@ static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap)
        if (s->hnext != NULL)
                s->hnext = s->hnext->hnext;
        else
-               printf("AP: could not remove AP " MACSTR " from hash table\n",
-                      MAC2STR(ap->addr));
+               wpa_printf(MSG_INFO, "AP: could not remove AP " MACSTR
+                          " from hash table",  MAC2STR(ap->addr));
 }
 
 
@@ -172,7 +172,6 @@ void ap_list_process_beacon(struct hostapd_iface *iface,
                            struct hostapd_frame_info *fi)
 {
        struct ap_info *ap;
-       struct os_time now;
        int new_ap = 0;
        int set_beacon = 0;
 
@@ -183,7 +182,8 @@ void ap_list_process_beacon(struct hostapd_iface *iface,
        if (!ap) {
                ap = ap_ap_add(iface, mgmt->bssid);
                if (!ap) {
-                       printf("Failed to allocate AP information entry\n");
+                       wpa_printf(MSG_INFO,
+                                  "Failed to allocate AP information entry");
                        return;
                }
                new_ap = 1;
@@ -210,8 +210,7 @@ void ap_list_process_beacon(struct hostapd_iface *iface,
        else
                ap->ht_support = 0;
 
-       os_get_time(&now);
-       ap->last_beacon = now.sec;
+       os_get_reltime(&ap->last_beacon);
 
        if (!new_ap && ap != iface->ap_list) {
                /* move AP entry into the beginning of the list so that the
@@ -252,7 +251,7 @@ void ap_list_process_beacon(struct hostapd_iface *iface,
 static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
 {
        struct hostapd_iface *iface = eloop_ctx;
-       struct os_time now;
+       struct os_reltime now;
        struct ap_info *ap;
        int set_beacon = 0;
 
@@ -261,12 +260,12 @@ static void ap_list_timer(void *eloop_ctx, void *timeout_ctx)
        if (!iface->ap_list)
                return;
 
-       os_get_time(&now);
+       os_get_reltime(&now);
 
        while (iface->ap_list) {
                ap = iface->ap_list->prev;
-               if (ap->last_beacon + iface->conf->ap_table_expiration_time >=
-                   now.sec)
+               if (!os_reltime_expired(&now, &ap->last_beacon,
+                                       iface->conf->ap_table_expiration_time))
                        break;
 
                ap_free_ap(iface, ap);
index d0529a1..93dc0ed 100644 (file)
@@ -26,7 +26,7 @@ struct ap_info {
 
        int ht_support;
 
-       os_time_t last_beacon;
+       struct os_reltime last_beacon;
 };
 
 struct ieee802_11_elems;
index a959694..13604ed 100644 (file)
@@ -16,6 +16,7 @@
 #include "wpa_auth.h"
 #include "sta_info.h"
 #include "ap_mlme.h"
+#include "hostapd.h"
 
 
 #ifndef CONFIG_NO_HOSTAPD_LOGGER
@@ -80,7 +81,8 @@ void mlme_deauthenticate_indication(struct hostapd_data *hapd,
                       HOSTAPD_LEVEL_DEBUG,
                       "MLME-DEAUTHENTICATE.indication(" MACSTR ", %d)",
                       MAC2STR(sta->addr), reason_code);
-       mlme_deletekeys_request(hapd, sta);
+       if (!hapd->iface->driver_ap_teardown)
+               mlme_deletekeys_request(hapd, sta);
 }
 
 
@@ -118,8 +120,6 @@ void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta)
  * reassociation procedure that was initiated by that specific peer MAC entity.
  *
  * PeerSTAAddress = sta->addr
- *
- * sta->previous_ap contains the "Current AP" information from ReassocReq.
  */
 void mlme_reassociate_indication(struct hostapd_data *hapd,
                                 struct sta_info *sta)
index 597b8dd..bd1778e 100644 (file)
@@ -79,7 +79,10 @@ static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity,
                user->password_hash = eap_user->password_hash;
        }
        user->force_version = eap_user->force_version;
+       user->macacl = eap_user->macacl;
        user->ttls_auth = eap_user->ttls_auth;
+       user->remediation = eap_user->remediation;
+       user->accept_attr = eap_user->accept_attr;
 
        return 0;
 }
@@ -92,6 +95,7 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
        os_memset(&srv, 0, sizeof(srv));
        srv.client_file = conf->radius_server_clients;
        srv.auth_port = conf->radius_server_auth_port;
+       srv.acct_port = conf->radius_server_acct_port;
        srv.conf_ctx = hapd;
        srv.eap_sim_db_priv = hapd->eap_sim_db_priv;
        srv.ssl_ctx = hapd->ssl_ctx;
@@ -111,9 +115,17 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
        srv.eap_req_id_text = conf->eap_req_id_text;
        srv.eap_req_id_text_len = conf->eap_req_id_text_len;
        srv.pwd_group = conf->pwd_group;
+       srv.server_id = conf->server_id ? conf->server_id : "hostapd";
+       srv.sqlite_file = conf->eap_user_sqlite;
 #ifdef CONFIG_RADIUS_TEST
        srv.dump_msk_file = conf->dump_msk_file;
 #endif /* CONFIG_RADIUS_TEST */
+#ifdef CONFIG_HS20
+       srv.subscr_remediation_url = conf->subscr_remediation_url;
+       srv.subscr_remediation_method = conf->subscr_remediation_method;
+#endif /* CONFIG_HS20 */
+       srv.erp = conf->eap_server_erp;
+       srv.erp_domain = conf->erp_domain;
 
        hapd->radius_srv = radius_server_init(&srv);
        if (hapd->radius_srv == NULL) {
@@ -132,7 +144,7 @@ int authsrv_init(struct hostapd_data *hapd)
 #ifdef EAP_TLS_FUNCS
        if (hapd->conf->eap_server &&
            (hapd->conf->ca_cert || hapd->conf->server_cert ||
-            hapd->conf->dh_file)) {
+            hapd->conf->private_key || hapd->conf->dh_file)) {
                struct tls_connection_params params;
 
                hapd->ssl_ctx = tls_init(NULL);
@@ -148,6 +160,7 @@ int authsrv_init(struct hostapd_data *hapd)
                params.private_key = hapd->conf->private_key;
                params.private_key_passwd = hapd->conf->private_key_passwd;
                params.dh_file = hapd->conf->dh_file;
+               params.openssl_ciphers = hapd->conf->openssl_ciphers;
                params.ocsp_stapling_response =
                        hapd->conf->ocsp_stapling_response;
 
index 2fbd527..e575b65 100644 (file)
@@ -4,14 +4,8 @@
  * Copyright (c) 2005-2006, Devicescape Software, Inc.
  * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "utils/includes.h"
@@ -21,7 +15,7 @@
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
-#include "drivers/driver.h"
+#include "common/hw_features_common.h"
 #include "wps/wps_defs.h"
 #include "p2p/p2p.h"
 #include "hostapd.h"
 #include "ap_drv_ops.h"
 #include "beacon.h"
 #include "hs20.h"
+#include "dfs.h"
 
 
 #ifdef NEED_AP_MLME
 
+static u8 * hostapd_eid_rm_enabled_capab(struct hostapd_data *hapd, u8 *eid,
+                                        size_t len)
+{
+       if (!hapd->conf->radio_measurements || len < 2 + 4)
+               return eid;
+
+       *eid++ = WLAN_EID_RRM_ENABLED_CAPABILITIES;
+       *eid++ = 5;
+       *eid++ = (hapd->conf->radio_measurements & BIT(0)) ?
+               WLAN_RRM_CAPS_NEIGHBOR_REPORT : 0x00;
+       *eid++ = 0x00;
+       *eid++ = 0x00;
+       *eid++ = 0x00;
+       *eid++ = 0x00;
+       return eid;
+}
+
+
+static u8 * hostapd_eid_bss_load(struct hostapd_data *hapd, u8 *eid, size_t len)
+{
+       if (len < 2 + 5)
+               return eid;
+
+#ifdef CONFIG_TESTING_OPTIONS
+       if (hapd->conf->bss_load_test_set) {
+               *eid++ = WLAN_EID_BSS_LOAD;
+               *eid++ = 5;
+               os_memcpy(eid, hapd->conf->bss_load_test, 5);
+               eid += 5;
+               return eid;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+       if (hapd->conf->bss_load_update_period) {
+               *eid++ = WLAN_EID_BSS_LOAD;
+               *eid++ = 5;
+               WPA_PUT_LE16(eid, hapd->num_sta);
+               eid += 2;
+               *eid++ = hapd->iface->channel_utilization;
+               WPA_PUT_LE16(eid, 0); /* no available admission capabity */
+               eid += 2;
+       }
+       return eid;
+}
+
+
 static u8 ieee802_11_erp_info(struct hostapd_data *hapd)
 {
        u8 erp = 0;
@@ -93,6 +133,74 @@ static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid)
 }
 
 
+static u8 * hostapd_eid_pwr_constraint(struct hostapd_data *hapd, u8 *eid)
+{
+       u8 *pos = eid;
+       u8 local_pwr_constraint = 0;
+       int dfs;
+
+       if (hapd->iface->current_mode == NULL ||
+           hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+               return eid;
+
+       /* Let host drivers add this IE if DFS support is offloaded */
+       if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+               return eid;
+
+       /*
+        * There is no DFS support and power constraint was not directly
+        * requested by config option.
+        */
+       if (!hapd->iconf->ieee80211h &&
+           hapd->iconf->local_pwr_constraint == -1)
+               return eid;
+
+       /* Check if DFS is required by regulatory. */
+       dfs = hostapd_is_dfs_required(hapd->iface);
+       if (dfs < 0) {
+               wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d",
+                          dfs);
+               dfs = 0;
+       }
+
+       if (dfs == 0 && hapd->iconf->local_pwr_constraint == -1)
+               return eid;
+
+       /*
+        * ieee80211h (DFS) is enabled so Power Constraint element shall
+        * be added when running on DFS channel whenever local_pwr_constraint
+        * is configured or not. In order to meet regulations when TPC is not
+        * implemented using a transmit power that is below the legal maximum
+        * (including any mitigation factor) should help. In this case,
+        * indicate 3 dB below maximum allowed transmit power.
+        */
+       if (hapd->iconf->local_pwr_constraint == -1)
+               local_pwr_constraint = 3;
+
+       /*
+        * A STA that is not an AP shall use a transmit power less than or
+        * equal to the local maximum transmit power level for the channel.
+        * The local maximum transmit power can be calculated from the formula:
+        * local max TX pwr = max TX pwr - local pwr constraint
+        * Where max TX pwr is maximum transmit power level specified for
+        * channel in Country element and local pwr constraint is specified
+        * for channel in this Power Constraint element.
+        */
+
+       /* Element ID */
+       *pos++ = WLAN_EID_PWR_CONSTRAINT;
+       /* Length */
+       *pos++ = 1;
+       /* Local Power Constraint */
+       if (local_pwr_constraint)
+               *pos++ = local_pwr_constraint;
+       else
+               *pos++ = hapd->iconf->local_pwr_constraint;
+
+       return pos;
+}
+
+
 static u8 * hostapd_eid_country_add(u8 *pos, u8 *end, int chan_spacing,
                                    struct hostapd_channel_data *start,
                                    struct hostapd_channel_data *prev)
@@ -146,7 +254,7 @@ static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid,
                        continue; /* can use same entry */
                }
 
-               if (start) {
+               if (start && prev) {
                        pos = hostapd_eid_country_add(pos, end, chan_spacing,
                                                      start, prev);
                        start = NULL;
@@ -187,6 +295,70 @@ static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len)
 }
 
 
+static u8 * hostapd_eid_csa(struct hostapd_data *hapd, u8 *eid)
+{
+       u8 chan;
+
+       if (!hapd->cs_freq_params.freq)
+               return eid;
+
+       if (ieee80211_freq_to_chan(hapd->cs_freq_params.freq, &chan) ==
+           NUM_HOSTAPD_MODES)
+               return eid;
+
+       *eid++ = WLAN_EID_CHANNEL_SWITCH;
+       *eid++ = 3;
+       *eid++ = hapd->cs_block_tx;
+       *eid++ = chan;
+       *eid++ = hapd->cs_count;
+
+       return eid;
+}
+
+
+static u8 * hostapd_eid_secondary_channel(struct hostapd_data *hapd, u8 *eid)
+{
+       u8 sec_ch;
+
+       if (!hapd->cs_freq_params.sec_channel_offset)
+               return eid;
+
+       if (hapd->cs_freq_params.sec_channel_offset == -1)
+               sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW;
+       else if (hapd->cs_freq_params.sec_channel_offset == 1)
+               sec_ch = HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE;
+       else
+               return eid;
+
+       *eid++ = WLAN_EID_SECONDARY_CHANNEL_OFFSET;
+       *eid++ = 1;
+       *eid++ = sec_ch;
+
+       return eid;
+}
+
+
+static u8 * hostapd_add_csa_elems(struct hostapd_data *hapd, u8 *pos,
+                                 u8 *start, unsigned int *csa_counter_off)
+{
+       u8 *old_pos = pos;
+
+       if (!csa_counter_off)
+               return pos;
+
+       *csa_counter_off = 0;
+       pos = hostapd_eid_csa(hapd, pos);
+
+       if (pos != old_pos) {
+               /* save an offset to the counter - should be last byte */
+               *csa_counter_off = pos - start - 1;
+               pos = hostapd_eid_secondary_channel(hapd, pos);
+       }
+
+       return pos;
+}
+
+
 static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
                                   struct sta_info *sta,
                                   const struct ieee80211_mgmt *req,
@@ -208,6 +380,10 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
 #endif /* CONFIG_P2P */
        if (hapd->conf->vendor_elements)
                buflen += wpabuf_len(hapd->conf->vendor_elements);
+       if (hapd->conf->vendor_vht) {
+               buflen += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) +
+                       2 + sizeof(struct ieee80211_vht_operation);
+       }
        resp = os_zalloc(buflen);
        if (resp == NULL)
                return NULL;
@@ -242,6 +418,9 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
 
        pos = hostapd_eid_country(hapd, pos, epos - pos);
 
+       /* Power Constraint element */
+       pos = hostapd_eid_pwr_constraint(hapd, pos);
+
        /* ERP Information element */
        pos = hostapd_eid_erp_info(hapd, pos);
 
@@ -251,6 +430,10 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
        /* RSN, MDIE, WPA */
        pos = hostapd_eid_wpa(hapd, pos, epos - pos);
 
+       pos = hostapd_eid_bss_load(hapd, pos, epos - pos);
+
+       pos = hostapd_eid_rm_enabled_capab(hapd, pos, epos - pos);
+
 #ifdef CONFIG_IEEE80211N
        pos = hostapd_eid_ht_capabilities(hapd, pos);
        pos = hostapd_eid_ht_operation(hapd, pos);
@@ -265,9 +448,15 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
        pos = hostapd_eid_adv_proto(hapd, pos);
        pos = hostapd_eid_roaming_consortium(hapd, pos);
 
+       pos = hostapd_add_csa_elems(hapd, pos, (u8 *)resp,
+                                   &hapd->cs_c_off_proberesp);
 #ifdef CONFIG_IEEE80211AC
-       pos = hostapd_eid_vht_capabilities(hapd, pos);
-       pos = hostapd_eid_vht_operation(hapd, pos);
+       if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+               pos = hostapd_eid_vht_capabilities(hapd, pos);
+               pos = hostapd_eid_vht_operation(hapd, pos);
+       }
+       if (hapd->conf->vendor_vht)
+               pos = hostapd_eid_vendor_vht(hapd, pos);
 #endif /* CONFIG_IEEE80211AC */
 
        /* Wi-Fi Alliance WMM */
@@ -297,6 +486,7 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
 
 #ifdef CONFIG_HS20
        pos = hostapd_eid_hs20_indication(hapd, pos);
+       pos = hostapd_eid_osen(hapd, pos);
 #endif /* CONFIG_HS20 */
 
        if (hapd->conf->vendor_elements) {
@@ -367,13 +557,12 @@ void handle_probe_req(struct hostapd_data *hapd,
        if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
                return;
        ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
-#ifndef TIZEN_EXT_P2P
+
        for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++)
                if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx,
                                            mgmt->sa, mgmt->da, mgmt->bssid,
                                            ie, ie_len, ssi_signal) > 0)
                        return;
-#endif
 
        if (!hapd->iconf->send_probe_response)
                return;
@@ -391,6 +580,27 @@ void handle_probe_req(struct hostapd_data *hapd,
                return;
        }
 
+       /*
+        * No need to reply if the Probe Request frame was sent on an adjacent
+        * channel. IEEE Std 802.11-2012 describes this as a requirement for an
+        * AP with dot11RadioMeasurementActivated set to true, but strictly
+        * speaking does not allow such ignoring of Probe Request frames if
+        * dot11RadioMeasurementActivated is false. Anyway, this can help reduce
+        * number of unnecessary Probe Response frames for cases where the STA
+        * is less likely to see them (Probe Request frame sent on a
+        * neighboring, but partially overlapping, channel).
+        */
+       if (elems.ds_params && elems.ds_params_len == 1 &&
+           hapd->iface->current_mode &&
+           (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G ||
+            hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211B) &&
+           hapd->iconf->channel != elems.ds_params[0]) {
+               wpa_printf(MSG_DEBUG,
+                          "Ignore Probe Request due to DS Params mismatch: chan=%u != ds.chan=%u",
+                          hapd->iconf->channel, elems.ds_params[0]);
+               return;
+       }
+
 #ifdef CONFIG_P2P
        if (hapd->p2p && elems.wps_ie) {
                struct wpabuf *wps;
@@ -444,12 +654,10 @@ void handle_probe_req(struct hostapd_data *hapd,
                        sta->ssid_probe = &hapd->conf->ssid;
        } else {
                if (!(mgmt->da[0] & 0x01)) {
-                       char ssid_txt[33];
-                       ieee802_11_print_ssid(ssid_txt, elems.ssid,
-                                             elems.ssid_len);
                        wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR
                                   " for foreign SSID '%s' (DA " MACSTR ")%s",
-                                  MAC2STR(mgmt->sa), ssid_txt,
+                                  MAC2STR(mgmt->sa),
+                                  wpa_ssid_txt(elems.ssid, elems.ssid_len),
                                   MAC2STR(mgmt->da),
                                   elems.ssid_list ? " (SSID list)" : "");
                }
@@ -457,7 +665,8 @@ void handle_probe_req(struct hostapd_data *hapd,
        }
 
 #ifdef CONFIG_INTERWORKING
-       if (elems.interworking && elems.interworking_len >= 1) {
+       if (hapd->conf->interworking &&
+           elems.interworking && elems.interworking_len >= 1) {
                u8 ant = elems.interworking[0] & 0x0f;
                if (ant != INTERWORKING_ANT_WILDCARD &&
                    ant != hapd->conf->access_network_type) {
@@ -468,7 +677,7 @@ void handle_probe_req(struct hostapd_data *hapd,
                }
        }
 
-       if (elems.interworking &&
+       if (hapd->conf->interworking && elems.interworking &&
            (elems.interworking_len == 7 || elems.interworking_len == 9)) {
                const u8 *hessid;
                if (elems.interworking_len == 7)
@@ -501,7 +710,7 @@ void handle_probe_req(struct hostapd_data *hapd,
         * with AP configuration */
 
 #ifdef CONFIG_TESTING_OPTIONS
-       if (hapd->iconf->ignore_probe_probability > 0.0d &&
+       if (hapd->iconf->ignore_probe_probability > 0.0 &&
            drand48() < hapd->iconf->ignore_probe_probability) {
                wpa_printf(MSG_INFO,
                           "TESTING: ignoring probe request from " MACSTR,
@@ -523,7 +732,7 @@ void handle_probe_req(struct hostapd_data *hapd,
                   is_broadcast_ether_addr(mgmt->da));
 
        if (hostapd_drv_send_mlme(hapd, resp, resp_len, noack) < 0)
-               perror("handle_probe_req: send");
+               wpa_printf(MSG_INFO, "handle_probe_req: send failed");
 
        os_free(resp);
 
@@ -571,23 +780,17 @@ static u8 * hostapd_probe_resp_offloads(struct hostapd_data *hapd,
 #endif /* NEED_AP_MLME */
 
 
-void ieee802_11_set_beacon(struct hostapd_data *hapd)
+int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+                              struct wpa_driver_ap_params *params)
 {
        struct ieee80211_mgmt *head = NULL;
        u8 *tail = NULL;
        size_t head_len = 0, tail_len = 0;
        u8 *resp = NULL;
        size_t resp_len = 0;
-       struct wpa_driver_ap_params params;
-       struct wpabuf *beacon, *proberesp, *assocresp;
 #ifdef NEED_AP_MLME
        u16 capab_info;
        u8 *pos, *tailpos;
-#endif /* NEED_AP_MLME */
-
-       hapd->beacon_set_done = 1;
-
-#ifdef NEED_AP_MLME
 
 #define BEACON_HEAD_BUF_SIZE 256
 #define BEACON_TAIL_BUF_SIZE 512
@@ -603,12 +806,20 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
 #endif /* CONFIG_P2P */
        if (hapd->conf->vendor_elements)
                tail_len += wpabuf_len(hapd->conf->vendor_elements);
+
+#ifdef CONFIG_IEEE80211AC
+       if (hapd->conf->vendor_vht) {
+               tail_len += 5 + 2 + sizeof(struct ieee80211_vht_capabilities) +
+                       2 + sizeof(struct ieee80211_vht_operation);
+       }
+#endif /* CONFIG_IEEE80211AC */
+
        tailpos = tail = os_malloc(tail_len);
        if (head == NULL || tail == NULL) {
                wpa_printf(MSG_ERROR, "Failed to set beacon data");
                os_free(head);
                os_free(tail);
-               return;
+               return -1;
        }
 
        head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
@@ -653,6 +864,9 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
        tailpos = hostapd_eid_country(hapd, tailpos,
                                      tail + BEACON_TAIL_BUF_SIZE - tailpos);
 
+       /* Power Constraint element */
+       tailpos = hostapd_eid_pwr_constraint(hapd, tailpos);
+
        /* ERP Information element */
        tailpos = hostapd_eid_erp_info(hapd, tailpos);
 
@@ -663,6 +877,13 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
        tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE -
                                  tailpos);
 
+       tailpos = hostapd_eid_rm_enabled_capab(hapd, tailpos,
+                                              tail + BEACON_TAIL_BUF_SIZE -
+                                              tailpos);
+
+       tailpos = hostapd_eid_bss_load(hapd, tailpos,
+                                      tail + BEACON_TAIL_BUF_SIZE - tailpos);
+
 #ifdef CONFIG_IEEE80211N
        tailpos = hostapd_eid_ht_capabilities(hapd, tailpos);
        tailpos = hostapd_eid_ht_operation(hapd, tailpos);
@@ -679,10 +900,15 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
        tailpos = hostapd_eid_interworking(hapd, tailpos);
        tailpos = hostapd_eid_adv_proto(hapd, tailpos);
        tailpos = hostapd_eid_roaming_consortium(hapd, tailpos);
-
+       tailpos = hostapd_add_csa_elems(hapd, tailpos, tail,
+                                       &hapd->cs_c_off_beacon);
 #ifdef CONFIG_IEEE80211AC
-       tailpos = hostapd_eid_vht_capabilities(hapd, tailpos);
-       tailpos = hostapd_eid_vht_operation(hapd, tailpos);
+       if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+               tailpos = hostapd_eid_vht_capabilities(hapd, tailpos);
+               tailpos = hostapd_eid_vht_operation(hapd, tailpos);
+       }
+       if (hapd->conf->vendor_vht)
+               tailpos = hostapd_eid_vendor_vht(hapd, tailpos);
 #endif /* CONFIG_IEEE80211AC */
 
        /* Wi-Fi Alliance WMM */
@@ -711,6 +937,7 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
 
 #ifdef CONFIG_HS20
        tailpos = hostapd_eid_hs20_indication(hapd, tailpos);
+       tailpos = hostapd_eid_osen(hapd, tailpos);
 #endif /* CONFIG_HS20 */
 
        if (hapd->conf->vendor_elements) {
@@ -724,94 +951,168 @@ void ieee802_11_set_beacon(struct hostapd_data *hapd)
        resp = hostapd_probe_resp_offloads(hapd, &resp_len);
 #endif /* NEED_AP_MLME */
 
-       os_memset(&params, 0, sizeof(params));
-       params.head = (u8 *) head;
-       params.head_len = head_len;
-       params.tail = tail;
-       params.tail_len = tail_len;
-       params.proberesp = resp;
-       params.proberesp_len = resp_len;
-       params.dtim_period = hapd->conf->dtim_period;
-       params.beacon_int = hapd->iconf->beacon_int;
-       params.basic_rates = hapd->iface->basic_rates;
-       params.ssid = hapd->conf->ssid.ssid;
-       params.ssid_len = hapd->conf->ssid.ssid_len;
-       params.pairwise_ciphers = hapd->conf->rsn_pairwise ?
-               hapd->conf->rsn_pairwise : hapd->conf->wpa_pairwise;
-       params.group_cipher = hapd->conf->wpa_group;
-       params.key_mgmt_suites = hapd->conf->wpa_key_mgmt;
-       params.auth_algs = hapd->conf->auth_algs;
-       params.wpa_version = hapd->conf->wpa;
-       params.privacy = hapd->conf->ssid.wep.keys_set || hapd->conf->wpa ||
+       os_memset(params, 0, sizeof(*params));
+       params->head = (u8 *) head;
+       params->head_len = head_len;
+       params->tail = tail;
+       params->tail_len = tail_len;
+       params->proberesp = resp;
+       params->proberesp_len = resp_len;
+       params->dtim_period = hapd->conf->dtim_period;
+       params->beacon_int = hapd->iconf->beacon_int;
+       params->basic_rates = hapd->iface->basic_rates;
+       params->ssid = hapd->conf->ssid.ssid;
+       params->ssid_len = hapd->conf->ssid.ssid_len;
+       params->pairwise_ciphers = hapd->conf->wpa_pairwise |
+               hapd->conf->rsn_pairwise;
+       params->group_cipher = hapd->conf->wpa_group;
+       params->key_mgmt_suites = hapd->conf->wpa_key_mgmt;
+       params->auth_algs = hapd->conf->auth_algs;
+       params->wpa_version = hapd->conf->wpa;
+       params->privacy = hapd->conf->ssid.wep.keys_set || hapd->conf->wpa ||
                (hapd->conf->ieee802_1x &&
                 (hapd->conf->default_wep_key_len ||
                  hapd->conf->individual_wep_key_len));
        switch (hapd->conf->ignore_broadcast_ssid) {
        case 0:
-               params.hide_ssid = NO_SSID_HIDING;
+               params->hide_ssid = NO_SSID_HIDING;
                break;
        case 1:
-               params.hide_ssid = HIDDEN_SSID_ZERO_LEN;
+               params->hide_ssid = HIDDEN_SSID_ZERO_LEN;
                break;
        case 2:
-               params.hide_ssid = HIDDEN_SSID_ZERO_CONTENTS;
+               params->hide_ssid = HIDDEN_SSID_ZERO_CONTENTS;
                break;
        }
-       hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp);
-       params.beacon_ies = beacon;
-       params.proberesp_ies = proberesp;
-       params.assocresp_ies = assocresp;
-       params.isolate = hapd->conf->isolate;
+       params->isolate = hapd->conf->isolate;
+       params->smps_mode = hapd->iconf->ht_capab & HT_CAP_INFO_SMPS_MASK;
 #ifdef NEED_AP_MLME
-       params.cts_protect = !!(ieee802_11_erp_info(hapd) &
+       params->cts_protect = !!(ieee802_11_erp_info(hapd) &
                                ERP_INFO_USE_PROTECTION);
-       params.preamble = hapd->iface->num_sta_no_short_preamble == 0 &&
+       params->preamble = hapd->iface->num_sta_no_short_preamble == 0 &&
                hapd->iconf->preamble == SHORT_PREAMBLE;
        if (hapd->iface->current_mode &&
            hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
-               params.short_slot_time =
+               params->short_slot_time =
                        hapd->iface->num_sta_no_short_slot_time > 0 ? 0 : 1;
        else
-               params.short_slot_time = -1;
+               params->short_slot_time = -1;
        if (!hapd->iconf->ieee80211n || hapd->conf->disable_11n)
-               params.ht_opmode = -1;
+               params->ht_opmode = -1;
        else
-               params.ht_opmode = hapd->iface->ht_op_mode;
+               params->ht_opmode = hapd->iface->ht_op_mode;
 #endif /* NEED_AP_MLME */
-       params.interworking = hapd->conf->interworking;
+       params->interworking = hapd->conf->interworking;
        if (hapd->conf->interworking &&
            !is_zero_ether_addr(hapd->conf->hessid))
-               params.hessid = hapd->conf->hessid;
-       params.access_network_type = hapd->conf->access_network_type;
-       params.ap_max_inactivity = hapd->conf->ap_max_inactivity;
+               params->hessid = hapd->conf->hessid;
+       params->access_network_type = hapd->conf->access_network_type;
+       params->ap_max_inactivity = hapd->conf->ap_max_inactivity;
+#ifdef CONFIG_P2P
+       params->p2p_go_ctwindow = hapd->iconf->p2p_go_ctwindow;
+#endif /* CONFIG_P2P */
 #ifdef CONFIG_HS20
-       params.disable_dgaf = hapd->conf->disable_dgaf;
+       params->disable_dgaf = hapd->conf->disable_dgaf;
+       if (hapd->conf->osen) {
+               params->privacy = 1;
+               params->osen = 1;
+       }
 #endif /* CONFIG_HS20 */
-       if (hostapd_drv_set_ap(hapd, &params))
-               wpa_printf(MSG_ERROR, "Failed to set beacon parameters");
-       hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
+       return 0;
+}
 
-       os_free(tail);
-       os_free(head);
-       os_free(resp);
+
+void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params)
+{
+       os_free(params->tail);
+       params->tail = NULL;
+       os_free(params->head);
+       params->head = NULL;
+       os_free(params->proberesp);
+       params->proberesp = NULL;
 }
 
 
-void ieee802_11_set_beacons(struct hostapd_iface *iface)
+int ieee802_11_set_beacon(struct hostapd_data *hapd)
+{
+       struct wpa_driver_ap_params params;
+       struct hostapd_freq_params freq;
+       struct hostapd_iface *iface = hapd->iface;
+       struct hostapd_config *iconf = iface->conf;
+       struct wpabuf *beacon, *proberesp, *assocresp;
+       int res, ret = -1;
+
+       if (hapd->csa_in_progress) {
+               wpa_printf(MSG_ERROR, "Cannot set beacons during CSA period");
+               return -1;
+       }
+
+       hapd->beacon_set_done = 1;
+
+       if (ieee802_11_build_ap_params(hapd, &params) < 0)
+               return -1;
+
+       if (hostapd_build_ap_extra_ies(hapd, &beacon, &proberesp, &assocresp) <
+           0)
+               goto fail;
+
+       params.beacon_ies = beacon;
+       params.proberesp_ies = proberesp;
+       params.assocresp_ies = assocresp;
+       params.reenable = hapd->reenable_beacon;
+       hapd->reenable_beacon = 0;
+
+       if (iface->current_mode &&
+           hostapd_set_freq_params(&freq, iconf->hw_mode, iface->freq,
+                                   iconf->channel, iconf->ieee80211n,
+                                   iconf->ieee80211ac,
+                                   iconf->secondary_channel,
+                                   iconf->vht_oper_chwidth,
+                                   iconf->vht_oper_centr_freq_seg0_idx,
+                                   iconf->vht_oper_centr_freq_seg1_idx,
+                                   iface->current_mode->vht_capab) == 0)
+               params.freq = &freq;
+
+       res = hostapd_drv_set_ap(hapd, &params);
+       hostapd_free_ap_extra_ies(hapd, beacon, proberesp, assocresp);
+       if (res)
+               wpa_printf(MSG_ERROR, "Failed to set beacon parameters");
+       else
+               ret = 0;
+fail:
+       ieee802_11_free_ap_params(&params);
+       return ret;
+}
+
+
+int ieee802_11_set_beacons(struct hostapd_iface *iface)
 {
        size_t i;
-       for (i = 0; i < iface->num_bss; i++)
-               ieee802_11_set_beacon(iface->bss[i]);
+       int ret = 0;
+
+       for (i = 0; i < iface->num_bss; i++) {
+               if (iface->bss[i]->started &&
+                   ieee802_11_set_beacon(iface->bss[i]) < 0)
+                       ret = -1;
+       }
+
+       return ret;
 }
 
 
 /* only update beacons if started */
-void ieee802_11_update_beacons(struct hostapd_iface *iface)
+int ieee802_11_update_beacons(struct hostapd_iface *iface)
 {
        size_t i;
-       for (i = 0; i < iface->num_bss; i++)
-               if (iface->bss[i]->beacon_set_done)
-                       ieee802_11_set_beacon(iface->bss[i]);
+       int ret = 0;
+
+       for (i = 0; i < iface->num_bss; i++) {
+               if (iface->bss[i]->beacon_set_done && iface->bss[i]->started &&
+                   ieee802_11_set_beacon(iface->bss[i]) < 0)
+                       ret = -1;
+       }
+
+       return ret;
 }
 
 #endif /* CONFIG_NATIVE_WINDOWS */
index 37f10d2..722159a 100644 (file)
@@ -3,14 +3,8 @@
  * Copyright (c) 2002-2004, Instant802 Networks, Inc.
  * Copyright (c) 2005-2006, Devicescape Software, Inc.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #ifndef BEACON_H
@@ -21,8 +15,11 @@ struct ieee80211_mgmt;
 void handle_probe_req(struct hostapd_data *hapd,
                      const struct ieee80211_mgmt *mgmt, size_t len,
                      int ssi_signal);
-void ieee802_11_set_beacon(struct hostapd_data *hapd);
-void ieee802_11_set_beacons(struct hostapd_iface *iface);
-void ieee802_11_update_beacons(struct hostapd_iface *iface);
+int ieee802_11_set_beacon(struct hostapd_data *hapd);
+int ieee802_11_set_beacons(struct hostapd_iface *iface);
+int ieee802_11_update_beacons(struct hostapd_iface *iface);
+int ieee802_11_build_ap_params(struct hostapd_data *hapd,
+                              struct wpa_driver_ap_params *params);
+void ieee802_11_free_ap_params(struct wpa_driver_ap_params *params);
 
 #endif /* BEACON_H */
diff --git a/src/ap/bss_load.c b/src/ap/bss_load.c
new file mode 100644 (file)
index 0000000..fb63942
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * BSS Load Element / Channel Utilization
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "hostapd.h"
+#include "bss_load.h"
+#include "ap_drv_ops.h"
+#include "beacon.h"
+
+
+static void update_channel_utilization(void *eloop_data, void *user_data)
+{
+       struct hostapd_data *hapd = eloop_data;
+       unsigned int sec, usec;
+       int err;
+
+       if (!(hapd->beacon_set_done && hapd->started))
+               return;
+
+       err = hostapd_drv_get_survey(hapd, hapd->iface->freq);
+       if (err) {
+               wpa_printf(MSG_ERROR, "BSS Load: Failed to get survey data");
+               return;
+       }
+
+       ieee802_11_set_beacon(hapd);
+
+       sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000;
+       usec = (hapd->bss_load_update_timeout % 1000) * 1024;
+       eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
+                              NULL);
+}
+
+
+int bss_load_update_init(struct hostapd_data *hapd)
+{
+       struct hostapd_bss_config *conf = hapd->conf;
+       struct hostapd_config *iconf = hapd->iconf;
+       unsigned int sec, usec;
+
+       if (!conf->bss_load_update_period || !iconf->beacon_int)
+               return -1;
+
+       hapd->bss_load_update_timeout = conf->bss_load_update_period *
+                                       iconf->beacon_int;
+       sec = ((hapd->bss_load_update_timeout / 1000) * 1024) / 1000;
+       usec = (hapd->bss_load_update_timeout % 1000) * 1024;
+       eloop_register_timeout(sec, usec, update_channel_utilization, hapd,
+                              NULL);
+       return 0;
+}
+
+
+void bss_load_update_deinit(struct hostapd_data *hapd)
+{
+       eloop_cancel_timeout(update_channel_utilization, hapd, NULL);
+}
diff --git a/src/ap/bss_load.h b/src/ap/bss_load.h
new file mode 100644 (file)
index 0000000..ac3c793
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * BSS load update
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BSS_LOAD_UPDATE_H
+#define BSS_LOAD_UPDATE_H
+
+
+int bss_load_update_init(struct hostapd_data *hapd);
+void bss_load_update_deinit(struct hostapd_data *hapd);
+
+
+#endif /* BSS_LOAD_UPDATE_H */
index 1cb7e73..41ab988 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Control interface for shared AP commands
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +10,8 @@
 
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/sae.h"
+#include "eapol_auth/eapol_auth_sm.h"
 #include "hostapd.h"
 #include "ieee802_1x.h"
 #include "wpa_auth.h"
 #include "ap_drv_ops.h"
 
 
+static int hostapd_get_sta_tx_rx(struct hostapd_data *hapd,
+                                struct sta_info *sta,
+                                char *buf, size_t buflen)
+{
+       struct hostap_sta_driver_data data;
+       int ret;
+
+       if (hostapd_drv_read_sta_data(hapd, &data, sta->addr) < 0)
+               return 0;
+
+       ret = os_snprintf(buf, buflen, "rx_packets=%lu\ntx_packets=%lu\n"
+                         "rx_bytes=%lu\ntx_bytes=%lu\n",
+                         data.rx_packets, data.tx_packets,
+                         data.rx_bytes, data.tx_bytes);
+       if (os_snprintf_error(buflen, ret))
+               return 0;
+       return ret;
+}
+
+
 static int hostapd_get_sta_conn_time(struct sta_info *sta,
                                     char *buf, size_t buflen)
 {
-       struct os_time now, age;
-       int len = 0, ret;
+       struct os_reltime age;
+       int ret;
 
        if (!sta->connected_time.sec)
                return 0;
 
-       os_get_time(&now);
-       os_time_sub(&now, &sta->connected_time, &age);
+       os_reltime_age(&sta->connected_time, &age);
 
-       ret = os_snprintf(buf + len, buflen - len, "connected_time=%u\n",
+       ret = os_snprintf(buf, buflen, "connected_time=%u\n",
                          (unsigned int) age.sec);
-       if (ret < 0 || (size_t) ret >= buflen - len)
-               return len;
-       len += ret;
+       if (os_snprintf_error(buflen, ret))
+               return 0;
+       return ret;
+}
 
-       return len;
+
+static const char * timeout_next_str(int val)
+{
+       switch (val) {
+       case STA_NULLFUNC:
+               return "NULLFUNC POLL";
+       case STA_DISASSOC:
+               return "DISASSOC";
+       case STA_DEAUTH:
+               return "DEAUTH";
+       case STA_REMOVE:
+               return "REMOVE";
+       case STA_DISASSOC_FROM_CLI:
+               return "DISASSOC_FROM_CLI";
+       }
+
+       return "?";
 }
 
 
@@ -47,19 +85,42 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
                                      struct sta_info *sta,
                                      char *buf, size_t buflen)
 {
-       int len, res, ret;
+       int len, res, ret, i;
 
-       if (sta == NULL) {
-               ret = os_snprintf(buf, buflen, "FAIL\n");
-               if (ret < 0 || (size_t) ret >= buflen)
-                       return 0;
-               return ret;
-       }
+       if (!sta)
+               return 0;
 
        len = 0;
-       ret = os_snprintf(buf + len, buflen - len, MACSTR "\n",
+       ret = os_snprintf(buf + len, buflen - len, MACSTR "\nflags=",
                          MAC2STR(sta->addr));
-       if (ret < 0 || (size_t) ret >= buflen - len)
+       if (os_snprintf_error(buflen - len, ret))
+               return len;
+       len += ret;
+
+       ret = ap_sta_flags_txt(sta->flags, buf + len, buflen - len);
+       if (ret < 0)
+               return len;
+       len += ret;
+
+       ret = os_snprintf(buf + len, buflen - len, "\naid=%d\ncapability=0x%x\n"
+                         "listen_interval=%d\nsupported_rates=",
+                         sta->aid, sta->capability, sta->listen_interval);
+       if (os_snprintf_error(buflen - len, ret))
+               return len;
+       len += ret;
+
+       for (i = 0; i < sta->supported_rates_len; i++) {
+               ret = os_snprintf(buf + len, buflen - len, "%02x%s",
+                                 sta->supported_rates[i],
+                                 i + 1 < sta->supported_rates_len ? " " : "");
+               if (os_snprintf_error(buflen - len, ret))
+                       return len;
+               len += ret;
+       }
+
+       ret = os_snprintf(buf + len, buflen - len, "\ntimeout_next=%s\n",
+                         timeout_next_str(sta->timeout_next));
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
 
@@ -80,9 +141,17 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
        if (res >= 0)
                len += res;
 
-       res = hostapd_get_sta_conn_time(sta, buf + len, buflen - len);
-       if (res >= 0)
-               len += res;
+       len += hostapd_get_sta_tx_rx(hapd, sta, buf + len, buflen - len);
+       len += hostapd_get_sta_conn_time(sta, buf + len, buflen - len);
+
+#ifdef CONFIG_SAE
+       if (sta->sae && sta->sae->state == SAE_ACCEPTED) {
+               res = os_snprintf(buf + len, buflen - len, "sae_group=%d\n",
+                                 sta->sae->group);
+               if (!os_snprintf_error(buflen - len, res))
+                       len += res;
+       }
+#endif /* CONFIG_SAE */
 
        return len;
 }
@@ -100,15 +169,37 @@ int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr,
 {
        u8 addr[ETH_ALEN];
        int ret;
+       const char *pos;
+       struct sta_info *sta;
 
        if (hwaddr_aton(txtaddr, addr)) {
                ret = os_snprintf(buf, buflen, "FAIL\n");
-               if (ret < 0 || (size_t) ret >= buflen)
+               if (os_snprintf_error(buflen, ret))
                        return 0;
                return ret;
        }
-       return hostapd_ctrl_iface_sta_mib(hapd, ap_get_sta(hapd, addr),
-                                         buf, buflen);
+
+       sta = ap_get_sta(hapd, addr);
+       if (sta == NULL)
+               return -1;
+
+       pos = os_strchr(txtaddr, ' ');
+       if (pos) {
+               pos++;
+
+#ifdef HOSTAPD_DUMP_STATE
+               if (os_strcmp(pos, "eapol") == 0) {
+                       if (sta->eapol_sm == NULL)
+                               return -1;
+                       return eapol_auth_dump_state(sta->eapol_sm, buf,
+                                                    buflen);
+               }
+#endif /* HOSTAPD_DUMP_STATE */
+
+               return -1;
+       }
+
+       return hostapd_ctrl_iface_sta_mib(hapd, sta, buf, buflen);
 }
 
 
@@ -122,10 +213,14 @@ int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr,
        if (hwaddr_aton(txtaddr, addr) ||
            (sta = ap_get_sta(hapd, addr)) == NULL) {
                ret = os_snprintf(buf, buflen, "FAIL\n");
-               if (ret < 0 || (size_t) ret >= buflen)
+               if (os_snprintf_error(buflen, ret))
                        return 0;
                return ret;
-       }               
+       }
+
+       if (!sta->next)
+               return 0;
+
        return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen);
 }
 
@@ -145,11 +240,12 @@ static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
        if (mgmt == NULL)
                return -1;
 
+       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype);
        wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "P2P: Disconnect STA " MACSTR
-               " with minor reason code %u (stype=%u)",
-               MAC2STR(addr), minor_reason_code, stype);
+               " with minor reason code %u (stype=%u (%s))",
+               MAC2STR(addr), minor_reason_code, stype,
+               fc2str(mgmt->frame_control));
 
-       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, stype);
        os_memcpy(mgmt->da, addr, ETH_ALEN);
        os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
        os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
@@ -165,9 +261,8 @@ static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
 
        *pos++ = WLAN_EID_VENDOR_SPECIFIC;
        *pos++ = 4 + 3 + 1;
-       WPA_PUT_BE24(pos, OUI_WFA);
-       pos += 3;
-       *pos++ = P2P_OUI_TYPE;
+       WPA_PUT_BE32(pos, P2P_IE_VENDOR_TYPE);
+       pos += 4;
 
        *pos++ = P2P_ATTR_MINOR_REASON_CODE;
        WPA_PUT_LE16(pos, 1);
@@ -197,6 +292,10 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
        if (hwaddr_aton(txtaddr, addr))
                return -1;
 
+       pos = os_strstr(txtaddr, " reason=");
+       if (pos)
+               reason = atoi(pos + 8);
+
        pos = os_strstr(txtaddr, " test=");
        if (pos) {
                struct ieee80211_mgmt mgmt;
@@ -211,8 +310,7 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
                os_memcpy(mgmt.da, addr, ETH_ALEN);
                os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
                os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
-               mgmt.u.deauth.reason_code =
-                       host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+               mgmt.u.deauth.reason_code = host_to_le16(reason);
                if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
                                             IEEE80211_HDRLEN +
                                             sizeof(mgmt.u.deauth),
@@ -229,10 +327,6 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
        }
 #endif /* CONFIG_P2P_MANAGER */
 
-       pos = os_strstr(txtaddr, " reason=");
-       if (pos)
-               reason = atoi(pos + 8);
-
        hostapd_drv_sta_deauth(hapd, addr, reason);
        sta = ap_get_sta(hapd, addr);
        if (sta)
@@ -258,6 +352,10 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
        if (hwaddr_aton(txtaddr, addr))
                return -1;
 
+       pos = os_strstr(txtaddr, " reason=");
+       if (pos)
+               reason = atoi(pos + 8);
+
        pos = os_strstr(txtaddr, " test=");
        if (pos) {
                struct ieee80211_mgmt mgmt;
@@ -272,8 +370,7 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
                os_memcpy(mgmt.da, addr, ETH_ALEN);
                os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
                os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
-               mgmt.u.disassoc.reason_code =
-                       host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
+               mgmt.u.disassoc.reason_code = host_to_le16(reason);
                if (hapd->driver->send_frame(hapd->drv_priv, (u8 *) &mgmt,
                                             IEEE80211_HDRLEN +
                                             sizeof(mgmt.u.deauth),
@@ -290,10 +387,6 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
        }
 #endif /* CONFIG_P2P_MANAGER */
 
-       pos = os_strstr(txtaddr, " reason=");
-       if (pos)
-               reason = atoi(pos + 8);
-
        hostapd_drv_sta_disassoc(hapd, addr, reason);
        sta = ap_get_sta(hapd, addr);
        if (sta)
@@ -303,3 +396,150 @@ int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
 
        return 0;
 }
+
+
+int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
+                             size_t buflen)
+{
+       struct hostapd_iface *iface = hapd->iface;
+       int len = 0, ret;
+       size_t i;
+
+       ret = os_snprintf(buf + len, buflen - len,
+                         "state=%s\n"
+                         "phy=%s\n"
+                         "freq=%d\n"
+                         "num_sta_non_erp=%d\n"
+                         "num_sta_no_short_slot_time=%d\n"
+                         "num_sta_no_short_preamble=%d\n"
+                         "olbc=%d\n"
+                         "num_sta_ht_no_gf=%d\n"
+                         "num_sta_no_ht=%d\n"
+                         "num_sta_ht_20_mhz=%d\n"
+                         "num_sta_ht40_intolerant=%d\n"
+                         "olbc_ht=%d\n"
+                         "ht_op_mode=0x%x\n",
+                         hostapd_state_text(iface->state),
+                         iface->phy,
+                         iface->freq,
+                         iface->num_sta_non_erp,
+                         iface->num_sta_no_short_slot_time,
+                         iface->num_sta_no_short_preamble,
+                         iface->olbc,
+                         iface->num_sta_ht_no_gf,
+                         iface->num_sta_no_ht,
+                         iface->num_sta_ht_20mhz,
+                         iface->num_sta_ht40_intolerant,
+                         iface->olbc_ht,
+                         iface->ht_op_mode);
+       if (os_snprintf_error(buflen - len, ret))
+               return len;
+       len += ret;
+
+       if (!iface->cac_started || !iface->dfs_cac_ms) {
+               ret = os_snprintf(buf + len, buflen - len,
+                                 "cac_time_seconds=%d\n"
+                                 "cac_time_left_seconds=N/A\n",
+                                 iface->dfs_cac_ms / 1000);
+       } else {
+               /* CAC started and CAC time set - calculate remaining time */
+               struct os_reltime now;
+               unsigned int left_time;
+
+               os_reltime_age(&iface->dfs_cac_start, &now);
+               left_time = iface->dfs_cac_ms / 1000 - now.sec;
+               ret = os_snprintf(buf + len, buflen - len,
+                                 "cac_time_seconds=%u\n"
+                                 "cac_time_left_seconds=%u\n",
+                                 iface->dfs_cac_ms / 1000,
+                                 left_time);
+       }
+       if (os_snprintf_error(buflen - len, ret))
+               return len;
+       len += ret;
+
+       ret = os_snprintf(buf + len, buflen - len,
+                         "channel=%u\n"
+                         "secondary_channel=%d\n"
+                         "ieee80211n=%d\n"
+                         "ieee80211ac=%d\n"
+                         "vht_oper_chwidth=%d\n"
+                         "vht_oper_centr_freq_seg0_idx=%d\n"
+                         "vht_oper_centr_freq_seg1_idx=%d\n",
+                         iface->conf->channel,
+                         iface->conf->secondary_channel,
+                         iface->conf->ieee80211n,
+                         iface->conf->ieee80211ac,
+                         iface->conf->vht_oper_chwidth,
+                         iface->conf->vht_oper_centr_freq_seg0_idx,
+                         iface->conf->vht_oper_centr_freq_seg1_idx);
+       if (os_snprintf_error(buflen - len, ret))
+               return len;
+       len += ret;
+
+       for (i = 0; i < iface->num_bss; i++) {
+               struct hostapd_data *bss = iface->bss[i];
+               ret = os_snprintf(buf + len, buflen - len,
+                                 "bss[%d]=%s\n"
+                                 "bssid[%d]=" MACSTR "\n"
+                                 "ssid[%d]=%s\n"
+                                 "num_sta[%d]=%d\n",
+                                 (int) i, bss->conf->iface,
+                                 (int) i, MAC2STR(bss->own_addr),
+                                 (int) i,
+                                 wpa_ssid_txt(bss->conf->ssid.ssid,
+                                              bss->conf->ssid.ssid_len),
+                                 (int) i, bss->num_sta);
+               if (os_snprintf_error(buflen - len, ret))
+                       return len;
+               len += ret;
+       }
+
+       return len;
+}
+
+
+int hostapd_parse_csa_settings(const char *pos,
+                              struct csa_settings *settings)
+{
+       char *end;
+
+       os_memset(settings, 0, sizeof(*settings));
+       settings->cs_count = strtol(pos, &end, 10);
+       if (pos == end) {
+               wpa_printf(MSG_ERROR, "chanswitch: invalid cs_count provided");
+               return -1;
+       }
+
+       settings->freq_params.freq = atoi(end);
+       if (settings->freq_params.freq == 0) {
+               wpa_printf(MSG_ERROR, "chanswitch: invalid freq provided");
+               return -1;
+       }
+
+#define SET_CSA_SETTING(str) \
+       do { \
+               const char *pos2 = os_strstr(pos, " " #str "="); \
+               if (pos2) { \
+                       pos2 += sizeof(" " #str "=") - 1; \
+                       settings->freq_params.str = atoi(pos2); \
+               } \
+       } while (0)
+
+       SET_CSA_SETTING(center_freq1);
+       SET_CSA_SETTING(center_freq2);
+       SET_CSA_SETTING(bandwidth);
+       SET_CSA_SETTING(sec_channel_offset);
+       settings->freq_params.ht_enabled = !!os_strstr(pos, " ht");
+       settings->freq_params.vht_enabled = !!os_strstr(pos, " vht");
+       settings->block_tx = !!os_strstr(pos, " blocktx");
+#undef SET_CSA_SETTING
+
+       return 0;
+}
+
+
+int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd)
+{
+       return hostapd_drv_stop_ap(hapd);
+}
index e83f894..e5297d0 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Control interface for shared AP commands
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -19,5 +19,10 @@ int hostapd_ctrl_iface_deauthenticate(struct hostapd_data *hapd,
                                      const char *txtaddr);
 int hostapd_ctrl_iface_disassociate(struct hostapd_data *hapd,
                                    const char *txtaddr);
+int hostapd_ctrl_iface_status(struct hostapd_data *hapd, char *buf,
+                             size_t buflen);
+int hostapd_parse_csa_settings(const char *pos,
+                              struct csa_settings *settings);
+int hostapd_ctrl_iface_stop_ap(struct hostapd_data *hapd);
 
 #endif /* CTRL_IFACE_AP_H */
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
new file mode 100644 (file)
index 0000000..da6fd46
--- /dev/null
@@ -0,0 +1,1064 @@
+/*
+ * DFS - Dynamic Frequency Selection
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2013-2015, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
+#include "common/wpa_ctrl.h"
+#include "hostapd.h"
+#include "ap_drv_ops.h"
+#include "drivers/driver.h"
+#include "dfs.h"
+
+
+static int dfs_get_used_n_chans(struct hostapd_iface *iface, int *seg1)
+{
+       int n_chans = 1;
+
+       *seg1 = 0;
+
+       if (iface->conf->ieee80211n && iface->conf->secondary_channel)
+               n_chans = 2;
+
+       if (iface->conf->ieee80211ac) {
+               switch (iface->conf->vht_oper_chwidth) {
+               case VHT_CHANWIDTH_USE_HT:
+                       break;
+               case VHT_CHANWIDTH_80MHZ:
+                       n_chans = 4;
+                       break;
+               case VHT_CHANWIDTH_160MHZ:
+                       n_chans = 8;
+                       break;
+               case VHT_CHANWIDTH_80P80MHZ:
+                       n_chans = 4;
+                       *seg1 = 4;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       return n_chans;
+}
+
+
+static int dfs_channel_available(struct hostapd_channel_data *chan,
+                                int skip_radar)
+{
+       /*
+        * When radar detection happens, CSA is performed. However, there's no
+        * time for CAC, so radar channels must be skipped when finding a new
+        * channel for CSA, unless they are available for immediate use.
+        */
+       if (skip_radar && (chan->flag & HOSTAPD_CHAN_RADAR) &&
+           ((chan->flag & HOSTAPD_CHAN_DFS_MASK) !=
+            HOSTAPD_CHAN_DFS_AVAILABLE))
+               return 0;
+
+       if (chan->flag & HOSTAPD_CHAN_DISABLED)
+               return 0;
+       if ((chan->flag & HOSTAPD_CHAN_RADAR) &&
+           ((chan->flag & HOSTAPD_CHAN_DFS_MASK) ==
+            HOSTAPD_CHAN_DFS_UNAVAILABLE))
+               return 0;
+       return 1;
+}
+
+
+static int dfs_is_chan_allowed(struct hostapd_channel_data *chan, int n_chans)
+{
+       /*
+        * The tables contain first valid channel number based on channel width.
+        * We will also choose this first channel as the control one.
+        */
+       int allowed_40[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
+                            184, 192 };
+       /*
+        * VHT80, valid channels based on center frequency:
+        * 42, 58, 106, 122, 138, 155
+        */
+       int allowed_80[] = { 36, 52, 100, 116, 132, 149 };
+       /*
+        * VHT160 valid channels based on center frequency:
+        * 50, 114
+        */
+       int allowed_160[] = { 36, 100 };
+       int *allowed = allowed_40;
+       unsigned int i, allowed_no = 0;
+
+       switch (n_chans) {
+       case 2:
+               allowed = allowed_40;
+               allowed_no = ARRAY_SIZE(allowed_40);
+               break;
+       case 4:
+               allowed = allowed_80;
+               allowed_no = ARRAY_SIZE(allowed_80);
+               break;
+       case 8:
+               allowed = allowed_160;
+               allowed_no = ARRAY_SIZE(allowed_160);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "Unknown width for %d channels", n_chans);
+               break;
+       }
+
+       for (i = 0; i < allowed_no; i++) {
+               if (chan->chan == allowed[i])
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
+                                   int first_chan_idx, int num_chans,
+                                   int skip_radar)
+{
+       struct hostapd_channel_data *first_chan, *chan;
+       int i;
+
+       if (first_chan_idx + num_chans >= mode->num_channels)
+               return 0;
+
+       first_chan = &mode->channels[first_chan_idx];
+
+       for (i = 0; i < num_chans; i++) {
+               chan = &mode->channels[first_chan_idx + i];
+
+               if (first_chan->freq + i * 20 != chan->freq)
+                       return 0;
+
+               if (!dfs_channel_available(chan, skip_radar))
+                       return 0;
+       }
+
+       return 1;
+}
+
+
+static int is_in_chanlist(struct hostapd_iface *iface,
+                         struct hostapd_channel_data *chan)
+{
+       int *entry;
+
+       if (!iface->conf->chanlist)
+               return 1;
+
+       for (entry = iface->conf->chanlist; *entry != -1; entry++) {
+               if (*entry == chan->chan)
+                       return 1;
+       }
+       return 0;
+}
+
+
+/*
+ * The function assumes HT40+ operation.
+ * Make sure to adjust the following variables after calling this:
+ *  - hapd->secondary_channel
+ *  - hapd->vht_oper_centr_freq_seg0_idx
+ *  - hapd->vht_oper_centr_freq_seg1_idx
+ */
+static int dfs_find_channel(struct hostapd_iface *iface,
+                           struct hostapd_channel_data **ret_chan,
+                           int idx, int skip_radar)
+{
+       struct hostapd_hw_modes *mode;
+       struct hostapd_channel_data *chan;
+       int i, channel_idx = 0, n_chans, n_chans1;
+
+       mode = iface->current_mode;
+       n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+       wpa_printf(MSG_DEBUG, "DFS new chan checking %d channels", n_chans);
+       for (i = 0; i < mode->num_channels; i++) {
+               chan = &mode->channels[i];
+
+               /* Skip HT40/VHT incompatible channels */
+               if (iface->conf->ieee80211n &&
+                   iface->conf->secondary_channel &&
+                   !dfs_is_chan_allowed(chan, n_chans))
+                       continue;
+
+               /* Skip incompatible chandefs */
+               if (!dfs_chan_range_available(mode, i, n_chans, skip_radar))
+                       continue;
+
+               if (!is_in_chanlist(iface, chan))
+                       continue;
+
+               if (ret_chan && idx == channel_idx) {
+                       wpa_printf(MSG_DEBUG, "Selected ch. #%d", chan->chan);
+                       *ret_chan = chan;
+                       return idx;
+               }
+               wpa_printf(MSG_DEBUG, "Adding channel: %d", chan->chan);
+               channel_idx++;
+       }
+       return channel_idx;
+}
+
+
+static void dfs_adjust_vht_center_freq(struct hostapd_iface *iface,
+                                      struct hostapd_channel_data *chan,
+                                      int secondary_channel,
+                                      u8 *vht_oper_centr_freq_seg0_idx,
+                                      u8 *vht_oper_centr_freq_seg1_idx)
+{
+       if (!iface->conf->ieee80211ac)
+               return;
+
+       if (!chan)
+               return;
+
+       *vht_oper_centr_freq_seg1_idx = 0;
+
+       switch (iface->conf->vht_oper_chwidth) {
+       case VHT_CHANWIDTH_USE_HT:
+               if (secondary_channel == 1)
+                       *vht_oper_centr_freq_seg0_idx = chan->chan + 2;
+               else if (secondary_channel == -1)
+                       *vht_oper_centr_freq_seg0_idx = chan->chan - 2;
+               else
+                       *vht_oper_centr_freq_seg0_idx = chan->chan;
+               break;
+       case VHT_CHANWIDTH_80MHZ:
+               *vht_oper_centr_freq_seg0_idx = chan->chan + 6;
+               break;
+       case VHT_CHANWIDTH_160MHZ:
+               *vht_oper_centr_freq_seg0_idx = chan->chan + 14;
+               break;
+       default:
+               wpa_printf(MSG_INFO, "DFS only VHT20/40/80/160 is supported now");
+               *vht_oper_centr_freq_seg0_idx = 0;
+               break;
+       }
+
+       wpa_printf(MSG_DEBUG, "DFS adjusting VHT center frequency: %d, %d",
+                  *vht_oper_centr_freq_seg0_idx,
+                  *vht_oper_centr_freq_seg1_idx);
+}
+
+
+/* Return start channel idx we will use for mode->channels[idx] */
+static int dfs_get_start_chan_idx(struct hostapd_iface *iface, int *seg1_start)
+{
+       struct hostapd_hw_modes *mode;
+       struct hostapd_channel_data *chan;
+       int channel_no = iface->conf->channel;
+       int res = -1, i;
+       int chan_seg1 = -1;
+
+       *seg1_start = -1;
+
+       /* HT40- */
+       if (iface->conf->ieee80211n && iface->conf->secondary_channel == -1)
+               channel_no -= 4;
+
+       /* VHT */
+       if (iface->conf->ieee80211ac) {
+               switch (iface->conf->vht_oper_chwidth) {
+               case VHT_CHANWIDTH_USE_HT:
+                       break;
+               case VHT_CHANWIDTH_80MHZ:
+                       channel_no =
+                               iface->conf->vht_oper_centr_freq_seg0_idx - 6;
+                       break;
+               case VHT_CHANWIDTH_160MHZ:
+                       channel_no =
+                               iface->conf->vht_oper_centr_freq_seg0_idx - 14;
+                       break;
+               case VHT_CHANWIDTH_80P80MHZ:
+                       channel_no =
+                               iface->conf->vht_oper_centr_freq_seg0_idx - 6;
+                       chan_seg1 =
+                               iface->conf->vht_oper_centr_freq_seg1_idx - 6;
+                       break;
+               default:
+                       wpa_printf(MSG_INFO,
+                                  "DFS only VHT20/40/80/160/80+80 is supported now");
+                       channel_no = -1;
+                       break;
+               }
+       }
+
+       /* Get idx */
+       mode = iface->current_mode;
+       for (i = 0; i < mode->num_channels; i++) {
+               chan = &mode->channels[i];
+               if (chan->chan == channel_no) {
+                       res = i;
+                       break;
+               }
+       }
+
+       if (res != -1 && chan_seg1 > -1) {
+               int found = 0;
+
+               /* Get idx for seg1 */
+               mode = iface->current_mode;
+               for (i = 0; i < mode->num_channels; i++) {
+                       chan = &mode->channels[i];
+                       if (chan->chan == chan_seg1) {
+                               *seg1_start = i;
+                               found = 1;
+                               break;
+                       }
+               }
+               if (!found)
+                       res = -1;
+       }
+
+       if (res == -1) {
+               wpa_printf(MSG_DEBUG,
+                          "DFS chan_idx seems wrong; num-ch: %d ch-no: %d conf-ch-no: %d 11n: %d sec-ch: %d vht-oper-width: %d",
+                          mode->num_channels, channel_no, iface->conf->channel,
+                          iface->conf->ieee80211n,
+                          iface->conf->secondary_channel,
+                          iface->conf->vht_oper_chwidth);
+
+               for (i = 0; i < mode->num_channels; i++) {
+                       wpa_printf(MSG_DEBUG, "Available channel: %d",
+                                  mode->channels[i].chan);
+               }
+       }
+
+       return res;
+}
+
+
+/* At least one channel have radar flag */
+static int dfs_check_chans_radar(struct hostapd_iface *iface,
+                                int start_chan_idx, int n_chans)
+{
+       struct hostapd_channel_data *channel;
+       struct hostapd_hw_modes *mode;
+       int i, res = 0;
+
+       mode = iface->current_mode;
+
+       for (i = 0; i < n_chans; i++) {
+               channel = &mode->channels[start_chan_idx + i];
+               if (channel->flag & HOSTAPD_CHAN_RADAR)
+                       res++;
+       }
+
+       return res;
+}
+
+
+/* All channels available */
+static int dfs_check_chans_available(struct hostapd_iface *iface,
+                                    int start_chan_idx, int n_chans)
+{
+       struct hostapd_channel_data *channel;
+       struct hostapd_hw_modes *mode;
+       int i;
+
+       mode = iface->current_mode;
+
+       for (i = 0; i < n_chans; i++) {
+               channel = &mode->channels[start_chan_idx + i];
+
+               if (channel->flag & HOSTAPD_CHAN_DISABLED)
+                       break;
+
+               if (!(channel->flag & HOSTAPD_CHAN_RADAR))
+                       continue;
+
+               if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) !=
+                   HOSTAPD_CHAN_DFS_AVAILABLE)
+                       break;
+       }
+
+       return i == n_chans;
+}
+
+
+/* At least one channel unavailable */
+static int dfs_check_chans_unavailable(struct hostapd_iface *iface,
+                                      int start_chan_idx,
+                                      int n_chans)
+{
+       struct hostapd_channel_data *channel;
+       struct hostapd_hw_modes *mode;
+       int i, res = 0;
+
+       mode = iface->current_mode;
+
+       for (i = 0; i < n_chans; i++) {
+               channel = &mode->channels[start_chan_idx + i];
+               if (channel->flag & HOSTAPD_CHAN_DISABLED)
+                       res++;
+               if ((channel->flag & HOSTAPD_CHAN_DFS_MASK) ==
+                   HOSTAPD_CHAN_DFS_UNAVAILABLE)
+                       res++;
+       }
+
+       return res;
+}
+
+
+static struct hostapd_channel_data *
+dfs_get_valid_channel(struct hostapd_iface *iface,
+                     int *secondary_channel,
+                     u8 *vht_oper_centr_freq_seg0_idx,
+                     u8 *vht_oper_centr_freq_seg1_idx,
+                     int skip_radar)
+{
+       struct hostapd_hw_modes *mode;
+       struct hostapd_channel_data *chan = NULL;
+       int num_available_chandefs;
+       int chan_idx;
+       u32 _rand;
+
+       wpa_printf(MSG_DEBUG, "DFS: Selecting random channel");
+       *secondary_channel = 0;
+       *vht_oper_centr_freq_seg0_idx = 0;
+       *vht_oper_centr_freq_seg1_idx = 0;
+
+       if (iface->current_mode == NULL)
+               return NULL;
+
+       mode = iface->current_mode;
+       if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+               return NULL;
+
+       /* Get the count first */
+       num_available_chandefs = dfs_find_channel(iface, NULL, 0, skip_radar);
+       if (num_available_chandefs == 0)
+               return NULL;
+
+       if (os_get_random((u8 *) &_rand, sizeof(_rand)) < 0)
+               _rand = os_random();
+       chan_idx = _rand % num_available_chandefs;
+       dfs_find_channel(iface, &chan, chan_idx, skip_radar);
+
+       /* dfs_find_channel() calculations assume HT40+ */
+       if (iface->conf->secondary_channel)
+               *secondary_channel = 1;
+       else
+               *secondary_channel = 0;
+
+       dfs_adjust_vht_center_freq(iface, chan,
+                                  *secondary_channel,
+                                  vht_oper_centr_freq_seg0_idx,
+                                  vht_oper_centr_freq_seg1_idx);
+
+       return chan;
+}
+
+
+static int set_dfs_state_freq(struct hostapd_iface *iface, int freq, u32 state)
+{
+       struct hostapd_hw_modes *mode;
+       struct hostapd_channel_data *chan = NULL;
+       int i;
+
+       mode = iface->current_mode;
+       if (mode == NULL)
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "set_dfs_state 0x%X for %d MHz", state, freq);
+       for (i = 0; i < iface->current_mode->num_channels; i++) {
+               chan = &iface->current_mode->channels[i];
+               if (chan->freq == freq) {
+                       if (chan->flag & HOSTAPD_CHAN_RADAR) {
+                               chan->flag &= ~HOSTAPD_CHAN_DFS_MASK;
+                               chan->flag |= state;
+                               return 1; /* Channel found */
+                       }
+               }
+       }
+       wpa_printf(MSG_WARNING, "Can't set DFS state for freq %d MHz", freq);
+       return 0;
+}
+
+
+static int set_dfs_state(struct hostapd_iface *iface, int freq, int ht_enabled,
+                        int chan_offset, int chan_width, int cf1,
+                        int cf2, u32 state)
+{
+       int n_chans = 1, i;
+       struct hostapd_hw_modes *mode;
+       int frequency = freq;
+       int ret = 0;
+
+       mode = iface->current_mode;
+       if (mode == NULL)
+               return 0;
+
+       if (mode->mode != HOSTAPD_MODE_IEEE80211A) {
+               wpa_printf(MSG_WARNING, "current_mode != IEEE80211A");
+               return 0;
+       }
+
+       /* Seems cf1 and chan_width is enough here */
+       switch (chan_width) {
+       case CHAN_WIDTH_20_NOHT:
+       case CHAN_WIDTH_20:
+               n_chans = 1;
+               if (frequency == 0)
+                       frequency = cf1;
+               break;
+       case CHAN_WIDTH_40:
+               n_chans = 2;
+               frequency = cf1 - 10;
+               break;
+       case CHAN_WIDTH_80:
+               n_chans = 4;
+               frequency = cf1 - 30;
+               break;
+       case CHAN_WIDTH_160:
+               n_chans = 8;
+               frequency = cf1 - 70;
+               break;
+       default:
+               wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
+                          chan_width);
+               break;
+       }
+
+       wpa_printf(MSG_DEBUG, "DFS freq: %dMHz, n_chans: %d", frequency,
+                  n_chans);
+       for (i = 0; i < n_chans; i++) {
+               ret += set_dfs_state_freq(iface, frequency, state);
+               frequency = frequency + 20;
+       }
+
+       return ret;
+}
+
+
+static int dfs_are_channels_overlapped(struct hostapd_iface *iface, int freq,
+                                      int chan_width, int cf1, int cf2)
+{
+       int start_chan_idx, start_chan_idx1;
+       struct hostapd_hw_modes *mode;
+       struct hostapd_channel_data *chan;
+       int n_chans, n_chans1, i, j, frequency = freq, radar_n_chans = 1;
+       u8 radar_chan;
+       int res = 0;
+
+       /* Our configuration */
+       mode = iface->current_mode;
+       start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
+       n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+       /* Check we are on DFS channel(s) */
+       if (!dfs_check_chans_radar(iface, start_chan_idx, n_chans))
+               return 0;
+
+       /* Reported via radar event */
+       switch (chan_width) {
+       case CHAN_WIDTH_20_NOHT:
+       case CHAN_WIDTH_20:
+               radar_n_chans = 1;
+               if (frequency == 0)
+                       frequency = cf1;
+               break;
+       case CHAN_WIDTH_40:
+               radar_n_chans = 2;
+               frequency = cf1 - 10;
+               break;
+       case CHAN_WIDTH_80:
+               radar_n_chans = 4;
+               frequency = cf1 - 30;
+               break;
+       case CHAN_WIDTH_160:
+               radar_n_chans = 8;
+               frequency = cf1 - 70;
+               break;
+       default:
+               wpa_printf(MSG_INFO, "DFS chan_width %d not supported",
+                          chan_width);
+               break;
+       }
+
+       ieee80211_freq_to_chan(frequency, &radar_chan);
+
+       for (i = 0; i < n_chans; i++) {
+               chan = &mode->channels[start_chan_idx + i];
+               if (!(chan->flag & HOSTAPD_CHAN_RADAR))
+                       continue;
+               for (j = 0; j < radar_n_chans; j++) {
+                       wpa_printf(MSG_DEBUG, "checking our: %d, radar: %d",
+                                  chan->chan, radar_chan + j * 4);
+                       if (chan->chan == radar_chan + j * 4)
+                               res++;
+               }
+       }
+
+       wpa_printf(MSG_DEBUG, "overlapped: %d", res);
+
+       return res;
+}
+
+
+static unsigned int dfs_get_cac_time(struct hostapd_iface *iface,
+                                    int start_chan_idx, int n_chans)
+{
+       struct hostapd_channel_data *channel;
+       struct hostapd_hw_modes *mode;
+       int i;
+       unsigned int cac_time_ms = 0;
+
+       mode = iface->current_mode;
+
+       for (i = 0; i < n_chans; i++) {
+               channel = &mode->channels[start_chan_idx + i];
+               if (!(channel->flag & HOSTAPD_CHAN_RADAR))
+                       continue;
+               if (channel->dfs_cac_ms > cac_time_ms)
+                       cac_time_ms = channel->dfs_cac_ms;
+       }
+
+       return cac_time_ms;
+}
+
+
+/*
+ * Main DFS handler
+ * 1 - continue channel/ap setup
+ * 0 - channel/ap setup will be continued after CAC
+ * -1 - hit critical error
+ */
+int hostapd_handle_dfs(struct hostapd_iface *iface)
+{
+       struct hostapd_channel_data *channel;
+       int res, n_chans, n_chans1, start_chan_idx, start_chan_idx1;
+       int skip_radar = 0;
+
+       if (!iface->current_mode) {
+               /*
+                * This can happen with drivers that do not provide mode
+                * information and as such, cannot really use hostapd for DFS.
+                */
+               wpa_printf(MSG_DEBUG,
+                          "DFS: No current_mode information - assume no need to perform DFS operations by hostapd");
+               return 1;
+       }
+
+       iface->cac_started = 0;
+
+       do {
+               /* Get start (first) channel for current configuration */
+               start_chan_idx = dfs_get_start_chan_idx(iface,
+                                                       &start_chan_idx1);
+               if (start_chan_idx == -1)
+                       return -1;
+
+               /* Get number of used channels, depend on width */
+               n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+               /* Setup CAC time */
+               iface->dfs_cac_ms = dfs_get_cac_time(iface, start_chan_idx,
+                                                    n_chans);
+
+               /* Check if any of configured channels require DFS */
+               res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
+               wpa_printf(MSG_DEBUG,
+                          "DFS %d channels required radar detection",
+                          res);
+               if (!res)
+                       return 1;
+
+               /* Check if all channels are DFS available */
+               res = dfs_check_chans_available(iface, start_chan_idx, n_chans);
+               wpa_printf(MSG_DEBUG,
+                          "DFS all channels available, (SKIP CAC): %s",
+                          res ? "yes" : "no");
+               if (res)
+                       return 1;
+
+               /* Check if any of configured channels is unavailable */
+               res = dfs_check_chans_unavailable(iface, start_chan_idx,
+                                                 n_chans);
+               wpa_printf(MSG_DEBUG, "DFS %d chans unavailable - choose other channel: %s",
+                          res, res ? "yes": "no");
+               if (res) {
+                       int sec = 0;
+                       u8 cf1 = 0, cf2 = 0;
+
+                       channel = dfs_get_valid_channel(iface, &sec, &cf1, &cf2,
+                                                       skip_radar);
+                       if (!channel) {
+                               wpa_printf(MSG_ERROR, "could not get valid channel");
+                               return -1;
+                       }
+
+                       iface->freq = channel->freq;
+                       iface->conf->channel = channel->chan;
+                       iface->conf->secondary_channel = sec;
+                       iface->conf->vht_oper_centr_freq_seg0_idx = cf1;
+                       iface->conf->vht_oper_centr_freq_seg1_idx = cf2;
+               }
+       } while (res);
+
+       /* Finally start CAC */
+       hostapd_set_state(iface, HAPD_IFACE_DFS);
+       wpa_printf(MSG_DEBUG, "DFS start CAC on %d MHz", iface->freq);
+       wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
+               "freq=%d chan=%d sec_chan=%d, width=%d, seg0=%d, seg1=%d, cac_time=%ds",
+               iface->freq,
+               iface->conf->channel, iface->conf->secondary_channel,
+               iface->conf->vht_oper_chwidth,
+               iface->conf->vht_oper_centr_freq_seg0_idx,
+               iface->conf->vht_oper_centr_freq_seg1_idx,
+               iface->dfs_cac_ms / 1000);
+
+       res = hostapd_start_dfs_cac(iface, iface->conf->hw_mode,
+                                   iface->freq,
+                                   iface->conf->channel,
+                                   iface->conf->ieee80211n,
+                                   iface->conf->ieee80211ac,
+                                   iface->conf->secondary_channel,
+                                   iface->conf->vht_oper_chwidth,
+                                   iface->conf->vht_oper_centr_freq_seg0_idx,
+                                   iface->conf->vht_oper_centr_freq_seg1_idx);
+
+       if (res) {
+               wpa_printf(MSG_ERROR, "DFS start_dfs_cac() failed, %d", res);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+                            int ht_enabled, int chan_offset, int chan_width,
+                            int cf1, int cf2)
+{
+       wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_COMPLETED
+               "success=%d freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+               success, freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+
+       if (success) {
+               /* Complete iface/ap configuration */
+               if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) {
+                       /* Complete AP configuration for the first bring up. */
+                       if (iface->state != HAPD_IFACE_ENABLED)
+                               hostapd_setup_interface_complete(iface, 0);
+                       else
+                               iface->cac_started = 0;
+               } else {
+                       set_dfs_state(iface, freq, ht_enabled, chan_offset,
+                                     chan_width, cf1, cf2,
+                                     HOSTAPD_CHAN_DFS_AVAILABLE);
+                       iface->cac_started = 0;
+                       hostapd_setup_interface_complete(iface, 0);
+               }
+       }
+
+       return 0;
+}
+
+
+static int hostapd_dfs_start_channel_switch_cac(struct hostapd_iface *iface)
+{
+       struct hostapd_channel_data *channel;
+       int secondary_channel;
+       u8 vht_oper_centr_freq_seg0_idx = 0;
+       u8 vht_oper_centr_freq_seg1_idx = 0;
+       int skip_radar = 0;
+       int err = 1;
+
+       /* Radar detected during active CAC */
+       iface->cac_started = 0;
+       channel = dfs_get_valid_channel(iface, &secondary_channel,
+                                       &vht_oper_centr_freq_seg0_idx,
+                                       &vht_oper_centr_freq_seg1_idx,
+                                       skip_radar);
+
+       if (!channel) {
+               wpa_printf(MSG_ERROR, "No valid channel available");
+               hostapd_setup_interface_complete(iface, err);
+               return err;
+       }
+
+       wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
+                  channel->chan);
+       wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
+               "freq=%d chan=%d sec_chan=%d", channel->freq,
+               channel->chan, secondary_channel);
+
+       iface->freq = channel->freq;
+       iface->conf->channel = channel->chan;
+       iface->conf->secondary_channel = secondary_channel;
+       iface->conf->vht_oper_centr_freq_seg0_idx =
+               vht_oper_centr_freq_seg0_idx;
+       iface->conf->vht_oper_centr_freq_seg1_idx =
+               vht_oper_centr_freq_seg1_idx;
+       err = 0;
+
+       hostapd_setup_interface_complete(iface, err);
+       return err;
+}
+
+
+static int hostapd_csa_in_progress(struct hostapd_iface *iface)
+{
+       unsigned int i;
+       for (i = 0; i < iface->num_bss; i++)
+               if (iface->bss[i]->csa_in_progress)
+                       return 1;
+       return 0;
+}
+
+
+static int hostapd_dfs_start_channel_switch(struct hostapd_iface *iface)
+{
+       struct hostapd_channel_data *channel;
+       int secondary_channel;
+       u8 vht_oper_centr_freq_seg0_idx;
+       u8 vht_oper_centr_freq_seg1_idx;
+       int skip_radar = 1;
+       struct csa_settings csa_settings;
+       unsigned int i;
+       int err = 1;
+
+       wpa_printf(MSG_DEBUG, "%s called (CAC active: %s, CSA active: %s)",
+                  __func__, iface->cac_started ? "yes" : "no",
+                  hostapd_csa_in_progress(iface) ? "yes" : "no");
+
+       /* Check if CSA in progress */
+       if (hostapd_csa_in_progress(iface))
+               return 0;
+
+       /* Check if active CAC */
+       if (iface->cac_started)
+               return hostapd_dfs_start_channel_switch_cac(iface);
+
+       /* Perform channel switch/CSA */
+       channel = dfs_get_valid_channel(iface, &secondary_channel,
+                                       &vht_oper_centr_freq_seg0_idx,
+                                       &vht_oper_centr_freq_seg1_idx,
+                                       skip_radar);
+
+       if (!channel) {
+               /*
+                * If there is no channel to switch immediately to, check if
+                * there is another channel where we can switch even if it
+                * requires to perform a CAC first.
+                */
+               skip_radar = 0;
+               channel = dfs_get_valid_channel(iface, &secondary_channel,
+                                               &vht_oper_centr_freq_seg0_idx,
+                                               &vht_oper_centr_freq_seg1_idx,
+                                               skip_radar);
+               if (!channel) {
+                       /* FIXME: Wait for channel(s) to become available */
+                       hostapd_disable_iface(iface);
+                       return err;
+               }
+
+               iface->freq = channel->freq;
+               iface->conf->channel = channel->chan;
+               iface->conf->secondary_channel = secondary_channel;
+               iface->conf->vht_oper_centr_freq_seg0_idx =
+                       vht_oper_centr_freq_seg0_idx;
+               iface->conf->vht_oper_centr_freq_seg1_idx =
+                       vht_oper_centr_freq_seg1_idx;
+
+               hostapd_disable_iface(iface);
+               hostapd_enable_iface(iface);
+               return 0;
+       }
+
+       wpa_printf(MSG_DEBUG, "DFS will switch to a new channel %d",
+                  channel->chan);
+       wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NEW_CHANNEL
+               "freq=%d chan=%d sec_chan=%d", channel->freq,
+               channel->chan, secondary_channel);
+
+       /* Setup CSA request */
+       os_memset(&csa_settings, 0, sizeof(csa_settings));
+       csa_settings.cs_count = 5;
+       csa_settings.block_tx = 1;
+       err = hostapd_set_freq_params(&csa_settings.freq_params,
+                                     iface->conf->hw_mode,
+                                     channel->freq,
+                                     channel->chan,
+                                     iface->conf->ieee80211n,
+                                     iface->conf->ieee80211ac,
+                                     secondary_channel,
+                                     iface->conf->vht_oper_chwidth,
+                                     vht_oper_centr_freq_seg0_idx,
+                                     vht_oper_centr_freq_seg1_idx,
+                                     iface->current_mode->vht_capab);
+
+       if (err) {
+               wpa_printf(MSG_ERROR, "DFS failed to calculate CSA freq params");
+               hostapd_disable_iface(iface);
+               return err;
+       }
+
+       for (i = 0; i < iface->num_bss; i++) {
+               err = hostapd_switch_channel(iface->bss[i], &csa_settings);
+               if (err)
+                       break;
+       }
+
+       if (err) {
+               wpa_printf(MSG_WARNING, "DFS failed to schedule CSA (%d) - trying fallback",
+                          err);
+               iface->freq = channel->freq;
+               iface->conf->channel = channel->chan;
+               iface->conf->secondary_channel = secondary_channel;
+               iface->conf->vht_oper_centr_freq_seg0_idx =
+                       vht_oper_centr_freq_seg0_idx;
+               iface->conf->vht_oper_centr_freq_seg1_idx =
+                       vht_oper_centr_freq_seg1_idx;
+
+               hostapd_disable_iface(iface);
+               hostapd_enable_iface(iface);
+               return 0;
+       }
+
+       /* Channel configuration will be updated once CSA completes and
+        * ch_switch_notify event is received */
+
+       wpa_printf(MSG_DEBUG, "DFS waiting channel switch event");
+       return 0;
+}
+
+
+int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+                              int ht_enabled, int chan_offset, int chan_width,
+                              int cf1, int cf2)
+{
+       int res;
+
+       wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_RADAR_DETECTED
+               "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+               freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+
+       /* Proceed only if DFS is not offloaded to the driver */
+       if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+               return 0;
+
+       if (!iface->conf->ieee80211h)
+               return 0;
+
+       /* mark radar frequency as invalid */
+       set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
+                     cf1, cf2, HOSTAPD_CHAN_DFS_UNAVAILABLE);
+
+       /* Skip if reported radar event not overlapped our channels */
+       res = dfs_are_channels_overlapped(iface, freq, chan_width, cf1, cf2);
+       if (!res)
+               return 0;
+
+       /* radar detected while operating, switch the channel. */
+       res = hostapd_dfs_start_channel_switch(iface);
+
+       return res;
+}
+
+
+int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
+                            int ht_enabled, int chan_offset, int chan_width,
+                            int cf1, int cf2)
+{
+       wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_NOP_FINISHED
+               "freq=%d ht_enabled=%d chan_offset=%d chan_width=%d cf1=%d cf2=%d",
+               freq, ht_enabled, chan_offset, chan_width, cf1, cf2);
+
+       /* Proceed only if DFS is not offloaded to the driver */
+       if (iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)
+               return 0;
+
+       /* TODO add correct implementation here */
+       set_dfs_state(iface, freq, ht_enabled, chan_offset, chan_width,
+                     cf1, cf2, HOSTAPD_CHAN_DFS_USABLE);
+       return 0;
+}
+
+
+int hostapd_is_dfs_required(struct hostapd_iface *iface)
+{
+       int n_chans, n_chans1, start_chan_idx, start_chan_idx1, res;
+
+       if (!iface->conf->ieee80211h || !iface->current_mode ||
+           iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
+               return 0;
+
+       /* Get start (first) channel for current configuration */
+       start_chan_idx = dfs_get_start_chan_idx(iface, &start_chan_idx1);
+       if (start_chan_idx == -1)
+               return -1;
+
+       /* Get number of used channels, depend on width */
+       n_chans = dfs_get_used_n_chans(iface, &n_chans1);
+
+       /* Check if any of configured channels require DFS */
+       res = dfs_check_chans_radar(iface, start_chan_idx, n_chans);
+       if (res)
+               return res;
+       if (start_chan_idx1 >= 0 && n_chans1 > 0)
+               res = dfs_check_chans_radar(iface, start_chan_idx1, n_chans1);
+       return res;
+}
+
+
+int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+                         int ht_enabled, int chan_offset, int chan_width,
+                         int cf1, int cf2)
+{
+       wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, DFS_EVENT_CAC_START
+               "freq=%d chan=%d chan_offset=%d width=%d seg0=%d "
+               "seg1=%d cac_time=%ds",
+               freq, (freq - 5000) / 5, chan_offset, chan_width, cf1, cf2, 60);
+       iface->cac_started = 1;
+       return 0;
+}
+
+
+/*
+ * Main DFS handler for offloaded case.
+ * 2 - continue channel/AP setup for non-DFS channel
+ * 1 - continue channel/AP setup for DFS channel
+ * 0 - channel/AP setup will be continued after CAC
+ * -1 - hit critical error
+ */
+int hostapd_handle_dfs_offload(struct hostapd_iface *iface)
+{
+       wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d",
+                  __func__, iface->cac_started);
+
+       /*
+        * If DFS has already been started, then we are being called from a
+        * callback to continue AP/channel setup. Reset the CAC start flag and
+        * return.
+        */
+       if (iface->cac_started) {
+               wpa_printf(MSG_DEBUG, "%s: iface->cac_started: %d",
+                          __func__, iface->cac_started);
+               iface->cac_started = 0;
+               return 1;
+       }
+
+       if (ieee80211_is_dfs(iface->freq)) {
+               wpa_printf(MSG_DEBUG, "%s: freq %d MHz requires DFS",
+                          __func__, iface->freq);
+               return 0;
+       }
+
+       wpa_printf(MSG_DEBUG,
+                  "%s: freq %d MHz does not require DFS. Continue channel/AP setup",
+                  __func__, iface->freq);
+       return 2;
+}
diff --git a/src/ap/dfs.h b/src/ap/dfs.h
new file mode 100644 (file)
index 0000000..be8c0e6
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * DFS - Dynamic Frequency Selection
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#ifndef DFS_H
+#define DFS_H
+
+int hostapd_handle_dfs(struct hostapd_iface *iface);
+
+int hostapd_dfs_complete_cac(struct hostapd_iface *iface, int success, int freq,
+                            int ht_enabled, int chan_offset, int chan_width,
+                            int cf1, int cf2);
+int hostapd_dfs_radar_detected(struct hostapd_iface *iface, int freq,
+                              int ht_enabled,
+                              int chan_offset, int chan_width,
+                              int cf1, int cf2);
+int hostapd_dfs_nop_finished(struct hostapd_iface *iface, int freq,
+                            int ht_enabled,
+                            int chan_offset, int chan_width, int cf1, int cf2);
+int hostapd_is_dfs_required(struct hostapd_iface *iface);
+int hostapd_dfs_start_cac(struct hostapd_iface *iface, int freq,
+                         int ht_enabled, int chan_offset, int chan_width,
+                         int cf1, int cf2);
+int hostapd_handle_dfs_offload(struct hostapd_iface *iface);
+
+#endif /* DFS_H */
diff --git a/src/ap/dhcp_snoop.c b/src/ap/dhcp_snoop.c
new file mode 100644 (file)
index 0000000..3a77225
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * DHCP snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+
+#include "utils/common.h"
+#include "l2_packet/l2_packet.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "x_snoop.h"
+#include "dhcp_snoop.h"
+
+struct bootp_pkt {
+       struct iphdr iph;
+       struct udphdr udph;
+       u8 op;
+       u8 htype;
+       u8 hlen;
+       u8 hops;
+       be32 xid;
+       be16 secs;
+       be16 flags;
+       be32 client_ip;
+       be32 your_ip;
+       be32 server_ip;
+       be32 relay_ip;
+       u8 hw_addr[16];
+       u8 serv_name[64];
+       u8 boot_file[128];
+       u8 exten[312];
+} STRUCT_PACKED;
+
+#define DHCPACK        5
+static const u8 ic_bootp_cookie[] = { 99, 130, 83, 99 };
+
+
+static const char * ipaddr_str(u32 addr)
+{
+       static char buf[17];
+
+       os_snprintf(buf, sizeof(buf), "%u.%u.%u.%u",
+                   (addr >> 24) & 0xff, (addr >> 16) & 0xff,
+                   (addr >> 8) & 0xff, addr & 0xff);
+       return buf;
+}
+
+
+static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf,
+                       size_t len)
+{
+       struct hostapd_data *hapd = ctx;
+       const struct bootp_pkt *b;
+       struct sta_info *sta;
+       int exten_len;
+       const u8 *end, *pos;
+       int res, msgtype = 0, prefixlen = 32;
+       u32 subnet_mask = 0;
+       u16 tot_len;
+
+       exten_len = len - ETH_HLEN - (sizeof(*b) - sizeof(b->exten));
+       if (exten_len < 4)
+               return;
+
+       b = (const struct bootp_pkt *) &buf[ETH_HLEN];
+       tot_len = ntohs(b->iph.tot_len);
+       if (tot_len > (unsigned int) (len - ETH_HLEN))
+               return;
+
+       if (os_memcmp(b->exten, ic_bootp_cookie, ARRAY_SIZE(ic_bootp_cookie)))
+               return;
+
+       /* Parse DHCP options */
+       end = (const u8 *) b + tot_len;
+       pos = &b->exten[4];
+       while (pos < end && *pos != 0xff) {
+               const u8 *opt = pos++;
+
+               if (*opt == 0) /* padding */
+                       continue;
+
+               pos += *pos + 1;
+               if (pos >= end)
+                       break;
+
+               switch (*opt) {
+               case 1:  /* subnet mask */
+                       if (opt[1] == 4)
+                               subnet_mask = WPA_GET_BE32(&opt[2]);
+                       if (subnet_mask == 0)
+                               return;
+                       while (!(subnet_mask & 0x1)) {
+                               subnet_mask >>= 1;
+                               prefixlen--;
+                       }
+                       break;
+               case 53: /* message type */
+                       if (opt[1])
+                               msgtype = opt[2];
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       if (msgtype == DHCPACK) {
+               if (b->your_ip == 0)
+                       return;
+
+               /* DHCPACK for DHCPREQUEST */
+               sta = ap_get_sta(hapd, b->hw_addr);
+               if (!sta)
+                       return;
+
+               wpa_printf(MSG_DEBUG, "dhcp_snoop: Found DHCPACK for " MACSTR
+                          " @ IPv4 address %s/%d",
+                          MAC2STR(sta->addr), ipaddr_str(ntohl(b->your_ip)),
+                          prefixlen);
+
+               if (sta->ipaddr == b->your_ip)
+                       return;
+
+               if (sta->ipaddr != 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "dhcp_snoop: Removing IPv4 address %s from the ip neigh table",
+                                  ipaddr_str(be_to_host32(sta->ipaddr)));
+                       hostapd_drv_br_delete_ip_neigh(hapd, 4,
+                                                      (u8 *) &sta->ipaddr);
+               }
+
+               res = hostapd_drv_br_add_ip_neigh(hapd, 4, (u8 *) &b->your_ip,
+                                                 prefixlen, sta->addr);
+               if (res) {
+                       wpa_printf(MSG_DEBUG,
+                                  "dhcp_snoop: Adding ip neigh table failed: %d",
+                                  res);
+                       return;
+               }
+               sta->ipaddr = b->your_ip;
+       }
+
+       if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) {
+               for (sta = hapd->sta_list; sta; sta = sta->next) {
+                       if (!(sta->flags & WLAN_STA_AUTHORIZED))
+                               continue;
+                       x_snoop_mcast_to_ucast_convert_send(hapd, sta,
+                                                           (u8 *) buf, len);
+               }
+       }
+}
+
+
+int dhcp_snoop_init(struct hostapd_data *hapd)
+{
+       hapd->sock_dhcp = x_snoop_get_l2_packet(hapd, handle_dhcp,
+                                               L2_PACKET_FILTER_DHCP);
+       if (hapd->sock_dhcp == NULL) {
+               wpa_printf(MSG_DEBUG,
+                          "dhcp_snoop: Failed to initialize L2 packet processing for DHCP packet: %s",
+                          strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}
+
+
+void dhcp_snoop_deinit(struct hostapd_data *hapd)
+{
+       l2_packet_deinit(hapd->sock_dhcp);
+}
diff --git a/src/ap/dhcp_snoop.h b/src/ap/dhcp_snoop.h
new file mode 100644 (file)
index 0000000..93d0050
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * DHCP snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DHCP_SNOOP_H
+#define DHCP_SNOOP_H
+
+#ifdef CONFIG_PROXYARP
+
+int dhcp_snoop_init(struct hostapd_data *hapd);
+void dhcp_snoop_deinit(struct hostapd_data *hapd);
+
+#else /* CONFIG_PROXYARP */
+
+static inline int dhcp_snoop_init(struct hostapd_data *hapd)
+{
+       return 0;
+}
+
+static inline void dhcp_snoop_deinit(struct hostapd_data *hapd)
+{
+}
+
+#endif /* CONFIG_PROXYARP */
+
+#endif /* DHCP_SNOOP_H */
index 7ea30ef..a0adc67 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / Callback functions for driver wrappers
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -9,6 +9,7 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "utils/eloop.h"
 #include "radius/radius.h"
 #include "drivers/driver.h"
 #include "common/ieee802_11_defs.h"
@@ -29,6 +30,8 @@
 #include "ap_drv_ops.h"
 #include "ap_config.h"
 #include "hw_features.h"
+#include "dfs.h"
+#include "beacon.h"
 
 
 int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
@@ -45,6 +48,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
 #endif /* CONFIG_IEEE80211R */
        u16 reason = WLAN_REASON_UNSPECIFIED;
        u16 status = WLAN_STATUS_SUCCESS;
+       const u8 *p2p_dev_addr = NULL;
 
        if (addr == NULL) {
                /*
@@ -76,6 +80,12 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
                ie = elems.wpa_ie - 2;
                ielen = elems.wpa_ie_len + 2;
                wpa_printf(MSG_DEBUG, "STA included WPA IE in (Re)AssocReq");
+#ifdef CONFIG_HS20
+       } else if (elems.osen) {
+               ie = elems.osen - 2;
+               ielen = elems.osen_len + 2;
+               wpa_printf(MSG_DEBUG, "STA included OSEN IE in (Re)AssocReq");
+#endif /* CONFIG_HS20 */
        } else {
                ie = NULL;
                ielen = 0;
@@ -108,9 +118,36 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
                wpabuf_free(sta->p2p_ie);
                sta->p2p_ie = ieee802_11_vendor_ie_concat(req_ies, req_ies_len,
                                                          P2P_IE_VENDOR_TYPE);
+               if (sta->p2p_ie)
+                       p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
        }
 #endif /* CONFIG_P2P */
 
+#ifdef CONFIG_IEEE80211N
+#ifdef NEED_AP_MLME
+       if (elems.ht_capabilities &&
+           elems.ht_capabilities_len >=
+           sizeof(struct ieee80211_ht_capabilities) &&
+           (hapd->iface->conf->ht_capab &
+            HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) {
+               struct ieee80211_ht_capabilities *ht_cap =
+                       (struct ieee80211_ht_capabilities *)
+                       elems.ht_capabilities;
+
+               if (le_to_host16(ht_cap->ht_capabilities_info) &
+                   HT_CAP_INFO_40MHZ_INTOLERANT)
+                       ht40_intolerant_add(hapd->iface, sta);
+       }
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_IEEE80211N */
+
+#ifdef CONFIG_INTERWORKING
+       if (elems.ext_capab && elems.ext_capab_len > 4) {
+               if (elems.ext_capab[4] & 0x01)
+                       sta->qos_map_enabled = 1;
+       }
+#endif /* CONFIG_INTERWORKING */
+
 #ifdef CONFIG_HS20
        wpabuf_free(sta->hs20_ie);
        if (elems.hs20 && elems.hs20_len > 4) {
@@ -156,7 +193,8 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
 
                if (sta->wpa_sm == NULL)
                        sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
-                                                       sta->addr);
+                                                       sta->addr,
+                                                       p2p_dev_addr);
                if (sta->wpa_sm == NULL) {
                        wpa_printf(MSG_ERROR, "Failed to initialize WPA state "
                                   "machine");
@@ -269,6 +307,29 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
                        sta->flags |= WLAN_STA_MAYBE_WPS;
                wpabuf_free(wps);
 #endif /* CONFIG_WPS */
+#ifdef CONFIG_HS20
+       } else if (hapd->conf->osen) {
+               if (elems.osen == NULL) {
+                       hostapd_logger(
+                               hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+                               HOSTAPD_LEVEL_INFO,
+                               "No HS 2.0 OSEN element in association request");
+                       return WLAN_STATUS_INVALID_IE;
+               }
+
+               wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association");
+               if (sta->wpa_sm == NULL)
+                       sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+                                                       sta->addr, NULL);
+               if (sta->wpa_sm == NULL) {
+                       wpa_printf(MSG_WARNING, "Failed to initialize WPA "
+                                  "state machine");
+                       return WLAN_STATUS_UNSPECIFIED_FAILURE;
+               }
+               if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
+                                     elems.osen - 2, elems.osen_len + 2) < 0)
+                       return WLAN_STATUS_INVALID_IE;
+#endif /* CONFIG_HS20 */
        }
 #ifdef CONFIG_WPS
 skip_wpa_check:
@@ -279,6 +340,9 @@ skip_wpa_check:
                                        sta->auth_alg, req_ies, req_ies_len);
 
        hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
+
+       if (sta->auth_alg == WLAN_AUTH_FT)
+               ap_sta_set_authorized(hapd, sta, 1);
 #else /* CONFIG_IEEE80211R */
        /* Keep compiler silent about unused variables */
        if (status) {
@@ -287,6 +351,9 @@ skip_wpa_check:
 
        new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0;
        sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC;
+       sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+
+       hostapd_set_sta_flags(hapd, sta);
 
        if (reassoc && (sta->auth_alg == WLAN_AUTH_FT))
                wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT);
@@ -343,10 +410,6 @@ void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr)
                return;
        }
 
-#if defined TIZEN_EXT
-       wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR,
-       MAC2STR(sta->addr));
-#endif
        ap_sta_set_authorized(hapd, sta, 0);
        sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
        wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
@@ -373,14 +436,16 @@ void hostapd_event_sta_low_ack(struct hostapd_data *hapd, const u8 *addr)
 
 
 void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
-                            int offset)
+                            int offset, int width, int cf1, int cf2)
 {
 #ifdef NEED_AP_MLME
-       int channel;
+       int channel, chwidth, seg0_idx = 0, seg1_idx = 0, is_dfs;
 
        hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
-                      HOSTAPD_LEVEL_INFO, "driver had channel switch: "
-                      "freq=%d, ht=%d, offset=%d", freq, ht, offset);
+                      HOSTAPD_LEVEL_INFO,
+                      "driver had channel switch: freq=%d, ht=%d, offset=%d, width=%d (%s), cf1=%d, cf2=%d",
+                      freq, ht, offset, width, channel_width_to_string(width),
+                      cf1, cf2);
 
        hapd->iface->freq = freq;
 
@@ -392,9 +457,59 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
                return;
        }
 
+       switch (width) {
+       case CHAN_WIDTH_80:
+               chwidth = VHT_CHANWIDTH_80MHZ;
+               break;
+       case CHAN_WIDTH_80P80:
+               chwidth = VHT_CHANWIDTH_80P80MHZ;
+               break;
+       case CHAN_WIDTH_160:
+               chwidth = VHT_CHANWIDTH_160MHZ;
+               break;
+       case CHAN_WIDTH_20_NOHT:
+       case CHAN_WIDTH_20:
+       case CHAN_WIDTH_40:
+       default:
+               chwidth = VHT_CHANWIDTH_USE_HT;
+               break;
+       }
+
+       switch (hapd->iface->current_mode->mode) {
+       case HOSTAPD_MODE_IEEE80211A:
+               if (cf1 > 5000)
+                       seg0_idx = (cf1 - 5000) / 5;
+               if (cf2 > 5000)
+                       seg1_idx = (cf2 - 5000) / 5;
+               break;
+       default:
+               seg0_idx = hostapd_hw_get_channel(hapd, cf1);
+               seg1_idx = hostapd_hw_get_channel(hapd, cf2);
+               break;
+       }
+
        hapd->iconf->channel = channel;
        hapd->iconf->ieee80211n = ht;
+       if (!ht)
+               hapd->iconf->ieee80211ac = 0;
        hapd->iconf->secondary_channel = offset;
+       hapd->iconf->vht_oper_chwidth = chwidth;
+       hapd->iconf->vht_oper_centr_freq_seg0_idx = seg0_idx;
+       hapd->iconf->vht_oper_centr_freq_seg1_idx = seg1_idx;
+
+       is_dfs = ieee80211_is_dfs(freq);
+
+       if (hapd->csa_in_progress &&
+           freq == hapd->cs_freq_params.freq) {
+               hostapd_cleanup_cs_params(hapd);
+               ieee802_11_set_beacon(hapd);
+
+               wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
+                       "freq=%d dfs=%d", freq, is_dfs);
+       } else if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) {
+               wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
+                       "freq=%d dfs=%d", freq, is_dfs);
+       }
 #endif /* NEED_AP_MLME */
 }
 
@@ -415,6 +530,51 @@ void hostapd_event_connect_failed_reason(struct hostapd_data *hapd,
 }
 
 
+#ifdef CONFIG_ACS
+static void hostapd_acs_channel_selected(struct hostapd_data *hapd,
+                                        u8 pri_channel, u8 sec_channel)
+{
+       int channel;
+       int ret;
+
+       if (hapd->iconf->channel) {
+               wpa_printf(MSG_INFO, "ACS: Channel was already set to %d",
+                          hapd->iconf->channel);
+               return;
+       }
+
+       hapd->iface->freq = hostapd_hw_get_freq(hapd, pri_channel);
+
+       channel = pri_channel;
+       if (!channel) {
+               hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
+                              HOSTAPD_LEVEL_WARNING,
+                              "driver switched to bad channel");
+               return;
+       }
+
+       hapd->iconf->channel = channel;
+
+       if (sec_channel == 0)
+               hapd->iconf->secondary_channel = 0;
+       else if (sec_channel < pri_channel)
+               hapd->iconf->secondary_channel = -1;
+       else if (sec_channel > pri_channel)
+               hapd->iconf->secondary_channel = 1;
+       else {
+               wpa_printf(MSG_ERROR, "Invalid secondary channel!");
+               return;
+       }
+
+       ret = hostapd_acs_completed(hapd->iface, 0);
+       if (ret) {
+               wpa_printf(MSG_ERROR,
+                          "ACS: Possibly channel configuration is invalid");
+       }
+}
+#endif /* CONFIG_ACS */
+
+
 int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
                         const u8 *bssid, const u8 *ie, size_t ie_len,
                         int ssi_signal)
@@ -485,7 +645,7 @@ static void hostapd_notif_auth(struct hostapd_data *hapd,
                sta->auth_alg = WLAN_AUTH_FT;
                if (sta->wpa_sm == NULL)
                        sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
-                                                       sta->addr);
+                                                       sta->addr, NULL);
                if (sta->wpa_sm == NULL) {
                        wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA "
                                   "state machine");
@@ -506,39 +666,48 @@ fail:
 
 
 static void hostapd_action_rx(struct hostapd_data *hapd,
-                             struct rx_action *action)
+                             struct rx_mgmt *drv_mgmt)
 {
+       struct ieee80211_mgmt *mgmt;
        struct sta_info *sta;
+       size_t plen __maybe_unused;
+       u16 fc;
+
+       if (drv_mgmt->frame_len < 24 + 1)
+               return;
+
+       plen = drv_mgmt->frame_len - 24 - 1;
+
+       mgmt = (struct ieee80211_mgmt *) drv_mgmt->frame;
+       fc = le_to_host16(mgmt->frame_control);
+       if (WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION)
+               return; /* handled by the driver */
 
         wpa_printf(MSG_DEBUG, "RX_ACTION cat %d action plen %d",
-                  action->category, (int) action->len);
+                  mgmt->u.action.category, (int) plen);
 
-       sta = ap_get_sta(hapd, action->sa);
+       sta = ap_get_sta(hapd, mgmt->sa);
        if (sta == NULL) {
                wpa_printf(MSG_DEBUG, "%s: station not found", __func__);
                return;
        }
 #ifdef CONFIG_IEEE80211R
-       if (action->category == WLAN_ACTION_FT) {
-               wpa_printf(MSG_DEBUG, "%s: FT_ACTION length %d",
-                          __func__, (int) action->len);
-               wpa_ft_action_rx(sta->wpa_sm, action->data, action->len);
+       if (mgmt->u.action.category == WLAN_ACTION_FT) {
+               const u8 *payload = drv_mgmt->frame + 24 + 1;
+               wpa_ft_action_rx(sta->wpa_sm, payload, plen);
        }
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_IEEE80211W
-       if (action->category == WLAN_ACTION_SA_QUERY && action->len >= 4) {
-               wpa_printf(MSG_DEBUG, "%s: SA_QUERY_ACTION length %d",
-                          __func__, (int) action->len);
-               ieee802_11_sa_query_action(hapd, action->sa,
-                                          *(action->data + 1),
-                                          action->data + 2);
+       if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY && plen >= 4) {
+               ieee802_11_sa_query_action(
+                       hapd, mgmt->sa,
+                       mgmt->u.action.u.sa_query_resp.action,
+                       mgmt->u.action.u.sa_query_resp.trans_id);
        }
 #endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_WNM
-       if (action->category == WLAN_ACTION_WNM) {
-               wpa_printf(MSG_DEBUG, "%s: WNM_ACTION length %d",
-                          __func__, (int) action->len);
-               ieee802_11_rx_wnm_action_ap(hapd, action);
+       if (mgmt->u.action.category == WLAN_ACTION_WNM) {
+               ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len);
        }
 #endif /* CONFIG_WNM */
 }
@@ -580,17 +749,32 @@ static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd,
 }
 
 
-static void hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
+static int hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
 {
        struct hostapd_iface *iface = hapd->iface;
        const struct ieee80211_hdr *hdr;
        const u8 *bssid;
        struct hostapd_frame_info fi;
+       int ret;
+
+#ifdef CONFIG_TESTING_OPTIONS
+       if (hapd->ext_mgmt_frame_handling) {
+               size_t hex_len = 2 * rx_mgmt->frame_len + 1;
+               char *hex = os_malloc(hex_len);
+               if (hex) {
+                       wpa_snprintf_hex(hex, hex_len, rx_mgmt->frame,
+                                        rx_mgmt->frame_len);
+                       wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-RX %s", hex);
+                       os_free(hex);
+               }
+               return 1;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
 
        hdr = (const struct ieee80211_hdr *) rx_mgmt->frame;
        bssid = get_hdr_bssid(hdr, rx_mgmt->frame_len);
        if (bssid == NULL)
-               return;
+               return 0;
 
        hapd = get_hapd_bssid(iface, bssid);
        if (hapd == NULL) {
@@ -605,7 +789,7 @@ static void hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
                    WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON)
                        hapd = iface->bss[0];
                else
-                       return;
+                       return 0;
        }
 
        os_memset(&fi, 0, sizeof(fi));
@@ -614,53 +798,25 @@ static void hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt)
 
        if (hapd == HAPD_BROADCAST) {
                size_t i;
-               for (i = 0; i < iface->num_bss; i++)
-                       ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame,
-                                       rx_mgmt->frame_len, &fi);
+               ret = 0;
+               for (i = 0; i < iface->num_bss; i++) {
+                       /* if bss is set, driver will call this function for
+                        * each bss individually. */
+                       if (rx_mgmt->drv_priv &&
+                           (iface->bss[i]->drv_priv != rx_mgmt->drv_priv))
+                               continue;
+
+                       if (ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame,
+                                           rx_mgmt->frame_len, &fi) > 0)
+                               ret = 1;
+               }
        } else
-               ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len, &fi);
+               ret = ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len,
+                                     &fi);
 
        random_add_randomness(&fi, sizeof(fi));
-}
-
-
-static void hostapd_rx_action(struct hostapd_data *hapd,
-                             struct rx_action *rx_action)
-{
-       struct rx_mgmt rx_mgmt;
-       u8 *buf;
-       struct ieee80211_hdr *hdr;
-
-       wpa_printf(MSG_DEBUG, "EVENT_RX_ACTION DA=" MACSTR " SA=" MACSTR
-                  " BSSID=" MACSTR " category=%u",
-                  MAC2STR(rx_action->da), MAC2STR(rx_action->sa),
-                  MAC2STR(rx_action->bssid), rx_action->category);
-       wpa_hexdump(MSG_MSGDUMP, "Received action frame contents",
-                   rx_action->data, rx_action->len);
 
-       buf = os_zalloc(24 + 1 + rx_action->len);
-       if (buf == NULL)
-               return;
-       hdr = (struct ieee80211_hdr *) buf;
-       hdr->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-                                         WLAN_FC_STYPE_ACTION);
-       if (rx_action->category == WLAN_ACTION_SA_QUERY) {
-               /*
-                * Assume frame was protected; it would have been dropped if
-                * not.
-                */
-               hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
-       }
-       os_memcpy(hdr->addr1, rx_action->da, ETH_ALEN);
-       os_memcpy(hdr->addr2, rx_action->sa, ETH_ALEN);
-       os_memcpy(hdr->addr3, rx_action->bssid, ETH_ALEN);
-       buf[24] = rx_action->category;
-       os_memcpy(buf + 24 + 1, rx_action->data, rx_action->len);
-       os_memset(&rx_mgmt, 0, sizeof(rx_mgmt));
-       rx_mgmt.frame = buf;
-       rx_mgmt.frame_len = 24 + 1 + rx_action->len;
-       hostapd_mgmt_rx(hapd, &rx_mgmt);
-       os_free(buf);
+       return ret;
 }
 
 
@@ -719,6 +875,181 @@ static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src,
 }
 
 
+static struct hostapd_channel_data * hostapd_get_mode_channel(
+       struct hostapd_iface *iface, unsigned int freq)
+{
+       int i;
+       struct hostapd_channel_data *chan;
+
+       for (i = 0; i < iface->current_mode->num_channels; i++) {
+               chan = &iface->current_mode->channels[i];
+               if (!chan)
+                       return NULL;
+               if ((unsigned int) chan->freq == freq)
+                       return chan;
+       }
+
+       return NULL;
+}
+
+
+static void hostapd_update_nf(struct hostapd_iface *iface,
+                             struct hostapd_channel_data *chan,
+                             struct freq_survey *survey)
+{
+       if (!iface->chans_surveyed) {
+               chan->min_nf = survey->nf;
+               iface->lowest_nf = survey->nf;
+       } else {
+               if (dl_list_empty(&chan->survey_list))
+                       chan->min_nf = survey->nf;
+               else if (survey->nf < chan->min_nf)
+                       chan->min_nf = survey->nf;
+               if (survey->nf < iface->lowest_nf)
+                       iface->lowest_nf = survey->nf;
+       }
+}
+
+
+static void hostapd_single_channel_get_survey(struct hostapd_iface *iface,
+                                             struct survey_results *survey_res)
+{
+       struct hostapd_channel_data *chan;
+       struct freq_survey *survey;
+       u64 divisor, dividend;
+
+       survey = dl_list_first(&survey_res->survey_list, struct freq_survey,
+                              list);
+       if (!survey || !survey->freq)
+               return;
+
+       chan = hostapd_get_mode_channel(iface, survey->freq);
+       if (!chan || chan->flag & HOSTAPD_CHAN_DISABLED)
+               return;
+
+       wpa_printf(MSG_DEBUG, "Single Channel Survey: (freq=%d channel_time=%ld channel_time_busy=%ld)",
+                  survey->freq,
+                  (unsigned long int) survey->channel_time,
+                  (unsigned long int) survey->channel_time_busy);
+
+       if (survey->channel_time > iface->last_channel_time &&
+           survey->channel_time > survey->channel_time_busy) {
+               dividend = survey->channel_time_busy -
+                       iface->last_channel_time_busy;
+               divisor = survey->channel_time - iface->last_channel_time;
+
+               iface->channel_utilization = dividend * 255 / divisor;
+               wpa_printf(MSG_DEBUG, "Channel Utilization: %d",
+                          iface->channel_utilization);
+       }
+       iface->last_channel_time = survey->channel_time;
+       iface->last_channel_time_busy = survey->channel_time_busy;
+}
+
+
+static void hostapd_event_get_survey(struct hostapd_data *hapd,
+                                    struct survey_results *survey_results)
+{
+       struct hostapd_iface *iface = hapd->iface;
+       struct freq_survey *survey, *tmp;
+       struct hostapd_channel_data *chan;
+
+       if (dl_list_empty(&survey_results->survey_list)) {
+               wpa_printf(MSG_DEBUG, "No survey data received");
+               return;
+       }
+
+       if (survey_results->freq_filter) {
+               hostapd_single_channel_get_survey(iface, survey_results);
+               return;
+       }
+
+       dl_list_for_each_safe(survey, tmp, &survey_results->survey_list,
+                             struct freq_survey, list) {
+               chan = hostapd_get_mode_channel(iface, survey->freq);
+               if (!chan)
+                       continue;
+               if (chan->flag & HOSTAPD_CHAN_DISABLED)
+                       continue;
+
+               dl_list_del(&survey->list);
+               dl_list_add_tail(&chan->survey_list, &survey->list);
+
+               hostapd_update_nf(iface, chan, survey);
+
+               iface->chans_surveyed++;
+       }
+}
+
+
+#ifdef NEED_AP_MLME
+
+static void hostapd_event_iface_unavailable(struct hostapd_data *hapd)
+{
+       wpa_printf(MSG_DEBUG, "Interface %s is unavailable -- stopped",
+                  hapd->conf->iface);
+
+       if (hapd->csa_in_progress) {
+               wpa_printf(MSG_INFO, "CSA failed (%s was stopped)",
+                          hapd->conf->iface);
+               hostapd_switch_channel_fallback(hapd->iface,
+                                               &hapd->cs_freq_params);
+       }
+}
+
+
+static void hostapd_event_dfs_radar_detected(struct hostapd_data *hapd,
+                                            struct dfs_event *radar)
+{
+       wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq);
+       hostapd_dfs_radar_detected(hapd->iface, radar->freq, radar->ht_enabled,
+                                  radar->chan_offset, radar->chan_width,
+                                  radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_cac_finished(struct hostapd_data *hapd,
+                                          struct dfs_event *radar)
+{
+       wpa_printf(MSG_DEBUG, "DFS CAC finished on %d MHz", radar->freq);
+       hostapd_dfs_complete_cac(hapd->iface, 1, radar->freq, radar->ht_enabled,
+                                radar->chan_offset, radar->chan_width,
+                                radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_cac_aborted(struct hostapd_data *hapd,
+                                         struct dfs_event *radar)
+{
+       wpa_printf(MSG_DEBUG, "DFS CAC aborted on %d MHz", radar->freq);
+       hostapd_dfs_complete_cac(hapd->iface, 0, radar->freq, radar->ht_enabled,
+                                radar->chan_offset, radar->chan_width,
+                                radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_nop_finished(struct hostapd_data *hapd,
+                                          struct dfs_event *radar)
+{
+       wpa_printf(MSG_DEBUG, "DFS NOP finished on %d MHz", radar->freq);
+       hostapd_dfs_nop_finished(hapd->iface, radar->freq, radar->ht_enabled,
+                                radar->chan_offset, radar->chan_width,
+                                radar->cf1, radar->cf2);
+}
+
+
+static void hostapd_event_dfs_cac_started(struct hostapd_data *hapd,
+                                         struct dfs_event *radar)
+{
+       wpa_printf(MSG_DEBUG, "DFS offload CAC started on %d MHz", radar->freq);
+       hostapd_dfs_start_cac(hapd->iface, radar->freq, radar->ht_enabled,
+                             radar->chan_offset, radar->chan_width,
+                             radar->cf1, radar->cf2);
+}
+
+#endif /* NEED_AP_MLME */
+
+
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                          union wpa_event_data *data)
 {
@@ -752,12 +1083,6 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                if (hapd->iface->scan_cb)
                        hapd->iface->scan_cb(hapd->iface);
                break;
-#ifdef CONFIG_IEEE80211R
-       case EVENT_FT_RRB_RX:
-               wpa_ft_rrb_rx(hapd->wpa_auth, data->ft_rrb_rx.src,
-                             data->ft_rrb_rx.data, data->ft_rrb_rx.data_len);
-               break;
-#endif /* CONFIG_IEEE80211R */
        case EVENT_WPS_BUTTON_PUSHED:
                hostapd_wps_button_pushed(hapd, NULL);
                break;
@@ -792,10 +1117,16 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                                            data->rx_from_unknown.addr,
                                            data->rx_from_unknown.wds);
                break;
+#endif /* NEED_AP_MLME */
        case EVENT_RX_MGMT:
-               hostapd_mgmt_rx(hapd, &data->rx_mgmt);
-               break;
+               if (!data->rx_mgmt.frame)
+                       break;
+#ifdef NEED_AP_MLME
+               if (hostapd_mgmt_rx(hapd, &data->rx_mgmt) > 0)
+                       break;
 #endif /* NEED_AP_MLME */
+               hostapd_action_rx(hapd, &data->rx_mgmt);
+               break;
        case EVENT_RX_PROBE_REQ:
                if (data->rx_probe_req.sa == NULL ||
                    data->rx_probe_req.ie == NULL)
@@ -816,6 +1147,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                                       data->eapol_rx.data_len);
                break;
        case EVENT_ASSOC:
+               if (!data)
+                       return;
                hostapd_notif_assoc(hapd, data->assoc_info.addr,
                                    data->assoc_info.req_ies,
                                    data->assoc_info.req_ies_len,
@@ -834,15 +1167,6 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                        break;
                hostapd_event_sta_low_ack(hapd, data->low_ack.addr);
                break;
-       case EVENT_RX_ACTION:
-               if (data->rx_action.da == NULL || data->rx_action.sa == NULL ||
-                   data->rx_action.bssid == NULL)
-                       break;
-#ifdef NEED_AP_MLME
-               hostapd_rx_action(hapd, &data->rx_action);
-#endif /* NEED_AP_MLME */
-               hostapd_action_rx(hapd, &data->rx_action);
-               break;
        case EVENT_AUTH:
                hostapd_notif_auth(hapd, &data->auth);
                break;
@@ -851,7 +1175,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                        break;
                hostapd_event_ch_switch(hapd, data->ch_switch.freq,
                                        data->ch_switch.ht_enabled,
-                                       data->ch_switch.ch_offset);
+                                       data->ch_switch.ch_offset,
+                                       data->ch_switch.ch_width,
+                                       data->ch_switch.cf1,
+                                       data->ch_switch.cf2);
                break;
        case EVENT_CONNECT_FAILED_REASON:
                if (!data)
@@ -860,6 +1187,72 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                        hapd, data->connect_failed_reason.addr,
                        data->connect_failed_reason.code);
                break;
+       case EVENT_SURVEY:
+               hostapd_event_get_survey(hapd, &data->survey_results);
+               break;
+#ifdef NEED_AP_MLME
+       case EVENT_INTERFACE_UNAVAILABLE:
+               hostapd_event_iface_unavailable(hapd);
+               break;
+       case EVENT_DFS_RADAR_DETECTED:
+               if (!data)
+                       break;
+               hostapd_event_dfs_radar_detected(hapd, &data->dfs_event);
+               break;
+       case EVENT_DFS_CAC_FINISHED:
+               if (!data)
+                       break;
+               hostapd_event_dfs_cac_finished(hapd, &data->dfs_event);
+               break;
+       case EVENT_DFS_CAC_ABORTED:
+               if (!data)
+                       break;
+               hostapd_event_dfs_cac_aborted(hapd, &data->dfs_event);
+               break;
+       case EVENT_DFS_NOP_FINISHED:
+               if (!data)
+                       break;
+               hostapd_event_dfs_nop_finished(hapd, &data->dfs_event);
+               break;
+       case EVENT_CHANNEL_LIST_CHANGED:
+               /* channel list changed (regulatory?), update channel list */
+               /* TODO: check this. hostapd_get_hw_features() initializes
+                * too much stuff. */
+               /* hostapd_get_hw_features(hapd->iface); */
+               hostapd_channel_list_updated(
+                       hapd->iface, data->channel_list_changed.initiator);
+               break;
+       case EVENT_DFS_CAC_STARTED:
+               if (!data)
+                       break;
+               hostapd_event_dfs_cac_started(hapd, &data->dfs_event);
+               break;
+#endif /* NEED_AP_MLME */
+       case EVENT_INTERFACE_ENABLED:
+               wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_ENABLED);
+               if (hapd->disabled && hapd->started) {
+                       hapd->disabled = 0;
+                       /*
+                        * Try to re-enable interface if the driver stopped it
+                        * when the interface got disabled.
+                        */
+                       wpa_auth_reconfig_group_keys(hapd->wpa_auth);
+                       hapd->reenable_beacon = 1;
+                       ieee802_11_set_beacon(hapd);
+               }
+               break;
+       case EVENT_INTERFACE_DISABLED:
+               hostapd_free_stas(hapd);
+               wpa_msg(hapd->msg_ctx, MSG_INFO, INTERFACE_DISABLED);
+               hapd->disabled = 1;
+               break;
+#ifdef CONFIG_ACS
+       case EVENT_ACS_CHANNEL_SELECTED:
+               hostapd_acs_channel_selected(
+                       hapd, data->acs_selected_channels.pri_channel,
+                       data->acs_selected_channels.sec_channel);
+               break;
+#endif /* CONFIG_ACS */
        default:
                wpa_printf(MSG_DEBUG, "Unknown event %d", event);
                break;
index 79d50e5..559d77f 100644 (file)
@@ -83,12 +83,14 @@ static int get_user_cb(void *ctx, int argc, char *argv[], char *col[])
 
        for (i = 0; i < argc; i++) {
                if (os_strcmp(col[i], "password") == 0 && argv[i]) {
-                       os_free(user->password);
+                       bin_clear_free(user->password, user->password_len);
                        user->password_len = os_strlen(argv[i]);
                        user->password = (u8 *) os_strdup(argv[i]);
                        user->next = (void *) 1;
                } else if (os_strcmp(col[i], "methods") == 0 && argv[i]) {
                        set_user_methods(user, argv[i]);
+               } else if (os_strcmp(col[i], "remediation") == 0 && argv[i]) {
+                       user->remediation = strlen(argv[i]) > 0;
                }
        }
 
@@ -116,7 +118,7 @@ static int get_wildcard_cb(void *ctx, int argc, char *argv[], char *col[])
        if (len <= user->identity_len &&
            os_memcmp(argv[id], user->identity, len) == 0 &&
            (user->password == NULL || len > user->password_len)) {
-               os_free(user->password);
+               bin_clear_free(user->password, user->password_len);
                user->password_len = os_strlen(argv[id]);
                user->password = (u8 *) os_strdup(argv[id]);
                user->next = (void *) 1;
@@ -156,8 +158,10 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity,
                return NULL;
        }
 
-       os_free(hapd->tmp_eap_user.identity);
-       os_free(hapd->tmp_eap_user.password);
+       bin_clear_free(hapd->tmp_eap_user.identity,
+                      hapd->tmp_eap_user.identity_len);
+       bin_clear_free(hapd->tmp_eap_user.password,
+                      hapd->tmp_eap_user.password_len);
        os_memset(&hapd->tmp_eap_user, 0, sizeof(hapd->tmp_eap_user));
        hapd->tmp_eap_user.phase2 = phase2;
        hapd->tmp_eap_user.identity = os_zalloc(identity_len + 1);
@@ -173,8 +177,8 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity,
        }
 
        os_snprintf(cmd, sizeof(cmd),
-                   "SELECT password,methods FROM users WHERE "
-                   "identity='%s' AND phase2=%d;", id_str, phase2);
+                   "SELECT * FROM users WHERE identity='%s' AND phase2=%d;",
+                   id_str, phase2);
        wpa_printf(MSG_DEBUG, "DB: %s", cmd);
        if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) !=
            SQLITE_OK) {
index b3574ba..9d19f98 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Generic advertisement service (GAS) server
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 #include "gas_serv.h"
 
 
+static void convert_to_protected_dual(struct wpabuf *msg)
+{
+       u8 *categ = wpabuf_mhead_u8(msg);
+       *categ = WLAN_ACTION_PROTECTED_DUAL;
+}
+
+
 static struct gas_dialog_info *
 gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
 {
@@ -46,10 +53,12 @@ gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
                 * it to be that long.
                 */
                ap_sta_session_timeout(hapd, sta, 5);
+       } else {
+               ap_sta_replenish_timeout(hapd, sta, 5);
        }
 
        if (sta->gas_dialog == NULL) {
-               sta->gas_dialog = os_zalloc(GAS_DIALOG_MAX *
+               sta->gas_dialog = os_calloc(GAS_DIALOG_MAX,
                                            sizeof(struct gas_dialog_info));
                if (sta->gas_dialog == NULL)
                        return NULL;
@@ -62,7 +71,6 @@ gas_dialog_create(struct hostapd_data *hapd, const u8 *addr, u8 dialog_token)
                        continue;
                dia = &sta->gas_dialog[i];
                dia->valid = 1;
-               dia->index = i;
                dia->dialog_token = dialog_token;
                sta->gas_dialog_next = (++i == GAS_DIALOG_MAX) ? 0 : i;
                return dia;
@@ -150,6 +158,10 @@ static void anqp_add_hs_capab_list(struct hostapd_data *hapd,
                wpabuf_put_u8(buf, HS20_STYPE_NAI_HOME_REALM_QUERY);
        if (hapd->conf->hs20_operating_class)
                wpabuf_put_u8(buf, HS20_STYPE_OPERATING_CLASS);
+       if (hapd->conf->hs20_osu_providers_count)
+               wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
+       if (hapd->conf->hs20_icons_count)
+               wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
        gas_anqp_set_element_len(buf, len);
 }
 #endif /* CONFIG_HS20 */
@@ -402,7 +414,7 @@ static void anqp_add_nai_realm(struct hostapd_data *hapd, struct wpabuf *buf,
                        gas_anqp_set_element_len(buf, realm_data_len);
                }
                gas_anqp_set_element_len(buf, len);
-       } else if (nai_home_realm && hapd->conf->nai_realm_data) {
+       } else if (nai_home_realm && hapd->conf->nai_realm_data && home_realm) {
                hs20_add_nai_home_realm_matches(hapd, buf, home_realm,
                                                home_realm_len);
        }
@@ -505,18 +517,188 @@ static void anqp_add_operating_class(struct hostapd_data *hapd,
        }
 }
 
+
+static void anqp_add_osu_provider(struct wpabuf *buf,
+                                 struct hostapd_bss_config *bss,
+                                 struct hs20_osu_provider *p)
+{
+       u8 *len, *len2, *count;
+       unsigned int i;
+
+       len = wpabuf_put(buf, 2); /* OSU Provider Length to be filled */
+
+       /* OSU Friendly Name Duples */
+       len2 = wpabuf_put(buf, 2);
+       for (i = 0; i < p->friendly_name_count; i++) {
+               struct hostapd_lang_string *s = &p->friendly_name[i];
+               wpabuf_put_u8(buf, 3 + s->name_len);
+               wpabuf_put_data(buf, s->lang, 3);
+               wpabuf_put_data(buf, s->name, s->name_len);
+       }
+       WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
+
+       /* OSU Server URI */
+       if (p->server_uri) {
+               wpabuf_put_u8(buf, os_strlen(p->server_uri));
+               wpabuf_put_str(buf, p->server_uri);
+       } else
+               wpabuf_put_u8(buf, 0);
+
+       /* OSU Method List */
+       count = wpabuf_put(buf, 1);
+       for (i = 0; p->method_list[i] >= 0; i++)
+               wpabuf_put_u8(buf, p->method_list[i]);
+       *count = i;
+
+       /* Icons Available */
+       len2 = wpabuf_put(buf, 2);
+       for (i = 0; i < p->icons_count; i++) {
+               size_t j;
+               struct hs20_icon *icon = NULL;
+
+               for (j = 0; j < bss->hs20_icons_count && !icon; j++) {
+                       if (os_strcmp(p->icons[i], bss->hs20_icons[j].name) ==
+                           0)
+                               icon = &bss->hs20_icons[j];
+               }
+               if (!icon)
+                       continue; /* icon info not found */
+
+               wpabuf_put_le16(buf, icon->width);
+               wpabuf_put_le16(buf, icon->height);
+               wpabuf_put_data(buf, icon->language, 3);
+               wpabuf_put_u8(buf, os_strlen(icon->type));
+               wpabuf_put_str(buf, icon->type);
+               wpabuf_put_u8(buf, os_strlen(icon->name));
+               wpabuf_put_str(buf, icon->name);
+       }
+       WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
+
+       /* OSU_NAI */
+       if (p->osu_nai) {
+               wpabuf_put_u8(buf, os_strlen(p->osu_nai));
+               wpabuf_put_str(buf, p->osu_nai);
+       } else
+               wpabuf_put_u8(buf, 0);
+
+       /* OSU Service Description Duples */
+       len2 = wpabuf_put(buf, 2);
+       for (i = 0; i < p->service_desc_count; i++) {
+               struct hostapd_lang_string *s = &p->service_desc[i];
+               wpabuf_put_u8(buf, 3 + s->name_len);
+               wpabuf_put_data(buf, s->lang, 3);
+               wpabuf_put_data(buf, s->name, s->name_len);
+       }
+       WPA_PUT_LE16(len2, (u8 *) wpabuf_put(buf, 0) - len2 - 2);
+
+       WPA_PUT_LE16(len, (u8 *) wpabuf_put(buf, 0) - len - 2);
+}
+
+
+static void anqp_add_osu_providers_list(struct hostapd_data *hapd,
+                                       struct wpabuf *buf)
+{
+       if (hapd->conf->hs20_osu_providers_count) {
+               size_t i;
+               u8 *len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+               wpabuf_put_be24(buf, OUI_WFA);
+               wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+               wpabuf_put_u8(buf, HS20_STYPE_OSU_PROVIDERS_LIST);
+               wpabuf_put_u8(buf, 0); /* Reserved */
+
+               /* OSU SSID */
+               wpabuf_put_u8(buf, hapd->conf->osu_ssid_len);
+               wpabuf_put_data(buf, hapd->conf->osu_ssid,
+                               hapd->conf->osu_ssid_len);
+
+               /* Number of OSU Providers */
+               wpabuf_put_u8(buf, hapd->conf->hs20_osu_providers_count);
+
+               for (i = 0; i < hapd->conf->hs20_osu_providers_count; i++) {
+                       anqp_add_osu_provider(
+                               buf, hapd->conf,
+                               &hapd->conf->hs20_osu_providers[i]);
+               }
+
+               gas_anqp_set_element_len(buf, len);
+       }
+}
+
+
+static void anqp_add_icon_binary_file(struct hostapd_data *hapd,
+                                     struct wpabuf *buf,
+                                     const u8 *name, size_t name_len)
+{
+       struct hs20_icon *icon;
+       size_t i;
+       u8 *len;
+
+       wpa_hexdump_ascii(MSG_DEBUG, "HS 2.0: Requested Icon Filename",
+                         name, name_len);
+       for (i = 0; i < hapd->conf->hs20_icons_count; i++) {
+               icon = &hapd->conf->hs20_icons[i];
+               if (name_len == os_strlen(icon->name) &&
+                   os_memcmp(name, icon->name, name_len) == 0)
+                       break;
+       }
+
+       if (i < hapd->conf->hs20_icons_count)
+               icon = &hapd->conf->hs20_icons[i];
+       else
+               icon = NULL;
+
+       len = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
+       wpabuf_put_be24(buf, OUI_WFA);
+       wpabuf_put_u8(buf, HS20_ANQP_OUI_TYPE);
+       wpabuf_put_u8(buf, HS20_STYPE_ICON_BINARY_FILE);
+       wpabuf_put_u8(buf, 0); /* Reserved */
+
+       if (icon) {
+               char *data;
+               size_t data_len;
+
+               data = os_readfile(icon->file, &data_len);
+               if (data == NULL || data_len > 65535) {
+                       wpabuf_put_u8(buf, 2); /* Download Status:
+                                               * Unspecified file error */
+                       wpabuf_put_u8(buf, 0);
+                       wpabuf_put_le16(buf, 0);
+               } else {
+                       wpabuf_put_u8(buf, 0); /* Download Status: Success */
+                       wpabuf_put_u8(buf, os_strlen(icon->type));
+                       wpabuf_put_str(buf, icon->type);
+                       wpabuf_put_le16(buf, data_len);
+                       wpabuf_put_data(buf, data, data_len);
+               }
+               os_free(data);
+       } else {
+               wpabuf_put_u8(buf, 1); /* Download Status: File not found */
+               wpabuf_put_u8(buf, 0);
+               wpabuf_put_le16(buf, 0);
+       }
+
+       gas_anqp_set_element_len(buf, len);
+}
+
 #endif /* CONFIG_HS20 */
 
 
 static struct wpabuf *
 gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
                                unsigned int request,
-                               struct gas_dialog_info *di,
-                               const u8 *home_realm, size_t home_realm_len)
+                               const u8 *home_realm, size_t home_realm_len,
+                               const u8 *icon_name, size_t icon_name_len)
 {
        struct wpabuf *buf;
+       size_t len;
+
+       len = 1400;
+       if (request & (ANQP_REQ_NAI_REALM | ANQP_REQ_NAI_HOME_REALM))
+               len += 1000;
+       if (request & ANQP_REQ_ICON_REQUEST)
+               len += 65536;
 
-       buf = wpabuf_alloc(1400);
+       buf = wpabuf_alloc(len);
        if (buf == NULL)
                return NULL;
 
@@ -550,44 +732,32 @@ gas_serv_build_gas_resp_payload(struct hostapd_data *hapd,
                anqp_add_connection_capability(hapd, buf);
        if (request & ANQP_REQ_OPERATING_CLASS)
                anqp_add_operating_class(hapd, buf);
+       if (request & ANQP_REQ_OSU_PROVIDERS_LIST)
+               anqp_add_osu_providers_list(hapd, buf);
+       if (request & ANQP_REQ_ICON_REQUEST)
+               anqp_add_icon_binary_file(hapd, buf, icon_name, icon_name_len);
 #endif /* CONFIG_HS20 */
 
        return buf;
 }
 
 
-static void gas_serv_clear_cached_ies(void *eloop_data, void *user_ctx)
-{
-       struct gas_dialog_info *dia = eloop_data;
-
-       wpa_printf(MSG_DEBUG, "GAS: Timeout triggered, clearing dialog for "
-                  "dialog token %d", dia->dialog_token);
-
-       gas_serv_dialog_clear(dia);
-}
-
-
 struct anqp_query_info {
        unsigned int request;
-       unsigned int remote_request;
        const u8 *home_realm_query;
        size_t home_realm_query_len;
-       u16 remote_delay;
+       const u8 *icon_name;
+       size_t icon_name_len;
+       int p2p_sd;
 };
 
 
 static void set_anqp_req(unsigned int bit, const char *name, int local,
-                        unsigned int remote, u16 remote_delay,
                         struct anqp_query_info *qi)
 {
        qi->request |= bit;
        if (local) {
                wpa_printf(MSG_DEBUG, "ANQP: %s (local)", name);
-       } else if (bit & remote) {
-               wpa_printf(MSG_DEBUG, "ANQP: %s (remote)", name);
-               qi->remote_request |= bit;
-               if (remote_delay > qi->remote_delay)
-                       qi->remote_delay = remote_delay;
        } else {
                wpa_printf(MSG_DEBUG, "ANQP: %s not available", name);
        }
@@ -599,43 +769,38 @@ static void rx_anqp_query_list_id(struct hostapd_data *hapd, u16 info_id,
 {
        switch (info_id) {
        case ANQP_CAPABILITY_LIST:
-               set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1, 0,
-                            0, qi);
+               set_anqp_req(ANQP_REQ_CAPABILITY_LIST, "Capability List", 1,
+                            qi);
                break;
        case ANQP_VENUE_NAME:
                set_anqp_req(ANQP_REQ_VENUE_NAME, "Venue Name",
-                            hapd->conf->venue_name != NULL, 0, 0, qi);
+                            hapd->conf->venue_name != NULL, qi);
                break;
        case ANQP_NETWORK_AUTH_TYPE:
                set_anqp_req(ANQP_REQ_NETWORK_AUTH_TYPE, "Network Auth Type",
-                            hapd->conf->network_auth_type != NULL,
-                            0, 0, qi);
+                            hapd->conf->network_auth_type != NULL, qi);
                break;
        case ANQP_ROAMING_CONSORTIUM:
                set_anqp_req(ANQP_REQ_ROAMING_CONSORTIUM, "Roaming Consortium",
-                            hapd->conf->roaming_consortium != NULL, 0, 0, qi);
+                            hapd->conf->roaming_consortium != NULL, qi);
                break;
        case ANQP_IP_ADDR_TYPE_AVAILABILITY:
                set_anqp_req(ANQP_REQ_IP_ADDR_TYPE_AVAILABILITY,
                             "IP Addr Type Availability",
-                            hapd->conf->ipaddr_type_configured,
-                            0, 0, qi);
+                            hapd->conf->ipaddr_type_configured, qi);
                break;
        case ANQP_NAI_REALM:
                set_anqp_req(ANQP_REQ_NAI_REALM, "NAI Realm",
-                            hapd->conf->nai_realm_data != NULL,
-                            0, 0, qi);
+                            hapd->conf->nai_realm_data != NULL, qi);
                break;
        case ANQP_3GPP_CELLULAR_NETWORK:
                set_anqp_req(ANQP_REQ_3GPP_CELLULAR_NETWORK,
                             "3GPP Cellular Network",
-                            hapd->conf->anqp_3gpp_cell_net != NULL,
-                            0, 0, qi);
+                            hapd->conf->anqp_3gpp_cell_net != NULL, qi);
                break;
        case ANQP_DOMAIN_NAME:
                set_anqp_req(ANQP_REQ_DOMAIN_NAME, "Domain Name",
-                            hapd->conf->domain_name != NULL,
-                            0, 0, qi);
+                            hapd->conf->domain_name != NULL, qi);
                break;
        default:
                wpa_printf(MSG_DEBUG, "ANQP: Unsupported Info Id %u",
@@ -667,29 +832,30 @@ static void rx_anqp_hs_query_list(struct hostapd_data *hapd, u8 subtype,
        switch (subtype) {
        case HS20_STYPE_CAPABILITY_LIST:
                set_anqp_req(ANQP_REQ_HS_CAPABILITY_LIST, "HS Capability List",
-                            1, 0, 0, qi);
+                            1, qi);
                break;
        case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
                set_anqp_req(ANQP_REQ_OPERATOR_FRIENDLY_NAME,
                             "Operator Friendly Name",
-                            hapd->conf->hs20_oper_friendly_name != NULL,
-                            0, 0, qi);
+                            hapd->conf->hs20_oper_friendly_name != NULL, qi);
                break;
        case HS20_STYPE_WAN_METRICS:
                set_anqp_req(ANQP_REQ_WAN_METRICS, "WAN Metrics",
-                            hapd->conf->hs20_wan_metrics != NULL,
-                            0, 0, qi);
+                            hapd->conf->hs20_wan_metrics != NULL, qi);
                break;
        case HS20_STYPE_CONNECTION_CAPABILITY:
                set_anqp_req(ANQP_REQ_CONNECTION_CAPABILITY,
                             "Connection Capability",
                             hapd->conf->hs20_connection_capability != NULL,
-                            0, 0, qi);
+                            qi);
                break;
        case HS20_STYPE_OPERATING_CLASS:
                set_anqp_req(ANQP_REQ_OPERATING_CLASS, "Operating Class",
-                            hapd->conf->hs20_operating_class != NULL,
-                            0, 0, qi);
+                            hapd->conf->hs20_operating_class != NULL, qi);
+               break;
+       case HS20_STYPE_OSU_PROVIDERS_LIST:
+               set_anqp_req(ANQP_REQ_OSU_PROVIDERS_LIST, "OSU Providers list",
+                            hapd->conf->hs20_osu_providers_count, qi);
                break;
        default:
                wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 subtype %u",
@@ -716,6 +882,23 @@ static void rx_anqp_hs_nai_home_realm(struct hostapd_data *hapd,
 }
 
 
+static void rx_anqp_hs_icon_request(struct hostapd_data *hapd,
+                                   const u8 *pos, const u8 *end,
+                                   struct anqp_query_info *qi)
+{
+       qi->request |= ANQP_REQ_ICON_REQUEST;
+       qi->icon_name = pos;
+       qi->icon_name_len = end - pos;
+       if (hapd->conf->hs20_icons_count) {
+               wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query "
+                          "(local)");
+       } else {
+               wpa_printf(MSG_DEBUG, "ANQP: HS 2.0 Icon Request Query not "
+                          "available");
+       }
+}
+
+
 static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
                                    const u8 *pos, const u8 *end,
                                    struct anqp_query_info *qi)
@@ -737,6 +920,21 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
                return;
        }
 
+#ifdef CONFIG_P2P
+       if (*pos == P2P_OUI_TYPE) {
+               /*
+                * This is for P2P SD and will be taken care of by the P2P
+                * implementation. This query needs to be ignored in the generic
+                * GAS server to avoid duplicated response.
+                */
+               wpa_printf(MSG_DEBUG,
+                          "ANQP: Ignore WFA vendor type %u (P2P SD) in generic GAS server",
+                          *pos);
+               qi->p2p_sd = 1;
+               return;
+       }
+#endif /* CONFIG_P2P */
+
        if (*pos != HS20_ANQP_OUI_TYPE) {
                wpa_printf(MSG_DEBUG, "ANQP: Unsupported WFA vendor type %u",
                           *pos);
@@ -760,6 +958,9 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
        case HS20_STYPE_NAI_HOME_REALM_QUERY:
                rx_anqp_hs_nai_home_realm(hapd, pos, end, qi);
                break;
+       case HS20_STYPE_ICON_REQUEST:
+               rx_anqp_hs_icon_request(hapd, pos, end, qi);
+               break;
        default:
                wpa_printf(MSG_DEBUG, "ANQP: Unsupported HS 2.0 query subtype "
                           "%u", subtype);
@@ -772,17 +973,26 @@ static void rx_anqp_vendor_specific(struct hostapd_data *hapd,
 
 static void gas_serv_req_local_processing(struct hostapd_data *hapd,
                                          const u8 *sa, u8 dialog_token,
-                                         struct anqp_query_info *qi)
+                                         struct anqp_query_info *qi, int prot)
 {
        struct wpabuf *buf, *tx_buf;
 
-       buf = gas_serv_build_gas_resp_payload(hapd, qi->request, NULL,
+       buf = gas_serv_build_gas_resp_payload(hapd, qi->request,
                                              qi->home_realm_query,
-                                             qi->home_realm_query_len);
+                                             qi->home_realm_query_len,
+                                             qi->icon_name, qi->icon_name_len);
        wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Locally generated ANQP responses",
                        buf);
        if (!buf)
                return;
+#ifdef CONFIG_P2P
+       if (wpabuf_len(buf) == 0 && qi->p2p_sd) {
+               wpa_printf(MSG_DEBUG,
+                          "ANQP: Do not send response to P2P SD from generic GAS service (P2P SD implementation will process this)");
+               wpabuf_free(buf);
+               return;
+       }
+#endif /* CONFIG_P2P */
 
        if (wpabuf_len(buf) > hapd->gas_frag_limit ||
            hapd->conf->gas_comeback_delay) {
@@ -802,13 +1012,17 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd,
                                   "for " MACSTR " (dialog token %u)",
                                   MAC2STR(sa), dialog_token);
                        wpabuf_free(buf);
-                       return;
+                       tx_buf = gas_anqp_build_initial_resp_buf(
+                               dialog_token, WLAN_STATUS_UNSPECIFIED_FAILURE,
+                               0, NULL);
+               } else {
+                       di->prot = prot;
+                       di->sd_resp = buf;
+                       di->sd_resp_pos = 0;
+                       tx_buf = gas_anqp_build_initial_resp_buf(
+                               dialog_token, WLAN_STATUS_SUCCESS,
+                               comeback_delay, NULL);
                }
-               di->sd_resp = buf;
-               di->sd_resp_pos = 0;
-               tx_buf = gas_anqp_build_initial_resp_buf(
-                       dialog_token, WLAN_STATUS_SUCCESS, comeback_delay,
-                       NULL);
        } else {
                wpa_printf(MSG_DEBUG, "ANQP: Initial response (no comeback)");
                tx_buf = gas_anqp_build_initial_resp_buf(
@@ -817,7 +1031,8 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd,
        }
        if (!tx_buf)
                return;
-
+       if (prot)
+               convert_to_protected_dual(tx_buf);
        hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
                                wpabuf_head(tx_buf), wpabuf_len(tx_buf));
        wpabuf_free(tx_buf);
@@ -826,7 +1041,7 @@ static void gas_serv_req_local_processing(struct hostapd_data *hapd,
 
 static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
                                        const u8 *sa,
-                                       const u8 *data, size_t len)
+                                       const u8 *data, size_t len, int prot)
 {
        const u8 *pos = data;
        const u8 *end = data + len;
@@ -876,6 +1091,8 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
                        return;
                wpabuf_put_data(buf, adv_proto, 2 + slen);
                wpabuf_put_le16(buf, 0); /* Query Response Length */
+               if (prot)
+                       convert_to_protected_dual(buf);
                hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
                                        wpabuf_head(buf), wpabuf_len(buf));
                wpabuf_free(buf);
@@ -927,100 +1144,13 @@ static void gas_serv_rx_gas_initial_req(struct hostapd_data *hapd,
                pos += elen;
        }
 
-       gas_serv_req_local_processing(hapd, sa, dialog_token, &qi);
-}
-
-
-void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst,
-                             struct gas_dialog_info *dialog)
-{
-       struct wpabuf *buf, *tx_buf;
-       u8 dialog_token = dialog->dialog_token;
-       size_t frag_len;
-
-       if (dialog->sd_resp == NULL) {
-               buf = gas_serv_build_gas_resp_payload(hapd,
-                                                     dialog->all_requested,
-                                                     dialog, NULL, 0);
-               wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
-                       buf);
-               if (!buf)
-                       goto tx_gas_response_done;
-               dialog->sd_resp = buf;
-               dialog->sd_resp_pos = 0;
-       }
-       frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
-       if (frag_len > hapd->gas_frag_limit || dialog->comeback_delay ||
-           hapd->conf->gas_comeback_delay) {
-               u16 comeback_delay_tus = dialog->comeback_delay +
-                       GAS_SERV_COMEBACK_DELAY_FUDGE;
-               u32 comeback_delay_secs, comeback_delay_usecs;
-
-               if (hapd->conf->gas_comeback_delay) {
-                       /* Testing - allow overriding of the delay value */
-                       comeback_delay_tus = hapd->conf->gas_comeback_delay;
-               }
-
-               wpa_printf(MSG_DEBUG, "GAS: Response frag_len %u (frag limit "
-                          "%u) and comeback delay %u, "
-                          "requesting comebacks", (unsigned int) frag_len,
-                          (unsigned int) hapd->gas_frag_limit,
-                          dialog->comeback_delay);
-               tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
-                                                        WLAN_STATUS_SUCCESS,
-                                                        comeback_delay_tus,
-                                                        NULL);
-               if (tx_buf) {
-                       wpa_msg(hapd->msg_ctx, MSG_DEBUG,
-                               "GAS: Tx GAS Initial Resp (comeback = 10TU)");
-                       hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
-                                               dst,
-                                               wpabuf_head(tx_buf),
-                                               wpabuf_len(tx_buf));
-               }
-               wpabuf_free(tx_buf);
-
-               /* start a timer of 1.5 * comeback-delay */
-               comeback_delay_tus = comeback_delay_tus +
-                       (comeback_delay_tus / 2);
-               comeback_delay_secs = (comeback_delay_tus * 1024) / 1000000;
-               comeback_delay_usecs = (comeback_delay_tus * 1024) -
-                       (comeback_delay_secs * 1000000);
-               eloop_register_timeout(comeback_delay_secs,
-                                      comeback_delay_usecs,
-                                      gas_serv_clear_cached_ies, dialog,
-                                      NULL);
-               goto tx_gas_response_done;
-       }
-
-       buf = wpabuf_alloc_copy(wpabuf_head_u8(dialog->sd_resp) +
-                               dialog->sd_resp_pos, frag_len);
-       if (buf == NULL) {
-               wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Buffer allocation "
-                       "failed");
-               goto tx_gas_response_done;
-       }
-       tx_buf = gas_anqp_build_initial_resp_buf(dialog_token,
-                                                WLAN_STATUS_SUCCESS, 0, buf);
-       wpabuf_free(buf);
-       if (tx_buf == NULL)
-               goto tx_gas_response_done;
-       wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Initial "
-               "Response (frag_id %d frag_len %d)",
-               dialog->sd_frag_id, (int) frag_len);
-       dialog->sd_frag_id++;
-
-       hostapd_drv_send_action(hapd, hapd->iface->freq, 0, dst,
-                               wpabuf_head(tx_buf), wpabuf_len(tx_buf));
-       wpabuf_free(tx_buf);
-tx_gas_response_done:
-       gas_serv_clear_cached_ies(dialog, NULL);
+       gas_serv_req_local_processing(hapd, sa, dialog_token, &qi, prot);
 }
 
 
 static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
                                         const u8 *sa,
-                                        const u8 *data, size_t len)
+                                        const u8 *data, size_t len, int prot)
 {
        struct gas_dialog_info *dialog;
        struct wpabuf *buf, *tx_buf;
@@ -1051,33 +1181,6 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
                goto send_resp;
        }
 
-       if (dialog->sd_resp == NULL) {
-               wpa_printf(MSG_DEBUG, "GAS: Remote request 0x%x received 0x%x",
-                          dialog->requested, dialog->received);
-               if ((dialog->requested & dialog->received) !=
-                   dialog->requested) {
-                       wpa_printf(MSG_DEBUG, "GAS: Did not receive response "
-                                  "from remote processing");
-                       gas_serv_dialog_clear(dialog);
-                       tx_buf = gas_anqp_build_comeback_resp_buf(
-                               dialog_token,
-                               WLAN_STATUS_GAS_RESP_NOT_RECEIVED, 0, 0, 0,
-                               NULL);
-                       if (tx_buf == NULL)
-                               return;
-                       goto send_resp;
-               }
-
-               buf = gas_serv_build_gas_resp_payload(hapd,
-                                                     dialog->all_requested,
-                                                     dialog, NULL, 0);
-               wpa_hexdump_buf(MSG_MSGDUMP, "ANQP: Generated ANQP responses",
-                       buf);
-               if (!buf)
-                       goto rx_gas_comeback_req_done;
-               dialog->sd_resp = buf;
-               dialog->sd_resp_pos = 0;
-       }
        frag_len = wpabuf_len(dialog->sd_resp) - dialog->sd_resp_pos;
        if (frag_len > hapd->gas_frag_limit) {
                frag_len = hapd->gas_frag_limit;
@@ -1090,15 +1193,18 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
        if (buf == NULL) {
                wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Failed to allocate "
                        "buffer");
-               goto rx_gas_comeback_req_done;
+               gas_serv_dialog_clear(dialog);
+               return;
        }
        tx_buf = gas_anqp_build_comeback_resp_buf(dialog_token,
                                                  WLAN_STATUS_SUCCESS,
                                                  dialog->sd_frag_id,
                                                  more, 0, buf);
        wpabuf_free(buf);
-       if (tx_buf == NULL)
-               goto rx_gas_comeback_req_done;
+       if (tx_buf == NULL) {
+               gas_serv_dialog_clear(dialog);
+               return;
+       }
        wpa_msg(hapd->msg_ctx, MSG_DEBUG, "GAS: Tx GAS Comeback Response "
                "(frag_id %d more=%d frag_len=%d)",
                dialog->sd_frag_id, more, (int) frag_len);
@@ -1118,13 +1224,11 @@ static void gas_serv_rx_gas_comeback_req(struct hostapd_data *hapd,
        }
 
 send_resp:
+       if (prot)
+               convert_to_protected_dual(tx_buf);
        hostapd_drv_send_action(hapd, hapd->iface->freq, 0, sa,
                                wpabuf_head(tx_buf), wpabuf_len(tx_buf));
        wpabuf_free(tx_buf);
-       return;
-
-rx_gas_comeback_req_done:
-       gas_serv_clear_cached_ies(dialog, NULL);
 }
 
 
@@ -1133,24 +1237,30 @@ static void gas_serv_rx_public_action(void *ctx, const u8 *buf, size_t len,
 {
        struct hostapd_data *hapd = ctx;
        const struct ieee80211_mgmt *mgmt;
-       size_t hdr_len;
        const u8 *sa, *data;
+       int prot;
 
        mgmt = (const struct ieee80211_mgmt *) buf;
-       hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
-       if (hdr_len > len)
+       if (len < IEEE80211_HDRLEN + 2)
                return;
-       if (mgmt->u.action.category != WLAN_ACTION_PUBLIC)
+       if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
+           mgmt->u.action.category != WLAN_ACTION_PROTECTED_DUAL)
                return;
+       /*
+        * Note: Public Action and Protected Dual of Public Action frames share
+        * the same payload structure, so it is fine to use definitions of
+        * Public Action frames to process both.
+        */
+       prot = mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL;
        sa = mgmt->sa;
-       len -= hdr_len;
-       data = &mgmt->u.action.u.public_action.action;
+       len -= IEEE80211_HDRLEN + 1;
+       data = buf + IEEE80211_HDRLEN + 1;
        switch (data[0]) {
        case WLAN_PA_GAS_INITIAL_REQ:
-               gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1);
+               gas_serv_rx_gas_initial_req(hapd, sa, data + 1, len - 1, prot);
                break;
        case WLAN_PA_GAS_COMEBACK_REQ:
-               gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1);
+               gas_serv_rx_gas_comeback_req(hapd, sa, data + 1, len - 1, prot);
                break;
        }
 }
index 4213cf6..4ec3201 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Generic advertisement service (GAS) server
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
        (0x10000 << HS20_STYPE_NAI_HOME_REALM_QUERY)
 #define ANQP_REQ_OPERATING_CLASS \
        (0x10000 << HS20_STYPE_OPERATING_CLASS)
-
-/* To account for latencies between hostapd and external ANQP processor */
-#define GAS_SERV_COMEBACK_DELAY_FUDGE 10
-#define GAS_SERV_MIN_COMEBACK_DELAY 100 /* in TU */
+#define ANQP_REQ_OSU_PROVIDERS_LIST \
+       (0x10000 << HS20_STYPE_OSU_PROVIDERS_LIST)
+#define ANQP_REQ_ICON_REQUEST \
+       (0x10000 << HS20_STYPE_ICON_REQUEST)
 
 struct gas_dialog_info {
        u8 valid;
-       u8 index;
        struct wpabuf *sd_resp; /* Fragmented response */
        u8 dialog_token;
        size_t sd_resp_pos; /* Offset in sd_resp */
        u8 sd_frag_id;
-       u16 comeback_delay;
-
-       unsigned int requested;
-       unsigned int received;
-       unsigned int all_requested;
+       int prot; /* whether Protected Dual of Public Action frame is used */
 };
 
 struct hostapd_data;
 
-void gas_serv_tx_gas_response(struct hostapd_data *hapd, const u8 *dst,
-                             struct gas_dialog_info *dialog);
 struct gas_dialog_info *
 gas_serv_dialog_find(struct hostapd_data *hapd, const u8 *addr,
                     u8 dialog_token);
index 780b2e2..3e4e16b 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / Initialization and configuration
- * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
 #include "radius/radius_client.h"
 #include "radius/radius_das.h"
-#include "drivers/driver.h"
+#include "eap_server/tncs.h"
+#include "eapol_auth/eapol_auth_sm.h"
+#include "eapol_auth/eapol_auth_sm_i.h"
 #include "hostapd.h"
 #include "authsrv.h"
 #include "sta_info.h"
 #include "ap_config.h"
 #include "p2p_hostapd.h"
 #include "gas_serv.h"
+#include "dfs.h"
+#include "ieee802_11.h"
+#include "bss_load.h"
+#include "x_snoop.h"
+#include "dhcp_snoop.h"
+#include "ndisc_snoop.h"
 
 
 static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason);
 static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd);
 static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd);
-
-extern int wpa_debug_level;
-extern struct wpa_driver_ops *wpa_drivers[];
+static int setup_interface2(struct hostapd_iface *iface);
+static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx);
 
 
 int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
@@ -61,10 +69,21 @@ int hostapd_for_each_interface(struct hapd_interfaces *interfaces,
 
 static void hostapd_reload_bss(struct hostapd_data *hapd)
 {
+       struct hostapd_ssid *ssid;
+
 #ifndef CONFIG_NO_RADIUS
        radius_client_reconfig(hapd->radius, hapd->conf->radius);
 #endif /* CONFIG_NO_RADIUS */
 
+       ssid = &hapd->conf->ssid;
+       if (!ssid->wpa_psk_set && ssid->wpa_psk && !ssid->wpa_psk->next &&
+           ssid->wpa_passphrase_set && ssid->wpa_passphrase) {
+               /*
+                * Force PSK to be derived again since SSID or passphrase may
+                * have changed.
+                */
+               hostapd_config_clear_wpa_psk(&hapd->conf->ssid.wpa_psk);
+       }
        if (hostapd_setup_wpa_psk(hapd->conf)) {
                wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK "
                           "after reloading configuration");
@@ -75,7 +94,7 @@ static void hostapd_reload_bss(struct hostapd_data *hapd)
        else
                hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0);
 
-       if (hapd->conf->wpa && hapd->wpa_auth == NULL) {
+       if ((hapd->conf->wpa || hapd->conf->osen) && hapd->wpa_auth == NULL) {
                hostapd_setup_wpa(hapd);
                if (hapd->wpa_auth)
                        wpa_init_keys(hapd->wpa_auth);
@@ -159,7 +178,18 @@ int hostapd_reload_config(struct hostapd_iface *iface)
        for (j = 0; j < iface->num_bss; j++) {
                hapd = iface->bss[j];
                hapd->iconf = newconf;
-               hapd->conf = &newconf->bss[j];
+               hapd->iconf->channel = oldconf->channel;
+               hapd->iconf->secondary_channel = oldconf->secondary_channel;
+               hapd->iconf->ieee80211n = oldconf->ieee80211n;
+               hapd->iconf->ieee80211ac = oldconf->ieee80211ac;
+               hapd->iconf->ht_capab = oldconf->ht_capab;
+               hapd->iconf->vht_capab = oldconf->vht_capab;
+               hapd->iconf->vht_oper_chwidth = oldconf->vht_oper_chwidth;
+               hapd->iconf->vht_oper_centr_freq_seg0_idx =
+                       oldconf->vht_oper_centr_freq_seg0_idx;
+               hapd->iconf->vht_oper_centr_freq_seg1_idx =
+                       oldconf->vht_oper_centr_freq_seg1_idx;
+               hapd->conf = newconf->bss[j];
                hostapd_reload_bss(hapd);
        }
 
@@ -221,36 +251,30 @@ static int hostapd_broadcast_wep_set(struct hostapd_data *hapd)
                errors++;
        }
 
-       if (ssid->dyn_vlan_keys) {
-               size_t i;
-               for (i = 0; i <= ssid->max_dyn_vlan_keys; i++) {
-                       const char *ifname;
-                       struct hostapd_wep_keys *key = ssid->dyn_vlan_keys[i];
-                       if (key == NULL)
-                               continue;
-                       ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan,
-                                                           i);
-                       if (ifname == NULL)
-                               continue;
-
-                       idx = key->idx;
-                       if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_WEP,
-                                               broadcast_ether_addr, idx, 1,
-                                               NULL, 0, key->key[idx],
-                                               key->len[idx])) {
-                               wpa_printf(MSG_WARNING, "Could not set "
-                                          "dynamic VLAN WEP encryption.");
-                               errors++;
-                       }
-               }
-       }
-
        return errors;
 }
 
 
 static void hostapd_free_hapd_data(struct hostapd_data *hapd)
 {
+       os_free(hapd->probereq_cb);
+       hapd->probereq_cb = NULL;
+
+#ifdef CONFIG_P2P
+       wpabuf_free(hapd->p2p_beacon_ie);
+       hapd->p2p_beacon_ie = NULL;
+       wpabuf_free(hapd->p2p_probe_resp_ie);
+       hapd->p2p_probe_resp_ie = NULL;
+#endif /* CONFIG_P2P */
+
+       if (!hapd->started) {
+               wpa_printf(MSG_ERROR, "%s: Interface %s wasn't started",
+                          __func__, hapd->conf->iface);
+               return;
+       }
+       hapd->started = 0;
+
+       wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface);
        iapp_deinit(hapd->iapp);
        hapd->iapp = NULL;
        accounting_deinit(hapd);
@@ -268,32 +292,45 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd)
 
        authsrv_deinit(hapd);
 
-       if (hapd->interface_added &&
-           hostapd_if_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface)) {
-               wpa_printf(MSG_WARNING, "Failed to remove BSS interface %s",
-                          hapd->conf->iface);
+       if (hapd->interface_added) {
+               hapd->interface_added = 0;
+               if (hostapd_if_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface)) {
+                       wpa_printf(MSG_WARNING,
+                                  "Failed to remove BSS interface %s",
+                                  hapd->conf->iface);
+                       hapd->interface_added = 1;
+               } else {
+                       /*
+                        * Since this was a dynamically added interface, the
+                        * driver wrapper may have removed its internal instance
+                        * and hapd->drv_priv is not valid anymore.
+                        */
+                       hapd->drv_priv = NULL;
+               }
        }
 
-       os_free(hapd->probereq_cb);
-       hapd->probereq_cb = NULL;
-
-#ifdef CONFIG_P2P
-       wpabuf_free(hapd->p2p_beacon_ie);
-       hapd->p2p_beacon_ie = NULL;
-       wpabuf_free(hapd->p2p_probe_resp_ie);
-       hapd->p2p_probe_resp_ie = NULL;
-#endif /* CONFIG_P2P */
-
        wpabuf_free(hapd->time_adv);
 
 #ifdef CONFIG_INTERWORKING
        gas_serv_deinit(hapd);
 #endif /* CONFIG_INTERWORKING */
 
+       bss_load_update_deinit(hapd);
+       ndisc_snoop_deinit(hapd);
+       dhcp_snoop_deinit(hapd);
+       x_snoop_deinit(hapd);
+
 #ifdef CONFIG_SQLITE
-       os_free(hapd->tmp_eap_user.identity);
-       os_free(hapd->tmp_eap_user.password);
+       bin_clear_free(hapd->tmp_eap_user.identity,
+                      hapd->tmp_eap_user.identity_len);
+       bin_clear_free(hapd->tmp_eap_user.password,
+                      hapd->tmp_eap_user.password_len);
 #endif /* CONFIG_SQLITE */
+
+#ifdef CONFIG_MESH
+       wpabuf_free(hapd->mesh_pending_auth);
+       hapd->mesh_pending_auth = NULL;
+#endif /* CONFIG_MESH */
 }
 
 
@@ -302,13 +339,13 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd)
  * @hapd: Pointer to BSS data
  *
  * This function is used to free all per-BSS data structures and resources.
- * This gets called in a loop for each BSS between calls to
- * hostapd_cleanup_iface_pre() and hostapd_cleanup_iface() when an interface
- * is deinitialized. Most of the modules that are initialized in
- * hostapd_setup_bss() are deinitialized here.
+ * Most of the modules that are initialized in hostapd_setup_bss() are
+ * deinitialized here.
  */
 static void hostapd_cleanup(struct hostapd_data *hapd)
 {
+       wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s))", __func__, hapd,
+                  hapd->conf->iface);
        if (hapd->iface->interfaces &&
            hapd->iface->interfaces->ctrl_iface_deinit)
                hapd->iface->interfaces->ctrl_iface_deinit(hapd);
@@ -316,20 +353,14 @@ static void hostapd_cleanup(struct hostapd_data *hapd)
 }
 
 
-/**
- * hostapd_cleanup_iface_pre - Preliminary per-interface cleanup
- * @iface: Pointer to interface data
- *
- * This function is called before per-BSS data structures are deinitialized
- * with hostapd_cleanup().
- */
-static void hostapd_cleanup_iface_pre(struct hostapd_iface *iface)
-{
-}
-
-
 static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
 {
+       wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+#ifdef CONFIG_IEEE80211N
+#ifdef NEED_AP_MLME
+       hostapd_stop_setup_timers(iface);
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_IEEE80211N */
        hostapd_free_hw_features(iface->hw_features, iface->num_hw_features);
        iface->hw_features = NULL;
        os_free(iface->current_rates);
@@ -349,19 +380,23 @@ static void hostapd_cleanup_iface_partial(struct hostapd_iface *iface)
  */
 static void hostapd_cleanup_iface(struct hostapd_iface *iface)
 {
+       wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+       eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+
        hostapd_cleanup_iface_partial(iface);
        hostapd_config_free(iface->conf);
        iface->conf = NULL;
 
        os_free(iface->config_fname);
        os_free(iface->bss);
+       wpa_printf(MSG_DEBUG, "%s: free iface=%p", __func__, iface);
        os_free(iface);
 }
 
 
 static void hostapd_clear_wep(struct hostapd_data *hapd)
 {
-       if (hapd->drv_priv) {
+       if (hapd->drv_priv && !hapd->iface->driver_ap_teardown) {
                hostapd_set_privacy(hapd, 0);
                hostapd_broadcast_wep_clear(hapd);
        }
@@ -412,11 +447,15 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
        if (hostapd_drv_none(hapd) || hapd->drv_priv == NULL)
                return 0;
 
-       wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Flushing old station entries");
-       if (hostapd_flush(hapd)) {
-               wpa_msg(hapd->msg_ctx, MSG_WARNING, "Could not connect to "
-                       "kernel driver");
-               ret = -1;
+       if (!hapd->iface->driver_ap_teardown) {
+               wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
+                       "Flushing old station entries");
+
+               if (hostapd_flush(hapd)) {
+                       wpa_msg(hapd->msg_ctx, MSG_WARNING,
+                               "Could not connect to kernel driver");
+                       ret = -1;
+               }
        }
        wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "Deauthenticate all stations");
        os_memset(addr, 0xff, ETH_ALEN);
@@ -427,6 +466,14 @@ static int hostapd_flush_old_stations(struct hostapd_data *hapd, u16 reason)
 }
 
 
+static void hostapd_bss_deinit_no_free(struct hostapd_data *hapd)
+{
+       hostapd_free_stas(hapd);
+       hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
+       hostapd_clear_wep(hapd);
+}
+
+
 /**
  * hostapd_validate_bssid_configuration - Validate BSSID configuration
  * @iface: Pointer to interface data
@@ -453,7 +500,7 @@ static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface)
        /* Determine the bits necessary to any configured BSSIDs,
           if they are higher than the number of BSSIDs. */
        for (j = 0; j < iface->conf->num_bss; j++) {
-               if (hostapd_mac_comp_empty(iface->conf->bss[j].bssid) == 0) {
+               if (hostapd_mac_comp_empty(iface->conf->bss[j]->bssid) == 0) {
                        if (j)
                                auto_addr++;
                        continue;
@@ -461,7 +508,7 @@ static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface)
 
                for (i = 0; i < ETH_ALEN; i++) {
                        mask[i] |=
-                               iface->conf->bss[j].bssid[i] ^
+                               iface->conf->bss[j]->bssid[i] ^
                                hapd->own_addr[i];
                }
        }
@@ -526,7 +573,7 @@ static int mac_in_conf(struct hostapd_config *conf, const void *a)
        size_t i;
 
        for (i = 0; i < conf->num_bss; i++) {
-               if (hostapd_mac_comp(conf->bss[i].bssid, a) == 0) {
+               if (hostapd_mac_comp(conf->bss[i]->bssid, a) == 0) {
                        return 1;
                }
        }
@@ -540,57 +587,223 @@ static int mac_in_conf(struct hostapd_config *conf, const void *a)
 static int hostapd_das_nas_mismatch(struct hostapd_data *hapd,
                                    struct radius_das_attrs *attr)
 {
-       /* TODO */
+       if (attr->nas_identifier &&
+           (!hapd->conf->nas_identifier ||
+            os_strlen(hapd->conf->nas_identifier) !=
+            attr->nas_identifier_len ||
+            os_memcmp(hapd->conf->nas_identifier, attr->nas_identifier,
+                      attr->nas_identifier_len) != 0)) {
+               wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-Identifier mismatch");
+               return 1;
+       }
+
+       if (attr->nas_ip_addr &&
+           (hapd->conf->own_ip_addr.af != AF_INET ||
+            os_memcmp(&hapd->conf->own_ip_addr.u.v4, attr->nas_ip_addr, 4) !=
+            0)) {
+               wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-IP-Address mismatch");
+               return 1;
+       }
+
+#ifdef CONFIG_IPV6
+       if (attr->nas_ipv6_addr &&
+           (hapd->conf->own_ip_addr.af != AF_INET6 ||
+            os_memcmp(&hapd->conf->own_ip_addr.u.v6, attr->nas_ipv6_addr, 16)
+            != 0)) {
+               wpa_printf(MSG_DEBUG, "RADIUS DAS: NAS-IPv6-Address mismatch");
+               return 1;
+       }
+#endif /* CONFIG_IPV6 */
+
        return 0;
 }
 
 
 static struct sta_info * hostapd_das_find_sta(struct hostapd_data *hapd,
-                                             struct radius_das_attrs *attr)
+                                             struct radius_das_attrs *attr,
+                                             int *multi)
 {
-       struct sta_info *sta = NULL;
+       struct sta_info *selected, *sta;
        char buf[128];
+       int num_attr = 0;
+       int count;
+
+       *multi = 0;
+
+       for (sta = hapd->sta_list; sta; sta = sta->next)
+               sta->radius_das_match = 1;
 
-       if (attr->sta_addr)
+       if (attr->sta_addr) {
+               num_attr++;
                sta = ap_get_sta(hapd, attr->sta_addr);
+               if (!sta) {
+                       wpa_printf(MSG_DEBUG,
+                                  "RADIUS DAS: No Calling-Station-Id match");
+                       return NULL;
+               }
+
+               selected = sta;
+               for (sta = hapd->sta_list; sta; sta = sta->next) {
+                       if (sta != selected)
+                               sta->radius_das_match = 0;
+               }
+               wpa_printf(MSG_DEBUG, "RADIUS DAS: Calling-Station-Id match");
+       }
+
+       if (attr->acct_session_id) {
+               num_attr++;
+               if (attr->acct_session_id_len != 17) {
+                       wpa_printf(MSG_DEBUG,
+                                  "RADIUS DAS: Acct-Session-Id cannot match");
+                       return NULL;
+               }
+               count = 0;
 
-       if (sta == NULL && attr->acct_session_id &&
-           attr->acct_session_id_len == 17) {
                for (sta = hapd->sta_list; sta; sta = sta->next) {
+                       if (!sta->radius_das_match)
+                               continue;
                        os_snprintf(buf, sizeof(buf), "%08X-%08X",
                                    sta->acct_session_id_hi,
                                    sta->acct_session_id_lo);
-                       if (os_memcmp(attr->acct_session_id, buf, 17) == 0)
-                               break;
+                       if (os_memcmp(attr->acct_session_id, buf, 17) != 0)
+                               sta->radius_das_match = 0;
+                       else
+                               count++;
+               }
+
+               if (count == 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "RADIUS DAS: No matches remaining after Acct-Session-Id check");
+                       return NULL;
+               }
+               wpa_printf(MSG_DEBUG, "RADIUS DAS: Acct-Session-Id match");
+       }
+
+       if (attr->acct_multi_session_id) {
+               num_attr++;
+               if (attr->acct_multi_session_id_len != 17) {
+                       wpa_printf(MSG_DEBUG,
+                                  "RADIUS DAS: Acct-Multi-Session-Id cannot match");
+                       return NULL;
+               }
+               count = 0;
+
+               for (sta = hapd->sta_list; sta; sta = sta->next) {
+                       if (!sta->radius_das_match)
+                               continue;
+                       if (!sta->eapol_sm ||
+                           !sta->eapol_sm->acct_multi_session_id_hi) {
+                               sta->radius_das_match = 0;
+                               continue;
+                       }
+                       os_snprintf(buf, sizeof(buf), "%08X+%08X",
+                                   sta->eapol_sm->acct_multi_session_id_hi,
+                                   sta->eapol_sm->acct_multi_session_id_lo);
+                       if (os_memcmp(attr->acct_multi_session_id, buf, 17) !=
+                           0)
+                               sta->radius_das_match = 0;
+                       else
+                               count++;
                }
+
+               if (count == 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "RADIUS DAS: No matches remaining after Acct-Multi-Session-Id check");
+                       return NULL;
+               }
+               wpa_printf(MSG_DEBUG,
+                          "RADIUS DAS: Acct-Multi-Session-Id match");
        }
 
-       if (sta == NULL && attr->cui) {
+       if (attr->cui) {
+               num_attr++;
+               count = 0;
+
                for (sta = hapd->sta_list; sta; sta = sta->next) {
                        struct wpabuf *cui;
+
+                       if (!sta->radius_das_match)
+                               continue;
                        cui = ieee802_1x_get_radius_cui(sta->eapol_sm);
-                       if (cui && wpabuf_len(cui) == attr->cui_len &&
+                       if (!cui || wpabuf_len(cui) != attr->cui_len ||
                            os_memcmp(wpabuf_head(cui), attr->cui,
-                                     attr->cui_len) == 0)
-                               break;
+                                     attr->cui_len) != 0)
+                               sta->radius_das_match = 0;
+                       else
+                               count++;
+               }
+
+               if (count == 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "RADIUS DAS: No matches remaining after Chargeable-User-Identity check");
+                       return NULL;
                }
+               wpa_printf(MSG_DEBUG,
+                          "RADIUS DAS: Chargeable-User-Identity match");
        }
 
-       if (sta == NULL && attr->user_name) {
+       if (attr->user_name) {
+               num_attr++;
+               count = 0;
+
                for (sta = hapd->sta_list; sta; sta = sta->next) {
                        u8 *identity;
                        size_t identity_len;
+
+                       if (!sta->radius_das_match)
+                               continue;
                        identity = ieee802_1x_get_identity(sta->eapol_sm,
                                                           &identity_len);
-                       if (identity &&
-                           identity_len == attr->user_name_len &&
+                       if (!identity ||
+                           identity_len != attr->user_name_len ||
                            os_memcmp(identity, attr->user_name, identity_len)
-                           == 0)
-                               break;
+                           != 0)
+                               sta->radius_das_match = 0;
+                       else
+                               count++;
+               }
+
+               if (count == 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "RADIUS DAS: No matches remaining after User-Name check");
+                       return NULL;
+               }
+               wpa_printf(MSG_DEBUG,
+                          "RADIUS DAS: User-Name match");
+       }
+
+       if (num_attr == 0) {
+               /*
+                * In theory, we could match all current associations, but it
+                * seems safer to just reject requests that do not include any
+                * session identification attributes.
+                */
+               wpa_printf(MSG_DEBUG,
+                          "RADIUS DAS: No session identification attributes included");
+               return NULL;
+       }
+
+       selected = NULL;
+       for (sta = hapd->sta_list; sta; sta = sta->next) {
+               if (sta->radius_das_match) {
+                       if (selected) {
+                               *multi = 1;
+                               return NULL;
+                       }
+                       selected = sta;
                }
        }
 
-       return sta;
+       return selected;
+}
+
+
+static int hostapd_das_disconnect_pmksa(struct hostapd_data *hapd,
+                                       struct radius_das_attrs *attr)
+{
+       if (!hapd->wpa_auth)
+               return -1;
+       return wpa_auth_radius_das_disconnect_pmksa(hapd->wpa_auth, attr);
 }
 
 
@@ -599,13 +812,30 @@ hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr)
 {
        struct hostapd_data *hapd = ctx;
        struct sta_info *sta;
+       int multi;
 
        if (hostapd_das_nas_mismatch(hapd, attr))
                return RADIUS_DAS_NAS_MISMATCH;
 
-       sta = hostapd_das_find_sta(hapd, attr);
-       if (sta == NULL)
+       sta = hostapd_das_find_sta(hapd, attr, &multi);
+       if (sta == NULL) {
+               if (multi) {
+                       wpa_printf(MSG_DEBUG,
+                                  "RADIUS DAS: Multiple sessions match - not supported");
+                       return RADIUS_DAS_MULTI_SESSION_MATCH;
+               }
+               if (hostapd_das_disconnect_pmksa(hapd, attr) == 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "RADIUS DAS: PMKSA cache entry matched");
+                       return RADIUS_DAS_SUCCESS;
+               }
+               wpa_printf(MSG_DEBUG, "RADIUS DAS: No matching session found");
                return RADIUS_DAS_SESSION_NOT_FOUND;
+       }
+
+       wpa_printf(MSG_DEBUG, "RADIUS DAS: Found a matching session " MACSTR
+                  " - disconnecting", MAC2STR(sta->addr));
+       wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
 
        hostapd_drv_sta_deauth(hapd, sta->addr,
                               WLAN_REASON_PREV_AUTH_NOT_VALID);
@@ -620,7 +850,8 @@ hostapd_das_disconnect(void *ctx, struct radius_das_attrs *attr)
 /**
  * hostapd_setup_bss - Per-BSS setup (initialization)
  * @hapd: Pointer to BSS data
- * @first: Whether this BSS is the first BSS of an interface
+ * @first: Whether this BSS is the first BSS of an interface; -1 = not first,
+ *     but interface may exist
  *
  * This function is used to initialize all per-BSS data structures and
  * resources. This gets called in a loop for each BSS when an interface is
@@ -634,35 +865,54 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
        int ssid_len, set_ssid;
        char force_ifname[IFNAMSIZ];
        u8 if_addr[ETH_ALEN];
+       int flush_old_stations = 1;
+
+       wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s), first=%d)",
+                  __func__, hapd, conf->iface, first);
 
-       if (!first) {
-               if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) {
+#ifdef EAP_SERVER_TNC
+       if (conf->tnc && tncs_global_init() < 0) {
+               wpa_printf(MSG_ERROR, "Failed to initialize TNCS");
+               return -1;
+       }
+#endif /* EAP_SERVER_TNC */
+
+       if (hapd->started) {
+               wpa_printf(MSG_ERROR, "%s: Interface %s was already started",
+                          __func__, conf->iface);
+               return -1;
+       }
+       hapd->started = 1;
+
+       if (!first || first == -1) {
+               if (hostapd_mac_comp_empty(conf->bssid) == 0) {
                        /* Allocate the next available BSSID. */
                        do {
                                inc_byte_array(hapd->own_addr, ETH_ALEN);
                        } while (mac_in_conf(hapd->iconf, hapd->own_addr));
                } else {
                        /* Allocate the configured BSSID. */
-                       os_memcpy(hapd->own_addr, hapd->conf->bssid, ETH_ALEN);
+                       os_memcpy(hapd->own_addr, conf->bssid, ETH_ALEN);
 
                        if (hostapd_mac_comp(hapd->own_addr,
                                             hapd->iface->bss[0]->own_addr) ==
                            0) {
                                wpa_printf(MSG_ERROR, "BSS '%s' may not have "
                                           "BSSID set to the MAC address of "
-                                          "the radio", hapd->conf->iface);
+                                          "the radio", conf->iface);
                                return -1;
                        }
                }
 
                hapd->interface_added = 1;
                if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS,
-                                  hapd->conf->iface, hapd->own_addr, hapd,
+                                  conf->iface, hapd->own_addr, hapd,
                                   &hapd->drv_priv, force_ifname, if_addr,
-                                  hapd->conf->bridge[0] ? hapd->conf->bridge :
-                                  NULL)) {
+                                  conf->bridge[0] ? conf->bridge : NULL,
+                                  first == -1)) {
                        wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID="
                                   MACSTR ")", MAC2STR(hapd->own_addr));
+                       hapd->interface_added = 0;
                        return -1;
                }
        }
@@ -670,11 +920,18 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
        if (conf->wmm_enabled < 0)
                conf->wmm_enabled = hapd->iconf->ieee80211n;
 
-       hostapd_flush_old_stations(hapd, WLAN_REASON_PREV_AUTH_NOT_VALID);
+#ifdef CONFIG_MESH
+       if (hapd->iface->mconf == NULL)
+               flush_old_stations = 0;
+#endif /* CONFIG_MESH */
+
+       if (flush_old_stations)
+               hostapd_flush_old_stations(hapd,
+                                          WLAN_REASON_PREV_AUTH_NOT_VALID);
        hostapd_set_privacy(hapd, 0);
 
        hostapd_broadcast_wep_clear(hapd);
-       if (hostapd_setup_encryption(hapd->conf->iface, hapd))
+       if (hostapd_setup_encryption(conf->iface, hapd))
                return -1;
 
        /*
@@ -708,9 +965,8 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
        if (!hostapd_drv_none(hapd)) {
                wpa_printf(MSG_ERROR, "Using interface %s with hwaddr " MACSTR
                           " and ssid \"%s\"",
-                          hapd->conf->iface, MAC2STR(hapd->own_addr),
-                          wpa_ssid_txt(hapd->conf->ssid.ssid,
-                                       hapd->conf->ssid.ssid_len));
+                          conf->iface, MAC2STR(hapd->own_addr),
+                          wpa_ssid_txt(conf->ssid.ssid, conf->ssid.ssid_len));
        }
 
        if (hostapd_setup_wpa_psk(conf)) {
@@ -726,7 +982,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
                return -1;
        }
 
-       if (wpa_debug_level == MSG_MSGDUMP)
+       if (wpa_debug_level <= MSG_MSGDUMP)
                conf->radius->msg_dumps = 1;
 #ifndef CONFIG_NO_RADIUS
        hapd->radius = radius_client_init(hapd, conf->radius);
@@ -735,17 +991,17 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
                return -1;
        }
 
-       if (hapd->conf->radius_das_port) {
+       if (conf->radius_das_port) {
                struct radius_das_conf das_conf;
                os_memset(&das_conf, 0, sizeof(das_conf));
-               das_conf.port = hapd->conf->radius_das_port;
-               das_conf.shared_secret = hapd->conf->radius_das_shared_secret;
+               das_conf.port = conf->radius_das_port;
+               das_conf.shared_secret = conf->radius_das_shared_secret;
                das_conf.shared_secret_len =
-                       hapd->conf->radius_das_shared_secret_len;
-               das_conf.client_addr = &hapd->conf->radius_das_client_addr;
-               das_conf.time_window = hapd->conf->radius_das_time_window;
+                       conf->radius_das_shared_secret_len;
+               das_conf.client_addr = &conf->radius_das_client_addr;
+               das_conf.time_window = conf->radius_das_time_window;
                das_conf.require_event_timestamp =
-                       hapd->conf->radius_das_require_event_timestamp;
+                       conf->radius_das_require_event_timestamp;
                das_conf.ctx = hapd;
                das_conf.disconnect = hostapd_das_disconnect;
                hapd->radius_das = radius_das_init(&das_conf);
@@ -772,7 +1028,7 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
                return -1;
        }
 
-       if (hapd->conf->wpa && hostapd_setup_wpa(hapd))
+       if ((conf->wpa || conf->osen) && hostapd_setup_wpa(hapd))
                return -1;
 
        if (accounting_init(hapd)) {
@@ -780,8 +1036,8 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
                return -1;
        }
 
-       if (hapd->conf->ieee802_11f &&
-           (hapd->iapp = iapp_init(hapd, hapd->conf->iapp_iface)) == NULL) {
+       if (conf->ieee802_11f &&
+           (hapd->iapp = iapp_init(hapd, conf->iapp_iface)) == NULL) {
                wpa_printf(MSG_ERROR, "IEEE 802.11F (IAPP) initialization "
                           "failed.");
                return -1;
@@ -792,21 +1048,47 @@ static int hostapd_setup_bss(struct hostapd_data *hapd, int first)
                wpa_printf(MSG_ERROR, "GAS server initialization failed");
                return -1;
        }
+
+       if (conf->qos_map_set_len &&
+           hostapd_drv_set_qos_map(hapd, conf->qos_map_set,
+                                   conf->qos_map_set_len)) {
+               wpa_printf(MSG_ERROR, "Failed to initialize QoS Map");
+               return -1;
+       }
 #endif /* CONFIG_INTERWORKING */
 
-       if (hapd->iface->interfaces &&
-           hapd->iface->interfaces->ctrl_iface_init &&
-           hapd->iface->interfaces->ctrl_iface_init(hapd)) {
-               wpa_printf(MSG_ERROR, "Failed to setup control interface");
+       if (conf->bss_load_update_period && bss_load_update_init(hapd)) {
+               wpa_printf(MSG_ERROR, "BSS Load initialization failed");
                return -1;
        }
 
+       if (conf->proxy_arp) {
+               if (x_snoop_init(hapd)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Generic snooping infrastructure initialization failed");
+                       return -1;
+               }
+
+               if (dhcp_snoop_init(hapd)) {
+                       wpa_printf(MSG_ERROR,
+                                  "DHCP snooping initialization failed");
+                       return -1;
+               }
+
+               if (ndisc_snoop_init(hapd)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Neighbor Discovery snooping initialization failed");
+                       return -1;
+               }
+       }
+
        if (!hostapd_drv_none(hapd) && vlan_init(hapd)) {
                wpa_printf(MSG_ERROR, "VLAN initialization failed.");
                return -1;
        }
 
-       ieee802_11_set_beacon(hapd);
+       if (!conf->start_disabled && ieee802_11_set_beacon(hapd) < 0)
+               return -1;
 
        if (hapd->wpa_auth && wpa_init_keys(hapd->wpa_auth) < 0)
                return -1;
@@ -824,6 +1106,11 @@ static void hostapd_tx_queue_params(struct hostapd_iface *iface)
        int i;
        struct hostapd_tx_queue_params *p;
 
+#ifdef CONFIG_MESH
+       if (iface->mconf == NULL)
+               return;
+#endif /* CONFIG_MESH */
+
        for (i = 0; i < NUM_TX_QUEUES; i++) {
                p = &iface->conf->tx_queue[i];
 
@@ -872,36 +1159,93 @@ static void hostapd_set_acl(struct hostapd_data *hapd)
 
        if (hapd->iface->drv_max_acl_mac_addrs == 0)
                return;
-       if (!(conf->bss->num_accept_mac || conf->bss->num_deny_mac))
-               return;
 
-       if (conf->bss->macaddr_acl == DENY_UNLESS_ACCEPTED) {
-               if (conf->bss->num_accept_mac) {
-                       accept_acl = 1;
-                       err = hostapd_set_acl_list(hapd, conf->bss->accept_mac,
-                                                  conf->bss->num_accept_mac,
-                                                  accept_acl);
-                       if (err) {
-                               wpa_printf(MSG_DEBUG, "Failed to set accept acl");
-                               return;
-                       }
-               } else {
-                       wpa_printf(MSG_DEBUG, "Mismatch between ACL Policy & Accept/deny lists file");
+       if (conf->bss[0]->macaddr_acl == DENY_UNLESS_ACCEPTED) {
+               accept_acl = 1;
+               err = hostapd_set_acl_list(hapd, conf->bss[0]->accept_mac,
+                                          conf->bss[0]->num_accept_mac,
+                                          accept_acl);
+               if (err) {
+                       wpa_printf(MSG_DEBUG, "Failed to set accept acl");
+                       return;
                }
-       } else if (conf->bss->macaddr_acl == ACCEPT_UNLESS_DENIED) {
-               if (conf->bss->num_deny_mac) {
-                       accept_acl = 0;
-                       err = hostapd_set_acl_list(hapd, conf->bss->deny_mac,
-                                                  conf->bss->num_deny_mac,
-                                                  accept_acl);
-                       if (err) {
-                               wpa_printf(MSG_DEBUG, "Failed to set deny acl");
-                               return;
-                       }
-               } else {
-                       wpa_printf(MSG_DEBUG, "Mismatch between ACL Policy & Accept/deny lists file");
+       } else if (conf->bss[0]->macaddr_acl == ACCEPT_UNLESS_DENIED) {
+               accept_acl = 0;
+               err = hostapd_set_acl_list(hapd, conf->bss[0]->deny_mac,
+                                          conf->bss[0]->num_deny_mac,
+                                          accept_acl);
+               if (err) {
+                       wpa_printf(MSG_DEBUG, "Failed to set deny acl");
+                       return;
+               }
+       }
+}
+
+
+static int start_ctrl_iface_bss(struct hostapd_data *hapd)
+{
+       if (!hapd->iface->interfaces ||
+           !hapd->iface->interfaces->ctrl_iface_init)
+               return 0;
+
+       if (hapd->iface->interfaces->ctrl_iface_init(hapd)) {
+               wpa_printf(MSG_ERROR,
+                          "Failed to setup control interface for %s",
+                          hapd->conf->iface);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int start_ctrl_iface(struct hostapd_iface *iface)
+{
+       size_t i;
+
+       if (!iface->interfaces || !iface->interfaces->ctrl_iface_init)
+               return 0;
+
+       for (i = 0; i < iface->num_bss; i++) {
+               struct hostapd_data *hapd = iface->bss[i];
+               if (iface->interfaces->ctrl_iface_init(hapd)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Failed to setup control interface for %s",
+                                  hapd->conf->iface);
+                       return -1;
                }
        }
+
+       return 0;
+}
+
+
+static void channel_list_update_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct hostapd_iface *iface = eloop_ctx;
+
+       if (!iface->wait_channel_update) {
+               wpa_printf(MSG_INFO, "Channel list update timeout, but interface was not waiting for it");
+               return;
+       }
+
+       /*
+        * It is possible that the existing channel list is acceptable, so try
+        * to proceed.
+        */
+       wpa_printf(MSG_DEBUG, "Channel list update timeout - try to continue anyway");
+       setup_interface2(iface);
+}
+
+
+void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator)
+{
+       if (!iface->wait_channel_update || initiator != REGDOM_SET_BY_USER)
+               return;
+
+       wpa_printf(MSG_DEBUG, "Channel list updated - continue setup");
+       eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+       setup_interface2(iface);
 }
 
 
@@ -909,7 +1253,23 @@ static int setup_interface(struct hostapd_iface *iface)
 {
        struct hostapd_data *hapd = iface->bss[0];
        size_t i;
-       char country[4];
+
+       /*
+        * It is possible that setup_interface() is called after the interface
+        * was disabled etc., in which case driver_ap_teardown is possibly set
+        * to 1. Clear it here so any other key/station deletion, which is not
+        * part of a teardown flow, would also call the relevant driver
+        * callbacks.
+        */
+       iface->driver_ap_teardown = 0;
+
+       if (!iface->phy[0]) {
+               const char *phy = hostapd_drv_get_radio_name(hapd);
+               if (phy) {
+                       wpa_printf(MSG_DEBUG, "phy: %s", phy);
+                       os_strlcpy(iface->phy, phy, sizeof(iface->phy));
+               }
+       }
 
        /*
         * Make sure that all BSSes get configured with a pointer to the same
@@ -923,15 +1283,49 @@ static int setup_interface(struct hostapd_iface *iface)
        if (hostapd_validate_bssid_configuration(iface))
                return -1;
 
+       /*
+        * Initialize control interfaces early to allow external monitoring of
+        * channel setup operations that may take considerable amount of time
+        * especially for DFS cases.
+        */
+       if (start_ctrl_iface(iface))
+               return -1;
+
        if (hapd->iconf->country[0] && hapd->iconf->country[1]) {
+               char country[4], previous_country[4];
+
+               hostapd_set_state(iface, HAPD_IFACE_COUNTRY_UPDATE);
+               if (hostapd_get_country(hapd, previous_country) < 0)
+                       previous_country[0] = '\0';
+
                os_memcpy(country, hapd->iconf->country, 3);
                country[3] = '\0';
                if (hostapd_set_country(hapd, country) < 0) {
                        wpa_printf(MSG_ERROR, "Failed to set country code");
                        return -1;
                }
+
+               wpa_printf(MSG_DEBUG, "Previous country code %s, new country code %s",
+                          previous_country, country);
+
+               if (os_strncmp(previous_country, country, 2) != 0) {
+                       wpa_printf(MSG_DEBUG, "Continue interface setup after channel list update");
+                       iface->wait_channel_update = 1;
+                       eloop_register_timeout(5, 0,
+                                              channel_list_update_timeout,
+                                              iface, NULL);
+                       return 0;
+               }
        }
 
+       return setup_interface2(iface);
+}
+
+
+static int setup_interface2(struct hostapd_iface *iface)
+{
+       iface->wait_channel_update = 0;
+
        if (hostapd_get_hw_features(iface)) {
                /* Not all drivers support this yet, so continue without hw
                 * feature data. */
@@ -940,42 +1334,107 @@ static int setup_interface(struct hostapd_iface *iface)
                if (ret < 0) {
                        wpa_printf(MSG_ERROR, "Could not select hw_mode and "
                                   "channel. (%d)", ret);
-                       return -1;
+                       goto fail;
+               }
+               if (ret == 1) {
+                       wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback (ACS)");
+                       return 0;
                }
                ret = hostapd_check_ht_capab(iface);
                if (ret < 0)
-                       return -1;
+                       goto fail;
                if (ret == 1) {
                        wpa_printf(MSG_DEBUG, "Interface initialization will "
                                   "be completed in a callback");
                        return 0;
                }
+
+               if (iface->conf->ieee80211h)
+                       wpa_printf(MSG_DEBUG, "DFS support is enabled");
        }
        return hostapd_setup_interface_complete(iface, 0);
+
+fail:
+       hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+       wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+       if (iface->interfaces && iface->interfaces->terminate_on_error)
+               eloop_terminate();
+       return -1;
 }
 
 
+/**
+ * hostapd_setup_interface_complete - Complete interface setup
+ *
+ * This function is called when previous steps in the interface setup has been
+ * completed. This can also start operations, e.g., DFS, that will require
+ * additional processing before interface is ready to be enabled. Such
+ * operations will call this function from eloop callbacks when finished.
+ */
 int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
 {
        struct hostapd_data *hapd = iface->bss[0];
        size_t j;
        u8 *prev_addr;
+       int delay_apply_cfg = 0;
+       int res_dfs_offload = 0;
 
-       if (err) {
-               wpa_printf(MSG_ERROR, "Interface initialization failed");
-               eloop_terminate();
-               return -1;
-       }
+       if (err)
+               goto fail;
 
        wpa_printf(MSG_DEBUG, "Completing interface initialization");
-       if (hapd->iconf->channel) {
-               iface->freq = hostapd_hw_get_freq(hapd, hapd->iconf->channel);
+       if (iface->conf->channel) {
+#ifdef NEED_AP_MLME
+               int res;
+#endif /* NEED_AP_MLME */
+
+               iface->freq = hostapd_hw_get_freq(hapd, iface->conf->channel);
                wpa_printf(MSG_DEBUG, "Mode: %s  Channel: %d  "
                           "Frequency: %d MHz",
-                          hostapd_hw_mode_txt(hapd->iconf->hw_mode),
-                          hapd->iconf->channel, iface->freq);
+                          hostapd_hw_mode_txt(iface->conf->hw_mode),
+                          iface->conf->channel, iface->freq);
+
+#ifdef NEED_AP_MLME
+               /* Handle DFS only if it is not offloaded to the driver */
+               if (!(iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD)) {
+                       /* Check DFS */
+                       res = hostapd_handle_dfs(iface);
+                       if (res <= 0) {
+                               if (res < 0)
+                                       goto fail;
+                               return res;
+                       }
+               } else {
+                       /* If DFS is offloaded to the driver */
+                       res_dfs_offload = hostapd_handle_dfs_offload(iface);
+                       if (res_dfs_offload <= 0) {
+                               if (res_dfs_offload < 0)
+                                       goto fail;
+                       } else {
+                               wpa_printf(MSG_DEBUG,
+                                          "Proceed with AP/channel setup");
+                               /*
+                                * If this is a DFS channel, move to completing
+                                * AP setup.
+                                */
+                               if (res_dfs_offload == 1)
+                                       goto dfs_offload;
+                               /* Otherwise fall through. */
+                       }
+               }
+#endif /* NEED_AP_MLME */
+
+#ifdef CONFIG_MESH
+               if (iface->mconf != NULL) {
+                       wpa_printf(MSG_DEBUG,
+                                  "%s: Mesh configuration will be applied while joining the mesh network",
+                                  iface->bss[0]->conf->iface);
+                       delay_apply_cfg = 1;
+               }
+#endif /* CONFIG_MESH */
 
-               if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
+               if (!delay_apply_cfg &&
+                   hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq,
                                     hapd->iconf->channel,
                                     hapd->iconf->ieee80211n,
                                     hapd->iconf->ieee80211ac,
@@ -985,7 +1444,7 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
                                     hapd->iconf->vht_oper_centr_freq_seg1_idx)) {
                        wpa_printf(MSG_ERROR, "Could not set channel for "
                                   "kernel driver");
-                       return -1;
+                       goto fail;
                }
        }
 
@@ -996,7 +1455,7 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
                        hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
                                       HOSTAPD_LEVEL_WARNING,
                                       "Failed to prepare rates table.");
-                       return -1;
+                       goto fail;
                }
        }
 
@@ -1004,14 +1463,14 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
            hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) {
                wpa_printf(MSG_ERROR, "Could not set RTS threshold for "
                           "kernel driver");
-               return -1;
+               goto fail;
        }
 
        if (hapd->iconf->fragm_threshold > -1 &&
            hostapd_set_frag(hapd, hapd->iconf->fragm_threshold)) {
                wpa_printf(MSG_ERROR, "Could not set fragmentation threshold "
                           "for kernel driver");
-               return -1;
+               goto fail;
        }
 
        prev_addr = hapd->own_addr;
@@ -1020,11 +1479,18 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
                hapd = iface->bss[j];
                if (j)
                        os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN);
-               if (hostapd_setup_bss(hapd, j == 0))
-                       return -1;
+               if (hostapd_setup_bss(hapd, j == 0)) {
+                       do {
+                               hapd = iface->bss[j];
+                               hostapd_bss_deinit_no_free(hapd);
+                               hostapd_free_hapd_data(hapd);
+                       } while (j-- > 0);
+                       goto fail;
+               }
                if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0)
                        prev_addr = hapd->own_addr;
        }
+       hapd = iface->bss[0];
 
        hostapd_tx_queue_params(iface);
 
@@ -1035,7 +1501,7 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
        if (hostapd_driver_commit(hapd) < 0) {
                wpa_printf(MSG_ERROR, "%s: Failed to commit driver "
                           "configuration", __func__);
-               return -1;
+               goto fail;
        }
 
        /*
@@ -1046,16 +1512,41 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
         */
        for (j = 0; j < iface->num_bss; j++) {
                if (hostapd_init_wps_complete(iface->bss[j]))
-                       return -1;
+                       goto fail;
+       }
+
+       if ((iface->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+           !res_dfs_offload) {
+               /*
+                * If freq is DFS, and DFS is offloaded to the driver, then wait
+                * for CAC to complete.
+                */
+               wpa_printf(MSG_DEBUG, "%s: Wait for CAC to complete", __func__);
+               return res_dfs_offload;
        }
 
+#ifdef NEED_AP_MLME
+dfs_offload:
+#endif /* NEED_AP_MLME */
+       hostapd_set_state(iface, HAPD_IFACE_ENABLED);
+       wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_ENABLED);
        if (hapd->setup_complete_cb)
                hapd->setup_complete_cb(hapd->setup_complete_cb_ctx);
 
        wpa_printf(MSG_DEBUG, "%s: Setup of interface done.",
                   iface->bss[0]->conf->iface);
+       if (iface->interfaces && iface->interfaces->terminate_on_error > 0)
+               iface->interfaces->terminate_on_error--;
 
        return 0;
+
+fail:
+       wpa_printf(MSG_ERROR, "Interface initialization failed");
+       hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+       wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+       if (iface->interfaces && iface->interfaces->terminate_on_error)
+               eloop_terminate();
+       return -1;
 }
 
 
@@ -1068,6 +1559,12 @@ int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err)
  * and sets driver parameters based on the configuration.
  * Flushes old stations, sets the channel, encryption,
  * beacons, and WDS links based on the configuration.
+ *
+ * If interface setup requires more time, e.g., to perform HT co-ex scans, ACS,
+ * or DFS operations, this function returns 0 before such operations have been
+ * completed. The pending operations are registered into eloop and will be
+ * completed from eloop callbacks. Those callbacks end up calling
+ * hostapd_setup_interface_complete() once setup has been completed.
  */
 int hostapd_setup_interface(struct hostapd_iface *iface)
 {
@@ -1117,68 +1614,327 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
 }
 
 
+static void hostapd_bss_deinit(struct hostapd_data *hapd)
+{
+       wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__,
+                  hapd->conf->iface);
+       hostapd_bss_deinit_no_free(hapd);
+       wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
+       hostapd_cleanup(hapd);
+}
+
+
 void hostapd_interface_deinit(struct hostapd_iface *iface)
 {
-       size_t j;
+       int j;
 
+       wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
        if (iface == NULL)
                return;
 
-       hostapd_cleanup_iface_pre(iface);
-       for (j = 0; j < iface->num_bss; j++) {
-               struct hostapd_data *hapd = iface->bss[j];
-               hostapd_free_stas(hapd);
-               hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
-               hostapd_clear_wep(hapd);
-               hostapd_cleanup(hapd);
-       }
+       hostapd_set_state(iface, HAPD_IFACE_DISABLED);
+
+#ifdef CONFIG_IEEE80211N
+#ifdef NEED_AP_MLME
+       hostapd_stop_setup_timers(iface);
+       eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_IEEE80211N */
+       eloop_cancel_timeout(channel_list_update_timeout, iface, NULL);
+       iface->wait_channel_update = 0;
+
+       for (j = iface->num_bss - 1; j >= 0; j--)
+               hostapd_bss_deinit(iface->bss[j]);
 }
 
 
 void hostapd_interface_free(struct hostapd_iface *iface)
 {
        size_t j;
-       for (j = 0; j < iface->num_bss; j++)
+       wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
+       for (j = 0; j < iface->num_bss; j++) {
+               wpa_printf(MSG_DEBUG, "%s: free hapd %p",
+                          __func__, iface->bss[j]);
                os_free(iface->bss[j]);
+       }
        hostapd_cleanup_iface(iface);
 }
 
 
-#ifdef HOSTAPD
+/**
+ * hostapd_init - Allocate and initialize per-interface data
+ * @config_file: Path to the configuration file
+ * Returns: Pointer to the allocated interface data or %NULL on failure
+ *
+ * This function is used to allocate main data structures for per-interface
+ * data. The allocated data buffer will be freed by calling
+ * hostapd_cleanup_iface().
+ */
+struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
+                                   const char *config_file)
+{
+       struct hostapd_iface *hapd_iface = NULL;
+       struct hostapd_config *conf = NULL;
+       struct hostapd_data *hapd;
+       size_t i;
+
+       hapd_iface = os_zalloc(sizeof(*hapd_iface));
+       if (hapd_iface == NULL)
+               goto fail;
+
+       hapd_iface->config_fname = os_strdup(config_file);
+       if (hapd_iface->config_fname == NULL)
+               goto fail;
+
+       conf = interfaces->config_read_cb(hapd_iface->config_fname);
+       if (conf == NULL)
+               goto fail;
+       hapd_iface->conf = conf;
+
+       hapd_iface->num_bss = conf->num_bss;
+       hapd_iface->bss = os_calloc(conf->num_bss,
+                                   sizeof(struct hostapd_data *));
+       if (hapd_iface->bss == NULL)
+               goto fail;
+
+       for (i = 0; i < conf->num_bss; i++) {
+               hapd = hapd_iface->bss[i] =
+                       hostapd_alloc_bss_data(hapd_iface, conf,
+                                              conf->bss[i]);
+               if (hapd == NULL)
+                       goto fail;
+               hapd->msg_ctx = hapd;
+       }
+
+       return hapd_iface;
+
+fail:
+       wpa_printf(MSG_ERROR, "Failed to set up interface with %s",
+                  config_file);
+       if (conf)
+               hostapd_config_free(conf);
+       if (hapd_iface) {
+               os_free(hapd_iface->config_fname);
+               os_free(hapd_iface->bss);
+               wpa_printf(MSG_DEBUG, "%s: free iface %p",
+                          __func__, hapd_iface);
+               os_free(hapd_iface);
+       }
+       return NULL;
+}
+
+
+static int ifname_in_use(struct hapd_interfaces *interfaces, const char *ifname)
+{
+       size_t i, j;
+
+       for (i = 0; i < interfaces->count; i++) {
+               struct hostapd_iface *iface = interfaces->iface[i];
+               for (j = 0; j < iface->num_bss; j++) {
+                       struct hostapd_data *hapd = iface->bss[j];
+                       if (os_strcmp(ifname, hapd->conf->iface) == 0)
+                               return 1;
+               }
+       }
+
+       return 0;
+}
+
+
+/**
+ * hostapd_interface_init_bss - Read configuration file and init BSS data
+ *
+ * This function is used to parse configuration file for a BSS. This BSS is
+ * added to an existing interface sharing the same radio (if any) or a new
+ * interface is created if this is the first interface on a radio. This
+ * allocate memory for the BSS. No actual driver operations are started.
+ *
+ * This is similar to hostapd_interface_init(), but for a case where the
+ * configuration is used to add a single BSS instead of all BSSes for a radio.
+ */
+struct hostapd_iface *
+hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
+                          const char *config_fname, int debug)
+{
+       struct hostapd_iface *new_iface = NULL, *iface = NULL;
+       struct hostapd_data *hapd;
+       int k;
+       size_t i, bss_idx;
+
+       if (!phy || !*phy)
+               return NULL;
+
+       for (i = 0; i < interfaces->count; i++) {
+               if (os_strcmp(interfaces->iface[i]->phy, phy) == 0) {
+                       iface = interfaces->iface[i];
+                       break;
+               }
+       }
+
+       wpa_printf(MSG_INFO, "Configuration file: %s (phy %s)%s",
+                  config_fname, phy, iface ? "" : " --> new PHY");
+       if (iface) {
+               struct hostapd_config *conf;
+               struct hostapd_bss_config **tmp_conf;
+               struct hostapd_data **tmp_bss;
+               struct hostapd_bss_config *bss;
+               const char *ifname;
+
+               /* Add new BSS to existing iface */
+               conf = interfaces->config_read_cb(config_fname);
+               if (conf == NULL)
+                       return NULL;
+               if (conf->num_bss > 1) {
+                       wpa_printf(MSG_ERROR, "Multiple BSSes specified in BSS-config");
+                       hostapd_config_free(conf);
+                       return NULL;
+               }
+
+               ifname = conf->bss[0]->iface;
+               if (ifname[0] != '\0' && ifname_in_use(interfaces, ifname)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Interface name %s already in use", ifname);
+                       hostapd_config_free(conf);
+                       return NULL;
+               }
+
+               tmp_conf = os_realloc_array(
+                       iface->conf->bss, iface->conf->num_bss + 1,
+                       sizeof(struct hostapd_bss_config *));
+               tmp_bss = os_realloc_array(iface->bss, iface->num_bss + 1,
+                                          sizeof(struct hostapd_data *));
+               if (tmp_bss)
+                       iface->bss = tmp_bss;
+               if (tmp_conf) {
+                       iface->conf->bss = tmp_conf;
+                       iface->conf->last_bss = tmp_conf[0];
+               }
+               if (tmp_bss == NULL || tmp_conf == NULL) {
+                       hostapd_config_free(conf);
+                       return NULL;
+               }
+               bss = iface->conf->bss[iface->conf->num_bss] = conf->bss[0];
+               iface->conf->num_bss++;
+
+               hapd = hostapd_alloc_bss_data(iface, iface->conf, bss);
+               if (hapd == NULL) {
+                       iface->conf->num_bss--;
+                       hostapd_config_free(conf);
+                       return NULL;
+               }
+               iface->conf->last_bss = bss;
+               iface->bss[iface->num_bss] = hapd;
+               hapd->msg_ctx = hapd;
+
+               bss_idx = iface->num_bss++;
+               conf->num_bss--;
+               conf->bss[0] = NULL;
+               hostapd_config_free(conf);
+       } else {
+               /* Add a new iface with the first BSS */
+               new_iface = iface = hostapd_init(interfaces, config_fname);
+               if (!iface)
+                       return NULL;
+               os_strlcpy(iface->phy, phy, sizeof(iface->phy));
+               iface->interfaces = interfaces;
+               bss_idx = 0;
+       }
+
+       for (k = 0; k < debug; k++) {
+               if (iface->bss[bss_idx]->conf->logger_stdout_level > 0)
+                       iface->bss[bss_idx]->conf->logger_stdout_level--;
+       }
+
+       if (iface->conf->bss[bss_idx]->iface[0] == '\0' &&
+           !hostapd_drv_none(iface->bss[bss_idx])) {
+               wpa_printf(MSG_ERROR, "Interface name not specified in %s",
+                          config_fname);
+               if (new_iface)
+                       hostapd_interface_deinit_free(new_iface);
+               return NULL;
+       }
+
+       return iface;
+}
+
 
 void hostapd_interface_deinit_free(struct hostapd_iface *iface)
 {
        const struct wpa_driver_ops *driver;
        void *drv_priv;
+
+       wpa_printf(MSG_DEBUG, "%s(%p)", __func__, iface);
        if (iface == NULL)
                return;
+       wpa_printf(MSG_DEBUG, "%s: num_bss=%u conf->num_bss=%u",
+                  __func__, (unsigned int) iface->num_bss,
+                  (unsigned int) iface->conf->num_bss);
        driver = iface->bss[0]->driver;
        drv_priv = iface->bss[0]->drv_priv;
        hostapd_interface_deinit(iface);
-       if (driver && driver->hapd_deinit && drv_priv)
+       wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
+                  __func__, driver, drv_priv);
+       if (driver && driver->hapd_deinit && drv_priv) {
                driver->hapd_deinit(drv_priv);
+               iface->bss[0]->drv_priv = NULL;
+       }
        hostapd_interface_free(iface);
 }
 
 
+static void hostapd_deinit_driver(const struct wpa_driver_ops *driver,
+                                 void *drv_priv,
+                                 struct hostapd_iface *hapd_iface)
+{
+       size_t j;
+
+       wpa_printf(MSG_DEBUG, "%s: driver=%p drv_priv=%p -> hapd_deinit",
+                  __func__, driver, drv_priv);
+       if (driver && driver->hapd_deinit && drv_priv) {
+               driver->hapd_deinit(drv_priv);
+               for (j = 0; j < hapd_iface->num_bss; j++) {
+                       wpa_printf(MSG_DEBUG, "%s:bss[%d]->drv_priv=%p",
+                                  __func__, (int) j,
+                                  hapd_iface->bss[j]->drv_priv);
+                       if (hapd_iface->bss[j]->drv_priv == drv_priv)
+                               hapd_iface->bss[j]->drv_priv = NULL;
+               }
+       }
+}
+
+
 int hostapd_enable_iface(struct hostapd_iface *hapd_iface)
 {
+       size_t j;
+
        if (hapd_iface->bss[0]->drv_priv != NULL) {
                wpa_printf(MSG_ERROR, "Interface %s already enabled",
-                          hapd_iface->conf->bss[0].iface);
+                          hapd_iface->conf->bss[0]->iface);
                return -1;
        }
 
        wpa_printf(MSG_DEBUG, "Enable interface %s",
-                  hapd_iface->conf->bss[0].iface);
+                  hapd_iface->conf->bss[0]->iface);
+
+       for (j = 0; j < hapd_iface->num_bss; j++)
+               hostapd_set_security_params(hapd_iface->conf->bss[j], 1);
+       if (hostapd_config_check(hapd_iface->conf, 1) < 0) {
+               wpa_printf(MSG_INFO, "Invalid configuration - cannot enable");
+               return -1;
+       }
 
        if (hapd_iface->interfaces == NULL ||
            hapd_iface->interfaces->driver_init == NULL ||
-           hapd_iface->interfaces->driver_init(hapd_iface) ||
-           hostapd_setup_interface(hapd_iface)) {
-               hostapd_interface_deinit_free(hapd_iface);
+           hapd_iface->interfaces->driver_init(hapd_iface))
+               return -1;
+
+       if (hostapd_setup_interface(hapd_iface)) {
+               hostapd_deinit_driver(hapd_iface->bss[0]->driver,
+                                     hapd_iface->bss[0]->drv_priv,
+                                     hapd_iface);
                return -1;
        }
+
        return 0;
 }
 
@@ -1188,19 +1944,17 @@ int hostapd_reload_iface(struct hostapd_iface *hapd_iface)
        size_t j;
 
        wpa_printf(MSG_DEBUG, "Reload interface %s",
-                  hapd_iface->conf->bss[0].iface);
-       for (j = 0; j < hapd_iface->num_bss; j++) {
-               hostapd_flush_old_stations(hapd_iface->bss[j],
-                                          WLAN_REASON_PREV_AUTH_NOT_VALID);
-
-#ifndef CONFIG_NO_RADIUS
-               /* TODO: update dynamic data based on changed configuration
-                * items (e.g., open/close sockets, etc.) */
-               radius_client_flush(hapd_iface->bss[j]->radius, 0);
-#endif  /* CONFIG_NO_RADIUS */
-
-               hostapd_reload_bss(hapd_iface->bss[j]);
+                  hapd_iface->conf->bss[0]->iface);
+       for (j = 0; j < hapd_iface->num_bss; j++)
+               hostapd_set_security_params(hapd_iface->conf->bss[j], 1);
+       if (hostapd_config_check(hapd_iface->conf, 1) < 0) {
+               wpa_printf(MSG_ERROR, "Updated configuration is invalid");
+               return -1;
        }
+       hostapd_clear_old(hapd_iface);
+       for (j = 0; j < hapd_iface->num_bss; j++)
+               hostapd_reload_bss(hapd_iface->bss[j]);
+
        return 0;
 }
 
@@ -1208,39 +1962,43 @@ int hostapd_reload_iface(struct hostapd_iface *hapd_iface)
 int hostapd_disable_iface(struct hostapd_iface *hapd_iface)
 {
        size_t j;
-       struct hostapd_bss_config *bss;
        const struct wpa_driver_ops *driver;
        void *drv_priv;
 
        if (hapd_iface == NULL)
                return -1;
-       bss = hapd_iface->bss[0]->conf;
+
+       if (hapd_iface->bss[0]->drv_priv == NULL) {
+               wpa_printf(MSG_INFO, "Interface %s already disabled",
+                          hapd_iface->conf->bss[0]->iface);
+               return -1;
+       }
+
+       wpa_msg(hapd_iface->bss[0]->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
        driver = hapd_iface->bss[0]->driver;
        drv_priv = hapd_iface->bss[0]->drv_priv;
 
-       /* whatever hostapd_interface_deinit does */
+       hapd_iface->driver_ap_teardown =
+               !!(hapd_iface->drv_flags &
+                  WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+
+       /* same as hostapd_interface_deinit without deinitializing ctrl-iface */
        for (j = 0; j < hapd_iface->num_bss; j++) {
                struct hostapd_data *hapd = hapd_iface->bss[j];
-               hostapd_free_stas(hapd);
-               hostapd_flush_old_stations(hapd, WLAN_REASON_DEAUTH_LEAVING);
-               hostapd_clear_wep(hapd);
+               hostapd_bss_deinit_no_free(hapd);
                hostapd_free_hapd_data(hapd);
        }
 
-       if (driver && driver->hapd_deinit && drv_priv) {
-               driver->hapd_deinit(drv_priv);
-               hapd_iface->bss[0]->drv_priv = NULL;
-       }
+       hostapd_deinit_driver(driver, drv_priv, hapd_iface);
 
        /* From hostapd_cleanup_iface: These were initialized in
         * hostapd_setup_interface and hostapd_setup_interface_complete
         */
        hostapd_cleanup_iface_partial(hapd_iface);
-       bss->wpa = 0;
-       bss->wpa_key_mgmt = -1;
-       bss->wpa_pairwise = -1;
 
-       wpa_printf(MSG_DEBUG, "Interface %s disabled", bss->iface);
+       wpa_printf(MSG_DEBUG, "Interface %s disabled",
+                  hapd_iface->bss[0]->conf->iface);
+       hostapd_set_state(hapd_iface, HAPD_IFACE_DISABLED);
        return 0;
 }
 
@@ -1291,7 +2049,7 @@ hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname,
                return NULL;
        }
 
-       bss = conf->last_bss = conf->bss;
+       bss = conf->last_bss = conf->bss[0];
 
        os_strlcpy(bss->iface, ifname, sizeof(bss->iface));
        bss->ctrl_interface = os_strdup(ctrl_iface);
@@ -1308,51 +2066,129 @@ hostapd_config_alloc(struct hapd_interfaces *interfaces, const char *ifname,
 }
 
 
-static struct hostapd_iface * hostapd_data_alloc(
-       struct hapd_interfaces *interfaces, struct hostapd_config *conf)
+static int hostapd_data_alloc(struct hostapd_iface *hapd_iface,
+                             struct hostapd_config *conf)
 {
        size_t i;
-       struct hostapd_iface *hapd_iface =
-               interfaces->iface[interfaces->count - 1];
        struct hostapd_data *hapd;
 
-       hapd_iface->conf = conf;
-       hapd_iface->num_bss = conf->num_bss;
-
-       hapd_iface->bss = os_zalloc(conf->num_bss *
+       hapd_iface->bss = os_calloc(conf->num_bss,
                                    sizeof(struct hostapd_data *));
        if (hapd_iface->bss == NULL)
-               return NULL;
+               return -1;
 
        for (i = 0; i < conf->num_bss; i++) {
                hapd = hapd_iface->bss[i] =
-                       hostapd_alloc_bss_data(hapd_iface, conf,
-                                              &conf->bss[i]);
-               if (hapd == NULL)
-                       return NULL;
+                       hostapd_alloc_bss_data(hapd_iface, conf, conf->bss[i]);
+               if (hapd == NULL) {
+                       while (i > 0) {
+                               i--;
+                               os_free(hapd_iface->bss[i]);
+                               hapd_iface->bss[i] = NULL;
+                       }
+                       os_free(hapd_iface->bss);
+                       hapd_iface->bss = NULL;
+                       return -1;
+               }
                hapd->msg_ctx = hapd;
        }
 
-       hapd_iface->interfaces = interfaces;
+       hapd_iface->conf = conf;
+       hapd_iface->num_bss = conf->num_bss;
 
-       return hapd_iface;
+       return 0;
 }
 
 
 int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf)
 {
        struct hostapd_config *conf = NULL;
-       struct hostapd_iface *hapd_iface = NULL;
+       struct hostapd_iface *hapd_iface = NULL, *new_iface = NULL;
+       struct hostapd_data *hapd;
        char *ptr;
-       size_t i;
+       size_t i, j;
+       const char *conf_file = NULL, *phy_name = NULL;
+
+       if (os_strncmp(buf, "bss_config=", 11) == 0) {
+               char *pos;
+               phy_name = buf + 11;
+               pos = os_strchr(phy_name, ':');
+               if (!pos)
+                       return -1;
+               *pos++ = '\0';
+               conf_file = pos;
+               if (!os_strlen(conf_file))
+                       return -1;
+
+               hapd_iface = hostapd_interface_init_bss(interfaces, phy_name,
+                                                       conf_file, 0);
+               if (!hapd_iface)
+                       return -1;
+               for (j = 0; j < interfaces->count; j++) {
+                       if (interfaces->iface[j] == hapd_iface)
+                               break;
+               }
+               if (j == interfaces->count) {
+                       struct hostapd_iface **tmp;
+                       tmp = os_realloc_array(interfaces->iface,
+                                              interfaces->count + 1,
+                                              sizeof(struct hostapd_iface *));
+                       if (!tmp) {
+                               hostapd_interface_deinit_free(hapd_iface);
+                               return -1;
+                       }
+                       interfaces->iface = tmp;
+                       interfaces->iface[interfaces->count++] = hapd_iface;
+                       new_iface = hapd_iface;
+               }
+
+               if (new_iface) {
+                       if (interfaces->driver_init(hapd_iface))
+                               goto fail;
+
+                       if (hostapd_setup_interface(hapd_iface)) {
+                               hostapd_deinit_driver(
+                                       hapd_iface->bss[0]->driver,
+                                       hapd_iface->bss[0]->drv_priv,
+                                       hapd_iface);
+                               goto fail;
+                       }
+               } else {
+                       /* Assign new BSS with bss[0]'s driver info */
+                       hapd = hapd_iface->bss[hapd_iface->num_bss - 1];
+                       hapd->driver = hapd_iface->bss[0]->driver;
+                       hapd->drv_priv = hapd_iface->bss[0]->drv_priv;
+                       os_memcpy(hapd->own_addr, hapd_iface->bss[0]->own_addr,
+                                 ETH_ALEN);
+
+                       if (start_ctrl_iface_bss(hapd) < 0 ||
+                           (hapd_iface->state == HAPD_IFACE_ENABLED &&
+                            hostapd_setup_bss(hapd, -1))) {
+                               hostapd_cleanup(hapd);
+                               hapd_iface->bss[hapd_iface->num_bss - 1] = NULL;
+                               hapd_iface->conf->num_bss--;
+                               hapd_iface->num_bss--;
+                               wpa_printf(MSG_DEBUG, "%s: free hapd %p %s",
+                                          __func__, hapd, hapd->conf->iface);
+                               hostapd_config_free_bss(hapd->conf);
+                               hapd->conf = NULL;
+                               os_free(hapd);
+                               return -1;
+                       }
+               }
+               return 0;
+       }
 
        ptr = os_strchr(buf, ' ');
        if (ptr == NULL)
                return -1;
        *ptr++ = '\0';
 
+       if (os_strncmp(ptr, "config=", 7) == 0)
+               conf_file = ptr + 7;
+
        for (i = 0; i < interfaces->count; i++) {
-               if (!os_strcmp(interfaces->iface[i]->conf->bss[0].iface,
+               if (!os_strcmp(interfaces->iface[i]->conf->bss[0]->iface,
                               buf)) {
                        wpa_printf(MSG_INFO, "Cannot add interface - it "
                                   "already exists");
@@ -1366,29 +2202,33 @@ int hostapd_add_iface(struct hapd_interfaces *interfaces, char *buf)
                           "for interface", __func__);
                goto fail;
        }
+       new_iface = hapd_iface;
 
-       conf = hostapd_config_alloc(interfaces, buf, ptr);
-       if (conf == NULL) {
+       if (conf_file && interfaces->config_read_cb) {
+               conf = interfaces->config_read_cb(conf_file);
+               if (conf && conf->bss)
+                       os_strlcpy(conf->bss[0]->iface, buf,
+                                  sizeof(conf->bss[0]->iface));
+       } else
+               conf = hostapd_config_alloc(interfaces, buf, ptr);
+       if (conf == NULL || conf->bss == NULL) {
                wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
                           "for configuration", __func__);
                goto fail;
        }
 
-       hapd_iface = hostapd_data_alloc(interfaces, conf);
-       if (hapd_iface == NULL) {
+       if (hostapd_data_alloc(hapd_iface, conf) < 0) {
                wpa_printf(MSG_ERROR, "%s: Failed to allocate memory "
                           "for hostapd", __func__);
                goto fail;
        }
+       conf = NULL;
 
-       if (hapd_iface->interfaces &&
-           hapd_iface->interfaces->ctrl_iface_init &&
-           hapd_iface->interfaces->ctrl_iface_init(hapd_iface->bss[0])) {
-               wpa_printf(MSG_ERROR, "%s: Failed to setup control "
-                          "interface", __func__);
+       if (start_ctrl_iface(hapd_iface) < 0)
                goto fail;
-       }
-       wpa_printf(MSG_INFO, "Add interface '%s'", conf->bss[0].iface);
+
+       wpa_printf(MSG_INFO, "Add interface '%s'",
+                  hapd_iface->conf->bss[0]->iface);
 
        return 0;
 
@@ -1396,24 +2236,84 @@ fail:
        if (conf)
                hostapd_config_free(conf);
        if (hapd_iface) {
-               os_free(hapd_iface->bss[interfaces->count]);
-               os_free(hapd_iface);
+               if (hapd_iface->bss) {
+                       for (i = 0; i < hapd_iface->num_bss; i++) {
+                               hapd = hapd_iface->bss[i];
+                               if (!hapd)
+                                       continue;
+                               if (hapd_iface->interfaces &&
+                                   hapd_iface->interfaces->ctrl_iface_deinit)
+                                       hapd_iface->interfaces->
+                                               ctrl_iface_deinit(hapd);
+                               wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)",
+                                          __func__, hapd_iface->bss[i],
+                                          hapd->conf->iface);
+                               hostapd_cleanup(hapd);
+                               os_free(hapd);
+                               hapd_iface->bss[i] = NULL;
+                       }
+                       os_free(hapd_iface->bss);
+                       hapd_iface->bss = NULL;
+               }
+               if (new_iface) {
+                       interfaces->count--;
+                       interfaces->iface[interfaces->count] = NULL;
+               }
+               hostapd_cleanup_iface(hapd_iface);
        }
        return -1;
 }
 
 
+static int hostapd_remove_bss(struct hostapd_iface *iface, unsigned int idx)
+{
+       size_t i;
+
+       wpa_printf(MSG_INFO, "Remove BSS '%s'", iface->conf->bss[idx]->iface);
+
+       /* Remove hostapd_data only if it has already been initialized */
+       if (idx < iface->num_bss) {
+               struct hostapd_data *hapd = iface->bss[idx];
+
+               hostapd_bss_deinit(hapd);
+               wpa_printf(MSG_DEBUG, "%s: free hapd %p (%s)",
+                          __func__, hapd, hapd->conf->iface);
+               hostapd_config_free_bss(hapd->conf);
+               hapd->conf = NULL;
+               os_free(hapd);
+
+               iface->num_bss--;
+
+               for (i = idx; i < iface->num_bss; i++)
+                       iface->bss[i] = iface->bss[i + 1];
+       } else {
+               hostapd_config_free_bss(iface->conf->bss[idx]);
+               iface->conf->bss[idx] = NULL;
+       }
+
+       iface->conf->num_bss--;
+       for (i = idx; i < iface->conf->num_bss; i++)
+               iface->conf->bss[i] = iface->conf->bss[i + 1];
+
+       return 0;
+}
+
+
 int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
 {
        struct hostapd_iface *hapd_iface;
-       size_t i, k = 0;
+       size_t i, j, k = 0;
 
        for (i = 0; i < interfaces->count; i++) {
                hapd_iface = interfaces->iface[i];
                if (hapd_iface == NULL)
                        return -1;
-               if (!os_strcmp(hapd_iface->conf->bss[0].iface, buf)) {
+               if (!os_strcmp(hapd_iface->conf->bss[0]->iface, buf)) {
                        wpa_printf(MSG_INFO, "Remove interface '%s'", buf);
+                       hapd_iface->driver_ap_teardown =
+                               !!(hapd_iface->drv_flags &
+                                  WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+
                        hostapd_interface_deinit_free(hapd_iface);
                        k = i;
                        while (k < (interfaces->count - 1)) {
@@ -1424,12 +2324,19 @@ int hostapd_remove_iface(struct hapd_interfaces *interfaces, char *buf)
                        interfaces->count--;
                        return 0;
                }
+
+               for (j = 0; j < hapd_iface->conf->num_bss; j++) {
+                       if (!os_strcmp(hapd_iface->conf->bss[j]->iface, buf)) {
+                               hapd_iface->driver_ap_teardown =
+                                       !(hapd_iface->drv_flags &
+                                         WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+                               return hostapd_remove_bss(hapd_iface, j);
+                       }
+               }
        }
        return -1;
 }
 
-#endif /* HOSTAPD */
-
 
 /**
  * hostapd_new_assoc_sta - Notify that a new station associated with the AP
@@ -1469,8 +2376,9 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
        /* Start accounting here, if IEEE 802.1X and WPA are not used.
         * IEEE 802.1X/WPA code will start accounting after the station has
         * been authorized. */
-       if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) {
-               os_get_time(&sta->connected_time);
+       if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) {
+               ap_sta_set_authorized(hapd, sta, 1);
+               os_get_reltime(&sta->connected_time);
                accounting_sta_start(hapd, sta);
        }
 
@@ -1483,11 +2391,335 @@ void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
        } else
                wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm);
 
-       wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
-                  "for " MACSTR " (%d seconds - ap_max_inactivity)",
-                  __func__, MAC2STR(sta->addr),
-                  hapd->conf->ap_max_inactivity);
-       eloop_cancel_timeout(ap_handle_timer, hapd, sta);
-       eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
-                              ap_handle_timer, hapd, sta);
+       if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
+               wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
+                          "for " MACSTR " (%d seconds - ap_max_inactivity)",
+                          __func__, MAC2STR(sta->addr),
+                          hapd->conf->ap_max_inactivity);
+               eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+               eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
+                                      ap_handle_timer, hapd, sta);
+       }
+}
+
+
+const char * hostapd_state_text(enum hostapd_iface_state s)
+{
+       switch (s) {
+       case HAPD_IFACE_UNINITIALIZED:
+               return "UNINITIALIZED";
+       case HAPD_IFACE_DISABLED:
+               return "DISABLED";
+       case HAPD_IFACE_COUNTRY_UPDATE:
+               return "COUNTRY_UPDATE";
+       case HAPD_IFACE_ACS:
+               return "ACS";
+       case HAPD_IFACE_HT_SCAN:
+               return "HT_SCAN";
+       case HAPD_IFACE_DFS:
+               return "DFS";
+       case HAPD_IFACE_ENABLED:
+               return "ENABLED";
+       }
+
+       return "UNKNOWN";
+}
+
+
+void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s)
+{
+       wpa_printf(MSG_INFO, "%s: interface state %s->%s",
+                  iface->conf->bss[0]->iface, hostapd_state_text(iface->state),
+                  hostapd_state_text(s));
+       iface->state = s;
+}
+
+
+#ifdef NEED_AP_MLME
+
+static void free_beacon_data(struct beacon_data *beacon)
+{
+       os_free(beacon->head);
+       beacon->head = NULL;
+       os_free(beacon->tail);
+       beacon->tail = NULL;
+       os_free(beacon->probe_resp);
+       beacon->probe_resp = NULL;
+       os_free(beacon->beacon_ies);
+       beacon->beacon_ies = NULL;
+       os_free(beacon->proberesp_ies);
+       beacon->proberesp_ies = NULL;
+       os_free(beacon->assocresp_ies);
+       beacon->assocresp_ies = NULL;
+}
+
+
+static int hostapd_build_beacon_data(struct hostapd_data *hapd,
+                                    struct beacon_data *beacon)
+{
+       struct wpabuf *beacon_extra, *proberesp_extra, *assocresp_extra;
+       struct wpa_driver_ap_params params;
+       int ret;
+
+       os_memset(beacon, 0, sizeof(*beacon));
+       ret = ieee802_11_build_ap_params(hapd, &params);
+       if (ret < 0)
+               return ret;
+
+       ret = hostapd_build_ap_extra_ies(hapd, &beacon_extra,
+                                        &proberesp_extra,
+                                        &assocresp_extra);
+       if (ret)
+               goto free_ap_params;
+
+       ret = -1;
+       beacon->head = os_malloc(params.head_len);
+       if (!beacon->head)
+               goto free_ap_extra_ies;
+
+       os_memcpy(beacon->head, params.head, params.head_len);
+       beacon->head_len = params.head_len;
+
+       beacon->tail = os_malloc(params.tail_len);
+       if (!beacon->tail)
+               goto free_beacon;
+
+       os_memcpy(beacon->tail, params.tail, params.tail_len);
+       beacon->tail_len = params.tail_len;
+
+       if (params.proberesp != NULL) {
+               beacon->probe_resp = os_malloc(params.proberesp_len);
+               if (!beacon->probe_resp)
+                       goto free_beacon;
+
+               os_memcpy(beacon->probe_resp, params.proberesp,
+                         params.proberesp_len);
+               beacon->probe_resp_len = params.proberesp_len;
+       }
+
+       /* copy the extra ies */
+       if (beacon_extra) {
+               beacon->beacon_ies = os_malloc(wpabuf_len(beacon_extra));
+               if (!beacon->beacon_ies)
+                       goto free_beacon;
+
+               os_memcpy(beacon->beacon_ies,
+                         beacon_extra->buf, wpabuf_len(beacon_extra));
+               beacon->beacon_ies_len = wpabuf_len(beacon_extra);
+       }
+
+       if (proberesp_extra) {
+               beacon->proberesp_ies =
+                       os_malloc(wpabuf_len(proberesp_extra));
+               if (!beacon->proberesp_ies)
+                       goto free_beacon;
+
+               os_memcpy(beacon->proberesp_ies, proberesp_extra->buf,
+                         wpabuf_len(proberesp_extra));
+               beacon->proberesp_ies_len = wpabuf_len(proberesp_extra);
+       }
+
+       if (assocresp_extra) {
+               beacon->assocresp_ies =
+                       os_malloc(wpabuf_len(assocresp_extra));
+               if (!beacon->assocresp_ies)
+                       goto free_beacon;
+
+               os_memcpy(beacon->assocresp_ies, assocresp_extra->buf,
+                         wpabuf_len(assocresp_extra));
+               beacon->assocresp_ies_len = wpabuf_len(assocresp_extra);
+       }
+
+       ret = 0;
+free_beacon:
+       /* if the function fails, the caller should not free beacon data */
+       if (ret)
+               free_beacon_data(beacon);
+
+free_ap_extra_ies:
+       hostapd_free_ap_extra_ies(hapd, beacon_extra, proberesp_extra,
+                                 assocresp_extra);
+free_ap_params:
+       ieee802_11_free_ap_params(&params);
+       return ret;
+}
+
+
+/*
+ * TODO: This flow currently supports only changing frequency within the
+ * same hw_mode. Any other changes to MAC parameters or provided settings (even
+ * width) are not supported.
+ */
+static int hostapd_change_config_freq(struct hostapd_data *hapd,
+                                     struct hostapd_config *conf,
+                                     struct hostapd_freq_params *params,
+                                     struct hostapd_freq_params *old_params)
+{
+       int channel;
+
+       if (!params->channel) {
+               /* check if the new channel is supported by hw */
+               params->channel = hostapd_hw_get_channel(hapd, params->freq);
+       }
+
+       channel = params->channel;
+       if (!channel)
+               return -1;
+
+       /* if a pointer to old_params is provided we save previous state */
+       if (old_params) {
+               old_params->channel = conf->channel;
+               old_params->ht_enabled = conf->ieee80211n;
+               old_params->sec_channel_offset = conf->secondary_channel;
+       }
+
+       conf->channel = channel;
+       conf->ieee80211n = params->ht_enabled;
+       conf->secondary_channel = params->sec_channel_offset;
+
+       /* TODO: maybe call here hostapd_config_check here? */
+
+       return 0;
+}
+
+
+static int hostapd_fill_csa_settings(struct hostapd_data *hapd,
+                                    struct csa_settings *settings)
+{
+       struct hostapd_iface *iface = hapd->iface;
+       struct hostapd_freq_params old_freq;
+       int ret;
+
+       os_memset(&old_freq, 0, sizeof(old_freq));
+       if (!iface || !iface->freq || hapd->csa_in_progress)
+               return -1;
+
+       ret = hostapd_change_config_freq(iface->bss[0], iface->conf,
+                                        &settings->freq_params,
+                                        &old_freq);
+       if (ret)
+               return ret;
+
+       ret = hostapd_build_beacon_data(hapd, &settings->beacon_after);
+
+       /* change back the configuration */
+       hostapd_change_config_freq(iface->bss[0], iface->conf,
+                                  &old_freq, NULL);
+
+       if (ret)
+               return ret;
+
+       /* set channel switch parameters for csa ie */
+       hapd->cs_freq_params = settings->freq_params;
+       hapd->cs_count = settings->cs_count;
+       hapd->cs_block_tx = settings->block_tx;
+
+       ret = hostapd_build_beacon_data(hapd, &settings->beacon_csa);
+       if (ret) {
+               free_beacon_data(&settings->beacon_after);
+               return ret;
+       }
+
+       settings->counter_offset_beacon = hapd->cs_c_off_beacon;
+       settings->counter_offset_presp = hapd->cs_c_off_proberesp;
+
+       return 0;
+}
+
+
+void hostapd_cleanup_cs_params(struct hostapd_data *hapd)
+{
+       os_memset(&hapd->cs_freq_params, 0, sizeof(hapd->cs_freq_params));
+       hapd->cs_count = 0;
+       hapd->cs_block_tx = 0;
+       hapd->cs_c_off_beacon = 0;
+       hapd->cs_c_off_proberesp = 0;
+       hapd->csa_in_progress = 0;
 }
+
+
+int hostapd_switch_channel(struct hostapd_data *hapd,
+                          struct csa_settings *settings)
+{
+       int ret;
+
+       if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_CSA)) {
+               wpa_printf(MSG_INFO, "CSA is not supported");
+               return -1;
+       }
+
+       ret = hostapd_fill_csa_settings(hapd, settings);
+       if (ret)
+               return ret;
+
+       ret = hostapd_drv_switch_channel(hapd, settings);
+       free_beacon_data(&settings->beacon_csa);
+       free_beacon_data(&settings->beacon_after);
+
+       if (ret) {
+               /* if we failed, clean cs parameters */
+               hostapd_cleanup_cs_params(hapd);
+               return ret;
+       }
+
+       hapd->csa_in_progress = 1;
+       return 0;
+}
+
+
+void
+hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+                               const struct hostapd_freq_params *freq_params)
+{
+       int vht_seg0_idx = 0, vht_seg1_idx = 0, vht_bw = VHT_CHANWIDTH_USE_HT;
+       unsigned int i;
+
+       wpa_printf(MSG_DEBUG, "Restarting all CSA-related BSSes");
+
+       if (freq_params->center_freq1)
+               vht_seg0_idx = 36 + (freq_params->center_freq1 - 5180) / 5;
+       if (freq_params->center_freq2)
+               vht_seg1_idx = 36 + (freq_params->center_freq2 - 5180) / 5;
+
+       switch (freq_params->bandwidth) {
+       case 0:
+       case 20:
+       case 40:
+               vht_bw = VHT_CHANWIDTH_USE_HT;
+               break;
+       case 80:
+               if (freq_params->center_freq2)
+                       vht_bw = VHT_CHANWIDTH_80P80MHZ;
+               else
+                       vht_bw = VHT_CHANWIDTH_80MHZ;
+               break;
+       case 160:
+               vht_bw = VHT_CHANWIDTH_160MHZ;
+               break;
+       default:
+               wpa_printf(MSG_WARNING, "Unknown CSA bandwidth: %d",
+                          freq_params->bandwidth);
+               break;
+       }
+
+       iface->freq = freq_params->freq;
+       iface->conf->channel = freq_params->channel;
+       iface->conf->secondary_channel = freq_params->sec_channel_offset;
+       iface->conf->vht_oper_centr_freq_seg0_idx = vht_seg0_idx;
+       iface->conf->vht_oper_centr_freq_seg1_idx = vht_seg1_idx;
+       iface->conf->vht_oper_chwidth = vht_bw;
+       iface->conf->ieee80211n = freq_params->ht_enabled;
+       iface->conf->ieee80211ac = freq_params->vht_enabled;
+
+       /*
+        * cs_params must not be cleared earlier because the freq_params
+        * argument may actually point to one of these.
+        */
+       for (i = 0; i < iface->num_bss; i++)
+               hostapd_cleanup_cs_params(iface->bss[i]);
+
+       hostapd_disable_iface(iface);
+       hostapd_enable_iface(iface);
+}
+
+#endif /* NEED_AP_MLME */
index 75d9c66..75cc24e 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / Initialization and configuration
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 #define HOSTAPD_H
 
 #include "common/defs.h"
+#include "utils/list.h"
 #include "ap_config.h"
+#include "drivers/driver.h"
 
-struct wpa_driver_ops;
 struct wpa_ctrl_dst;
 struct radius_server_data;
 struct upnp_wps_device_sm;
 struct hostapd_data;
 struct sta_info;
-struct hostap_sta_driver_data;
 struct ieee80211_ht_capabilities;
 struct full_dynamic_vlan;
 enum wps_event;
 union wps_event_data;
+#ifdef CONFIG_MESH
+struct mesh_conf;
+#endif /* CONFIG_MESH */
 
 struct hostapd_iface;
-struct hostapd_dynamic_iface;
 
 struct hapd_interfaces {
        int (*reload_config)(struct hostapd_iface *iface);
@@ -38,15 +40,22 @@ struct hapd_interfaces {
        int (*driver_init)(struct hostapd_iface *iface);
 
        size_t count;
-       size_t count_dynamic;
        int global_ctrl_sock;
        char *global_iface_path;
        char *global_iface_name;
+#ifndef CONFIG_NATIVE_WINDOWS
        gid_t ctrl_iface_group;
+#endif /* CONFIG_NATIVE_WINDOWS */
        struct hostapd_iface **iface;
-       struct hostapd_dynamic_iface **dynamic_iface;
+
+       size_t terminate_on_error;
 };
 
+enum hostapd_chan_status {
+       HOSTAPD_CHAN_VALID = 0, /* channel is ready */
+       HOSTAPD_CHAN_INVALID = 1, /* no usable channel found */
+       HOSTAPD_CHAN_ACS = 2, /* ACS work being performed */
+};
 
 struct hostapd_probereq_cb {
        int (*cb)(void *ctx, const u8 *sa, const u8 *da, const u8 *bssid,
@@ -67,6 +76,25 @@ struct hostapd_frame_info {
        int ssi_signal; /* dBm */
 };
 
+enum wps_status {
+       WPS_STATUS_SUCCESS = 1,
+       WPS_STATUS_FAILURE
+};
+
+enum pbc_status {
+       WPS_PBC_STATUS_DISABLE,
+       WPS_PBC_STATUS_ACTIVE,
+       WPS_PBC_STATUS_TIMEOUT,
+       WPS_PBC_STATUS_OVERLAP
+};
+
+struct wps_stat {
+       enum wps_status status;
+       enum wps_error_indication failure_reason;
+       enum pbc_status pbc_status;
+       u8 peer_addr[ETH_ALEN];
+};
+
 
 /**
  * struct hostapd_data - hostapd per-BSS data structure
@@ -76,6 +104,9 @@ struct hostapd_data {
        struct hostapd_config *iconf;
        struct hostapd_bss_config *conf;
        int interface_added; /* virtual interface added for this BSS */
+       unsigned int started:1;
+       unsigned int disabled:1;
+       unsigned int reenable_beacon:1;
 
        u8 own_addr[ETH_ALEN];
 
@@ -115,7 +146,7 @@ struct hostapd_data {
        struct eapol_authenticator *eapol_auth;
 
        struct rsn_preauth_interface *preauth_iface;
-       time_t michael_mic_failure;
+       struct os_reltime michael_mic_failure;
        int michael_mic_failures;
        int tkip_countermeasures;
 
@@ -125,6 +156,7 @@ struct hostapd_data {
        void *ssl_ctx;
        void *eap_sim_db_priv;
        struct radius_server_data *radius_srv;
+       struct dl_list erp_keys; /* struct eap_server_erp_key */
 
        int parameter_set_count;
 
@@ -147,6 +179,8 @@ struct hostapd_data {
        unsigned int ap_pin_failures_consecutive;
        struct upnp_wps_device_sm *wps_upnp;
        unsigned int ap_pin_lockout_time;
+
+       struct wps_stat wps_stats;
 #endif /* CONFIG_WPS */
 
        struct hostapd_probereq_cb *probereq_cb;
@@ -178,6 +212,22 @@ struct hostapd_data {
        void (*setup_complete_cb)(void *ctx);
        void *setup_complete_cb_ctx;
 
+       void (*new_psk_cb)(void *ctx, const u8 *mac_addr,
+                          const u8 *p2p_dev_addr, const u8 *psk,
+                          size_t psk_len);
+       void *new_psk_cb_ctx;
+
+       /* channel switch parameters */
+       struct hostapd_freq_params cs_freq_params;
+       u8 cs_count;
+       int cs_block_tx;
+       unsigned int cs_c_off_beacon;
+       unsigned int cs_c_off_proberesp;
+       int csa_in_progress;
+
+       /* BSS Load */
+       unsigned int bss_load_update_timeout;
+
 #ifdef CONFIG_P2P
        struct p2p_data *p2p;
        struct p2p_group *p2p_group;
@@ -195,6 +245,17 @@ struct hostapd_data {
 #ifdef CONFIG_INTERWORKING
        size_t gas_frag_limit;
 #endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_PROXYARP
+       struct l2_packet_data *sock_dhcp;
+       struct l2_packet_data *sock_ndisc;
+#endif /* CONFIG_PROXYARP */
+#ifdef CONFIG_MESH
+       int num_plinks;
+       int max_plinks;
+       void (*mesh_sta_free_cb)(struct sta_info *sta);
+       struct wpabuf *mesh_pending_auth;
+       struct os_reltime mesh_pending_auth_time;
+#endif /* CONFIG_MESH */
 
 #ifdef CONFIG_SQLITE
        struct hostapd_eap_user tmp_eap_user;
@@ -203,8 +264,15 @@ struct hostapd_data {
 #ifdef CONFIG_SAE
        /** Key used for generating SAE anti-clogging tokens */
        u8 sae_token_key[8];
-       os_time_t last_sae_token_key_update;
+       struct os_reltime last_sae_token_key_update;
 #endif /* CONFIG_SAE */
+
+#ifdef CONFIG_TESTING_OPTIONS
+       unsigned int ext_mgmt_frame_handling:1;
+       unsigned int ext_eapol_frame_io:1;
+
+       struct l2_packet_data *l2_test;
+#endif /* CONFIG_TESTING_OPTIONS */
 };
 
 
@@ -216,15 +284,42 @@ struct hostapd_iface {
        void *owner;
        char *config_fname;
        struct hostapd_config *conf;
+       char phy[16]; /* Name of the PHY (radio) */
+
+       enum hostapd_iface_state {
+               HAPD_IFACE_UNINITIALIZED,
+               HAPD_IFACE_DISABLED,
+               HAPD_IFACE_COUNTRY_UPDATE,
+               HAPD_IFACE_ACS,
+               HAPD_IFACE_HT_SCAN,
+               HAPD_IFACE_DFS,
+               HAPD_IFACE_ENABLED
+       } state;
+
+#ifdef CONFIG_MESH
+       struct mesh_conf *mconf;
+#endif /* CONFIG_MESH */
 
        size_t num_bss;
        struct hostapd_data **bss;
 
+       unsigned int wait_channel_update:1;
+       unsigned int cac_started:1;
+
+       /*
+        * When set, indicates that the driver will handle the AP
+        * teardown: delete global keys, station keys, and stations.
+        */
+       unsigned int driver_ap_teardown:1;
+
        int num_ap; /* number of entries in ap_list */
        struct ap_info *ap_list; /* AP info list head */
        struct ap_info *ap_hash[STA_HASH_SIZE];
 
-       unsigned int drv_flags;
+       u64 drv_flags;
+
+       /* SMPS modes supported by the driver (WPA_DRIVER_SMPS_MODE_*) */
+       unsigned int smps_modes;
 
        /*
         * A bitmap of supported protocols for probe response offload. See
@@ -271,21 +366,40 @@ struct hostapd_iface {
        /* Number of HT associated stations 20 MHz */
        int num_sta_ht_20mhz;
 
+       /* Number of HT40 intolerant stations */
+       int num_sta_ht40_intolerant;
+
        /* Overlapping BSS information */
        int olbc_ht;
 
        u16 ht_op_mode;
-       void (*scan_cb)(struct hostapd_iface *iface);
-};
 
-/**
- * struct hostapd_dynamic_iface - hostapd per dynamically allocated
- * or added interface data structure
- */
-struct hostapd_dynamic_iface {
-       char parent[IFNAMSIZ + 1];
-       char iface[IFNAMSIZ + 1];
-       unsigned int usage;
+       /* surveying helpers */
+
+       /* number of channels surveyed */
+       unsigned int chans_surveyed;
+
+       /* lowest observed noise floor in dBm */
+       s8 lowest_nf;
+
+       /* channel utilization calculation */
+       u64 last_channel_time;
+       u64 last_channel_time_busy;
+       u8 channel_utilization;
+
+       unsigned int dfs_cac_ms;
+       struct os_reltime dfs_cac_start;
+
+       /* Latched with the actual secondary channel information and will be
+        * used while juggling between HT20 and HT40 modes. */
+       int secondary_ch;
+
+#ifdef CONFIG_ACS
+       unsigned int acs_num_completed_scans;
+#endif /* CONFIG_ACS */
+
+       void (*scan_cb)(struct hostapd_iface *iface);
+       int num_ht40_scan_tries;
 };
 
 /* hostapd.c */
@@ -301,6 +415,11 @@ int hostapd_setup_interface(struct hostapd_iface *iface);
 int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err);
 void hostapd_interface_deinit(struct hostapd_iface *iface);
 void hostapd_interface_free(struct hostapd_iface *iface);
+struct hostapd_iface * hostapd_init(struct hapd_interfaces *interfaces,
+                                   const char *config_file);
+struct hostapd_iface *
+hostapd_interface_init_bss(struct hapd_interfaces *interfaces, const char *phy,
+                          const char *config_fname, int debug);
 void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
                           int reassoc);
 void hostapd_interface_deinit_free(struct hostapd_iface *iface);
@@ -309,6 +428,15 @@ int hostapd_reload_iface(struct hostapd_iface *hapd_iface);
 int hostapd_disable_iface(struct hostapd_iface *hapd_iface);
 int hostapd_add_iface(struct hapd_interfaces *ifaces, char *buf);
 int hostapd_remove_iface(struct hapd_interfaces *ifaces, char *buf);
+void hostapd_channel_list_updated(struct hostapd_iface *iface, int initiator);
+void hostapd_set_state(struct hostapd_iface *iface, enum hostapd_iface_state s);
+const char * hostapd_state_text(enum hostapd_iface_state s);
+int hostapd_switch_channel(struct hostapd_data *hapd,
+                          struct csa_settings *settings);
+void
+hostapd_switch_channel_fallback(struct hostapd_iface *iface,
+                               const struct hostapd_freq_params *freq_params);
+void hostapd_cleanup_cs_params(struct hostapd_data *hapd);
 
 /* utils.c */
 int hostapd_register_probereq_cb(struct hostapd_data *hapd,
@@ -330,7 +458,7 @@ int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, const u8 *da,
                         const u8 *bssid, const u8 *ie, size_t ie_len,
                         int ssi_signal);
 void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
-                            int offset);
+                            int offset, int width, int cf1, int cf2);
 
 const struct hostapd_eap_user *
 hostapd_get_eap_user(struct hostapd_data *hapd, const u8 *identity,
index 45d518b..d7909fa 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Hotspot 2.0 AP ANQP processing
  * Copyright (c) 2009, Atheros Communications, Inc.
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 #include "common/ieee802_11_defs.h"
 #include "hostapd.h"
 #include "ap_config.h"
+#include "ap_drv_ops.h"
 #include "hs20.h"
 
 
 u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid)
 {
+       u8 conf;
        if (!hapd->conf->hs20)
                return eid;
        *eid++ = WLAN_EID_VENDOR_SPECIFIC;
-       *eid++ = 5;
+       *eid++ = 7;
        WPA_PUT_BE24(eid, OUI_WFA);
        eid += 3;
        *eid++ = HS20_INDICATION_OUI_TYPE;
-       /* Hotspot Configuration: DGAF Enabled */
-       *eid++ = hapd->conf->disable_dgaf ? 0x01 : 0x00;
+       conf = HS20_VERSION; /* Release Number */
+       conf |= HS20_ANQP_DOMAIN_ID_PRESENT;
+       if (hapd->conf->disable_dgaf)
+               conf |= HS20_DGAF_DISABLED;
+       *eid++ = conf;
+       WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id);
+       eid += 2;
+
        return eid;
 }
+
+
+u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid)
+{
+       u8 *len;
+       u16 capab;
+
+       if (!hapd->conf->osen)
+               return eid;
+
+       *eid++ = WLAN_EID_VENDOR_SPECIFIC;
+       len = eid++; /* to be filled */
+       WPA_PUT_BE24(eid, OUI_WFA);
+       eid += 3;
+       *eid++ = HS20_OSEN_OUI_TYPE;
+
+       /* Group Data Cipher Suite */
+       RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+       eid += RSN_SELECTOR_LEN;
+
+       /* Pairwise Cipher Suite Count and List */
+       WPA_PUT_LE16(eid, 1);
+       eid += 2;
+       RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP);
+       eid += RSN_SELECTOR_LEN;
+
+       /* AKM Suite Count and List */
+       WPA_PUT_LE16(eid, 1);
+       eid += 2;
+       RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN);
+       eid += RSN_SELECTOR_LEN;
+
+       /* RSN Capabilities */
+       capab = 0;
+       if (hapd->conf->wmm_enabled) {
+               /* 4 PTKSA replay counters when using WMM */
+               capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
+       }
+#ifdef CONFIG_IEEE80211W
+       if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+               capab |= WPA_CAPABILITY_MFPC;
+               if (hapd->conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
+                       capab |= WPA_CAPABILITY_MFPR;
+       }
+#endif /* CONFIG_IEEE80211W */
+       WPA_PUT_LE16(eid, capab);
+       eid += 2;
+
+       *len = eid - len - 1;
+
+       return eid;
+}
+
+
+int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
+                              u8 osu_method, const char *url)
+{
+       struct wpabuf *buf;
+       size_t len = 0;
+       int ret;
+
+       /* TODO: should refuse to send notification if the STA is not associated
+        * or if the STA did not indicate support for WNM-Notification */
+
+       if (url) {
+               len = 1 + os_strlen(url);
+               if (5 + len > 255) {
+                       wpa_printf(MSG_INFO, "HS 2.0: Too long URL for "
+                                  "WNM-Notification: '%s'", url);
+                       return -1;
+               }
+       }
+
+       buf = wpabuf_alloc(4 + 7 + len);
+       if (buf == NULL)
+               return -1;
+
+       wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+       wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+       wpabuf_put_u8(buf, 1); /* Dialog token */
+       wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
+
+       /* Subscription Remediation subelement */
+       wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+       wpabuf_put_u8(buf, 5 + len);
+       wpabuf_put_be24(buf, OUI_WFA);
+       wpabuf_put_u8(buf, HS20_WNM_SUB_REM_NEEDED);
+       if (url) {
+               wpabuf_put_u8(buf, len - 1);
+               wpabuf_put_data(buf, url, len - 1);
+               wpabuf_put_u8(buf, osu_method);
+       } else {
+               /* Server URL and Server Method fields not included */
+               wpabuf_put_u8(buf, 0);
+       }
+
+       ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+                                     wpabuf_head(buf), wpabuf_len(buf));
+
+       wpabuf_free(buf);
+
+       return ret;
+}
+
+
+int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
+                                         const u8 *addr,
+                                         const struct wpabuf *payload)
+{
+       struct wpabuf *buf;
+       int ret;
+
+       /* TODO: should refuse to send notification if the STA is not associated
+        * or if the STA did not indicate support for WNM-Notification */
+
+       buf = wpabuf_alloc(4 + 6 + wpabuf_len(payload));
+       if (buf == NULL)
+               return -1;
+
+       wpabuf_put_u8(buf, WLAN_ACTION_WNM);
+       wpabuf_put_u8(buf, WNM_NOTIFICATION_REQ);
+       wpabuf_put_u8(buf, 1); /* Dialog token */
+       wpabuf_put_u8(buf, 1); /* Type - 1 reserved for WFA */
+
+       /* Deauthentication Imminent Notice subelement */
+       wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+       wpabuf_put_u8(buf, 4 + wpabuf_len(payload));
+       wpabuf_put_be24(buf, OUI_WFA);
+       wpabuf_put_u8(buf, HS20_WNM_DEAUTH_IMMINENT_NOTICE);
+       wpabuf_put_buf(buf, payload);
+
+       ret = hostapd_drv_send_action(hapd, hapd->iface->freq, 0, addr,
+                                     wpabuf_head(buf), wpabuf_len(buf));
+
+       wpabuf_free(buf);
+
+       return ret;
+}
index 98698ce..152439f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Hotspot 2.0 AP ANQP processing
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 struct hostapd_data;
 
 u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid);
+int hs20_send_wnm_notification(struct hostapd_data *hapd, const u8 *addr,
+                              u8 osu_method, const char *url);
+int hs20_send_wnm_notification_deauth_req(struct hostapd_data *hapd,
+                                         const u8 *addr,
+                                         const struct wpabuf *payload);
 
 #endif /* HS20_H */
index 37112bd..05431d3 100644 (file)
@@ -4,14 +4,8 @@
  * Copyright 2005-2006, Devicescape Software, Inc.
  * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "utils/includes.h"
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
-#include "drivers/driver.h"
+#include "common/wpa_ctrl.h"
+#include "common/hw_features_common.h"
 #include "hostapd.h"
 #include "ap_config.h"
 #include "ap_drv_ops.h"
+#include "acs.h"
+#include "ieee802_11.h"
+#include "beacon.h"
 #include "hw_features.h"
 
 
@@ -44,10 +42,40 @@ void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
 }
 
 
+#ifndef CONFIG_NO_STDOUT_DEBUG
+static char * dfs_info(struct hostapd_channel_data *chan)
+{
+       static char info[256];
+       char *state;
+
+       switch (chan->flag & HOSTAPD_CHAN_DFS_MASK) {
+       case HOSTAPD_CHAN_DFS_UNKNOWN:
+               state = "unknown";
+               break;
+       case HOSTAPD_CHAN_DFS_USABLE:
+               state = "usable";
+               break;
+       case HOSTAPD_CHAN_DFS_UNAVAILABLE:
+               state = "unavailable";
+               break;
+       case HOSTAPD_CHAN_DFS_AVAILABLE:
+               state = "available";
+               break;
+       default:
+               return "";
+       }
+       os_snprintf(info, sizeof(info), " (DFS state = %s)", state);
+       info[sizeof(info) - 1] = '\0';
+
+       return info;
+}
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+
+
 int hostapd_get_hw_features(struct hostapd_iface *iface)
 {
        struct hostapd_data *hapd = iface->bss[0];
-       int ret = 0, i, j;
+       int i, j;
        u16 num_modes, flags;
        struct hostapd_hw_modes *modes;
 
@@ -70,34 +98,47 @@ int hostapd_get_hw_features(struct hostapd_iface *iface)
 
        for (i = 0; i < num_modes; i++) {
                struct hostapd_hw_modes *feature = &modes[i];
+               int dfs_enabled = hapd->iconf->ieee80211h &&
+                       (iface->drv_flags & WPA_DRIVER_FLAGS_RADAR);
+
                /* set flag for channels we can use in current regulatory
                 * domain */
                for (j = 0; j < feature->num_channels; j++) {
+                       int dfs = 0;
+
                        /*
                         * Disable all channels that are marked not to allow
-                        * IBSS operation or active scanning. In addition,
-                        * disable all channels that require radar detection,
-                        * since that (in addition to full DFS) is not yet
-                        * supported.
+                        * to initiate radiation (a.k.a. passive scan and no
+                        * IBSS).
+                        * Use radar channels only if the driver supports DFS.
                         */
-                       if (feature->channels[j].flag &
-                           (HOSTAPD_CHAN_NO_IBSS |
-                            HOSTAPD_CHAN_PASSIVE_SCAN |
-                            HOSTAPD_CHAN_RADAR))
+                       if ((feature->channels[j].flag &
+                            HOSTAPD_CHAN_RADAR) && dfs_enabled) {
+                               dfs = 1;
+                       } else if (((feature->channels[j].flag &
+                                    HOSTAPD_CHAN_RADAR) &&
+                                   !(iface->drv_flags &
+                                     WPA_DRIVER_FLAGS_DFS_OFFLOAD)) ||
+                                  (feature->channels[j].flag &
+                                   HOSTAPD_CHAN_NO_IR)) {
                                feature->channels[j].flag |=
                                        HOSTAPD_CHAN_DISABLED;
+                       }
+
                        if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED)
                                continue;
+
                        wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d "
-                                  "chan=%d freq=%d MHz max_tx_power=%d dBm",
+                                  "chan=%d freq=%d MHz max_tx_power=%d dBm%s",
                                   feature->mode,
                                   feature->channels[j].chan,
                                   feature->channels[j].freq,
-                                  feature->channels[j].max_tx_power);
+                                  feature->channels[j].max_tx_power,
+                                  dfs ? dfs_info(&feature->channels[j]) : "");
                }
        }
 
-       return ret;
+       return 0;
 }
 
 
@@ -183,66 +224,16 @@ int hostapd_prepare_rates(struct hostapd_iface *iface,
 #ifdef CONFIG_IEEE80211N
 static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
 {
-       int sec_chan, ok, j, first;
-       int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
-                         184, 192 };
-       size_t k;
+       int pri_chan, sec_chan;
 
        if (!iface->conf->secondary_channel)
                return 1; /* HT40 not used */
 
-       sec_chan = iface->conf->channel + iface->conf->secondary_channel * 4;
-       wpa_printf(MSG_DEBUG, "HT40: control channel: %d  "
-                  "secondary channel: %d",
-                  iface->conf->channel, sec_chan);
-
-       /* Verify that HT40 secondary channel is an allowed 20 MHz
-        * channel */
-       ok = 0;
-       for (j = 0; j < iface->current_mode->num_channels; j++) {
-               struct hostapd_channel_data *chan =
-                       &iface->current_mode->channels[j];
-               if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
-                   chan->chan == sec_chan) {
-                       ok = 1;
-                       break;
-               }
-       }
-       if (!ok) {
-               wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
-                          sec_chan);
-               return 0;
-       }
-
-       /*
-        * Verify that HT40 primary,secondary channel pair is allowed per
-        * IEEE 802.11n Annex J. This is only needed for 5 GHz band since
-        * 2.4 GHz rules allow all cases where the secondary channel fits into
-        * the list of allowed channels (already checked above).
-        */
-       if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A)
-               return 1;
-
-       if (iface->conf->secondary_channel > 0)
-               first = iface->conf->channel;
-       else
-               first = sec_chan;
-
-       ok = 0;
-       for (k = 0; k < sizeof(allowed) / sizeof(allowed[0]); k++) {
-               if (first == allowed[k]) {
-                       ok = 1;
-                       break;
-               }
-       }
-       if (!ok) {
-               wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed",
-                          iface->conf->channel,
-                          iface->conf->secondary_channel);
-               return 0;
-       }
+       pri_chan = iface->conf->channel;
+       sec_chan = pri_chan + iface->conf->secondary_channel * 4;
 
-       return 1;
+       return allowed_ht40_channel_pair(iface->current_mode, pri_chan,
+                                        sec_chan);
 }
 
 
@@ -258,158 +249,34 @@ static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface)
 }
 
 
-static void ieee80211n_get_pri_sec_chan(struct wpa_scan_res *bss,
-                                       int *pri_chan, int *sec_chan)
-{
-       struct ieee80211_ht_operation *oper;
-       struct ieee802_11_elems elems;
-
-       *pri_chan = *sec_chan = 0;
-
-       ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
-       if (elems.ht_operation &&
-           elems.ht_operation_len >= sizeof(*oper)) {
-               oper = (struct ieee80211_ht_operation *) elems.ht_operation;
-               *pri_chan = oper->control_chan;
-               if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) {
-                       int sec = oper->ht_param &
-                               HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
-                       if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
-                               *sec_chan = *pri_chan + 4;
-                       else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
-                               *sec_chan = *pri_chan - 4;
-               }
-       }
-}
-
-
 static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface,
                                     struct wpa_scan_results *scan_res)
 {
-       int pri_chan, sec_chan, pri_freq, sec_freq, pri_bss, sec_bss;
-       int bss_pri_chan, bss_sec_chan;
-       size_t i;
-       int match;
+       int pri_chan, sec_chan;
+       int res;
 
        pri_chan = iface->conf->channel;
-       sec_chan = iface->conf->secondary_channel * 4;
-       pri_freq = hostapd_hw_get_freq(iface->bss[0], pri_chan);
-       if (iface->conf->secondary_channel > 0)
-               sec_freq = pri_freq + 20;
-       else
-               sec_freq = pri_freq - 20;
+       sec_chan = pri_chan + iface->conf->secondary_channel * 4;
 
-       /*
-        * Switch PRI/SEC channels if Beacons were detected on selected SEC
-        * channel, but not on selected PRI channel.
-        */
-       pri_bss = sec_bss = 0;
-       for (i = 0; i < scan_res->num; i++) {
-               struct wpa_scan_res *bss = scan_res->res[i];
-               if (bss->freq == pri_freq)
-                       pri_bss++;
-               else if (bss->freq == sec_freq)
-                       sec_bss++;
-       }
-       if (sec_bss && !pri_bss) {
-               wpa_printf(MSG_INFO, "Switch own primary and secondary "
-                          "channel to get secondary channel with no Beacons "
-                          "from other BSSes");
-               ieee80211n_switch_pri_sec(iface);
-       }
+       res = check_40mhz_5g(iface->current_mode, scan_res, pri_chan, sec_chan);
 
-       /*
-        * Match PRI/SEC channel with any existing HT40 BSS on the same
-        * channels that we are about to use (if already mixed order in
-        * existing BSSes, use own preference).
-        */
-       match = 0;
-       for (i = 0; i < scan_res->num; i++) {
-               struct wpa_scan_res *bss = scan_res->res[i];
-               ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
-               if (pri_chan == bss_pri_chan &&
-                   sec_chan == bss_sec_chan) {
-                       match = 1;
-                       break;
-               }
-       }
-       if (!match) {
-               for (i = 0; i < scan_res->num; i++) {
-                       struct wpa_scan_res *bss = scan_res->res[i];
-                       ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan,
-                                                   &bss_sec_chan);
-                       if (pri_chan == bss_sec_chan &&
-                           sec_chan == bss_pri_chan) {
-                               wpa_printf(MSG_INFO, "Switch own primary and "
-                                          "secondary channel due to BSS "
-                                          "overlap with " MACSTR,
-                                          MAC2STR(bss->bssid));
-                               ieee80211n_switch_pri_sec(iface);
-                               break;
-                       }
-               }
-       }
+       if (res == 2)
+               ieee80211n_switch_pri_sec(iface);
 
-       return 1;
+       return !!res;
 }
 
 
 static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface,
                                      struct wpa_scan_results *scan_res)
 {
-       int pri_freq, sec_freq;
-       int affected_start, affected_end;
-       size_t i;
+       int pri_chan, sec_chan;
 
-       pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
-       if (iface->conf->secondary_channel > 0)
-               sec_freq = pri_freq + 20;
-       else
-               sec_freq = pri_freq - 20;
-       affected_start = (pri_freq + sec_freq) / 2 - 25;
-       affected_end = (pri_freq + sec_freq) / 2 + 25;
-       wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
-                  affected_start, affected_end);
-       for (i = 0; i < scan_res->num; i++) {
-               struct wpa_scan_res *bss = scan_res->res[i];
-               int pri = bss->freq;
-               int sec = pri;
-               int sec_chan, pri_chan;
-
-               ieee80211n_get_pri_sec_chan(bss, &pri_chan, &sec_chan);
-
-               if (sec_chan) {
-                       if (sec_chan < pri_chan)
-                               sec = pri - 20;
-                       else
-                               sec = pri + 20;
-               }
-
-               if ((pri < affected_start || pri > affected_end) &&
-                   (sec < affected_start || sec > affected_end))
-                       continue; /* not within affected channel range */
-
-               wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
-                          " freq=%d pri=%d sec=%d",
-                          MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
-
-               if (sec_chan) {
-                       if (pri_freq != pri || sec_freq != sec) {
-                               wpa_printf(MSG_DEBUG, "40 MHz pri/sec "
-                                          "mismatch with BSS " MACSTR
-                                          " <%d,%d> (chan=%d%c) vs. <%d,%d>",
-                                          MAC2STR(bss->bssid),
-                                          pri, sec, pri_chan,
-                                          sec > pri ? '+' : '-',
-                                          pri_freq, sec_freq);
-                               return 0;
-                       }
-               }
-
-               /* TODO: 40 MHz intolerant */
-       }
+       pri_chan = iface->conf->channel;
+       sec_chan = pri_chan + iface->conf->secondary_channel * 4;
 
-       return 1;
+       return check_40mhz_2g4(iface->current_mode, scan_res, pri_chan,
+                              sec_chan);
 }
 
 
@@ -436,6 +303,7 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface)
                oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res);
        wpa_scan_results_free(scan_res);
 
+       iface->secondary_ch = iface->conf->secondary_channel;
        if (!oper40) {
                wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on "
                           "channel pri=%d sec=%d based on overlapping BSSes",
@@ -443,9 +311,21 @@ static void ieee80211n_check_scan(struct hostapd_iface *iface)
                           iface->conf->channel +
                           iface->conf->secondary_channel * 4);
                iface->conf->secondary_channel = 0;
+               if (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX) {
+                       /*
+                        * TODO: Could consider scheduling another scan to check
+                        * if channel width can be changed if no coex reports
+                        * are received from associating stations.
+                        */
+               }
        }
 
        res = ieee80211n_allowed_ht40_channel_pair(iface);
+       if (!res) {
+               iface->conf->secondary_channel = 0;
+               wpa_printf(MSG_INFO, "Fallback to 20 MHz");
+       }
+
        hostapd_setup_interface_complete(iface, !res);
 }
 
@@ -490,25 +370,128 @@ static void ieee80211n_scan_channels_2g4(struct hostapd_iface *iface,
 }
 
 
+static void ieee80211n_scan_channels_5g(struct hostapd_iface *iface,
+                                       struct wpa_driver_scan_params *params)
+{
+       /* Scan only the affected frequency range */
+       int pri_freq;
+       int affected_start, affected_end;
+       int i, pos;
+       struct hostapd_hw_modes *mode;
+
+       if (iface->current_mode == NULL)
+               return;
+
+       pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
+       if (iface->conf->secondary_channel > 0) {
+               affected_start = pri_freq - 10;
+               affected_end = pri_freq + 30;
+       } else {
+               affected_start = pri_freq - 30;
+               affected_end = pri_freq + 10;
+       }
+       wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
+                  affected_start, affected_end);
+
+       mode = iface->current_mode;
+       params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
+       if (params->freqs == NULL)
+               return;
+       pos = 0;
+
+       for (i = 0; i < mode->num_channels; i++) {
+               struct hostapd_channel_data *chan = &mode->channels[i];
+               if (chan->flag & HOSTAPD_CHAN_DISABLED)
+                       continue;
+               if (chan->freq < affected_start ||
+                   chan->freq > affected_end)
+                       continue;
+               params->freqs[pos++] = chan->freq;
+       }
+}
+
+
+static void ap_ht40_scan_retry(void *eloop_data, void *user_data)
+{
+#define HT2040_COEX_SCAN_RETRY 15
+       struct hostapd_iface *iface = eloop_data;
+       struct wpa_driver_scan_params params;
+       int ret;
+
+       os_memset(&params, 0, sizeof(params));
+       if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
+               ieee80211n_scan_channels_2g4(iface, &params);
+       else
+               ieee80211n_scan_channels_5g(iface, &params);
+
+       ret = hostapd_driver_scan(iface->bss[0], &params);
+       iface->num_ht40_scan_tries++;
+       os_free(params.freqs);
+
+       if (ret == -EBUSY &&
+           iface->num_ht40_scan_tries < HT2040_COEX_SCAN_RETRY) {
+               wpa_printf(MSG_ERROR,
+                          "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again (attempt %d)",
+                          ret, strerror(-ret), iface->num_ht40_scan_tries);
+               eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL);
+               return;
+       }
+
+       if (ret == 0) {
+               iface->scan_cb = ieee80211n_check_scan;
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG,
+                  "Failed to request a scan in device, bringing up in HT20 mode");
+       iface->conf->secondary_channel = 0;
+       iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
+       hostapd_setup_interface_complete(iface, 0);
+}
+
+
+void hostapd_stop_setup_timers(struct hostapd_iface *iface)
+{
+       eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
+}
+
+
 static int ieee80211n_check_40mhz(struct hostapd_iface *iface)
 {
        struct wpa_driver_scan_params params;
+       int ret;
 
        if (!iface->conf->secondary_channel)
                return 0; /* HT40 not used */
 
+       hostapd_set_state(iface, HAPD_IFACE_HT_SCAN);
        wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling "
                   "40 MHz channel");
        os_memset(&params, 0, sizeof(params));
        if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G)
                ieee80211n_scan_channels_2g4(iface, &params);
-       if (hostapd_driver_scan(iface->bss[0], &params) < 0) {
-               wpa_printf(MSG_ERROR, "Failed to request a scan of "
-                          "neighboring BSSes");
-               os_free(params.freqs);
+       else
+               ieee80211n_scan_channels_5g(iface, &params);
+
+       ret = hostapd_driver_scan(iface->bss[0], &params);
+       os_free(params.freqs);
+
+       if (ret == -EBUSY) {
+               wpa_printf(MSG_ERROR,
+                          "Failed to request a scan of neighboring BSSes ret=%d (%s) - try to scan again",
+                          ret, strerror(-ret));
+               iface->num_ht40_scan_tries = 1;
+               eloop_cancel_timeout(ap_ht40_scan_retry, iface, NULL);
+               eloop_register_timeout(1, 0, ap_ht40_scan_retry, iface, NULL);
+               return 1;
+       }
+
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR,
+                          "Failed to request a scan of neighboring BSSes ret=%d (%s)",
+                          ret, strerror(-ret));
                return -1;
        }
-       os_free(params.freqs);
 
        iface->scan_cb = ieee80211n_check_scan;
        return 1;
@@ -534,11 +517,24 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
                return 0;
        }
 
-       if ((conf & HT_CAP_INFO_SMPS_MASK) != (hw & HT_CAP_INFO_SMPS_MASK) &&
-           (conf & HT_CAP_INFO_SMPS_MASK) != HT_CAP_INFO_SMPS_DISABLED) {
-               wpa_printf(MSG_ERROR, "Driver does not support configured "
-                          "HT capability [SMPS-*]");
-               return 0;
+       switch (conf & HT_CAP_INFO_SMPS_MASK) {
+       case HT_CAP_INFO_SMPS_STATIC:
+               if (!(iface->smps_modes & WPA_DRIVER_SMPS_MODE_STATIC)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Driver does not support configured HT capability [SMPS-STATIC]");
+                       return 0;
+               }
+               break;
+       case HT_CAP_INFO_SMPS_DYNAMIC:
+               if (!(iface->smps_modes & WPA_DRIVER_SMPS_MODE_DYNAMIC)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Driver does not support configured HT capability [SMPS-DYNAMIC]");
+                       return 0;
+               }
+               break;
+       case HT_CAP_INFO_SMPS_DISABLED:
+       default:
+               break;
        }
 
        if ((conf & HT_CAP_INFO_GREEN_FIELD) &&
@@ -596,12 +592,6 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
                return 0;
        }
 
-       if ((conf & HT_CAP_INFO_PSMP_SUPP) && !(hw & HT_CAP_INFO_PSMP_SUPP)) {
-               wpa_printf(MSG_ERROR, "Driver does not support configured "
-                          "HT capability [PSMP]");
-               return 0;
-       }
-
        if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) &&
            !(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) {
                wpa_printf(MSG_ERROR, "Driver does not support configured "
@@ -612,6 +602,112 @@ static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface)
        return 1;
 }
 
+
+#ifdef CONFIG_IEEE80211AC
+
+static int ieee80211ac_cap_check(u32 hw, u32 conf, u32 cap, const char *name)
+{
+       u32 req_cap = conf & cap;
+
+       /*
+        * Make sure we support all requested capabilities.
+        * NOTE: We assume that 'cap' represents a capability mask,
+        * not a discrete value.
+        */
+       if ((hw & req_cap) != req_cap) {
+               wpa_printf(MSG_ERROR, "Driver does not support configured VHT capability [%s]",
+                          name);
+               return 0;
+       }
+       return 1;
+}
+
+
+static int ieee80211ac_cap_check_max(u32 hw, u32 conf, u32 mask,
+                                    unsigned int shift,
+                                    const char *name)
+{
+       u32 hw_max = hw & mask;
+       u32 conf_val = conf & mask;
+
+       if (conf_val > hw_max) {
+               wpa_printf(MSG_ERROR, "Configured VHT capability [%s] exceeds max value supported by the driver (%d > %d)",
+                          name, conf_val >> shift, hw_max >> shift);
+               return 0;
+       }
+       return 1;
+}
+
+
+static int ieee80211ac_supported_vht_capab(struct hostapd_iface *iface)
+{
+       struct hostapd_hw_modes *mode = iface->current_mode;
+       u32 hw = mode->vht_capab;
+       u32 conf = iface->conf->vht_capab;
+
+       wpa_printf(MSG_DEBUG, "hw vht capab: 0x%x, conf vht capab: 0x%x",
+                  hw, conf);
+
+       if (mode->mode == HOSTAPD_MODE_IEEE80211G &&
+           iface->conf->bss[0]->vendor_vht &&
+           mode->vht_capab == 0 && iface->hw_features) {
+               int i;
+
+               for (i = 0; i < iface->num_hw_features; i++) {
+                       if (iface->hw_features[i].mode ==
+                           HOSTAPD_MODE_IEEE80211A) {
+                               mode = &iface->hw_features[i];
+                               hw = mode->vht_capab;
+                               wpa_printf(MSG_DEBUG,
+                                          "update hw vht capab based on 5 GHz band: 0x%x",
+                                          hw);
+                               break;
+                       }
+               }
+       }
+
+#define VHT_CAP_CHECK(cap) \
+       do { \
+               if (!ieee80211ac_cap_check(hw, conf, cap, #cap)) \
+                       return 0; \
+       } while (0)
+
+#define VHT_CAP_CHECK_MAX(cap) \
+       do { \
+               if (!ieee80211ac_cap_check_max(hw, conf, cap, cap ## _SHIFT, \
+                                              #cap)) \
+                       return 0; \
+       } while (0)
+
+       VHT_CAP_CHECK_MAX(VHT_CAP_MAX_MPDU_LENGTH_MASK);
+       VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160MHZ);
+       VHT_CAP_CHECK(VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ);
+       VHT_CAP_CHECK(VHT_CAP_RXLDPC);
+       VHT_CAP_CHECK(VHT_CAP_SHORT_GI_80);
+       VHT_CAP_CHECK(VHT_CAP_SHORT_GI_160);
+       VHT_CAP_CHECK(VHT_CAP_TXSTBC);
+       VHT_CAP_CHECK_MAX(VHT_CAP_RXSTBC_MASK);
+       VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMER_CAPABLE);
+       VHT_CAP_CHECK(VHT_CAP_SU_BEAMFORMEE_CAPABLE);
+       VHT_CAP_CHECK_MAX(VHT_CAP_BEAMFORMEE_STS_MAX);
+       VHT_CAP_CHECK_MAX(VHT_CAP_SOUNDING_DIMENSION_MAX);
+       VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMER_CAPABLE);
+       VHT_CAP_CHECK(VHT_CAP_MU_BEAMFORMEE_CAPABLE);
+       VHT_CAP_CHECK(VHT_CAP_VHT_TXOP_PS);
+       VHT_CAP_CHECK(VHT_CAP_HTC_VHT);
+       VHT_CAP_CHECK_MAX(VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX);
+       VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB);
+       VHT_CAP_CHECK(VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB);
+       VHT_CAP_CHECK(VHT_CAP_RX_ANTENNA_PATTERN);
+       VHT_CAP_CHECK(VHT_CAP_TX_ANTENNA_PATTERN);
+
+#undef VHT_CAP_CHECK
+#undef VHT_CAP_CHECK_MAX
+
+       return 1;
+}
+#endif /* CONFIG_IEEE80211AC */
+
 #endif /* CONFIG_IEEE80211N */
 
 
@@ -623,6 +719,10 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface)
                return 0;
        if (!ieee80211n_supported_ht_capab(iface))
                return -1;
+#ifdef CONFIG_IEEE80211AC
+       if (!ieee80211ac_supported_vht_capab(iface))
+               return -1;
+#endif /* CONFIG_IEEE80211AC */
        ret = ieee80211n_check_40mhz(iface);
        if (ret)
                return ret;
@@ -634,6 +734,129 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface)
 }
 
 
+static int hostapd_is_usable_chan(struct hostapd_iface *iface,
+                                 int channel, int primary)
+{
+       int i;
+       struct hostapd_channel_data *chan;
+
+       for (i = 0; i < iface->current_mode->num_channels; i++) {
+               chan = &iface->current_mode->channels[i];
+               if (chan->chan != channel)
+                       continue;
+
+               if (!(chan->flag & HOSTAPD_CHAN_DISABLED))
+                       return 1;
+
+               wpa_printf(MSG_DEBUG,
+                          "%schannel [%i] (%i) is disabled for use in AP mode, flags: 0x%x%s%s",
+                          primary ? "" : "Configured HT40 secondary ",
+                          i, chan->chan, chan->flag,
+                          chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
+                          chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
+       }
+
+       return 0;
+}
+
+
+static int hostapd_is_usable_chans(struct hostapd_iface *iface)
+{
+       if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1))
+               return 0;
+
+       if (!iface->conf->secondary_channel)
+               return 1;
+
+       return hostapd_is_usable_chan(iface, iface->conf->channel +
+                                     iface->conf->secondary_channel * 4, 0);
+}
+
+
+static enum hostapd_chan_status
+hostapd_check_chans(struct hostapd_iface *iface)
+{
+       if (iface->conf->channel) {
+               if (hostapd_is_usable_chans(iface))
+                       return HOSTAPD_CHAN_VALID;
+               else
+                       return HOSTAPD_CHAN_INVALID;
+       }
+
+       /*
+        * The user set channel=0 or channel=acs_survey
+        * which is used to trigger ACS.
+        */
+
+       switch (acs_init(iface)) {
+       case HOSTAPD_CHAN_ACS:
+               return HOSTAPD_CHAN_ACS;
+       case HOSTAPD_CHAN_VALID:
+       case HOSTAPD_CHAN_INVALID:
+       default:
+               return HOSTAPD_CHAN_INVALID;
+       }
+}
+
+
+static void hostapd_notify_bad_chans(struct hostapd_iface *iface)
+{
+       hostapd_logger(iface->bss[0], NULL,
+                      HOSTAPD_MODULE_IEEE80211,
+                      HOSTAPD_LEVEL_WARNING,
+                      "Configured channel (%d) not found from the "
+                      "channel list of current mode (%d) %s",
+                      iface->conf->channel,
+                      iface->current_mode->mode,
+                      hostapd_hw_mode_txt(iface->current_mode->mode));
+       hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
+                      HOSTAPD_LEVEL_WARNING,
+                      "Hardware does not support configured channel");
+}
+
+
+int hostapd_acs_completed(struct hostapd_iface *iface, int err)
+{
+       int ret = -1;
+
+       if (err)
+               goto out;
+
+       switch (hostapd_check_chans(iface)) {
+       case HOSTAPD_CHAN_VALID:
+               wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO,
+                       ACS_EVENT_COMPLETED "freq=%d channel=%d",
+                       hostapd_hw_get_freq(iface->bss[0],
+                                           iface->conf->channel),
+                       iface->conf->channel);
+               break;
+       case HOSTAPD_CHAN_ACS:
+               wpa_printf(MSG_ERROR, "ACS error - reported complete, but no result available");
+               wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED);
+               hostapd_notify_bad_chans(iface);
+               goto out;
+       case HOSTAPD_CHAN_INVALID:
+       default:
+               wpa_printf(MSG_ERROR, "ACS picked unusable channels");
+               wpa_msg(iface->bss[0]->msg_ctx, MSG_INFO, ACS_EVENT_FAILED);
+               hostapd_notify_bad_chans(iface);
+               goto out;
+       }
+
+       ret = hostapd_check_ht_capab(iface);
+       if (ret < 0)
+               goto out;
+       if (ret == 1) {
+               wpa_printf(MSG_DEBUG, "Interface initialization will be completed in a callback");
+               return 0;
+       }
+
+       ret = 0;
+out:
+       return hostapd_setup_interface_complete(iface, ret);
+}
+
+
 /**
  * hostapd_select_hw_mode - Select the hardware mode
  * @iface: Pointer to interface data.
@@ -644,11 +867,20 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface)
  */
 int hostapd_select_hw_mode(struct hostapd_iface *iface)
 {
-       int i, j, ok;
+       int i;
 
        if (iface->num_hw_features < 1)
                return -1;
 
+       if ((iface->conf->hw_mode == HOSTAPD_MODE_IEEE80211G ||
+            iface->conf->ieee80211n || iface->conf->ieee80211ac) &&
+           iface->conf->channel == 14) {
+               wpa_printf(MSG_INFO, "Disable OFDM/HT/VHT on channel 14");
+               iface->conf->hw_mode = HOSTAPD_MODE_IEEE80211B;
+               iface->conf->ieee80211n = 0;
+               iface->conf->ieee80211ac = 0;
+       }
+
        iface->current_mode = NULL;
        for (i = 0; i < iface->num_hw_features; i++) {
                struct hostapd_hw_modes *mode = &iface->hw_features[i];
@@ -669,82 +901,16 @@ int hostapd_select_hw_mode(struct hostapd_iface *iface)
                return -2;
        }
 
-       ok = 0;
-       for (j = 0; j < iface->current_mode->num_channels; j++) {
-               struct hostapd_channel_data *chan =
-                       &iface->current_mode->channels[j];
-               if (chan->chan == iface->conf->channel) {
-                       if (chan->flag & HOSTAPD_CHAN_DISABLED) {
-                               wpa_printf(MSG_ERROR,
-                                          "channel [%i] (%i) is disabled for "
-                                          "use in AP mode, flags: 0x%x%s%s%s",
-                                          j, chan->chan, chan->flag,
-                                          chan->flag & HOSTAPD_CHAN_NO_IBSS ?
-                                          " NO-IBSS" : "",
-                                          chan->flag &
-                                          HOSTAPD_CHAN_PASSIVE_SCAN ?
-                                          " PASSIVE-SCAN" : "",
-                                          chan->flag & HOSTAPD_CHAN_RADAR ?
-                                          " RADAR" : "");
-                       } else {
-                               ok = 1;
-                               break;
-                       }
-               }
-       }
-       if (ok && iface->conf->secondary_channel) {
-               int sec_ok = 0;
-               int sec_chan = iface->conf->channel +
-                       iface->conf->secondary_channel * 4;
-               for (j = 0; j < iface->current_mode->num_channels; j++) {
-                       struct hostapd_channel_data *chan =
-                               &iface->current_mode->channels[j];
-                       if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
-                           (chan->chan == sec_chan)) {
-                               sec_ok = 1;
-                               break;
-                       }
-               }
-               if (!sec_ok) {
-                       hostapd_logger(iface->bss[0], NULL,
-                                      HOSTAPD_MODULE_IEEE80211,
-                                      HOSTAPD_LEVEL_WARNING,
-                                      "Configured HT40 secondary channel "
-                                      "(%d) not found from the channel list "
-                                      "of current mode (%d) %s",
-                                      sec_chan, iface->current_mode->mode,
-                                      hostapd_hw_mode_txt(
-                                              iface->current_mode->mode));
-                       ok = 0;
-               }
-       }
-       if (iface->conf->channel == 0) {
-               /* TODO: could request a scan of neighboring BSSes and select
-                * the channel automatically */
-               wpa_printf(MSG_ERROR, "Channel not configured "
-                          "(hw_mode/channel in hostapd.conf)");
+       switch (hostapd_check_chans(iface)) {
+       case HOSTAPD_CHAN_VALID:
+               return 0;
+       case HOSTAPD_CHAN_ACS: /* ACS will run and later complete */
+               return 1;
+       case HOSTAPD_CHAN_INVALID:
+       default:
+               hostapd_notify_bad_chans(iface);
                return -3;
        }
-       if (ok == 0 && iface->conf->channel != 0) {
-               hostapd_logger(iface->bss[0], NULL,
-                              HOSTAPD_MODULE_IEEE80211,
-                              HOSTAPD_LEVEL_WARNING,
-                              "Configured channel (%d) not found from the "
-                              "channel list of current mode (%d) %s",
-                              iface->conf->channel,
-                              iface->current_mode->mode,
-                              hostapd_hw_mode_txt(iface->current_mode->mode));
-               iface->current_mode = NULL;
-       }
-
-       if (iface->current_mode == NULL) {
-               hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211,
-                              HOSTAPD_LEVEL_WARNING,
-                              "Hardware does not support configured channel");
-               return -4;
-       }
-
-       return 0;
 }
 
 
@@ -767,35 +933,11 @@ const char * hostapd_hw_mode_txt(int mode)
 
 int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan)
 {
-       int i;
-
-       if (!hapd->iface->current_mode)
-               return 0;
-
-       for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
-               struct hostapd_channel_data *ch =
-                       &hapd->iface->current_mode->channels[i];
-               if (ch->chan == chan)
-                       return ch->freq;
-       }
-
-       return 0;
+       return hw_get_freq(hapd->iface->current_mode, chan);
 }
 
 
 int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq)
 {
-       int i;
-
-       if (!hapd->iface->current_mode)
-               return 0;
-
-       for (i = 0; i < hapd->iface->current_mode->num_channels; i++) {
-               struct hostapd_channel_data *ch =
-                       &hapd->iface->current_mode->channels[i];
-               if (ch->freq == freq)
-                       return ch->chan;
-       }
-
-       return 0;
+       return hw_get_chan(hapd->iface->current_mode, freq);
 }
index abadcd1..0f67ab8 100644 (file)
@@ -4,14 +4,8 @@
  * Copyright 2005-2006, Devicescape Software, Inc.
  * Copyright (c) 2008-2011, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #ifndef HW_FEATURES_H
@@ -21,6 +15,7 @@
 void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
                              size_t num_hw_features);
 int hostapd_get_hw_features(struct hostapd_iface *iface);
+int hostapd_acs_completed(struct hostapd_iface *iface, int err);
 int hostapd_select_hw_mode(struct hostapd_iface *iface);
 const char * hostapd_hw_mode_txt(int mode);
 int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan);
@@ -28,6 +23,7 @@ int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq);
 int hostapd_check_ht_capab(struct hostapd_iface *iface);
 int hostapd_prepare_rates(struct hostapd_iface *iface,
                          struct hostapd_hw_modes *mode);
+void hostapd_stop_setup_timers(struct hostapd_iface *iface);
 #else /* NEED_AP_MLME */
 static inline void
 hostapd_free_hw_features(struct hostapd_hw_modes *hw_features,
@@ -66,6 +62,10 @@ static inline int hostapd_prepare_rates(struct hostapd_iface *iface,
        return 0;
 }
 
+static inline void hostapd_stop_setup_timers(struct hostapd_iface *iface)
+{
+}
+
 #endif /* NEED_AP_MLME */
 
 #endif /* HW_FEATURES_H */
index be55c69..99aa04d 100644 (file)
@@ -204,7 +204,7 @@ static void iapp_send_add(struct iapp_data *iapp, u8 *mac_addr, u16 seq_num)
        addr.sin_port = htons(IAPP_UDP_PORT);
        if (sendto(iapp->udp_sock, buf, (char *) (add + 1) - buf, 0,
                   (struct sockaddr *) &addr, sizeof(addr)) < 0)
-               perror("sendto[IAPP-ADD]");
+               wpa_printf(MSG_INFO, "sendto[IAPP-ADD]: %s", strerror(errno));
 }
 
 
@@ -231,7 +231,7 @@ static void iapp_send_layer2_update(struct iapp_data *iapp, u8 *addr)
                                   * FIX: what is correct RW with 802.11? */
 
        if (send(iapp->packet_sock, &msg, sizeof(msg), 0) < 0)
-               perror("send[L2 Update]");
+               wpa_printf(MSG_INFO, "send[L2 Update]: %s", strerror(errno));
 }
 
 
@@ -242,29 +242,22 @@ static void iapp_send_layer2_update(struct iapp_data *iapp, u8 *addr)
  */
 void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta)
 {
-       struct ieee80211_mgmt *assoc;
-       u16 seq;
+       u16 seq = 0; /* TODO */
 
        if (iapp == NULL)
                return;
 
-       assoc = sta->last_assoc_req;
-       seq = assoc ? WLAN_GET_SEQ_SEQ(le_to_host16(assoc->seq_ctrl)) : 0;
-
        /* IAPP-ADD.request(MAC Address, Sequence Number, Timeout) */
        hostapd_logger(iapp->hapd, sta->addr, HOSTAPD_MODULE_IAPP,
                       HOSTAPD_LEVEL_DEBUG, "IAPP-ADD.request(seq=%d)", seq);
        iapp_send_layer2_update(iapp, sta->addr);
        iapp_send_add(iapp, sta->addr, seq);
 
-       if (assoc && WLAN_FC_GET_STYPE(le_to_host16(assoc->frame_control)) ==
-           WLAN_FC_STYPE_REASSOC_REQ) {
-               /* IAPP-MOVE.request(MAC Address, Sequence Number, Old AP,
-                *                   Context Block, Timeout)
-                */
-               /* TODO: Send IAPP-MOVE to the old AP; Map Old AP BSSID to
-                * IP address */
-       }
+       /* TODO: If this was reassociation:
+        * IAPP-MOVE.request(MAC Address, Sequence Number, Old AP,
+        *                   Context Block, Timeout)
+        * TODO: Send IAPP-MOVE to the old AP; Map Old AP BSSID to
+        * IP address */
 }
 
 
@@ -276,8 +269,8 @@ static void iapp_process_add_notify(struct iapp_data *iapp,
        struct sta_info *sta;
 
        if (len != sizeof(*add)) {
-               printf("Invalid IAPP-ADD packet length %d (expected %lu)\n",
-                      len, (unsigned long) sizeof(*add));
+               wpa_printf(MSG_INFO, "Invalid IAPP-ADD packet length %d (expected %lu)",
+                          len, (unsigned long) sizeof(*add));
                return;
        }
 
@@ -326,7 +319,8 @@ static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx)
        len = recvfrom(iapp->udp_sock, buf, sizeof(buf), 0,
                       (struct sockaddr *) &from, &fromlen);
        if (len < 0) {
-               perror("recvfrom");
+               wpa_printf(MSG_INFO, "iapp_receive_udp - recvfrom: %s",
+                          strerror(errno));
                return;
        }
 
@@ -350,23 +344,24 @@ static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx)
                       hdr->version, hdr->command,
                       be_to_host16(hdr->identifier), hlen);
        if (hdr->version != IAPP_VERSION) {
-               printf("Dropping IAPP frame with unknown version %d\n",
-                      hdr->version);
+               wpa_printf(MSG_INFO, "Dropping IAPP frame with unknown version %d",
+                          hdr->version);
                return;
        }
        if (hlen > len) {
-               printf("Underflow IAPP frame (hlen=%d len=%d)\n", hlen, len);
+               wpa_printf(MSG_INFO, "Underflow IAPP frame (hlen=%d len=%d)",
+                          hlen, len);
                return;
        }
        if (hlen < len) {
-               printf("Ignoring %d extra bytes from IAPP frame\n",
-                      len - hlen);
+               wpa_printf(MSG_INFO, "Ignoring %d extra bytes from IAPP frame",
+                          len - hlen);
                len = hlen;
        }
 
        switch (hdr->command) {
        case IAPP_CMD_ADD_notify:
-               iapp_process_add_notify(iapp, &from, hdr, hlen - sizeof(*hdr));
+               iapp_process_add_notify(iapp, &from, hdr, len - sizeof(*hdr));
                break;
        case IAPP_CMD_MOVE_notify:
                /* TODO: MOVE is using TCP; so move this to TCP handler once it
@@ -376,7 +371,7 @@ static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx)
                /* TODO: process */
                break;
        default:
-               printf("Unknown IAPP command %d\n", hdr->command);
+               wpa_printf(MSG_INFO, "Unknown IAPP command %d", hdr->command);
                break;
        }
 }
@@ -403,7 +398,8 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface)
 
        iapp->udp_sock = socket(PF_INET, SOCK_DGRAM, 0);
        if (iapp->udp_sock < 0) {
-               perror("socket[PF_INET,SOCK_DGRAM]");
+               wpa_printf(MSG_INFO, "iapp_init - socket[PF_INET,SOCK_DGRAM]: %s",
+                          strerror(errno));
                iapp_deinit(iapp);
                return NULL;
        }
@@ -411,35 +407,38 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface)
        os_memset(&ifr, 0, sizeof(ifr));
        os_strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name));
        if (ioctl(iapp->udp_sock, SIOCGIFINDEX, &ifr) != 0) {
-               perror("ioctl(SIOCGIFINDEX)");
+               wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFINDEX): %s",
+                          strerror(errno));
                iapp_deinit(iapp);
                return NULL;
        }
        ifindex = ifr.ifr_ifindex;
 
        if (ioctl(iapp->udp_sock, SIOCGIFADDR, &ifr) != 0) {
-               perror("ioctl(SIOCGIFADDR)");
+               wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFADDR): %s",
+                          strerror(errno));
                iapp_deinit(iapp);
                return NULL;
        }
        paddr = (struct sockaddr_in *) &ifr.ifr_addr;
        if (paddr->sin_family != AF_INET) {
-               printf("Invalid address family %i (SIOCGIFADDR)\n",
-                      paddr->sin_family);
+               wpa_printf(MSG_INFO, "IAPP: Invalid address family %i (SIOCGIFADDR)",
+                          paddr->sin_family);
                iapp_deinit(iapp);
                return NULL;
        }
        iapp->own.s_addr = paddr->sin_addr.s_addr;
 
        if (ioctl(iapp->udp_sock, SIOCGIFBRDADDR, &ifr) != 0) {
-               perror("ioctl(SIOCGIFBRDADDR)");
+               wpa_printf(MSG_INFO, "iapp_init - ioctl(SIOCGIFBRDADDR): %s",
+                          strerror(errno));
                iapp_deinit(iapp);
                return NULL;
        }
        paddr = (struct sockaddr_in *) &ifr.ifr_addr;
        if (paddr->sin_family != AF_INET) {
-               printf("Invalid address family %i (SIOCGIFBRDADDR)\n",
-                      paddr->sin_family);
+               wpa_printf(MSG_INFO, "Invalid address family %i (SIOCGIFBRDADDR)",
+                          paddr->sin_family);
                iapp_deinit(iapp);
                return NULL;
        }
@@ -450,7 +449,8 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface)
        uaddr.sin_port = htons(IAPP_UDP_PORT);
        if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr,
                 sizeof(uaddr)) < 0) {
-               perror("bind[UDP]");
+               wpa_printf(MSG_INFO, "iapp_init - bind[UDP]: %s",
+                          strerror(errno));
                iapp_deinit(iapp);
                return NULL;
        }
@@ -461,14 +461,16 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface)
        mreq.imr_ifindex = 0;
        if (setsockopt(iapp->udp_sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq,
                       sizeof(mreq)) < 0) {
-               perror("setsockopt[UDP,IP_ADD_MEMBERSHIP]");
+               wpa_printf(MSG_INFO, "iapp_init - setsockopt[UDP,IP_ADD_MEMBERSHIP]: %s",
+                          strerror(errno));
                iapp_deinit(iapp);
                return NULL;
        }
 
        iapp->packet_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
        if (iapp->packet_sock < 0) {
-               perror("socket[PF_PACKET,SOCK_RAW]");
+               wpa_printf(MSG_INFO, "iapp_init - socket[PF_PACKET,SOCK_RAW]: %s",
+                          strerror(errno));
                iapp_deinit(iapp);
                return NULL;
        }
@@ -478,19 +480,20 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface)
        addr.sll_ifindex = ifindex;
        if (bind(iapp->packet_sock, (struct sockaddr *) &addr,
                 sizeof(addr)) < 0) {
-               perror("bind[PACKET]");
+               wpa_printf(MSG_INFO, "iapp_init - bind[PACKET]: %s",
+                          strerror(errno));
                iapp_deinit(iapp);
                return NULL;
        }
 
        if (eloop_register_read_sock(iapp->udp_sock, iapp_receive_udp,
                                     iapp, NULL)) {
-               printf("Could not register read socket for IAPP.\n");
+               wpa_printf(MSG_INFO, "Could not register read socket for IAPP");
                iapp_deinit(iapp);
                return NULL;
        }
 
-       printf("IEEE 802.11F (IAPP) using interface %s\n", iface);
+       wpa_printf(MSG_INFO, "IEEE 802.11F (IAPP) using interface %s", iface);
 
        /* TODO: For levels 2 and 3: send RADIUS Initiate-Request, receive
         * RADIUS Initiate-Accept or Initiate-Reject. IAPP port should actually
@@ -515,7 +518,8 @@ void iapp_deinit(struct iapp_data *iapp)
                mreq.imr_ifindex = 0;
                if (setsockopt(iapp->udp_sock, SOL_IP, IP_DROP_MEMBERSHIP,
                               &mreq, sizeof(mreq)) < 0) {
-                       perror("setsockopt[UDP,IP_DEL_MEMBERSHIP]");
+                       wpa_printf(MSG_INFO, "iapp_deinit - setsockopt[UDP,IP_DEL_MEMBERSHIP]: %s",
+                                  strerror(errno));
                }
 
                eloop_unregister_read_sock(iapp->udp_sock);
index 5503af1..89911b1 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / IEEE 802.11 Management
- * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -15,7 +15,6 @@
 #include "crypto/crypto.h"
 #include "crypto/sha256.h"
 #include "crypto/random.h"
-#include "drivers/driver.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
@@ -30,6 +29,7 @@
 #include "sta_info.h"
 #include "ieee802_1x.h"
 #include "wpa_auth.h"
+#include "pmksa_cache_auth.h"
 #include "wmm.h"
 #include "ap_list.h"
 #include "accounting.h"
@@ -39,6 +39,7 @@
 #include "ap_drv_ops.h"
 #include "wnm_ap.h"
 #include "ieee802_11.h"
+#include "dfs.h"
 
 
 u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
@@ -62,7 +63,6 @@ u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
        }
 
        *pos++ = num;
-       count = 0;
        for (i = 0, count = 0; i < hapd->iface->num_rates && count < num;
             i++) {
                count++;
@@ -105,7 +105,6 @@ u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid)
 
        *pos++ = WLAN_EID_EXT_SUPP_RATES;
        *pos++ = num;
-       count = 0;
        for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8;
             i++) {
                count++;
@@ -138,6 +137,15 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
 {
        int capab = WLAN_CAPABILITY_ESS;
        int privacy;
+       int dfs;
+
+       /* Check if any of configured channels require DFS */
+       dfs = hostapd_is_dfs_required(hapd->iface);
+       if (dfs < 0) {
+               wpa_printf(MSG_WARNING, "Failed to check if DFS is required; ret=%d",
+                          dfs);
+               dfs = 0;
+       }
 
        if (hapd->iface->num_sta_no_short_preamble == 0 &&
            hapd->iconf->preamble == SHORT_PREAMBLE)
@@ -153,6 +161,11 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
        if (hapd->conf->wpa)
                privacy = 1;
 
+#ifdef CONFIG_HS20
+       if (hapd->conf->osen)
+               privacy = 1;
+#endif /* CONFIG_HS20 */
+
        if (sta) {
                int policy, def_klen;
                if (probe && sta->ssid_probe) {
@@ -175,22 +188,21 @@ u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
            hapd->iface->num_sta_no_short_slot_time == 0)
                capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
 
-       return capab;
-}
+       /*
+        * Currently, Spectrum Management capability bit is set when directly
+        * requested in configuration by spectrum_mgmt_required or when AP is
+        * running on DFS channel.
+        * TODO: Also consider driver support for TPC to set Spectrum Mgmt bit
+        */
+       if (hapd->iface->current_mode &&
+           hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
+           (hapd->iconf->spectrum_mgmt_required || dfs))
+               capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
 
+       if (hapd->conf->radio_measurements)
+               capab |= IEEE80211_CAP_RRM;
 
-void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len)
-{
-       int i;
-       if (len > HOSTAPD_MAX_SSID_LEN)
-               len = HOSTAPD_MAX_SSID_LEN;
-       for (i = 0; i < len; i++) {
-               if (ssid[i] >= 32 && ssid[i] < 127)
-                       buf[i] = ssid[i];
-               else
-                       buf[i] = '.';
-       }
-       buf[len] = '\0';
+       return capab;
 }
 
 
@@ -228,7 +240,8 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta,
 
        /* Transaction 3 */
        if (!iswep || !sta->challenge || !challenge ||
-           os_memcmp(sta->challenge, challenge, WLAN_AUTH_CHALLENGE_LEN)) {
+           os_memcmp_const(sta->challenge, challenge,
+                           WLAN_AUTH_CHALLENGE_LEN)) {
                hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
                               HOSTAPD_LEVEL_INFO,
                               "shared key authentication - invalid "
@@ -239,13 +252,8 @@ static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta,
        hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
                       HOSTAPD_LEVEL_DEBUG,
                       "authentication OK (shared key)");
-#ifdef IEEE80211_REQUIRE_AUTH_ACK
-       /* Station will be marked authenticated if it ACKs the
-        * authentication reply. */
-#else
        sta->flags |= WLAN_STA_AUTH;
        wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
-#endif
        os_free(sta->challenge);
        sta->challenge = NULL;
 
@@ -286,7 +294,7 @@ static void send_auth_reply(struct hostapd_data *hapd,
                   MAC2STR(dst), auth_alg, auth_transaction,
                   resp, (unsigned long) ies_len);
        if (hostapd_drv_send_mlme(hapd, reply, rlen, 0) < 0)
-               perror("send_auth_reply: send");
+               wpa_printf(MSG_INFO, "send_auth_reply: send");
 
        os_free(buf);
 }
@@ -320,8 +328,12 @@ static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid,
 
 #ifdef CONFIG_SAE
 
-static struct wpabuf * auth_process_sae_commit(struct hostapd_data *hapd,
-                                              struct sta_info *sta)
+#define dot11RSNASAERetransPeriod 40   /* msec */
+#define dot11RSNASAESync 5             /* attempts */
+
+
+static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
+                                            struct sta_info *sta, int update)
 {
        struct wpabuf *buf;
 
@@ -330,7 +342,8 @@ static struct wpabuf * auth_process_sae_commit(struct hostapd_data *hapd,
                return NULL;
        }
 
-       if (sae_prepare_commit(hapd->own_addr, sta->addr,
+       if (update &&
+           sae_prepare_commit(hapd->own_addr, sta->addr,
                               (u8 *) hapd->conf->ssid.wpa_passphrase,
                               os_strlen(hapd->conf->ssid.wpa_passphrase),
                               sta->sae) < 0) {
@@ -338,15 +351,11 @@ static struct wpabuf * auth_process_sae_commit(struct hostapd_data *hapd,
                return NULL;
        }
 
-       if (sae_process_commit(sta->sae) < 0) {
-               wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit");
-               return NULL;
-       }
-
        buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN);
        if (buf == NULL)
                return NULL;
-       sae_write_commit(sta->sae, buf, NULL);
+       sae_write_commit(sta->sae, buf, sta->sae->tmp ?
+                        sta->sae->tmp->anti_clogging_token : NULL);
 
        return buf;
 }
@@ -367,6 +376,46 @@ static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd,
 }
 
 
+static int auth_sae_send_commit(struct hostapd_data *hapd,
+                               struct sta_info *sta,
+                               const u8 *bssid, int update)
+{
+       struct wpabuf *data;
+
+       data = auth_build_sae_commit(hapd, sta, update);
+       if (data == NULL)
+               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+       send_auth_reply(hapd, sta->addr, bssid,
+                       WLAN_AUTH_SAE, 1, WLAN_STATUS_SUCCESS,
+                       wpabuf_head(data), wpabuf_len(data));
+
+       wpabuf_free(data);
+
+       return WLAN_STATUS_SUCCESS;
+}
+
+
+static int auth_sae_send_confirm(struct hostapd_data *hapd,
+                                struct sta_info *sta,
+                                const u8 *bssid)
+{
+       struct wpabuf *data;
+
+       data = auth_build_sae_confirm(hapd, sta);
+       if (data == NULL)
+               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+       send_auth_reply(hapd, sta->addr, bssid,
+                       WLAN_AUTH_SAE, 2, WLAN_STATUS_SUCCESS,
+                       wpabuf_head(data), wpabuf_len(data));
+
+       wpabuf_free(data);
+
+       return WLAN_STATUS_SUCCESS;
+}
+
+
 static int use_sae_anti_clogging(struct hostapd_data *hapd)
 {
        struct sta_info *sta;
@@ -399,7 +448,7 @@ static int check_sae_token(struct hostapd_data *hapd, const u8 *addr,
                return -1;
        if (hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
                        addr, ETH_ALEN, mac) < 0 ||
-           os_memcmp(token, mac, SHA256_MAC_LEN) != 0)
+           os_memcmp_const(token, mac, SHA256_MAC_LEN) != 0)
                return -1;
 
        return 0;
@@ -407,27 +456,29 @@ static int check_sae_token(struct hostapd_data *hapd, const u8 *addr,
 
 
 static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd,
-                                           const u8 *addr)
+                                           int group, const u8 *addr)
 {
        struct wpabuf *buf;
        u8 *token;
-       struct os_time t;
+       struct os_reltime now;
 
-       os_get_time(&t);
-       if (hapd->last_sae_token_key_update == 0 ||
-           t.sec > hapd->last_sae_token_key_update + 60) {
+       os_get_reltime(&now);
+       if (!os_reltime_initialized(&hapd->last_sae_token_key_update) ||
+           os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60)) {
                if (random_get_bytes(hapd->sae_token_key,
                                     sizeof(hapd->sae_token_key)) < 0)
                        return NULL;
                wpa_hexdump(MSG_DEBUG, "SAE: Updated token key",
                            hapd->sae_token_key, sizeof(hapd->sae_token_key));
-               hapd->last_sae_token_key_update = t.sec;
+               hapd->last_sae_token_key_update = now;
        }
 
-       buf = wpabuf_alloc(SHA256_MAC_LEN);
+       buf = wpabuf_alloc(sizeof(le16) + SHA256_MAC_LEN);
        if (buf == NULL)
                return NULL;
 
+       wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
+
        token = wpabuf_put(buf, SHA256_MAC_LEN);
        hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
                    addr, ETH_ALEN, token);
@@ -436,28 +487,299 @@ static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd,
 }
 
 
+static int sae_check_big_sync(struct sta_info *sta)
+{
+       if (sta->sae->sync > dot11RSNASAESync) {
+               sta->sae->state = SAE_NOTHING;
+               sta->sae->sync = 0;
+               return -1;
+       }
+       return 0;
+}
+
+
+static void auth_sae_retransmit_timer(void *eloop_ctx, void *eloop_data)
+{
+       struct hostapd_data *hapd = eloop_ctx;
+       struct sta_info *sta = eloop_data;
+       int ret;
+
+       if (sae_check_big_sync(sta))
+               return;
+       sta->sae->sync++;
+
+       switch (sta->sae->state) {
+       case SAE_COMMITTED:
+               ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0);
+               eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000,
+                                      auth_sae_retransmit_timer, hapd, sta);
+               break;
+       case SAE_CONFIRMED:
+               ret = auth_sae_send_confirm(hapd, sta, hapd->own_addr);
+               eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000,
+                                      auth_sae_retransmit_timer, hapd, sta);
+               break;
+       default:
+               ret = -1;
+               break;
+       }
+
+       if (ret != WLAN_STATUS_SUCCESS)
+               wpa_printf(MSG_INFO, "SAE: Failed to retransmit: ret=%d", ret);
+}
+
+
+void sae_clear_retransmit_timer(struct hostapd_data *hapd, struct sta_info *sta)
+{
+       eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
+}
+
+
+static void sae_set_retransmit_timer(struct hostapd_data *hapd,
+                                    struct sta_info *sta)
+{
+       if (!(hapd->conf->mesh & MESH_ENABLED))
+               return;
+
+       eloop_cancel_timeout(auth_sae_retransmit_timer, hapd, sta);
+       eloop_register_timeout(0, dot11RSNASAERetransPeriod * 1000,
+                              auth_sae_retransmit_timer, hapd, sta);
+}
+
+
+static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
+                      const u8 *bssid, u8 auth_transaction)
+{
+       int ret;
+
+       if (auth_transaction != 1 && auth_transaction != 2)
+               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+       switch (sta->sae->state) {
+       case SAE_NOTHING:
+               if (auth_transaction == 1) {
+                       ret = auth_sae_send_commit(hapd, sta, bssid, 1);
+                       if (ret)
+                               return ret;
+                       sta->sae->state = SAE_COMMITTED;
+
+                       if (sae_process_commit(sta->sae) < 0)
+                               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+                       /*
+                        * In mesh case, both Commit and Confirm can be sent
+                        * immediately. In infrastructure BSS, only a single
+                        * Authentication frame (Commit) is expected from the AP
+                        * here and the second one (Confirm) will be sent once
+                        * the STA has sent its second Authentication frame
+                        * (Confirm).
+                        */
+                       if (hapd->conf->mesh & MESH_ENABLED) {
+                               /*
+                                * Send both Commit and Confirm immediately
+                                * based on SAE finite state machine
+                                * Nothing -> Confirm transition.
+                                */
+                               ret = auth_sae_send_confirm(hapd, sta, bssid);
+                               if (ret)
+                                       return ret;
+                               sta->sae->state = SAE_CONFIRMED;
+                       } else {
+                               /*
+                                * For infrastructure BSS, send only the Commit
+                                * message now to get alternating sequence of
+                                * Authentication frames between the AP and STA.
+                                * Confirm will be sent in
+                                * Commited -> Confirmed/Accepted transition
+                                * when receiving Confirm from STA.
+                                */
+                       }
+                       sta->sae->sync = 0;
+                       sae_set_retransmit_timer(hapd, sta);
+               } else {
+                       hostapd_logger(hapd, sta->addr,
+                                      HOSTAPD_MODULE_IEEE80211,
+                                      HOSTAPD_LEVEL_DEBUG,
+                                      "SAE confirm before commit");
+               }
+               break;
+       case SAE_COMMITTED:
+               sae_clear_retransmit_timer(hapd, sta);
+               if (auth_transaction == 1) {
+                       if (sae_process_commit(sta->sae) < 0)
+                               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+                       ret = auth_sae_send_confirm(hapd, sta, bssid);
+                       if (ret)
+                               return ret;
+                       sta->sae->state = SAE_CONFIRMED;
+                       sta->sae->sync = 0;
+                       sae_set_retransmit_timer(hapd, sta);
+               } else if (hapd->conf->mesh & MESH_ENABLED) {
+                       /*
+                        * In mesh case, follow SAE finite state machine and
+                        * send Commit now, if sync count allows.
+                        */
+                       if (sae_check_big_sync(sta))
+                               return WLAN_STATUS_SUCCESS;
+                       sta->sae->sync++;
+
+                       ret = auth_sae_send_commit(hapd, sta, bssid, 1);
+                       if (ret)
+                               return ret;
+
+                       sae_set_retransmit_timer(hapd, sta);
+               } else {
+                       /*
+                        * For instructure BSS, send the postponed Confirm from
+                        * Nothing -> Confirmed transition that was reduced to
+                        * Nothing -> Committed above.
+                        */
+                       ret = auth_sae_send_confirm(hapd, sta, bssid);
+                       if (ret)
+                               return ret;
+
+                       sta->sae->state = SAE_CONFIRMED;
+
+                       /*
+                        * Since this was triggered on Confirm RX, run another
+                        * step to get to Accepted without waiting for
+                        * additional events.
+                        */
+                       return sae_sm_step(hapd, sta, bssid, auth_transaction);
+               }
+               break;
+       case SAE_CONFIRMED:
+               sae_clear_retransmit_timer(hapd, sta);
+               if (auth_transaction == 1) {
+                       if (sae_check_big_sync(sta))
+                               return WLAN_STATUS_SUCCESS;
+                       sta->sae->sync++;
+
+                       ret = auth_sae_send_commit(hapd, sta, bssid, 1);
+                       if (ret)
+                               return ret;
+
+                       if (sae_process_commit(sta->sae) < 0)
+                               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+                       ret = auth_sae_send_confirm(hapd, sta, bssid);
+                       if (ret)
+                               return ret;
+
+                       sae_set_retransmit_timer(hapd, sta);
+               } else {
+                       sta->flags |= WLAN_STA_AUTH;
+                       sta->auth_alg = WLAN_AUTH_SAE;
+                       mlme_authenticate_indication(hapd, sta);
+                       wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
+                       sta->sae->state = SAE_ACCEPTED;
+                       wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
+                                              sta->sae->pmk);
+               }
+               break;
+       case SAE_ACCEPTED:
+               if (auth_transaction == 1) {
+                       wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR
+                                  ") doing reauthentication",
+                                  MAC2STR(sta->addr));
+                       ap_free_sta(hapd, sta);
+               } else {
+                       if (sae_check_big_sync(sta))
+                               return WLAN_STATUS_SUCCESS;
+                       sta->sae->sync++;
+
+                       ret = auth_sae_send_confirm(hapd, sta, bssid);
+                       sae_clear_temp_data(sta->sae);
+                       if (ret)
+                               return ret;
+               }
+               break;
+       default:
+               wpa_printf(MSG_ERROR, "SAE: invalid state %d",
+                          sta->sae->state);
+               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+       }
+       return WLAN_STATUS_SUCCESS;
+}
+
+
 static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
                            const struct ieee80211_mgmt *mgmt, size_t len,
-                           u8 auth_transaction)
+                           u16 auth_transaction, u16 status_code)
 {
        u16 resp = WLAN_STATUS_SUCCESS;
        struct wpabuf *data = NULL;
 
        if (!sta->sae) {
-               if (auth_transaction != 1)
+               if (auth_transaction != 1 || status_code != WLAN_STATUS_SUCCESS)
                        return;
                sta->sae = os_zalloc(sizeof(*sta->sae));
                if (sta->sae == NULL)
                        return;
                sta->sae->state = SAE_NOTHING;
+               sta->sae->sync = 0;
        }
 
        if (auth_transaction == 1) {
-               const u8 *token = NULL;
+               const u8 *token = NULL, *pos, *end;
                size_t token_len = 0;
                hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
                               HOSTAPD_LEVEL_DEBUG,
-                              "start SAE authentication (RX commit)");
+                              "start SAE authentication (RX commit, status=%u)",
+                              status_code);
+
+               if ((hapd->conf->mesh & MESH_ENABLED) &&
+                   status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
+                   sta->sae->tmp) {
+                       pos = mgmt->u.auth.variable;
+                       end = ((const u8 *) mgmt) + len;
+                       if (pos + sizeof(le16) > end) {
+                               wpa_printf(MSG_ERROR,
+                                          "SAE: Too short anti-clogging token request");
+                               resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                               goto reply;
+                       }
+                       resp = sae_group_allowed(sta->sae,
+                                                hapd->conf->sae_groups,
+                                                WPA_GET_LE16(pos));
+                       if (resp != WLAN_STATUS_SUCCESS) {
+                               wpa_printf(MSG_ERROR,
+                                          "SAE: Invalid group in anti-clogging token request");
+                               goto reply;
+                       }
+                       pos += sizeof(le16);
+
+                       wpabuf_free(sta->sae->tmp->anti_clogging_token);
+                       sta->sae->tmp->anti_clogging_token =
+                               wpabuf_alloc_copy(pos, end - pos);
+                       if (sta->sae->tmp->anti_clogging_token == NULL) {
+                               wpa_printf(MSG_ERROR,
+                                          "SAE: Failed to alloc for anti-clogging token");
+                               return;
+                       }
+
+                       /*
+                        * IEEE Std 802.11-2012, 11.3.8.6.4: If the Status code
+                        * is 76, a new Commit Message shall be constructed
+                        * with the Anti-Clogging Token from the received
+                        * Authentication frame, and the commit-scalar and
+                        * COMMIT-ELEMENT previously sent.
+                        */
+                       if (auth_sae_send_commit(hapd, sta, mgmt->bssid, 0)) {
+                               wpa_printf(MSG_ERROR,
+                                          "SAE: Failed to send commit message");
+                               return;
+                       }
+                       sta->sae->state = SAE_COMMITTED;
+                       sta->sae->sync = 0;
+                       sae_set_retransmit_timer(hapd, sta);
+                       return;
+               }
+
+               if (status_code != WLAN_STATUS_SUCCESS)
+                       return;
+
                resp = sae_parse_commit(sta->sae, mgmt->u.auth.variable,
                                        ((const u8 *) mgmt) + len -
                                        mgmt->u.auth.variable, &token,
@@ -470,67 +792,91 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
                        return;
                }
 
-               if (resp == WLAN_STATUS_SUCCESS) {
-                       if (!token && use_sae_anti_clogging(hapd)) {
-                               wpa_printf(MSG_DEBUG, "SAE: Request anti-"
-                                          "clogging token from " MACSTR,
-                                          MAC2STR(sta->addr));
-                               data = auth_build_token_req(hapd, sta->addr);
-                               resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
-                       } else {
-                               data = auth_process_sae_commit(hapd, sta);
-                               if (data == NULL)
-                                       resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
-                               else
-                                       sta->sae->state = SAE_COMMITTED;
-                       }
+               if (resp != WLAN_STATUS_SUCCESS)
+                       goto reply;
+
+               if (!token && use_sae_anti_clogging(hapd)) {
+                       wpa_printf(MSG_DEBUG,
+                                  "SAE: Request anti-clogging token from "
+                                  MACSTR, MAC2STR(sta->addr));
+                       data = auth_build_token_req(hapd, sta->sae->group,
+                                                   sta->addr);
+                       resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
+                       if (hapd->conf->mesh & MESH_ENABLED)
+                               sta->sae->state = SAE_NOTHING;
+                       goto reply;
                }
+
+               resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction);
        } else if (auth_transaction == 2) {
-               if (sta->sae->state != SAE_COMMITTED) {
-                       hostapd_logger(hapd, sta->addr,
-                                      HOSTAPD_MODULE_IEEE80211,
-                                      HOSTAPD_LEVEL_DEBUG,
-                                      "SAE confirm before commit");
-                       resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
-               }
                hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
                               HOSTAPD_LEVEL_DEBUG,
-                              "SAE authentication (RX confirm)");
-               if (sae_check_confirm(sta->sae, mgmt->u.auth.variable,
-                                      ((u8 *) mgmt) + len -
-                                      mgmt->u.auth.variable) < 0) {
-                       resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
-               } else {
-                       resp = WLAN_STATUS_SUCCESS;
-                       sta->flags |= WLAN_STA_AUTH;
-                       wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
-                       sta->auth_alg = WLAN_AUTH_SAE;
-                       mlme_authenticate_indication(hapd, sta);
-
-                       data = auth_build_sae_confirm(hapd, sta);
-                       if (data == NULL)
+                              "SAE authentication (RX confirm, status=%u)",
+                              status_code);
+               if (status_code != WLAN_STATUS_SUCCESS)
+                       return;
+               if (sta->sae->state >= SAE_CONFIRMED ||
+                   !(hapd->conf->mesh & MESH_ENABLED)) {
+                       if (sae_check_confirm(sta->sae, mgmt->u.auth.variable,
+                                             ((u8 *) mgmt) + len -
+                                             mgmt->u.auth.variable) < 0) {
                                resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
-                       else {
-                               sta->sae->state = SAE_ACCEPTED;
-                               sae_clear_temp_data(sta->sae);
+                               goto reply;
                        }
                }
+               resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction);
        } else {
                hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
                               HOSTAPD_LEVEL_DEBUG,
-                              "unexpected SAE authentication transaction %u",
-                              auth_transaction);
+                              "unexpected SAE authentication transaction %u (status=%u)",
+                              auth_transaction, status_code);
+               if (status_code != WLAN_STATUS_SUCCESS)
+                       return;
                resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
        }
 
-       sta->auth_alg = WLAN_AUTH_SAE;
-
-       send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
-                       auth_transaction, resp,
-                       data ? wpabuf_head(data) : (u8 *) "",
-                       data ? wpabuf_len(data) : 0);
+reply:
+       if (resp != WLAN_STATUS_SUCCESS) {
+               send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
+                               auth_transaction, resp,
+                               data ? wpabuf_head(data) : (u8 *) "",
+                               data ? wpabuf_len(data) : 0);
+       }
        wpabuf_free(data);
 }
+
+
+/**
+ * auth_sae_init_committed - Send COMMIT and start SAE in committed state
+ * @hapd: BSS data for the device initiating the authentication
+ * @sta: the peer to which commit authentication frame is sent
+ *
+ * This function implements Init event handling (IEEE Std 802.11-2012,
+ * 11.3.8.6.3) in which initial COMMIT message is sent. Prior to calling, the
+ * sta->sae structure should be initialized appropriately via a call to
+ * sae_prepare_commit().
+ */
+int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta)
+{
+       int ret;
+
+       if (!sta->sae || !sta->sae->tmp)
+               return -1;
+
+       if (sta->sae->state != SAE_NOTHING)
+               return -1;
+
+       ret = auth_sae_send_commit(hapd, sta, hapd->own_addr, 0);
+       if (ret)
+               return -1;
+
+       sta->sae->state = SAE_COMMITTED;
+       sta->sae->sync = 0;
+       sae_set_retransmit_timer(hapd, sta);
+
+       return 0;
+}
+
 #endif /* CONFIG_SAE */
 
 
@@ -550,15 +896,16 @@ static void handle_auth(struct hostapd_data *hapd,
        size_t resp_ies_len = 0;
        char *identity = NULL;
        char *radius_cui = NULL;
+       u16 seq_ctrl;
 
        if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
-               printf("handle_auth - too short payload (len=%lu)\n",
-                      (unsigned long) len);
+               wpa_printf(MSG_INFO, "handle_auth - too short payload (len=%lu)",
+                          (unsigned long) len);
                return;
        }
 
 #ifdef CONFIG_TESTING_OPTIONS
-       if (hapd->iconf->ignore_auth_probability > 0.0d &&
+       if (hapd->iconf->ignore_auth_probability > 0.0 &&
            drand48() < hapd->iconf->ignore_auth_probability) {
                wpa_printf(MSG_INFO,
                           "TESTING: ignoring auth frame from " MACSTR,
@@ -571,6 +918,7 @@ static void handle_auth(struct hostapd_data *hapd,
        auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction);
        status_code = le_to_host16(mgmt->u.auth.status_code);
        fc = le_to_host16(mgmt->frame_control);
+       seq_ctrl = le_to_host16(mgmt->seq_ctrl);
 
        if (len >= IEEE80211_HDRLEN + sizeof(mgmt->u.auth) +
            2 + WLAN_AUTH_CHALLENGE_LEN &&
@@ -579,10 +927,12 @@ static void handle_auth(struct hostapd_data *hapd,
                challenge = &mgmt->u.auth.variable[2];
 
        wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d "
-                  "auth_transaction=%d status_code=%d wep=%d%s",
+                  "auth_transaction=%d status_code=%d wep=%d%s "
+                  "seq_ctrl=0x%x%s",
                   MAC2STR(mgmt->sa), auth_alg, auth_transaction,
                   status_code, !!(fc & WLAN_FC_ISWEP),
-                  challenge ? " challenge" : "");
+                  challenge ? " challenge" : "",
+                  seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
 
        if (hapd->tkip_countermeasures) {
                resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
@@ -601,23 +951,23 @@ static void handle_auth(struct hostapd_data *hapd,
 #endif /* CONFIG_SAE */
              ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) &&
               auth_alg == WLAN_AUTH_SHARED_KEY))) {
-               printf("Unsupported authentication algorithm (%d)\n",
-                      auth_alg);
+               wpa_printf(MSG_INFO, "Unsupported authentication algorithm (%d)",
+                          auth_alg);
                resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG;
                goto fail;
        }
 
        if (!(auth_transaction == 1 || auth_alg == WLAN_AUTH_SAE ||
              (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) {
-               printf("Unknown authentication transaction number (%d)\n",
-                      auth_transaction);
+               wpa_printf(MSG_INFO, "Unknown authentication transaction number (%d)",
+                          auth_transaction);
                resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
                goto fail;
        }
 
        if (os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) {
-               printf("Station " MACSTR " not allowed to authenticate.\n",
-                      MAC2STR(mgmt->sa));
+               wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
+                          MAC2STR(mgmt->sa));
                resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
                goto fail;
        }
@@ -628,8 +978,8 @@ static void handle_auth(struct hostapd_data *hapd,
                                      &psk, &identity, &radius_cui);
 
        if (res == HOSTAPD_ACL_REJECT) {
-               printf("Station " MACSTR " not allowed to authenticate.\n",
-                      MAC2STR(mgmt->sa));
+               wpa_printf(MSG_INFO, "Station " MACSTR " not allowed to authenticate",
+                          MAC2STR(mgmt->sa));
                resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
                goto fail;
        }
@@ -643,11 +993,46 @@ static void handle_auth(struct hostapd_data *hapd,
                return;
        }
 
-       sta = ap_sta_add(hapd, mgmt->sa);
-       if (!sta) {
-               resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
-               goto fail;
+       sta = ap_get_sta(hapd, mgmt->sa);
+       if (sta) {
+               if ((fc & WLAN_FC_RETRY) &&
+                   sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+                   sta->last_seq_ctrl == seq_ctrl &&
+                   sta->last_subtype == WLAN_FC_STYPE_AUTH) {
+                       hostapd_logger(hapd, sta->addr,
+                                      HOSTAPD_MODULE_IEEE80211,
+                                      HOSTAPD_LEVEL_DEBUG,
+                                      "Drop repeated authentication frame seq_ctrl=0x%x",
+                                      seq_ctrl);
+                       return;
+               }
+       } else {
+#ifdef CONFIG_MESH
+               if (hapd->conf->mesh & MESH_ENABLED) {
+                       /* if the mesh peer is not available, we don't do auth.
+                        */
+                       wpa_printf(MSG_DEBUG, "Mesh peer " MACSTR
+                                  " not yet known - drop Authentiation frame",
+                                  MAC2STR(mgmt->sa));
+                       /*
+                        * Save a copy of the frame so that it can be processed
+                        * if a new peer entry is added shortly after this.
+                        */
+                       wpabuf_free(hapd->mesh_pending_auth);
+                       hapd->mesh_pending_auth = wpabuf_alloc_copy(mgmt, len);
+                       os_get_reltime(&hapd->mesh_pending_auth_time);
+                       return;
+               }
+#endif /* CONFIG_MESH */
+
+               sta = ap_sta_add(hapd, mgmt->sa);
+               if (!sta) {
+                       resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
+                       goto fail;
+               }
        }
+       sta->last_seq_ctrl = seq_ctrl;
+       sta->last_subtype = WLAN_FC_STYPE_AUTH;
 
        if (vlan_id > 0) {
                if (!hostapd_vlan_id_valid(hapd->conf->vlan, vlan_id)) {
@@ -691,15 +1076,10 @@ static void handle_auth(struct hostapd_data *hapd,
                hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
                               HOSTAPD_LEVEL_DEBUG,
                               "authentication OK (open system)");
-#ifdef IEEE80211_REQUIRE_AUTH_ACK
-               /* Station will be marked authenticated if it ACKs the
-                * authentication reply. */
-#else
                sta->flags |= WLAN_STA_AUTH;
                wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH);
                sta->auth_alg = WLAN_AUTH_OPEN;
                mlme_authenticate_indication(hapd, sta);
-#endif
                break;
        case WLAN_AUTH_SHARED_KEY:
                resp = auth_shared_key(hapd, sta, auth_transaction, challenge,
@@ -719,7 +1099,7 @@ static void handle_auth(struct hostapd_data *hapd,
                sta->auth_alg = WLAN_AUTH_FT;
                if (sta->wpa_sm == NULL)
                        sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
-                                                       sta->addr);
+                                                       sta->addr, NULL);
                if (sta->wpa_sm == NULL) {
                        wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA "
                                   "state machine");
@@ -736,7 +1116,23 @@ static void handle_auth(struct hostapd_data *hapd,
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_SAE
        case WLAN_AUTH_SAE:
-               handle_auth_sae(hapd, sta, mgmt, len, auth_transaction);
+#ifdef CONFIG_MESH
+               if (status_code == WLAN_STATUS_SUCCESS &&
+                   hapd->conf->mesh & MESH_ENABLED) {
+                       if (sta->wpa_sm == NULL)
+                               sta->wpa_sm =
+                                       wpa_auth_sta_init(hapd->wpa_auth,
+                                                         sta->addr, NULL);
+                       if (sta->wpa_sm == NULL) {
+                               wpa_printf(MSG_DEBUG,
+                                          "SAE: Failed to initialize WPA state machine");
+                               resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                               goto fail;
+                       }
+               }
+#endif /* CONFIG_MESH */
+               handle_auth_sae(hapd, sta, mgmt, len, auth_transaction,
+                               status_code);
                return;
 #endif /* CONFIG_SAE */
        }
@@ -792,12 +1188,10 @@ static u16 check_ssid(struct hostapd_data *hapd, struct sta_info *sta,
 
        if (ssid_ie_len != hapd->conf->ssid.ssid_len ||
            os_memcmp(ssid_ie, hapd->conf->ssid.ssid, ssid_ie_len) != 0) {
-               char ssid_txt[33];
-               ieee802_11_print_ssid(ssid_txt, ssid_ie, ssid_ie_len);
                hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
                               HOSTAPD_LEVEL_INFO,
                               "Station tried to associate with unknown SSID "
-                              "'%s'", ssid_txt);
+                              "'%s'", wpa_ssid_txt(ssid_ie, ssid_ie_len));
                return WLAN_STATUS_UNSPECIFIED_FAILURE;
        }
 
@@ -859,6 +1253,21 @@ static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
 }
 
 
+static u16 check_ext_capab(struct hostapd_data *hapd, struct sta_info *sta,
+                          const u8 *ext_capab_ie, size_t ext_capab_ie_len)
+{
+#ifdef CONFIG_INTERWORKING
+       /* check for QoS Map support */
+       if (ext_capab_ie_len >= 5) {
+               if (ext_capab_ie[4] & 0x01)
+                       sta->qos_map_enabled = 1;
+       }
+#endif /* CONFIG_INTERWORKING */
+
+       return WLAN_STATUS_SUCCESS;
+}
+
+
 static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
                           const u8 *ies, size_t ies_len, int reassoc)
 {
@@ -866,6 +1275,7 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
        u16 resp;
        const u8 *wpa_ie;
        size_t wpa_ie_len;
+       const u8 *p2p_dev_addr = NULL;
 
        if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
                hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
@@ -880,6 +1290,9 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
        resp = check_wmm(hapd, sta, elems.wmm, elems.wmm_len);
        if (resp != WLAN_STATUS_SUCCESS)
                return resp;
+       resp = check_ext_capab(hapd, sta, elems.ext_capab, elems.ext_capab_len);
+       if (resp != WLAN_STATUS_SUCCESS)
+               return resp;
        resp = copy_supp_rates(hapd, sta, &elems);
        if (resp != WLAN_STATUS_SUCCESS)
                return resp;
@@ -902,15 +1315,40 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
                                  elems.vht_capabilities_len);
        if (resp != WLAN_STATUS_SUCCESS)
                return resp;
+
+       resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif);
+       if (resp != WLAN_STATUS_SUCCESS)
+               return resp;
+
        if (hapd->iconf->ieee80211ac && hapd->iconf->require_vht &&
            !(sta->flags & WLAN_STA_VHT)) {
                hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
                               HOSTAPD_LEVEL_INFO, "Station does not support "
                               "mandatory VHT PHY - reject association");
-               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+               return WLAN_STATUS_ASSOC_DENIED_NO_VHT;
+       }
+
+       if (hapd->conf->vendor_vht && !elems.vht_capabilities) {
+               resp = copy_sta_vendor_vht(hapd, sta, elems.vendor_vht,
+                                          elems.vendor_vht_len);
+               if (resp != WLAN_STATUS_SUCCESS)
+                       return resp;
        }
 #endif /* CONFIG_IEEE80211AC */
 
+#ifdef CONFIG_P2P
+       if (elems.p2p) {
+               wpabuf_free(sta->p2p_ie);
+               sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
+                                                         P2P_IE_VENDOR_TYPE);
+               if (sta->p2p_ie)
+                       p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
+       } else {
+               wpabuf_free(sta->p2p_ie);
+               sta->p2p_ie = NULL;
+       }
+#endif /* CONFIG_P2P */
+
        if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) {
                wpa_ie = elems.rsn_ie;
                wpa_ie_len = elems.rsn_ie_len;
@@ -962,7 +1400,8 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
                wpa_ie_len += 2;
                if (sta->wpa_sm == NULL)
                        sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
-                                                       sta->addr);
+                                                       sta->addr,
+                                                       p2p_dev_addr);
                if (sta->wpa_sm == NULL) {
                        wpa_printf(MSG_WARNING, "Failed to initialize WPA "
                                   "state machine");
@@ -1035,7 +1474,21 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
 
 #ifdef CONFIG_SAE
                if (wpa_auth_uses_sae(sta->wpa_sm) &&
-                   sta->auth_alg != WLAN_AUTH_SAE) {
+                   sta->auth_alg == WLAN_AUTH_OPEN) {
+                       struct rsn_pmksa_cache_entry *sa;
+                       sa = wpa_auth_sta_get_pmksa(sta->wpa_sm);
+                       if (!sa || sa->akmp != WPA_KEY_MGMT_SAE) {
+                               wpa_printf(MSG_DEBUG,
+                                          "SAE: No PMKSA cache entry found for "
+                                          MACSTR, MAC2STR(sta->addr));
+                               return WLAN_STATUS_INVALID_PMKID;
+                       }
+                       wpa_printf(MSG_DEBUG, "SAE: " MACSTR
+                                  " using PMKSA caching", MAC2STR(sta->addr));
+               } else if (wpa_auth_uses_sae(sta->wpa_sm) &&
+                          sta->auth_alg != WLAN_AUTH_SAE &&
+                          !(sta->auth_alg == WLAN_AUTH_FT &&
+                            wpa_auth_uses_ft_sae(sta->wpa_sm))) {
                        wpa_printf(MSG_DEBUG, "SAE: " MACSTR " tried to use "
                                   "SAE AKM after non-SAE auth_alg %u",
                                   MAC2STR(sta->addr), sta->auth_alg);
@@ -1054,20 +1507,33 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
                        return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
                }
 #endif /* CONFIG_IEEE80211N */
+#ifdef CONFIG_HS20
+       } else if (hapd->conf->osen) {
+               if (elems.osen == NULL) {
+                       hostapd_logger(
+                               hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+                               HOSTAPD_LEVEL_INFO,
+                               "No HS 2.0 OSEN element in association request");
+                       return WLAN_STATUS_INVALID_IE;
+               }
+
+               wpa_printf(MSG_DEBUG, "HS 2.0: OSEN association");
+               if (sta->wpa_sm == NULL)
+                       sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth,
+                                                       sta->addr, NULL);
+               if (sta->wpa_sm == NULL) {
+                       wpa_printf(MSG_WARNING, "Failed to initialize WPA "
+                                  "state machine");
+                       return WLAN_STATUS_UNSPECIFIED_FAILURE;
+               }
+               if (wpa_validate_osen(hapd->wpa_auth, sta->wpa_sm,
+                                     elems.osen - 2, elems.osen_len + 2) < 0)
+                       return WLAN_STATUS_INVALID_IE;
+#endif /* CONFIG_HS20 */
        } else
                wpa_auth_sta_no_wpa(sta->wpa_sm);
 
 #ifdef CONFIG_P2P
-       if (elems.p2p) {
-               wpabuf_free(sta->p2p_ie);
-               sta->p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
-                                                         P2P_IE_VENDOR_TYPE);
-
-       } else {
-               wpabuf_free(sta->p2p_ie);
-               sta->p2p_ie = NULL;
-       }
-
        p2p_group_notif_assoc(hapd->p2p_group, sta->addr, ies, ies_len);
 #endif /* CONFIG_P2P */
 
@@ -1130,8 +1596,7 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
        reply->u.assoc_resp.capab_info =
                host_to_le16(hostapd_own_capab_info(hapd, sta, 0));
        reply->u.assoc_resp.status_code = host_to_le16(status_code);
-       reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0)
-                                              | BIT(14) | BIT(15));
+       reply->u.assoc_resp.aid = host_to_le16(sta->aid | BIT(14) | BIT(15));
        /* Supported rates */
        p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable);
        /* Extended supported rates */
@@ -1158,12 +1623,21 @@ static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
 #endif /* CONFIG_IEEE80211N */
 
 #ifdef CONFIG_IEEE80211AC
-       p = hostapd_eid_vht_capabilities(hapd, p);
-       p = hostapd_eid_vht_operation(hapd, p);
+       if (hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac) {
+               p = hostapd_eid_vht_capabilities(hapd, p);
+               p = hostapd_eid_vht_operation(hapd, p);
+       }
 #endif /* CONFIG_IEEE80211AC */
 
        p = hostapd_eid_ext_capab(hapd, p);
        p = hostapd_eid_bss_max_idle_period(hapd, p);
+       if (sta->qos_map_enabled)
+               p = hostapd_eid_qos_map_set(hapd, p);
+
+#ifdef CONFIG_IEEE80211AC
+       if (hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
+               p = hostapd_eid_vendor_vht(hapd, p);
+#endif /* CONFIG_IEEE80211AC */
 
        if (sta->flags & WLAN_STA_WMM)
                p = hostapd_eid_wmm(hapd, p);
@@ -1222,7 +1696,7 @@ static void handle_assoc(struct hostapd_data *hapd,
                         const struct ieee80211_mgmt *mgmt, size_t len,
                         int reassoc)
 {
-       u16 capab_info, listen_interval;
+       u16 capab_info, listen_interval, seq_ctrl, fc;
        u16 resp = WLAN_STATUS_SUCCESS;
        const u8 *pos;
        int left, i;
@@ -1230,14 +1704,14 @@ static void handle_assoc(struct hostapd_data *hapd,
 
        if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) :
                                      sizeof(mgmt->u.assoc_req))) {
-               printf("handle_assoc(reassoc=%d) - too short payload (len=%lu)"
-                      "\n", reassoc, (unsigned long) len);
+               wpa_printf(MSG_INFO, "handle_assoc(reassoc=%d) - too short payload (len=%lu)",
+                          reassoc, (unsigned long) len);
                return;
        }
 
 #ifdef CONFIG_TESTING_OPTIONS
        if (reassoc) {
-               if (hapd->iconf->ignore_reassoc_probability > 0.0d &&
+               if (hapd->iconf->ignore_reassoc_probability > 0.0 &&
                    drand48() < hapd->iconf->ignore_reassoc_probability) {
                        wpa_printf(MSG_INFO,
                                   "TESTING: ignoring reassoc request from "
@@ -1245,7 +1719,7 @@ static void handle_assoc(struct hostapd_data *hapd,
                        return;
                }
        } else {
-               if (hapd->iconf->ignore_assoc_probability > 0.0d &&
+               if (hapd->iconf->ignore_assoc_probability > 0.0 &&
                    drand48() < hapd->iconf->ignore_assoc_probability) {
                        wpa_printf(MSG_INFO,
                                   "TESTING: ignoring assoc request from "
@@ -1255,15 +1729,19 @@ static void handle_assoc(struct hostapd_data *hapd,
        }
 #endif /* CONFIG_TESTING_OPTIONS */
 
+       fc = le_to_host16(mgmt->frame_control);
+       seq_ctrl = le_to_host16(mgmt->seq_ctrl);
+
        if (reassoc) {
                capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info);
                listen_interval = le_to_host16(
                        mgmt->u.reassoc_req.listen_interval);
                wpa_printf(MSG_DEBUG, "reassociation request: STA=" MACSTR
                           " capab_info=0x%02x listen_interval=%d current_ap="
-                          MACSTR,
+                          MACSTR " seq_ctrl=0x%x%s",
                           MAC2STR(mgmt->sa), capab_info, listen_interval,
-                          MAC2STR(mgmt->u.reassoc_req.current_ap));
+                          MAC2STR(mgmt->u.reassoc_req.current_ap),
+                          seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
                left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
                pos = mgmt->u.reassoc_req.variable;
        } else {
@@ -1271,8 +1749,10 @@ static void handle_assoc(struct hostapd_data *hapd,
                listen_interval = le_to_host16(
                        mgmt->u.assoc_req.listen_interval);
                wpa_printf(MSG_DEBUG, "association request: STA=" MACSTR
-                          " capab_info=0x%02x listen_interval=%d",
-                          MAC2STR(mgmt->sa), capab_info, listen_interval);
+                          " capab_info=0x%02x listen_interval=%d "
+                          "seq_ctrl=0x%x%s",
+                          MAC2STR(mgmt->sa), capab_info, listen_interval,
+                          seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
                left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
                pos = mgmt->u.assoc_req.variable;
        }
@@ -1298,6 +1778,21 @@ static void handle_assoc(struct hostapd_data *hapd,
                return;
        }
 
+       if ((fc & WLAN_FC_RETRY) &&
+           sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+           sta->last_seq_ctrl == seq_ctrl &&
+           sta->last_subtype == reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
+           WLAN_FC_STYPE_ASSOC_REQ) {
+               hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+                              HOSTAPD_LEVEL_DEBUG,
+                              "Drop repeated association frame seq_ctrl=0x%x",
+                              seq_ctrl);
+               return;
+       }
+       sta->last_seq_ctrl = seq_ctrl;
+       sta->last_subtype = reassoc ? WLAN_FC_STYPE_REASSOC_REQ :
+               WLAN_FC_STYPE_ASSOC_REQ;
+
        if (hapd->tkip_countermeasures) {
                resp = WLAN_REASON_MICHAEL_MIC_FAILURE;
                goto fail;
@@ -1391,17 +1886,6 @@ static void handle_assoc(struct hostapd_data *hapd,
        }
 #endif /* CONFIG_IEEE80211W */
 
-       if (reassoc) {
-               os_memcpy(sta->previous_ap, mgmt->u.reassoc_req.current_ap,
-                         ETH_ALEN);
-       }
-
-       if (sta->last_assoc_req)
-               os_free(sta->last_assoc_req);
-       sta->last_assoc_req = os_malloc(len);
-       if (sta->last_assoc_req)
-               os_memcpy(sta->last_assoc_req, mgmt, len);
-
        /* Make sure that the previously registered inactivity timer will not
         * remove the STA immediately. */
        sta->timeout_next = STA_NULLFUNC;
@@ -1417,8 +1901,8 @@ static void handle_disassoc(struct hostapd_data *hapd,
        struct sta_info *sta;
 
        if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) {
-               printf("handle_disassoc - too short payload (len=%lu)\n",
-                      (unsigned long) len);
+               wpa_printf(MSG_INFO, "handle_disassoc - too short payload (len=%lu)",
+                          (unsigned long) len);
                return;
        }
 
@@ -1428,12 +1912,13 @@ static void handle_disassoc(struct hostapd_data *hapd,
 
        sta = ap_get_sta(hapd, mgmt->sa);
        if (sta == NULL) {
-               printf("Station " MACSTR " trying to disassociate, but it "
-                      "is not associated.\n", MAC2STR(mgmt->sa));
+               wpa_printf(MSG_INFO, "Station " MACSTR " trying to disassociate, but it is not associated",
+                          MAC2STR(mgmt->sa));
                return;
        }
 
        ap_sta_set_authorized(hapd, sta, 0);
+       sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
        sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
        wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC);
        hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
@@ -1444,6 +1929,9 @@ static void handle_disassoc(struct hostapd_data *hapd,
         * authenticated. */
        accounting_sta_stop(hapd, sta);
        ieee802_1x_free_station(sta);
+       if (sta->ipaddr)
+               hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
+       ap_sta_ip6addr_del(hapd, sta);
        hostapd_drv_sta_remove(hapd, sta->addr);
 
        if (sta->timeout_next == STA_NULLFUNC ||
@@ -1483,6 +1971,7 @@ static void handle_deauth(struct hostapd_data *hapd,
        }
 
        ap_sta_set_authorized(hapd, sta, 0);
+       sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
        sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC |
                        WLAN_STA_ASSOC_REQ_OK);
        wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH);
@@ -1503,8 +1992,8 @@ static void handle_beacon(struct hostapd_data *hapd,
        struct ieee802_11_elems elems;
 
        if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) {
-               printf("handle_beacon - too short payload (len=%lu)\n",
-                      (unsigned long) len);
+               wpa_printf(MSG_INFO, "handle_beacon - too short payload (len=%lu)",
+                          (unsigned long) len);
                return;
        }
 
@@ -1519,9 +2008,9 @@ static void handle_beacon(struct hostapd_data *hapd,
 
 #ifdef CONFIG_IEEE80211W
 
-static void hostapd_sa_query_action(struct hostapd_data *hapd,
-                                   const struct ieee80211_mgmt *mgmt,
-                                   size_t len)
+static int hostapd_sa_query_action(struct hostapd_data *hapd,
+                                  const struct ieee80211_mgmt *mgmt,
+                                  size_t len)
 {
        const u8 *end;
 
@@ -1530,12 +2019,13 @@ static void hostapd_sa_query_action(struct hostapd_data *hapd,
        if (((u8 *) mgmt) + len < end) {
                wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action "
                           "frame (len=%lu)", (unsigned long) len);
-               return;
+               return 0;
        }
 
        ieee802_11_sa_query_action(hapd, mgmt->sa,
                                   mgmt->u.action.u.sa_query_resp.action,
                                   mgmt->u.action.u.sa_query_resp.trans_id);
+       return 1;
 }
 
 
@@ -1547,29 +2037,8 @@ static int robust_action_frame(u8 category)
 #endif /* CONFIG_IEEE80211W */
 
 
-#ifdef CONFIG_WNM
-static void hostapd_wnm_action(struct hostapd_data *hapd, struct sta_info *sta,
-                              const struct ieee80211_mgmt *mgmt,
-                              size_t len)
-{
-       struct rx_action action;
-       if (len < IEEE80211_HDRLEN + 2)
-               return;
-       os_memset(&action, 0, sizeof(action));
-       action.da = mgmt->da;
-       action.sa = mgmt->sa;
-       action.bssid = mgmt->bssid;
-       action.category = mgmt->u.action.category;
-       action.data = (const u8 *) &mgmt->u.action.u.wnm_sleep_req.action;
-       action.len = len - IEEE80211_HDRLEN - 1;
-       action.freq = hapd->iface->freq;
-       ieee802_11_rx_wnm_action_ap(hapd, &action);
-}
-#endif /* CONFIG_WNM */
-
-
-static void handle_action(struct hostapd_data *hapd,
-                         const struct ieee80211_mgmt *mgmt, size_t len)
+static int handle_action(struct hostapd_data *hapd,
+                        const struct ieee80211_mgmt *mgmt, size_t len)
 {
        struct sta_info *sta;
        sta = ap_get_sta(hapd, mgmt->sa);
@@ -1579,7 +2048,7 @@ static void handle_action(struct hostapd_data *hapd,
                               HOSTAPD_LEVEL_DEBUG,
                               "handle_action - too short payload (len=%lu)",
                               (unsigned long) len);
-               return;
+               return 0;
        }
 
        if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
@@ -1587,43 +2056,73 @@ static void handle_action(struct hostapd_data *hapd,
                wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored Action "
                           "frame (category=%u) from unassociated STA " MACSTR,
                           MAC2STR(mgmt->sa), mgmt->u.action.category);
-               return;
+               return 0;
        }
 
 #ifdef CONFIG_IEEE80211W
        if (sta && (sta->flags & WLAN_STA_MFP) &&
-           !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP) &&
-             robust_action_frame(mgmt->u.action.category))) {
+           !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP)) &&
+           robust_action_frame(mgmt->u.action.category)) {
                hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
                               HOSTAPD_LEVEL_DEBUG,
                               "Dropped unprotected Robust Action frame from "
                               "an MFP STA");
-               return;
+               return 0;
        }
 #endif /* CONFIG_IEEE80211W */
 
+       if (sta) {
+               u16 fc = le_to_host16(mgmt->frame_control);
+               u16 seq_ctrl = le_to_host16(mgmt->seq_ctrl);
+
+               if ((fc & WLAN_FC_RETRY) &&
+                   sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
+                   sta->last_seq_ctrl == seq_ctrl &&
+                   sta->last_subtype == WLAN_FC_STYPE_ACTION) {
+                       hostapd_logger(hapd, sta->addr,
+                                      HOSTAPD_MODULE_IEEE80211,
+                                      HOSTAPD_LEVEL_DEBUG,
+                                      "Drop repeated action frame seq_ctrl=0x%x",
+                                      seq_ctrl);
+                       return 1;
+               }
+
+               sta->last_seq_ctrl = seq_ctrl;
+               sta->last_subtype = WLAN_FC_STYPE_ACTION;
+       }
+
        switch (mgmt->u.action.category) {
 #ifdef CONFIG_IEEE80211R
        case WLAN_ACTION_FT:
-               if (wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
+               if (!sta ||
+                   wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action,
                                     len - IEEE80211_HDRLEN))
                        break;
-               return;
+               return 1;
 #endif /* CONFIG_IEEE80211R */
        case WLAN_ACTION_WMM:
                hostapd_wmm_action(hapd, mgmt, len);
-               return;
+               return 1;
 #ifdef CONFIG_IEEE80211W
        case WLAN_ACTION_SA_QUERY:
-               hostapd_sa_query_action(hapd, mgmt, len);
-               return;
+               return hostapd_sa_query_action(hapd, mgmt, len);
 #endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_WNM
        case WLAN_ACTION_WNM:
-               hostapd_wnm_action(hapd, sta, mgmt, len);
-               return;
+               ieee802_11_rx_wnm_action_ap(hapd, mgmt, len);
+               return 1;
 #endif /* CONFIG_WNM */
        case WLAN_ACTION_PUBLIC:
+       case WLAN_ACTION_PROTECTED_DUAL:
+#ifdef CONFIG_IEEE80211N
+               if (mgmt->u.action.u.public_action.action ==
+                   WLAN_PA_20_40_BSS_COEX) {
+                       wpa_printf(MSG_DEBUG,
+                                  "HT20/40 coex mgmt frame received from STA "
+                                  MACSTR, MAC2STR(mgmt->sa));
+                       hostapd_2040_coex_action(hapd, mgmt, len);
+               }
+#endif /* CONFIG_IEEE80211N */
                if (hapd->public_action_cb) {
                        hapd->public_action_cb(hapd->public_action_cb_ctx,
                                               (u8 *) mgmt, len,
@@ -1635,14 +2134,14 @@ static void handle_action(struct hostapd_data *hapd,
                                                hapd->iface->freq);
                }
                if (hapd->public_action_cb || hapd->public_action_cb2)
-                       return;
+                       return 1;
                break;
        case WLAN_ACTION_VENDOR_SPECIFIC:
                if (hapd->vendor_action_cb) {
                        if (hapd->vendor_action_cb(hapd->vendor_action_cb_ctx,
                                                   (u8 *) mgmt, len,
                                                   hapd->iface->freq) == 0)
-                               return;
+                               return 1;
                }
                break;
        }
@@ -1665,7 +2164,7 @@ static void handle_action(struct hostapd_data *hapd,
                           "frame back to sender");
                resp = os_malloc(len);
                if (resp == NULL)
-                       return;
+                       return 0;
                os_memcpy(resp, mgmt, len);
                os_memcpy(resp->da, resp->sa, ETH_ALEN);
                os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
@@ -1678,6 +2177,8 @@ static void handle_action(struct hostapd_data *hapd,
                }
                os_free(resp);
        }
+
+       return 1;
 }
 
 
@@ -1694,15 +2195,16 @@ static void handle_action(struct hostapd_data *hapd,
  * addition, it can be called to re-inserted pending frames (e.g., when using
  * external RADIUS server as an MAC ACL).
  */
-void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
-                    struct hostapd_frame_info *fi)
+int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+                   struct hostapd_frame_info *fi)
 {
        struct ieee80211_mgmt *mgmt;
        int broadcast;
        u16 fc, stype;
+       int ret = 0;
 
        if (len < 24)
-               return;
+               return 0;
 
        mgmt = (struct ieee80211_mgmt *) buf;
        fc = le_to_host16(mgmt->frame_control);
@@ -1710,7 +2212,7 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
 
        if (stype == WLAN_FC_STYPE_BEACON) {
                handle_beacon(hapd, mgmt, len, fi);
-               return;
+               return 1;
        }
 
        broadcast = mgmt->bssid[0] == 0xff && mgmt->bssid[1] == 0xff &&
@@ -1723,16 +2225,19 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
            !((hapd->conf->p2p & P2P_GROUP_OWNER) &&
              stype == WLAN_FC_STYPE_ACTION) &&
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_MESH
+           !(hapd->conf->mesh & MESH_ENABLED) &&
+#endif /* CONFIG_MESH */
            os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) {
-               printf("MGMT: BSSID=" MACSTR " not our address\n",
-                      MAC2STR(mgmt->bssid));
-               return;
+               wpa_printf(MSG_INFO, "MGMT: BSSID=" MACSTR " not our address",
+                          MAC2STR(mgmt->bssid));
+               return 0;
        }
 
 
        if (stype == WLAN_FC_STYPE_PROBE_REQ) {
                handle_probe_req(hapd, mgmt, len, fi->ssi_signal);
-               return;
+               return 1;
        }
 
        if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) {
@@ -1740,33 +2245,38 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
                               HOSTAPD_LEVEL_DEBUG,
                               "MGMT: DA=" MACSTR " not our address",
                               MAC2STR(mgmt->da));
-               return;
+               return 0;
        }
 
        switch (stype) {
        case WLAN_FC_STYPE_AUTH:
                wpa_printf(MSG_DEBUG, "mgmt::auth");
                handle_auth(hapd, mgmt, len);
+               ret = 1;
                break;
        case WLAN_FC_STYPE_ASSOC_REQ:
                wpa_printf(MSG_DEBUG, "mgmt::assoc_req");
                handle_assoc(hapd, mgmt, len, 0);
+               ret = 1;
                break;
        case WLAN_FC_STYPE_REASSOC_REQ:
                wpa_printf(MSG_DEBUG, "mgmt::reassoc_req");
                handle_assoc(hapd, mgmt, len, 1);
+               ret = 1;
                break;
        case WLAN_FC_STYPE_DISASSOC:
                wpa_printf(MSG_DEBUG, "mgmt::disassoc");
                handle_disassoc(hapd, mgmt, len);
+               ret = 1;
                break;
        case WLAN_FC_STYPE_DEAUTH:
                wpa_msg(hapd->msg_ctx, MSG_DEBUG, "mgmt::deauth");
                handle_deauth(hapd, mgmt, len);
+               ret = 1;
                break;
        case WLAN_FC_STYPE_ACTION:
                wpa_printf(MSG_DEBUG, "mgmt::action");
-               handle_action(hapd, mgmt, len);
+               ret = handle_action(hapd, mgmt, len);
                break;
        default:
                hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@@ -1774,6 +2284,8 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
                               "unknown mgmt frame subtype %d", stype);
                break;
        }
+
+       return ret;
 }
 
 
@@ -1792,8 +2304,8 @@ static void handle_auth_cb(struct hostapd_data *hapd,
        }
 
        if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) {
-               printf("handle_auth_cb - too short payload (len=%lu)\n",
-                      (unsigned long) len);
+               wpa_printf(MSG_INFO, "handle_auth_cb - too short payload (len=%lu)",
+                          (unsigned long) len);
                return;
        }
 
@@ -1803,8 +2315,8 @@ static void handle_auth_cb(struct hostapd_data *hapd,
 
        sta = ap_get_sta(hapd, mgmt->da);
        if (!sta) {
-               printf("handle_auth_cb: STA " MACSTR " not found\n",
-                      MAC2STR(mgmt->da));
+               wpa_printf(MSG_INFO, "handle_auth_cb: STA " MACSTR " not found",
+                          MAC2STR(mgmt->da));
                return;
        }
 
@@ -1818,6 +2330,30 @@ static void handle_auth_cb(struct hostapd_data *hapd,
 }
 
 
+static void hostapd_set_wds_encryption(struct hostapd_data *hapd,
+                                      struct sta_info *sta,
+                                      char *ifname_wds)
+{
+       int i;
+       struct hostapd_ssid *ssid = sta->ssid;
+
+       if (hapd->conf->ieee802_1x || hapd->conf->wpa)
+               return;
+
+       for (i = 0; i < 4; i++) {
+               if (ssid->wep.key[i] &&
+                   hostapd_drv_set_key(ifname_wds, hapd, WPA_ALG_WEP, NULL, i,
+                                       i == ssid->wep.idx, NULL, 0,
+                                       ssid->wep.key[i], ssid->wep.len[i])) {
+                       wpa_printf(MSG_WARNING,
+                                  "Could not set WEP keys for WDS interface; %s",
+                                  ifname_wds);
+                       break;
+               }
+       }
+}
+
+
 static void handle_assoc_cb(struct hostapd_data *hapd,
                            const struct ieee80211_mgmt *mgmt,
                            size_t len, int reassoc, int ok)
@@ -1830,15 +2366,15 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
 
        if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) :
                                      sizeof(mgmt->u.assoc_resp))) {
-               printf("handle_assoc_cb(reassoc=%d) - too short payload "
-                      "(len=%lu)\n", reassoc, (unsigned long) len);
+               wpa_printf(MSG_INFO, "handle_assoc_cb(reassoc=%d) - too short payload (len=%lu)",
+                          reassoc, (unsigned long) len);
                return;
        }
 
        sta = ap_get_sta(hapd, mgmt->da);
        if (!sta) {
-               printf("handle_assoc_cb: STA " MACSTR " not found\n",
-                      MAC2STR(mgmt->da));
+               wpa_printf(MSG_INFO, "handle_assoc_cb: STA " MACSTR " not found",
+                          MAC2STR(mgmt->da));
                return;
        }
 
@@ -1856,7 +2392,7 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
                status = le_to_host16(mgmt->u.assoc_resp.status_code);
 
        if (status != WLAN_STATUS_SUCCESS)
-               goto fail;
+               return;
 
        /* Stop previous accounting session, if one is started, and allocate
         * new session id for the new session. */
@@ -1870,7 +2406,8 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
        if (sta->flags & WLAN_STA_ASSOC)
                new_assoc = 0;
        sta->flags |= WLAN_STA_ASSOC;
-       if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa) ||
+       sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
+       if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen) ||
            sta->auth_alg == WLAN_AUTH_FT) {
                /*
                 * Open, static WEP, or FT protocol; no separate authorization
@@ -1909,7 +2446,7 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
                            sta->listen_interval,
                            sta->flags & WLAN_STA_HT ? &ht_cap : NULL,
                            sta->flags & WLAN_STA_VHT ? &vht_cap : NULL,
-                           sta->flags, sta->qosinfo)) {
+                           sta->flags, sta->qosinfo, sta->vht_opmode)) {
                hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
                               HOSTAPD_LEVEL_NOTICE,
                               "Could not add STA to kernel driver");
@@ -1917,11 +2454,18 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
                ap_sta_disconnect(hapd, sta, sta->addr,
                                  WLAN_REASON_DISASSOC_AP_BUSY);
 
-               goto fail;
+               return;
        }
 
-       if (sta->flags & WLAN_STA_WDS)
-               hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1);
+       if (sta->flags & WLAN_STA_WDS) {
+               int ret;
+               char ifname_wds[IFNAMSIZ + 1];
+
+               ret = hostapd_set_wds_sta(hapd, ifname_wds, sta->addr,
+                                         sta->aid, 1);
+               if (!ret)
+                       hostapd_set_wds_encryption(hapd, sta, ifname_wds);
+       }
 
        if (sta->eapol_sm == NULL) {
                /*
@@ -1930,11 +2474,11 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
                 * interface selection is not going to change anymore.
                 */
                if (ap_sta_bind_vlan(hapd, sta, 0) < 0)
-                       goto fail;
+                       return;
        } else if (sta->vlan_id) {
                /* VLAN ID already set (e.g., by PMKSA caching), so bind STA */
                if (ap_sta_bind_vlan(hapd, sta, 0) < 0)
-                       goto fail;
+                       return;
        }
 
        hostapd_set_sta_flags(hapd, sta);
@@ -1946,13 +2490,6 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
        hapd->new_assoc_sta_cb(hapd, sta, !new_assoc);
 
        ieee802_1x_notify_port_enabled(sta->eapol_sm, 1);
-
- fail:
-       /* Copy of the association request is not needed anymore */
-       if (sta->last_assoc_req) {
-               os_free(sta->last_assoc_req);
-               sta->last_assoc_req = NULL;
-       }
 }
 
 
@@ -2019,6 +2556,14 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
        const struct ieee80211_mgmt *mgmt;
        mgmt = (const struct ieee80211_mgmt *) buf;
 
+#ifdef CONFIG_TESTING_OPTIONS
+       if (hapd->ext_mgmt_frame_handling) {
+               wpa_msg(hapd->msg_ctx, MSG_INFO, "MGMT-TX-STATUS stype=%u ok=%d",
+                       stype, ok);
+               return;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
        switch (stype) {
        case WLAN_FC_STYPE_AUTH:
                wpa_printf(MSG_DEBUG, "mgmt::auth cb");
@@ -2047,7 +2592,7 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
                wpa_printf(MSG_DEBUG, "mgmt::action cb");
                break;
        default:
-               printf("unknown mgmt cb frame subtype %d\n", stype);
+               wpa_printf(MSG_INFO, "unknown mgmt cb frame subtype %d", stype);
                break;
        }
 }
@@ -2162,11 +2707,18 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
                        return;
 
                if (wds && !(sta->flags & WLAN_STA_WDS)) {
+                       int ret;
+                       char ifname_wds[IFNAMSIZ + 1];
+
                        wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for "
                                   "STA " MACSTR " (aid %u)",
                                   MAC2STR(sta->addr), sta->aid);
                        sta->flags |= WLAN_STA_WDS;
-                       hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 1);
+                       ret = hostapd_set_wds_sta(hapd, ifname_wds,
+                                                 sta->addr, sta->aid, 1);
+                       if (!ret)
+                               hostapd_set_wds_encryption(hapd, sta,
+                                                          ifname_wds);
                }
                return;
        }
index 2aab56d..41c27d9 100644 (file)
@@ -14,12 +14,14 @@ struct hostapd_data;
 struct sta_info;
 struct hostapd_frame_info;
 struct ieee80211_ht_capabilities;
+struct ieee80211_mgmt;
 
-void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
-                    struct hostapd_frame_info *fi);
+int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
+                   struct hostapd_frame_info *fi);
 void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len,
                        u16 stype, int ok);
-void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len);
+void hostapd_2040_coex_action(struct hostapd_data *hapd,
+                             const struct ieee80211_mgmt *mgmt, size_t len);
 #ifdef NEED_AP_MLME
 int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen);
 int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
@@ -40,13 +42,16 @@ static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd,
 #endif /* NEED_AP_MLME */
 u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta,
                           int probe);
+void ap_ht2040_timeout(void *eloop_data, void *user_data);
 u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid);
 u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid);
 int hostapd_ht_operation_update(struct hostapd_iface *iface);
 void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
                                  const u8 *addr, const u8 *trans_id);
@@ -58,9 +63,16 @@ void hostapd_get_vht_capab(struct hostapd_data *hapd,
                           struct ieee80211_vht_capabilities *neg_vht_cap);
 u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
                      const u8 *ht_capab, size_t ht_capab_len);
+u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
+                       const u8 *ie, size_t len);
+
 void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta);
+void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta);
+void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta);
 u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
                       const u8 *vht_capab, size_t vht_capab_len);
+u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
+                      const u8 *vht_opmode);
 void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
                       const u8 *buf, size_t len, int ack);
 void hostapd_eapol_tx_status(struct hostapd_data *hapd, const u8 *dst,
@@ -81,4 +93,15 @@ int hostapd_update_time_adv(struct hostapd_data *hapd);
 void hostapd_client_poll_ok(struct hostapd_data *hapd, const u8 *addr);
 u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid);
 
+int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta);
+#ifdef CONFIG_SAE
+void sae_clear_retransmit_timer(struct hostapd_data *hapd,
+                               struct sta_info *sta);
+#else /* CONFIG_SAE */
+static inline void sae_clear_retransmit_timer(struct hostapd_data *hapd,
+                                             struct sta_info *sta)
+{
+}
+#endif /* CONFIG_SAE */
+
 #endif /* IEEE802_11_H */
index c311e55..56c3ce0 100644 (file)
@@ -29,7 +29,7 @@
 
 
 struct hostapd_cached_radius_acl {
-       os_time_t timestamp;
+       struct os_reltime timestamp;
        macaddr addr;
        int accepted; /* HOSTAPD_ACL_* */
        struct hostapd_cached_radius_acl *next;
@@ -43,7 +43,7 @@ struct hostapd_cached_radius_acl {
 
 
 struct hostapd_acl_query_data {
-       os_time_t timestamp;
+       struct os_reltime timestamp;
        u8 radius_id;
        macaddr addr;
        u8 *auth_msg; /* IEEE 802.11 authentication frame from station */
@@ -104,15 +104,16 @@ static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr,
                                 char **identity, char **radius_cui)
 {
        struct hostapd_cached_radius_acl *entry;
-       struct os_time now;
+       struct os_reltime now;
 
-       os_get_time(&now);
+       os_get_reltime(&now);
 
        for (entry = hapd->acl_cache; entry; entry = entry->next) {
                if (os_memcmp(entry->addr, addr, ETH_ALEN) != 0)
                        continue;
 
-               if (now.sec - entry->timestamp > RADIUS_ACL_TIMEOUT)
+               if (os_reltime_expired(&now, &entry->timestamp,
+                                      RADIUS_ACL_TIMEOUT))
                        return -1; /* entry has expired */
                if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT)
                        if (session_timeout)
@@ -265,7 +266,6 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
                return HOSTAPD_ACL_REJECT;
 #else /* CONFIG_NO_RADIUS */
                struct hostapd_acl_query_data *query;
-               struct os_time t;
 
                /* Check whether ACL cache has an entry for this station */
                int res = hostapd_acl_cache_get(hapd, addr, session_timeout,
@@ -305,8 +305,7 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
                        wpa_printf(MSG_ERROR, "malloc for query data failed");
                        return HOSTAPD_ACL_REJECT;
                }
-               os_get_time(&t);
-               query->timestamp = t.sec;
+               os_get_reltime(&query->timestamp);
                os_memcpy(query->addr, addr, ETH_ALEN);
                if (hostapd_radius_acl_query(hapd, addr, query)) {
                        wpa_printf(MSG_DEBUG, "Failed to send Access-Request "
@@ -338,7 +337,8 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
 
 
 #ifndef CONFIG_NO_RADIUS
-static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now)
+static void hostapd_acl_expire_cache(struct hostapd_data *hapd,
+                                    struct os_reltime *now)
 {
        struct hostapd_cached_radius_acl *prev, *entry, *tmp;
 
@@ -346,7 +346,8 @@ static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now)
        entry = hapd->acl_cache;
 
        while (entry) {
-               if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) {
+               if (os_reltime_expired(now, &entry->timestamp,
+                                      RADIUS_ACL_TIMEOUT)) {
                        wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR
                                   " has expired.", MAC2STR(entry->addr));
                        if (prev)
@@ -367,7 +368,7 @@ static void hostapd_acl_expire_cache(struct hostapd_data *hapd, os_time_t now)
 
 
 static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
-                                      os_time_t now)
+                                      struct os_reltime *now)
 {
        struct hostapd_acl_query_data *prev, *entry, *tmp;
 
@@ -375,7 +376,8 @@ static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
        entry = hapd->acl_queries;
 
        while (entry) {
-               if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) {
+               if (os_reltime_expired(now, &entry->timestamp,
+                                      RADIUS_ACL_TIMEOUT)) {
                        wpa_printf(MSG_DEBUG, "ACL query for " MACSTR
                                   " has expired.", MAC2STR(entry->addr));
                        if (prev)
@@ -403,11 +405,11 @@ static void hostapd_acl_expire_queries(struct hostapd_data *hapd,
 static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx)
 {
        struct hostapd_data *hapd = eloop_ctx;
-       struct os_time now;
+       struct os_reltime now;
 
-       os_get_time(&now);
-       hostapd_acl_expire_cache(hapd, now.sec);
-       hostapd_acl_expire_queries(hapd, now.sec);
+       os_get_reltime(&now);
+       hostapd_acl_expire_cache(hapd, &now);
+       hostapd_acl_expire_queries(hapd, &now);
 
        eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL);
 }
@@ -480,7 +482,6 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
        struct hostapd_acl_query_data *query, *prev;
        struct hostapd_cached_radius_acl *cache;
        struct radius_hdr *hdr = radius_msg_get_hdr(msg);
-       struct os_time t;
 
        query = hapd->acl_queries;
        prev = NULL;
@@ -515,8 +516,7 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
                wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry");
                goto done;
        }
-       os_get_time(&t);
-       cache->timestamp = t.sec;
+       os_get_reltime(&cache->timestamp);
        os_memcpy(cache->addr, query->addr, sizeof(cache->addr));
        if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
                u8 *buf;
index 6483e1c..4b0653d 100644 (file)
@@ -3,26 +3,22 @@
  * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2007-2008, Intel Corporation
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
-#include "drivers/driver.h"
 #include "hostapd.h"
 #include "ap_config.h"
 #include "sta_info.h"
 #include "beacon.h"
 #include "ieee802_11.h"
+#include "hw_features.h"
+#include "ap_drv_ops.h"
 
 
 u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
@@ -50,6 +46,35 @@ u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid)
 
        pos += sizeof(*cap);
 
+       if (hapd->iconf->obss_interval) {
+               struct ieee80211_obss_scan_parameters *scan_params;
+
+               *pos++ = WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS;
+               *pos++ = sizeof(*scan_params);
+
+               scan_params = (struct ieee80211_obss_scan_parameters *) pos;
+               os_memset(scan_params, 0, sizeof(*scan_params));
+               scan_params->width_trigger_scan_interval =
+                       host_to_le16(hapd->iconf->obss_interval);
+
+               /* Fill in default values for remaining parameters
+                * (IEEE Std 802.11-2012, 8.4.2.61 and MIB defval) */
+               scan_params->scan_passive_dwell =
+                       host_to_le16(20);
+               scan_params->scan_active_dwell =
+                       host_to_le16(10);
+               scan_params->scan_passive_total_per_channel =
+                       host_to_le16(200);
+               scan_params->scan_active_total_per_channel =
+                       host_to_le16(20);
+               scan_params->channel_transition_delay_factor =
+                       host_to_le16(5);
+               scan_params->scan_activity_threshold =
+                       host_to_le16(25);
+
+               pos += sizeof(*scan_params);
+       }
+
        return pos;
 }
 
@@ -68,14 +93,14 @@ u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid)
        oper = (struct ieee80211_ht_operation *) pos;
        os_memset(oper, 0, sizeof(*oper));
 
-       oper->control_chan = hapd->iconf->channel;
+       oper->primary_chan = hapd->iconf->channel;
        oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode);
        if (hapd->iconf->secondary_channel == 1)
                oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE |
-                       HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH;
+                       HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
        if (hapd->iconf->secondary_channel == -1)
                oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW |
-                       HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH;
+                       HT_INFO_HT_PARAM_STA_CHNL_WIDTH;
 
        pos += sizeof(*oper);
 
@@ -105,44 +130,40 @@ int hostapd_ht_operation_update(struct hostapd_iface *iface)
        wpa_printf(MSG_DEBUG, "%s current operation mode=0x%X",
                   __func__, iface->ht_op_mode);
 
-       if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT)
+       if (!(iface->ht_op_mode & HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT)
            && iface->num_sta_ht_no_gf) {
-               iface->ht_op_mode |=
-                       HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT;
+               iface->ht_op_mode |= HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT;
                op_mode_changes++;
        } else if ((iface->ht_op_mode &
-                   HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) &&
+                   HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT) &&
                   iface->num_sta_ht_no_gf == 0) {
-               iface->ht_op_mode &=
-                       ~HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT;
+               iface->ht_op_mode &= ~HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT;
                op_mode_changes++;
        }
 
-       if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) &&
+       if (!(iface->ht_op_mode & HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) &&
            (iface->num_sta_no_ht || iface->olbc_ht)) {
-               iface->ht_op_mode |= HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT;
+               iface->ht_op_mode |= HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT;
                op_mode_changes++;
        } else if ((iface->ht_op_mode &
-                   HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) &&
+                   HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT) &&
                   (iface->num_sta_no_ht == 0 && !iface->olbc_ht)) {
-               iface->ht_op_mode &=
-                       ~HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT;
+               iface->ht_op_mode &= ~HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT;
                op_mode_changes++;
        }
 
-       new_op_mode = 0;
        if (iface->num_sta_no_ht)
-               new_op_mode = OP_MODE_MIXED;
+               new_op_mode = HT_PROT_NON_HT_MIXED;
        else if (iface->conf->secondary_channel && iface->num_sta_ht_20mhz)
-               new_op_mode = OP_MODE_20MHZ_HT_STA_ASSOCED;
+               new_op_mode = HT_PROT_20MHZ_PROTECTION;
        else if (iface->olbc_ht)
-               new_op_mode = OP_MODE_MAY_BE_LEGACY_STAS;
+               new_op_mode = HT_PROT_NONMEMBER_PROTECTION;
        else
-               new_op_mode = OP_MODE_PURE;
+               new_op_mode = HT_PROT_NO_PROTECTION;
 
-       cur_op_mode = iface->ht_op_mode & HT_INFO_OPERATION_MODE_OP_MODE_MASK;
+       cur_op_mode = iface->ht_op_mode & HT_OPER_OP_MODE_HT_PROT_MASK;
        if (cur_op_mode != new_op_mode) {
-               iface->ht_op_mode &= ~HT_INFO_OPERATION_MODE_OP_MODE_MASK;
+               iface->ht_op_mode &= ~HT_OPER_OP_MODE_HT_PROT_MASK;
                iface->ht_op_mode |= new_op_mode;
                op_mode_changes++;
        }
@@ -154,6 +175,140 @@ int hostapd_ht_operation_update(struct hostapd_iface *iface)
 }
 
 
+static int is_40_allowed(struct hostapd_iface *iface, int channel)
+{
+       int pri_freq, sec_freq;
+       int affected_start, affected_end;
+       int pri = 2407 + 5 * channel;
+
+       if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+               return 1;
+
+       pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel);
+
+       if (iface->conf->secondary_channel > 0)
+               sec_freq = pri_freq + 20;
+       else
+               sec_freq = pri_freq - 20;
+
+       affected_start = (pri_freq + sec_freq) / 2 - 25;
+       affected_end = (pri_freq + sec_freq) / 2 + 25;
+       if ((pri < affected_start || pri > affected_end))
+               return 1; /* not within affected channel range */
+
+       wpa_printf(MSG_ERROR, "40 MHz affected channel range: [%d,%d] MHz",
+                  affected_start, affected_end);
+       wpa_printf(MSG_ERROR, "Neighboring BSS: freq=%d", pri);
+       return 0;
+}
+
+
+void hostapd_2040_coex_action(struct hostapd_data *hapd,
+                             const struct ieee80211_mgmt *mgmt, size_t len)
+{
+       struct hostapd_iface *iface = hapd->iface;
+       struct ieee80211_2040_bss_coex_ie *bc_ie;
+       struct ieee80211_2040_intol_chan_report *ic_report;
+       int is_ht_allowed = 1;
+       int i;
+       const u8 *start = (const u8 *) mgmt;
+       const u8 *data = start + IEEE80211_HDRLEN + 2;
+
+       hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
+                      HOSTAPD_LEVEL_DEBUG, "hostapd_public_action - action=%d",
+                      mgmt->u.action.u.public_action.action);
+
+       if (!(iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+               return;
+
+       if (len < IEEE80211_HDRLEN + 2 + sizeof(*bc_ie))
+               return;
+
+       bc_ie = (struct ieee80211_2040_bss_coex_ie *) data;
+       if (bc_ie->element_id != WLAN_EID_20_40_BSS_COEXISTENCE ||
+           bc_ie->length < 1) {
+               wpa_printf(MSG_DEBUG, "Unexpected IE (%u,%u) in coex report",
+                          bc_ie->element_id, bc_ie->length);
+               return;
+       }
+       if (len < IEEE80211_HDRLEN + 2 + 2 + bc_ie->length)
+               return;
+       data += 2 + bc_ie->length;
+
+       wpa_printf(MSG_DEBUG, "20/40 BSS Coexistence Information field: 0x%x",
+                  bc_ie->coex_param);
+       if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_20MHZ_WIDTH_REQ) {
+               hostapd_logger(hapd, mgmt->sa,
+                              HOSTAPD_MODULE_IEEE80211,
+                              HOSTAPD_LEVEL_DEBUG,
+                              "20 MHz BSS width request bit is set in BSS coexistence information field");
+               is_ht_allowed = 0;
+       }
+
+       if (bc_ie->coex_param & WLAN_20_40_BSS_COEX_40MHZ_INTOL) {
+               hostapd_logger(hapd, mgmt->sa,
+                              HOSTAPD_MODULE_IEEE80211,
+                              HOSTAPD_LEVEL_DEBUG,
+                              "40 MHz intolerant bit is set in BSS coexistence information field");
+               is_ht_allowed = 0;
+       }
+
+       if (start + len - data >= 3 &&
+           data[0] == WLAN_EID_20_40_BSS_INTOLERANT && data[1] >= 1) {
+               u8 ielen = data[1];
+
+               if (ielen > start + len - data - 2)
+                       return;
+               ic_report = (struct ieee80211_2040_intol_chan_report *) data;
+               wpa_printf(MSG_DEBUG,
+                          "20/40 BSS Intolerant Channel Report: Operating Class %u",
+                          ic_report->op_class);
+
+               /* Go through the channel report to find any BSS there in the
+                * affected channel range */
+               for (i = 0; i < ielen - 1; i++) {
+                       u8 chan = ic_report->variable[i];
+
+                       if (is_40_allowed(iface, chan))
+                               continue;
+                       hostapd_logger(hapd, mgmt->sa,
+                                      HOSTAPD_MODULE_IEEE80211,
+                                      HOSTAPD_LEVEL_DEBUG,
+                                      "20_40_INTOLERANT channel %d reported",
+                                      chan);
+                       is_ht_allowed = 0;
+               }
+       }
+       wpa_printf(MSG_DEBUG, "is_ht_allowed=%d num_sta_ht40_intolerant=%d",
+                  is_ht_allowed, iface->num_sta_ht40_intolerant);
+
+       if (!is_ht_allowed &&
+           (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
+               if (iface->conf->secondary_channel) {
+                       hostapd_logger(hapd, mgmt->sa,
+                                      HOSTAPD_MODULE_IEEE80211,
+                                      HOSTAPD_LEVEL_INFO,
+                                      "Switching to 20 MHz operation");
+                       iface->conf->secondary_channel = 0;
+                       ieee802_11_set_beacons(iface);
+               }
+               if (!iface->num_sta_ht40_intolerant &&
+                   iface->conf->obss_interval) {
+                       unsigned int delay_time;
+                       delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR *
+                               iface->conf->obss_interval;
+                       eloop_cancel_timeout(ap_ht2040_timeout, hapd->iface,
+                                            NULL);
+                       eloop_register_timeout(delay_time, 0, ap_ht2040_timeout,
+                                              hapd->iface, NULL);
+                       wpa_printf(MSG_DEBUG,
+                                  "Reschedule HT 20/40 timeout to occur in %u seconds",
+                                  delay_time);
+               }
+       }
+}
+
+
 u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
                      const u8 *ht_capab, size_t ht_capab_len)
 {
@@ -182,6 +337,52 @@ u16 copy_sta_ht_capab(struct hostapd_data *hapd, struct sta_info *sta,
 }
 
 
+void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta)
+{
+       if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G)
+               return;
+
+       wpa_printf(MSG_INFO, "HT: Forty MHz Intolerant is set by STA " MACSTR
+                  " in Association Request", MAC2STR(sta->addr));
+
+       if (sta->ht40_intolerant_set)
+               return;
+
+       sta->ht40_intolerant_set = 1;
+       iface->num_sta_ht40_intolerant++;
+       eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
+
+       if (iface->conf->secondary_channel &&
+           (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
+               iface->conf->secondary_channel = 0;
+               ieee802_11_set_beacons(iface);
+       }
+}
+
+
+void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta)
+{
+       if (!sta->ht40_intolerant_set)
+               return;
+
+       sta->ht40_intolerant_set = 0;
+       iface->num_sta_ht40_intolerant--;
+
+       if (iface->num_sta_ht40_intolerant == 0 &&
+           (iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) &&
+           (iface->drv_flags & WPA_DRIVER_FLAGS_HT_2040_COEX)) {
+               unsigned int delay_time = OVERLAPPING_BSS_TRANS_DELAY_FACTOR *
+                       iface->conf->obss_interval;
+               wpa_printf(MSG_DEBUG,
+                          "HT: Start 20->40 MHz transition timer (%d seconds)",
+                          delay_time);
+               eloop_cancel_timeout(ap_ht2040_timeout, iface, NULL);
+               eloop_register_timeout(delay_time, 0, ap_ht2040_timeout,
+                                      iface, NULL);
+       }
+}
+
+
 static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta)
 {
        u16 ht_capab;
@@ -209,6 +410,9 @@ static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta)
                           __func__, MAC2STR(sta->addr),
                           hapd->iface->num_sta_ht_20mhz);
        }
+
+       if (ht_capab & HT_CAP_INFO_40MHZ_INTOLERANT)
+               ht40_intolerant_add(hapd->iface, sta);
 }
 
 
@@ -270,3 +474,14 @@ void hostapd_get_ht_capab(struct hostapd_data *hapd,
 
        neg_ht_cap->ht_capabilities_info = host_to_le16(cap);
 }
+
+
+void ap_ht2040_timeout(void *eloop_data, void *user_data)
+{
+       struct hostapd_iface *iface = eloop_data;
+
+       wpa_printf(MSG_INFO, "Switching to 40 MHz operation");
+
+       iface->conf->secondary_channel = iface->secondary_ch;
+       ieee802_11_set_beacons(iface);
+}
index c36bbe3..d462ac8 100644 (file)
@@ -24,13 +24,13 @@ u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
 {
        u8 *pos = eid;
        u32 timeout, tu;
-       struct os_time now, passed;
+       struct os_reltime now, passed;
 
        *pos++ = WLAN_EID_TIMEOUT_INTERVAL;
        *pos++ = 5;
        *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK;
-       os_get_time(&now);
-       os_time_sub(&now, &sta->sa_query_start, &passed);
+       os_get_reltime(&now);
+       os_reltime_sub(&now, &sta->sa_query_start, &passed);
        tu = (passed.sec * 1000000 + passed.usec) / 1024;
        if (hapd->conf->assoc_sa_query_max_timeout > tu)
                timeout = hapd->conf->assoc_sa_query_max_timeout - tu;
@@ -69,7 +69,7 @@ void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
                  WLAN_SA_QUERY_TR_ID_LEN);
        end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
        if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt, 0) < 0)
-               perror("ieee802_11_send_sa_query_req: send");
+               wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed");
 }
 
 
@@ -107,7 +107,7 @@ static void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd,
                  WLAN_SA_QUERY_TR_ID_LEN);
        end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
        if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp, 0) < 0)
-               perror("ieee80211_mgmt_sa_query_request: send");
+               wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed");
 }
 
 
@@ -170,8 +170,12 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
 
        switch (idx) {
        case 0: /* Bits 0-7 */
+               if (hapd->iconf->obss_interval)
+                       *pos |= 0x01; /* Bit 0 - Coexistence management */
                break;
        case 1: /* Bits 8-15 */
+               if (hapd->conf->proxy_arp)
+                       *pos |= 0x10; /* Bit 12 - Proxy ARP */
                break;
        case 2: /* Bits 16-23 */
                if (hapd->conf->wnm_sleep_mode)
@@ -189,6 +193,8 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
                        *pos |= 0x80; /* Bit 31 - Interworking */
                break;
        case 4: /* Bits 32-39 */
+               if (hapd->conf->qos_map_set_len)
+                       *pos |= 0x01; /* Bit 32 - QoS Map */
                if (hapd->conf->tdls & TDLS_PROHIBIT)
                        *pos |= 0x40; /* Bit 38 - TDLS Prohibited */
                if (hapd->conf->tdls & TDLS_PROHIBIT_CHAN_SWITCH) {
@@ -197,6 +203,10 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
                }
                break;
        case 5: /* Bits 40-47 */
+#ifdef CONFIG_HS20
+               if (hapd->conf->hs20)
+                       *pos |= 0x40; /* Bit 46 - WNM-Notification */
+#endif /* CONFIG_HS20 */
                break;
        case 6: /* Bits 48-55 */
                if (hapd->conf->ssid.utf8_ssid)
@@ -217,12 +227,18 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
                len = 4;
        if (len < 3 && hapd->conf->wnm_sleep_mode)
                len = 3;
+       if (len < 1 && hapd->iconf->obss_interval)
+               len = 1;
        if (len < 7 && hapd->conf->ssid.utf8_ssid)
                len = 7;
 #ifdef CONFIG_WNM
        if (len < 4)
                len = 4;
 #endif /* CONFIG_WNM */
+#ifdef CONFIG_HS20
+       if (hapd->conf->hs20 && len < 6)
+               len = 6;
+#endif /* CONFIG_HS20 */
        if (len < hapd->iface->extended_capa_len)
                len = hapd->iface->extended_capa_len;
        if (len == 0)
@@ -250,6 +266,23 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
 }
 
 
+u8 * hostapd_eid_qos_map_set(struct hostapd_data *hapd, u8 *eid)
+{
+       u8 *pos = eid;
+       u8 len = hapd->conf->qos_map_set_len;
+
+       if (!len)
+               return eid;
+
+       *pos++ = WLAN_EID_QOS_MAP_SET;
+       *pos++ = len;
+       os_memcpy(pos, hapd->conf->qos_map_set, len);
+       pos += len;
+
+       return pos;
+}
+
+
 u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid)
 {
        u8 *pos = eid;
index 0012c0f..171538a 100644 (file)
@@ -12,7 +12,6 @@
 
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
-#include "drivers/driver.h"
 #include "hostapd.h"
 #include "ap_config.h"
 #include "sta_info.h"
 u8 * hostapd_eid_vht_capabilities(struct hostapd_data *hapd, u8 *eid)
 {
        struct ieee80211_vht_capabilities *cap;
+       struct hostapd_hw_modes *mode = hapd->iface->current_mode;
        u8 *pos = eid;
 
-       if (!hapd->iconf->ieee80211ac || !hapd->iface->current_mode ||
-           hapd->conf->disable_11ac)
+       if (!mode)
                return eid;
 
+       if (mode->mode == HOSTAPD_MODE_IEEE80211G && hapd->conf->vendor_vht &&
+           mode->vht_capab == 0 && hapd->iface->hw_features) {
+               int i;
+
+               for (i = 0; i < hapd->iface->num_hw_features; i++) {
+                       if (hapd->iface->hw_features[i].mode ==
+                           HOSTAPD_MODE_IEEE80211A) {
+                               mode = &hapd->iface->hw_features[i];
+                               break;
+                       }
+               }
+       }
+
        *pos++ = WLAN_EID_VHT_CAP;
        *pos++ = sizeof(*cap);
 
        cap = (struct ieee80211_vht_capabilities *) pos;
        os_memset(cap, 0, sizeof(*cap));
        cap->vht_capabilities_info = host_to_le32(
-               hapd->iface->current_mode->vht_capab);
+               hapd->iface->conf->vht_capab);
 
        /* Supported MCS set comes from hw */
-       os_memcpy(&cap->vht_supported_mcs_set,
-                 hapd->iface->current_mode->vht_mcs_set, 8);
+       os_memcpy(&cap->vht_supported_mcs_set, mode->vht_mcs_set, 8);
 
        pos += sizeof(*cap);
 
@@ -52,9 +63,6 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
        struct ieee80211_vht_operation *oper;
        u8 *pos = eid;
 
-       if (!hapd->iconf->ieee80211ac || hapd->conf->disable_11ac)
-               return eid;
-
        *pos++ = WLAN_EID_VHT_OPERATION;
        *pos++ = sizeof(*oper);
 
@@ -82,13 +90,55 @@ u8 * hostapd_eid_vht_operation(struct hostapd_data *hapd, u8 *eid)
 }
 
 
+static int check_valid_vht_mcs(struct hostapd_hw_modes *mode,
+                              const u8 *sta_vht_capab)
+{
+       const struct ieee80211_vht_capabilities *vht_cap;
+       struct ieee80211_vht_capabilities ap_vht_cap;
+       u16 sta_rx_mcs_set, ap_tx_mcs_set;
+       int i;
+
+       if (!mode)
+               return 1;
+
+       /*
+        * Disable VHT caps for STAs for which there is not even a single
+        * allowed MCS in any supported number of streams, i.e., STA is
+        * advertising 3 (not supported) as VHT MCS rates for all supported
+        * stream cases.
+        */
+       os_memcpy(&ap_vht_cap.vht_supported_mcs_set, mode->vht_mcs_set,
+                 sizeof(ap_vht_cap.vht_supported_mcs_set));
+       vht_cap = (const struct ieee80211_vht_capabilities *) sta_vht_capab;
+
+       /* AP Tx MCS map vs. STA Rx MCS map */
+       sta_rx_mcs_set = le_to_host16(vht_cap->vht_supported_mcs_set.rx_map);
+       ap_tx_mcs_set = le_to_host16(ap_vht_cap.vht_supported_mcs_set.tx_map);
+
+       for (i = 0; i < VHT_RX_NSS_MAX_STREAMS; i++) {
+               if ((ap_tx_mcs_set & (0x3 << (i * 2))) == 3)
+                       continue;
+
+               if ((sta_rx_mcs_set & (0x3 << (i * 2))) == 3)
+                       continue;
+
+               return 1;
+       }
+
+       wpa_printf(MSG_DEBUG,
+                  "No matching VHT MCS found between AP TX and STA RX");
+       return 0;
+}
+
+
 u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
                       const u8 *vht_capab, size_t vht_capab_len)
 {
        /* Disable VHT caps for STAs associated to no-VHT BSSes. */
        if (!vht_capab ||
            vht_capab_len < sizeof(struct ieee80211_vht_capabilities) ||
-           hapd->conf->disable_11ac) {
+           hapd->conf->disable_11ac ||
+           !check_valid_vht_mcs(hapd->iface->current_mode, vht_capab)) {
                sta->flags &= ~WLAN_STA_VHT;
                os_free(sta->vht_capabilities);
                sta->vht_capabilities = NULL;
@@ -109,13 +159,139 @@ u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
        return WLAN_STATUS_SUCCESS;
 }
 
+
+u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
+                       const u8 *ie, size_t len)
+{
+       const u8 *vht_capab;
+       unsigned int vht_capab_len;
+
+       if (!ie || len < 5 + 2 + sizeof(struct ieee80211_vht_capabilities) ||
+           hapd->conf->disable_11ac)
+               goto no_capab;
+
+       /* The VHT Capabilities element embedded in vendor VHT */
+       vht_capab = ie + 5;
+       if (vht_capab[0] != WLAN_EID_VHT_CAP)
+               goto no_capab;
+       vht_capab_len = vht_capab[1];
+       if (vht_capab_len < sizeof(struct ieee80211_vht_capabilities) ||
+           (int) vht_capab_len > ie + len - vht_capab - 2)
+               goto no_capab;
+       vht_capab += 2;
+
+       if (sta->vht_capabilities == NULL) {
+               sta->vht_capabilities =
+                       os_zalloc(sizeof(struct ieee80211_vht_capabilities));
+               if (sta->vht_capabilities == NULL)
+                       return WLAN_STATUS_UNSPECIFIED_FAILURE;
+       }
+
+       sta->flags |= WLAN_STA_VHT | WLAN_STA_VENDOR_VHT;
+       os_memcpy(sta->vht_capabilities, vht_capab,
+                 sizeof(struct ieee80211_vht_capabilities));
+       return WLAN_STATUS_SUCCESS;
+
+no_capab:
+       sta->flags &= ~WLAN_STA_VENDOR_VHT;
+       return WLAN_STATUS_SUCCESS;
+}
+
+
+u8 * hostapd_eid_vendor_vht(struct hostapd_data *hapd, u8 *eid)
+{
+       u8 *pos = eid;
+
+       if (!hapd->iface->current_mode)
+               return eid;
+
+       *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+       *pos++ = (5 +           /* The Vendor OUI, type and subtype */
+                 2 + sizeof(struct ieee80211_vht_capabilities) +
+                 2 + sizeof(struct ieee80211_vht_operation));
+
+       WPA_PUT_BE32(pos, (OUI_BROADCOM << 8) | VENDOR_VHT_TYPE);
+       pos += 4;
+       *pos++ = VENDOR_VHT_SUBTYPE;
+       pos = hostapd_eid_vht_capabilities(hapd, pos);
+       pos = hostapd_eid_vht_operation(hapd, pos);
+
+       return pos;
+}
+
+
+u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
+                      const u8 *vht_oper_notif)
+{
+       if (!vht_oper_notif) {
+               sta->flags &= ~WLAN_STA_VHT_OPMODE_ENABLED;
+               return WLAN_STATUS_SUCCESS;
+       }
+
+       sta->flags |= WLAN_STA_VHT_OPMODE_ENABLED;
+       sta->vht_opmode = *vht_oper_notif;
+       return WLAN_STATUS_SUCCESS;
+}
+
+
 void hostapd_get_vht_capab(struct hostapd_data *hapd,
                           struct ieee80211_vht_capabilities *vht_cap,
                           struct ieee80211_vht_capabilities *neg_vht_cap)
 {
+       u32 cap, own_cap, sym_caps;
+
        if (vht_cap == NULL)
                return;
        os_memcpy(neg_vht_cap, vht_cap, sizeof(*neg_vht_cap));
 
-       /* TODO: mask own capabilities, like get_ht_capab() */
+       cap = le_to_host32(neg_vht_cap->vht_capabilities_info);
+       own_cap = hapd->iconf->vht_capab;
+
+       /* mask out symmetric VHT capabilities we don't support */
+       sym_caps = VHT_CAP_SHORT_GI_80 | VHT_CAP_SHORT_GI_160;
+       cap &= ~sym_caps | (own_cap & sym_caps);
+
+       /* mask out beamformer/beamformee caps if not supported */
+       if (!(own_cap & VHT_CAP_SU_BEAMFORMER_CAPABLE))
+               cap &= ~(VHT_CAP_SU_BEAMFORMEE_CAPABLE |
+                        VHT_CAP_BEAMFORMEE_STS_MAX);
+
+       if (!(own_cap & VHT_CAP_SU_BEAMFORMEE_CAPABLE))
+               cap &= ~(VHT_CAP_SU_BEAMFORMER_CAPABLE |
+                        VHT_CAP_SOUNDING_DIMENSION_MAX);
+
+       if (!(own_cap & VHT_CAP_MU_BEAMFORMER_CAPABLE))
+               cap &= ~VHT_CAP_MU_BEAMFORMEE_CAPABLE;
+
+       if (!(own_cap & VHT_CAP_MU_BEAMFORMEE_CAPABLE))
+               cap &= ~VHT_CAP_MU_BEAMFORMER_CAPABLE;
+
+       /* mask channel widths we don't support */
+       switch (own_cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK) {
+       case VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ:
+               break;
+       case VHT_CAP_SUPP_CHAN_WIDTH_160MHZ:
+               if (cap & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ) {
+                       cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+                       cap |= VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
+               }
+               break;
+       default:
+               cap &= ~VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+               break;
+       }
+
+       if (!(cap & VHT_CAP_SUPP_CHAN_WIDTH_MASK))
+               cap &= ~VHT_CAP_SHORT_GI_160;
+
+       /*
+        * if we don't support RX STBC, mask out TX STBC in the STA's HT caps
+        * if we don't support TX STBC, mask out RX STBC in the STA's HT caps
+        */
+       if (!(own_cap & VHT_CAP_RXSTBC_MASK))
+               cap &= ~VHT_CAP_TXSTBC;
+       if (!(own_cap & VHT_CAP_TXSTBC))
+               cap &= ~VHT_CAP_RXSTBC_MASK;
+
+       neg_vht_cap->vht_capabilities_info = host_to_le32(cap);
 }
index 82ea9a6..79dc0f9 100644 (file)
 #include "pmksa_cache_auth.h"
 #include "ap_config.h"
 #include "ap_drv_ops.h"
+#include "wps_hostapd.h"
+#include "hs20.h"
 #include "ieee802_1x.h"
 
 
 static void ieee802_1x_finished(struct hostapd_data *hapd,
-                               struct sta_info *sta, int success);
+                               struct sta_info *sta, int success,
+                               int remediation);
 
 
 static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta,
@@ -63,6 +66,20 @@ static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta,
 
        if (wpa_auth_pairwise_set(sta->wpa_sm))
                encrypt = 1;
+#ifdef CONFIG_TESTING_OPTIONS
+       if (hapd->ext_eapol_frame_io) {
+               size_t hex_len = 2 * len + 1;
+               char *hex = os_malloc(hex_len);
+
+               if (hex) {
+                       wpa_snprintf_hex(hex, hex_len, buf, len);
+                       wpa_msg(hapd->msg_ctx, MSG_INFO,
+                               "EAPOL-TX " MACSTR " %s",
+                               MAC2STR(sta->addr), hex);
+                       os_free(hex);
+               }
+       } else
+#endif /* CONFIG_TESTING_OPTIONS */
        if (sta->flags & WLAN_STA_PREAUTH) {
                rsn_preauth_send(hapd, sta, buf, len);
        } else {
@@ -96,12 +113,13 @@ void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
        }
 
        if (res && errno != ENOENT) {
-               printf("Could not set station " MACSTR " flags for kernel "
-                      "driver (errno=%d).\n", MAC2STR(sta->addr), errno);
+               wpa_printf(MSG_DEBUG, "Could not set station " MACSTR
+                          " flags for kernel driver (errno=%d).",
+                          MAC2STR(sta->addr), errno);
        }
 
        if (authorized) {
-               os_get_time(&sta->connected_time);
+               os_get_reltime(&sta->connected_time);
                accounting_sta_start(hapd, sta);
        }
 }
@@ -186,114 +204,10 @@ static void ieee802_1x_tx_key_one(struct hostapd_data *hapd,
 }
 
 
-#ifndef CONFIG_NO_VLAN
-static struct hostapd_wep_keys *
-ieee802_1x_group_alloc(struct hostapd_data *hapd, const char *ifname)
-{
-       struct hostapd_wep_keys *key;
-
-       key = os_zalloc(sizeof(*key));
-       if (key == NULL)
-               return NULL;
-
-       key->default_len = hapd->conf->default_wep_key_len;
-
-       if (key->idx >= hapd->conf->broadcast_key_idx_max ||
-           key->idx < hapd->conf->broadcast_key_idx_min)
-               key->idx = hapd->conf->broadcast_key_idx_min;
-       else
-               key->idx++;
-
-       if (!key->key[key->idx])
-               key->key[key->idx] = os_malloc(key->default_len);
-       if (key->key[key->idx] == NULL ||
-           random_get_bytes(key->key[key->idx], key->default_len)) {
-               printf("Could not generate random WEP key (dynamic VLAN).\n");
-               os_free(key->key[key->idx]);
-               key->key[key->idx] = NULL;
-               os_free(key);
-               return NULL;
-       }
-       key->len[key->idx] = key->default_len;
-
-       wpa_printf(MSG_DEBUG, "%s: Default WEP idx %d for dynamic VLAN\n",
-                  ifname, key->idx);
-       wpa_hexdump_key(MSG_DEBUG, "Default WEP key (dynamic VLAN)",
-                       key->key[key->idx], key->len[key->idx]);
-
-       if (hostapd_drv_set_key(ifname, hapd, WPA_ALG_WEP,
-                               broadcast_ether_addr, key->idx, 1,
-                               NULL, 0, key->key[key->idx],
-                               key->len[key->idx]))
-               printf("Could not set dynamic VLAN WEP encryption key.\n");
-
-       hostapd_set_drv_ieee8021x(hapd, ifname, 1);
-
-       return key;
-}
-
-
-static struct hostapd_wep_keys *
-ieee802_1x_get_group(struct hostapd_data *hapd, struct hostapd_ssid *ssid,
-                    size_t vlan_id)
-{
-       const char *ifname;
-
-       if (vlan_id == 0)
-               return &ssid->wep;
-
-       if (vlan_id <= ssid->max_dyn_vlan_keys && ssid->dyn_vlan_keys &&
-           ssid->dyn_vlan_keys[vlan_id])
-               return ssid->dyn_vlan_keys[vlan_id];
-
-       wpa_printf(MSG_DEBUG, "IEEE 802.1X: Creating new group "
-                  "state machine for VLAN ID %lu",
-                  (unsigned long) vlan_id);
-
-       ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id);
-       if (ifname == NULL) {
-               wpa_printf(MSG_DEBUG, "IEEE 802.1X: Unknown VLAN ID %lu - "
-                          "cannot create group key state machine",
-                          (unsigned long) vlan_id);
-               return NULL;
-       }
-
-       if (ssid->dyn_vlan_keys == NULL) {
-               int size = (vlan_id + 1) * sizeof(ssid->dyn_vlan_keys[0]);
-               ssid->dyn_vlan_keys = os_zalloc(size);
-               if (ssid->dyn_vlan_keys == NULL)
-                       return NULL;
-               ssid->max_dyn_vlan_keys = vlan_id;
-       }
-
-       if (ssid->max_dyn_vlan_keys < vlan_id) {
-               struct hostapd_wep_keys **na;
-               int size = (vlan_id + 1) * sizeof(ssid->dyn_vlan_keys[0]);
-               na = os_realloc(ssid->dyn_vlan_keys, size);
-               if (na == NULL)
-                       return NULL;
-               ssid->dyn_vlan_keys = na;
-               os_memset(&ssid->dyn_vlan_keys[ssid->max_dyn_vlan_keys + 1], 0,
-                         (vlan_id - ssid->max_dyn_vlan_keys) *
-                         sizeof(ssid->dyn_vlan_keys[0]));
-               ssid->max_dyn_vlan_keys = vlan_id;
-       }
-
-       ssid->dyn_vlan_keys[vlan_id] = ieee802_1x_group_alloc(hapd, ifname);
-
-       return ssid->dyn_vlan_keys[vlan_id];
-}
-#endif /* CONFIG_NO_VLAN */
-
-
 void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
 {
        struct eapol_authenticator *eapol = hapd->eapol_auth;
        struct eapol_state_machine *sm = sta->eapol_sm;
-#ifndef CONFIG_NO_VLAN
-       struct hostapd_wep_keys *key = NULL;
-       int vlan_id;
-#endif /* CONFIG_NO_VLAN */
 
        if (sm == NULL || !sm->eap_if->eapKeyData)
                return;
@@ -302,18 +216,12 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta)
                   MAC2STR(sta->addr));
 
 #ifndef CONFIG_NO_VLAN
-       vlan_id = sta->vlan_id;
-       if (vlan_id < 0 || vlan_id > MAX_VLAN_ID)
-               vlan_id = 0;
-
-       if (vlan_id) {
-               key = ieee802_1x_get_group(hapd, sta->ssid, vlan_id);
-               if (key && key->key[key->idx])
-                       ieee802_1x_tx_key_one(hapd, sta, key->idx, 1,
-                                             key->key[key->idx],
-                                             key->len[key->idx]);
-       } else
+       if (sta->vlan_id > 0 && sta->vlan_id <= MAX_VLAN_ID) {
+               wpa_printf(MSG_ERROR, "Using WEP with vlans is not supported.");
+               return;
+       }
 #endif /* CONFIG_NO_VLAN */
+
        if (eapol->default_wep_key) {
                ieee802_1x_tx_key_one(hapd, sta, eapol->default_wep_key_idx, 1,
                                      eapol->default_wep_key,
@@ -388,9 +296,15 @@ static void ieee802_1x_learn_identity(struct hostapd_data *hapd,
 {
        const u8 *identity;
        size_t identity_len;
+       const struct eap_hdr *hdr = (const struct eap_hdr *) eap;
 
        if (len <= sizeof(struct eap_hdr) ||
-           eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY)
+           (hdr->code == EAP_CODE_RESPONSE &&
+            eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY) ||
+           (hdr->code == EAP_CODE_INITIATE &&
+            eap[sizeof(struct eap_hdr)] != EAP_ERP_TYPE_REAUTH) ||
+           (hdr->code != EAP_CODE_RESPONSE &&
+            hdr->code != EAP_CODE_INITIATE))
                return;
 
        identity = eap_get_identity(sm->eap, &identity_len);
@@ -412,6 +326,67 @@ static void ieee802_1x_learn_identity(struct hostapd_data *hapd,
 }
 
 
+static int add_common_radius_sta_attr_rsn(struct hostapd_data *hapd,
+                                         struct hostapd_radius_attr *req_attr,
+                                         struct sta_info *sta,
+                                         struct radius_msg *msg)
+{
+       u32 suite;
+       int ver, val;
+
+       ver = wpa_auth_sta_wpa_version(sta->wpa_sm);
+       val = wpa_auth_get_pairwise(sta->wpa_sm);
+       suite = wpa_cipher_to_suite(ver, val);
+       if (val != -1 &&
+           !hostapd_config_get_radius_attr(req_attr,
+                                           RADIUS_ATTR_WLAN_PAIRWISE_CIPHER) &&
+           !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_PAIRWISE_CIPHER,
+                                      suite)) {
+               wpa_printf(MSG_ERROR, "Could not add WLAN-Pairwise-Cipher");
+               return -1;
+       }
+
+       suite = wpa_cipher_to_suite((hapd->conf->wpa & 0x2) ?
+                                   WPA_PROTO_RSN : WPA_PROTO_WPA,
+                                   hapd->conf->wpa_group);
+       if (!hostapd_config_get_radius_attr(req_attr,
+                                           RADIUS_ATTR_WLAN_GROUP_CIPHER) &&
+           !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_GROUP_CIPHER,
+                                      suite)) {
+               wpa_printf(MSG_ERROR, "Could not add WLAN-Group-Cipher");
+               return -1;
+       }
+
+       val = wpa_auth_sta_key_mgmt(sta->wpa_sm);
+       suite = wpa_akm_to_suite(val);
+       if (val != -1 &&
+           !hostapd_config_get_radius_attr(req_attr,
+                                           RADIUS_ATTR_WLAN_AKM_SUITE) &&
+           !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_AKM_SUITE,
+                                      suite)) {
+               wpa_printf(MSG_ERROR, "Could not add WLAN-AKM-Suite");
+               return -1;
+       }
+
+#ifdef CONFIG_IEEE80211W
+       if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+               suite = wpa_cipher_to_suite(WPA_PROTO_RSN,
+                                           hapd->conf->group_mgmt_cipher);
+               if (!hostapd_config_get_radius_attr(
+                           req_attr, RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER) &&
+                   !radius_msg_add_attr_int32(
+                           msg, RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, suite)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Could not add WLAN-Group-Mgmt-Cipher");
+                       return -1;
+               }
+       }
+#endif /* CONFIG_IEEE80211W */
+
+       return 0;
+}
+
+
 static int add_common_radius_sta_attr(struct hostapd_data *hapd,
                                      struct hostapd_radius_attr *req_attr,
                                      struct sta_info *sta,
@@ -463,6 +438,25 @@ static int add_common_radius_sta_attr(struct hostapd_data *hapd,
                }
        }
 
+#ifdef CONFIG_IEEE80211R
+       if (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) &&
+           sta->wpa_sm &&
+           (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) ||
+            sta->auth_alg == WLAN_AUTH_FT) &&
+           !hostapd_config_get_radius_attr(req_attr,
+                                           RADIUS_ATTR_MOBILITY_DOMAIN_ID) &&
+           !radius_msg_add_attr_int32(msg, RADIUS_ATTR_MOBILITY_DOMAIN_ID,
+                                      WPA_GET_BE16(
+                                              hapd->conf->mobility_domain))) {
+               wpa_printf(MSG_ERROR, "Could not add Mobility-Domain-Id");
+               return -1;
+       }
+#endif /* CONFIG_IEEE80211R */
+
+       if (hapd->conf->wpa && sta->wpa_sm &&
+           add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0)
+               return -1;
+
        return 0;
 }
 
@@ -526,6 +520,22 @@ int add_common_radius_attr(struct hostapd_data *hapd,
                return -1;
        }
 
+#ifdef CONFIG_INTERWORKING
+       if (hapd->conf->interworking &&
+           !is_zero_ether_addr(hapd->conf->hessid)) {
+               os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT,
+                           MAC2STR(hapd->conf->hessid));
+               buf[sizeof(buf) - 1] = '\0';
+               if (!hostapd_config_get_radius_attr(req_attr,
+                                                   RADIUS_ATTR_WLAN_HESSID) &&
+                   !radius_msg_add_attr(msg, RADIUS_ATTR_WLAN_HESSID,
+                                        (u8 *) buf, os_strlen(buf))) {
+                       wpa_printf(MSG_ERROR, "Could not add WLAN-HESSID");
+                       return -1;
+               }
+       }
+#endif /* CONFIG_INTERWORKING */
+
        if (sta && add_common_radius_sta_attr(hapd, req_attr, sta, msg) < 0)
                return -1;
 
@@ -562,7 +572,7 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
        msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST,
                             sm->radius_identifier);
        if (msg == NULL) {
-               printf("Could not create net RADIUS packet\n");
+               wpa_printf(MSG_INFO, "Could not create new RADIUS packet");
                return;
        }
 
@@ -571,7 +581,7 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
        if (sm->identity &&
            !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME,
                                 sm->identity, sm->identity_len)) {
-               printf("Could not add User-Name\n");
+               wpa_printf(MSG_INFO, "Could not add User-Name");
                goto fail;
        }
 
@@ -585,12 +595,12 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
        if (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr,
                                            RADIUS_ATTR_FRAMED_MTU) &&
            !radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) {
-               printf("Could not add Framed-MTU\n");
+               wpa_printf(MSG_INFO, "Could not add Framed-MTU");
                goto fail;
        }
 
        if (eap && !radius_msg_add_eap(msg, eap, len)) {
-               printf("Could not add EAP-Message\n");
+               wpa_printf(MSG_INFO, "Could not add EAP-Message");
                goto fail;
        }
 
@@ -602,8 +612,7 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
                int res = radius_msg_copy_attr(msg, sm->last_recv_radius,
                                               RADIUS_ATTR_STATE);
                if (res < 0) {
-                       printf("Could not copy State attribute from previous "
-                              "Access-Challenge\n");
+                       wpa_printf(MSG_INFO, "Could not copy State attribute from previous Access-Challenge");
                        goto fail;
                }
                if (res > 0) {
@@ -630,6 +639,41 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
                }
        }
 
+#ifdef CONFIG_HS20
+       if (hapd->conf->hs20) {
+               u8 ver = 1; /* Release 2 */
+               if (!radius_msg_add_wfa(
+                           msg, RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION,
+                           &ver, 1)) {
+                       wpa_printf(MSG_ERROR, "Could not add HS 2.0 AP "
+                                  "version");
+                       goto fail;
+               }
+
+               if (sta->hs20_ie && wpabuf_len(sta->hs20_ie) > 0) {
+                       const u8 *pos;
+                       u8 buf[3];
+                       u16 id;
+                       pos = wpabuf_head_u8(sta->hs20_ie);
+                       buf[0] = (*pos) >> 4;
+                       if (((*pos) & HS20_PPS_MO_ID_PRESENT) &&
+                           wpabuf_len(sta->hs20_ie) >= 3)
+                               id = WPA_GET_LE16(pos + 1);
+                       else
+                               id = 0;
+                       WPA_PUT_BE16(buf + 1, id);
+                       if (!radius_msg_add_wfa(
+                                   msg,
+                                   RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION,
+                                   buf, sizeof(buf))) {
+                               wpa_printf(MSG_ERROR, "Could not add HS 2.0 "
+                                          "STA version");
+                               goto fail;
+                       }
+               }
+       }
+#endif /* CONFIG_HS20 */
+
        if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0)
                goto fail;
 
@@ -653,7 +697,7 @@ static void handle_eap_response(struct hostapd_data *hapd,
        data = (u8 *) (eap + 1);
 
        if (len < sizeof(*eap) + 1) {
-               printf("handle_eap_response: too short response data\n");
+               wpa_printf(MSG_INFO, "handle_eap_response: too short response data");
                return;
        }
 
@@ -673,6 +717,39 @@ static void handle_eap_response(struct hostapd_data *hapd,
 }
 
 
+static void handle_eap_initiate(struct hostapd_data *hapd,
+                               struct sta_info *sta, struct eap_hdr *eap,
+                               size_t len)
+{
+#ifdef CONFIG_ERP
+       u8 type, *data;
+       struct eapol_state_machine *sm = sta->eapol_sm;
+
+       if (sm == NULL)
+               return;
+
+       if (len < sizeof(*eap) + 1) {
+               wpa_printf(MSG_INFO,
+                          "handle_eap_initiate: too short response data");
+               return;
+       }
+
+       data = (u8 *) (eap + 1);
+       type = data[0];
+
+       hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X,
+                      HOSTAPD_LEVEL_DEBUG, "received EAP packet (code=%d "
+                      "id=%d len=%d) from STA: EAP Initiate type %u",
+                      eap->code, eap->identifier, be_to_host16(eap->length),
+                      type);
+
+       wpabuf_free(sm->eap_if->eapRespData);
+       sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len);
+       sm->eapolEap = TRUE;
+#endif /* CONFIG_ERP */
+}
+
+
 /* Process incoming EAP packet from Supplicant */
 static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta,
                       u8 *buf, size_t len)
@@ -681,7 +758,7 @@ static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta,
        u16 eap_len;
 
        if (len < sizeof(*eap)) {
-               printf("   too short EAP packet\n");
+               wpa_printf(MSG_INFO, "   too short EAP packet");
                return;
        }
 
@@ -716,6 +793,13 @@ static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta,
        case EAP_CODE_FAILURE:
                wpa_printf(MSG_DEBUG, " (failure)");
                return;
+       case EAP_CODE_INITIATE:
+               wpa_printf(MSG_DEBUG, " (initiate)");
+               handle_eap_initiate(hapd, sta, eap, eap_len);
+               break;
+       case EAP_CODE_FINISH:
+               wpa_printf(MSG_DEBUG, " (finish)");
+               break;
        default:
                wpa_printf(MSG_DEBUG, " (unknown code)");
                return;
@@ -759,7 +843,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
        struct rsn_pmksa_cache_entry *pmksa;
        int key_mgmt;
 
-       if (!hapd->conf->ieee802_1x && !hapd->conf->wpa &&
+       if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen &&
            !hapd->conf->wps_state)
                return;
 
@@ -774,7 +858,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
        }
 
        if (len < sizeof(*hdr)) {
-               printf("   too short IEEE 802.1X packet\n");
+               wpa_printf(MSG_INFO, "   too short IEEE 802.1X packet");
                return;
        }
 
@@ -784,7 +868,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
                   hdr->version, hdr->type, datalen);
 
        if (len - sizeof(*hdr) < datalen) {
-               printf("   frame too short for this IEEE 802.1X packet\n");
+               wpa_printf(MSG_INFO, "   frame too short for this IEEE 802.1X packet");
                if (sta->eapol_sm)
                        sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++;
                return;
@@ -810,7 +894,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
                return;
        }
 
-       if (!hapd->conf->ieee802_1x &&
+       if (!hapd->conf->ieee802_1x && !hapd->conf->osen &&
            !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) {
                wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - "
                           "802.1X not enabled and WPS not used");
@@ -830,7 +914,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf,
                        return;
 
 #ifdef CONFIG_WPS
-               if (!hapd->conf->ieee802_1x) {
+               if (!hapd->conf->ieee802_1x && hapd->conf->wps_state) {
                        u32 wflags = sta->flags & (WLAN_STA_WPS |
                                                   WLAN_STA_WPS2 |
                                                   WLAN_STA_MAYBE_WPS);
@@ -937,8 +1021,9 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
        int key_mgmt;
 
 #ifdef CONFIG_WPS
-       if (hapd->conf->wps_state && hapd->conf->wpa &&
-           (sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) {
+       if (hapd->conf->wps_state &&
+           ((hapd->conf->wpa && (sta->flags & WLAN_STA_MAYBE_WPS)) ||
+            (sta->flags & WLAN_STA_WPS))) {
                /*
                 * Need to enable IEEE 802.1X/EAPOL state machines for possible
                 * WPS handshake even if IEEE 802.1X/EAPOL is not used for
@@ -948,7 +1033,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
        }
 #endif /* CONFIG_WPS */
 
-       if (!force_1x && !hapd->conf->ieee802_1x) {
+       if (!force_1x && !hapd->conf->ieee802_1x && !hapd->conf->osen) {
                wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - "
                           "802.1X not enabled or forced for WPS");
                /*
@@ -986,7 +1071,8 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
 
 #ifdef CONFIG_WPS
        sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
-       if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS2)) {
+       if (!hapd->conf->ieee802_1x && hapd->conf->wps_state &&
+           !(sta->flags & WLAN_STA_WPS2)) {
                /*
                 * Delay EAPOL frame transmission until a possible WPS STA
                 * initiates the handshake with EAPOL-Start. Only allow the
@@ -1125,15 +1211,11 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd,
                if (eap_type >= 0)
                        sm->eap_type_authsrv = eap_type;
                os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)",
-                           eap_type >= 0 ? eap_server_get_name(0, eap_type) :
-                           "??",
-                           eap_type);
+                           eap_server_get_name(0, eap_type), eap_type);
                break;
        case EAP_CODE_RESPONSE:
                os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)",
-                           eap_type >= 0 ? eap_server_get_name(0, eap_type) :
-                           "??",
-                           eap_type);
+                           eap_server_get_name(0, eap_type), eap_type);
                break;
        case EAP_CODE_SUCCESS:
                os_strlcpy(buf, "EAP Success", sizeof(buf));
@@ -1189,6 +1271,11 @@ static void ieee802_1x_get_keys(struct hostapd_data *hapd,
                        sm->eap_if->aaaEapKeyDataLen = len;
                        sm->eap_if->aaaEapKeyAvailable = TRUE;
                }
+       } else {
+               wpa_printf(MSG_DEBUG,
+                          "MS-MPPE: 1x_get_keys, could not get keys: %p  send: %p  recv: %p",
+                          keys, keys ? keys->send : NULL,
+                          keys ? keys->recv : NULL);
        }
 
        if (keys) {
@@ -1312,6 +1399,147 @@ static void ieee802_1x_update_sta_cui(struct hostapd_data *hapd,
 }
 
 
+#ifdef CONFIG_HS20
+
+static void ieee802_1x_hs20_sub_rem(struct sta_info *sta, u8 *pos, size_t len)
+{
+       sta->remediation = 1;
+       os_free(sta->remediation_url);
+       if (len > 2) {
+               sta->remediation_url = os_malloc(len);
+               if (!sta->remediation_url)
+                       return;
+               sta->remediation_method = pos[0];
+               os_memcpy(sta->remediation_url, pos + 1, len - 1);
+               sta->remediation_url[len - 1] = '\0';
+               wpa_printf(MSG_DEBUG, "HS 2.0: Subscription remediation needed "
+                          "for " MACSTR " - server method %u URL %s",
+                          MAC2STR(sta->addr), sta->remediation_method,
+                          sta->remediation_url);
+       } else {
+               sta->remediation_url = NULL;
+               wpa_printf(MSG_DEBUG, "HS 2.0: Subscription remediation needed "
+                          "for " MACSTR, MAC2STR(sta->addr));
+       }
+       /* TODO: assign the STA into remediation VLAN or add filtering */
+}
+
+
+static void ieee802_1x_hs20_deauth_req(struct hostapd_data *hapd,
+                                      struct sta_info *sta, u8 *pos,
+                                      size_t len)
+{
+       if (len < 3)
+               return; /* Malformed information */
+       sta->hs20_deauth_requested = 1;
+       wpa_printf(MSG_DEBUG, "HS 2.0: Deauthentication request - Code %u  "
+                  "Re-auth Delay %u",
+                  *pos, WPA_GET_LE16(pos + 1));
+       wpabuf_free(sta->hs20_deauth_req);
+       sta->hs20_deauth_req = wpabuf_alloc(len + 1);
+       if (sta->hs20_deauth_req) {
+               wpabuf_put_data(sta->hs20_deauth_req, pos, 3);
+               wpabuf_put_u8(sta->hs20_deauth_req, len - 3);
+               wpabuf_put_data(sta->hs20_deauth_req, pos + 3, len - 3);
+       }
+       ap_sta_session_timeout(hapd, sta, hapd->conf->hs20_deauth_req_timeout);
+}
+
+
+static void ieee802_1x_hs20_session_info(struct hostapd_data *hapd,
+                                        struct sta_info *sta, u8 *pos,
+                                        size_t len, int session_timeout)
+{
+       unsigned int swt;
+       int warning_time, beacon_int;
+
+       if (len < 1)
+               return; /* Malformed information */
+       os_free(sta->hs20_session_info_url);
+       sta->hs20_session_info_url = os_malloc(len);
+       if (sta->hs20_session_info_url == NULL)
+               return;
+       swt = pos[0];
+       os_memcpy(sta->hs20_session_info_url, pos + 1, len - 1);
+       sta->hs20_session_info_url[len - 1] = '\0';
+       wpa_printf(MSG_DEBUG, "HS 2.0: Session Information URL='%s' SWT=%u "
+                  "(session_timeout=%d)",
+                  sta->hs20_session_info_url, swt, session_timeout);
+       if (session_timeout < 0) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: No Session-Timeout set - ignore session info URL");
+               return;
+       }
+       if (swt == 255)
+               swt = 1; /* Use one minute as the AP selected value */
+
+       if ((unsigned int) session_timeout < swt * 60)
+               warning_time = 0;
+       else
+               warning_time = session_timeout - swt * 60;
+
+       beacon_int = hapd->iconf->beacon_int;
+       if (beacon_int < 1)
+               beacon_int = 100; /* best guess */
+       sta->hs20_disassoc_timer = swt * 60 * 1000 / beacon_int * 125 / 128;
+       if (sta->hs20_disassoc_timer > 65535)
+               sta->hs20_disassoc_timer = 65535;
+
+       ap_sta_session_warning_timeout(hapd, sta, warning_time);
+}
+
+#endif /* CONFIG_HS20 */
+
+
+static void ieee802_1x_check_hs20(struct hostapd_data *hapd,
+                                 struct sta_info *sta,
+                                 struct radius_msg *msg,
+                                 int session_timeout)
+{
+#ifdef CONFIG_HS20
+       u8 *buf, *pos, *end, type, sublen;
+       size_t len;
+
+       buf = NULL;
+       sta->remediation = 0;
+       sta->hs20_deauth_requested = 0;
+
+       for (;;) {
+               if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+                                           &buf, &len, buf) < 0)
+                       break;
+               if (len < 6)
+                       continue;
+               pos = buf;
+               end = buf + len;
+               if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA)
+                       continue;
+               pos += 4;
+
+               type = *pos++;
+               sublen = *pos++;
+               if (sublen < 2)
+                       continue; /* invalid length */
+               sublen -= 2; /* skip header */
+               if (pos + sublen > end)
+                       continue; /* invalid WFA VSA */
+
+               switch (type) {
+               case RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION:
+                       ieee802_1x_hs20_sub_rem(sta, pos, sublen);
+                       break;
+               case RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ:
+                       ieee802_1x_hs20_deauth_req(hapd, sta, pos, sublen);
+                       break;
+               case RADIUS_VENDOR_ATTR_WFA_HS20_SESSION_INFO_URL:
+                       ieee802_1x_hs20_session_info(hapd, sta, pos, sublen,
+                                                    session_timeout);
+                       break;
+               }
+       }
+#endif /* CONFIG_HS20 */
+}
+
+
 struct sta_id_search {
        u8 identifier;
        struct eapol_state_machine *sm;
@@ -1386,15 +1614,14 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
                           "EAP-Message");
        } else if (radius_msg_verify(msg, shared_secret, shared_secret_len,
                                     req, 1)) {
-               printf("Incoming RADIUS packet did not have correct "
-                      "Message-Authenticator - dropped\n");
+               wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Message-Authenticator - dropped");
                return RADIUS_RX_INVALID_AUTHENTICATOR;
        }
 
        if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT &&
            hdr->code != RADIUS_CODE_ACCESS_REJECT &&
            hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) {
-               printf("Unknown RADIUS message code\n");
+               wpa_printf(MSG_INFO, "Unknown RADIUS message code");
                return RADIUS_RX_UNKNOWN;
        }
 
@@ -1457,6 +1684,9 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
                if (ap_sta_bind_vlan(hapd, sta, old_vlanid) < 0)
                        break;
 
+               sta->session_timeout_set = !!session_timeout_set;
+               sta->session_timeout = session_timeout;
+
                /* RFC 3580, Ch. 3.17 */
                if (session_timeout_set && termination_action ==
                    RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) {
@@ -1471,7 +1701,11 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
                ieee802_1x_store_radius_class(hapd, sta, msg);
                ieee802_1x_update_sta_identity(hapd, sta, msg);
                ieee802_1x_update_sta_cui(hapd, sta, msg);
-               if (sm->eap_if->eapKeyAvailable &&
+               ieee802_1x_check_hs20(hapd, sta, msg,
+                                     session_timeout_set ?
+                                     (int) session_timeout : -1);
+               if (sm->eap_if->eapKeyAvailable && !sta->remediation &&
+                   !sta->hs20_deauth_requested &&
                    wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt,
                                       session_timeout_set ?
                                       (int) session_timeout : -1, sm) == 0) {
@@ -1558,7 +1792,7 @@ static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd)
        if (eapol->default_wep_key == NULL ||
            random_get_bytes(eapol->default_wep_key,
                             hapd->conf->default_wep_key_len)) {
-               printf("Could not generate random WEP key.\n");
+               wpa_printf(MSG_INFO, "Could not generate random WEP key");
                os_free(eapol->default_wep_key);
                eapol->default_wep_key = NULL;
                return -1;
@@ -1674,14 +1908,14 @@ static void ieee802_1x_aaa_send(void *ctx, void *sta_ctx,
 
 
 static void _ieee802_1x_finished(void *ctx, void *sta_ctx, int success,
-                                int preauth)
+                                int preauth, int remediation)
 {
        struct hostapd_data *hapd = ctx;
        struct sta_info *sta = sta_ctx;
        if (preauth)
                rsn_preauth_finished(hapd, sta, success);
        else
-               ieee802_1x_finished(hapd, sta, success);
+               ieee802_1x_finished(hapd, sta, success, remediation);
 }
 
 
@@ -1714,7 +1948,9 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity,
                user->password_hash = eap_user->password_hash;
        }
        user->force_version = eap_user->force_version;
+       user->macacl = eap_user->macacl;
        user->ttls_auth = eap_user->ttls_auth;
+       user->remediation = eap_user->remediation;
 
        return 0;
 }
@@ -1798,12 +2034,43 @@ static void ieee802_1x_eapol_event(void *ctx, void *sta_ctx,
 }
 
 
+#ifdef CONFIG_ERP
+
+static struct eap_server_erp_key *
+ieee802_1x_erp_get_key(void *ctx, const char *keyname)
+{
+       struct hostapd_data *hapd = ctx;
+       struct eap_server_erp_key *erp;
+
+       dl_list_for_each(erp, &hapd->erp_keys, struct eap_server_erp_key,
+                        list) {
+               if (os_strcmp(erp->keyname_nai, keyname) == 0)
+                       return erp;
+       }
+
+       return NULL;
+}
+
+
+static int ieee802_1x_erp_add_key(void *ctx, struct eap_server_erp_key *erp)
+{
+       struct hostapd_data *hapd = ctx;
+
+       dl_list_add(&hapd->erp_keys, &erp->list);
+       return 0;
+}
+
+#endif /* CONFIG_ERP */
+
+
 int ieee802_1x_init(struct hostapd_data *hapd)
 {
        int i;
        struct eapol_auth_config conf;
        struct eapol_auth_cb cb;
 
+       dl_list_init(&hapd->erp_keys);
+
        os_memset(&conf, 0, sizeof(conf));
        conf.ctx = hapd;
        conf.eap_reauth_period = hapd->conf->eap_reauth_period;
@@ -1815,6 +2082,9 @@ int ieee802_1x_init(struct hostapd_data *hapd)
        conf.eap_sim_db_priv = hapd->eap_sim_db_priv;
        conf.eap_req_id_text = hapd->conf->eap_req_id_text;
        conf.eap_req_id_text_len = hapd->conf->eap_req_id_text_len;
+       conf.erp_send_reauth_start = hapd->conf->erp_send_reauth_start;
+       conf.erp_domain = hapd->conf->erp_domain;
+       conf.erp = hapd->conf->eap_server_erp;
        conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key;
        conf.eap_fast_a_id = hapd->conf->eap_fast_a_id;
        conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len;
@@ -1828,6 +2098,13 @@ int ieee802_1x_init(struct hostapd_data *hapd)
        conf.fragment_size = hapd->conf->fragment_size;
        conf.pwd_group = hapd->conf->pwd_group;
        conf.pbc_in_m1 = hapd->conf->pbc_in_m1;
+       if (hapd->conf->server_id) {
+               conf.server_id = (const u8 *) hapd->conf->server_id;
+               conf.server_id_len = os_strlen(hapd->conf->server_id);
+       } else {
+               conf.server_id = (const u8 *) "hostapd";
+               conf.server_id_len = 7;
+       }
 
        os_memset(&cb, 0, sizeof(cb));
        cb.eapol_send = ieee802_1x_eapol_send;
@@ -1840,6 +2117,10 @@ int ieee802_1x_init(struct hostapd_data *hapd)
        cb.abort_auth = _ieee802_1x_abort_auth;
        cb.tx_key = _ieee802_1x_tx_key;
        cb.eapol_event = ieee802_1x_eapol_event;
+#ifdef CONFIG_ERP
+       cb.erp_get_key = ieee802_1x_erp_get_key;
+       cb.erp_add_key = ieee802_1x_erp_add_key;
+#endif /* CONFIG_ERP */
 
        hapd->eapol_auth = eapol_auth_init(&conf, &cb);
        if (hapd->eapol_auth == NULL)
@@ -1871,6 +2152,18 @@ int ieee802_1x_init(struct hostapd_data *hapd)
 }
 
 
+void ieee802_1x_erp_flush(struct hostapd_data *hapd)
+{
+       struct eap_server_erp_key *erp;
+
+       while ((erp = dl_list_first(&hapd->erp_keys, struct eap_server_erp_key,
+                                   list)) != NULL) {
+               dl_list_del(&erp->list);
+               bin_clear_free(erp, sizeof(*erp));
+       }
+}
+
+
 void ieee802_1x_deinit(struct hostapd_data *hapd)
 {
        eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL);
@@ -1881,6 +2174,8 @@ void ieee802_1x_deinit(struct hostapd_data *hapd)
 
        eapol_auth_deinit(hapd->eapol_auth);
        hapd->eapol_auth = NULL;
+
+       ieee802_1x_erp_flush(hapd);
 }
 
 
@@ -2055,7 +2350,9 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
 {
        int len = 0, ret;
        struct eapol_state_machine *sm = sta->eapol_sm;
-       struct os_time t;
+       struct os_reltime diff;
+       const char *name1;
+       const char *name2;
 
        if (sm == NULL)
                return 0;
@@ -2069,7 +2366,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
                          sta->aid,
                          EAPOL_VERSION,
                          sm->initialize);
-       if (ret < 0 || (size_t) ret >= buflen - len)
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
 
@@ -2097,7 +2394,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
                          sm->reAuthPeriod,
                          bool_txt(sm->reAuthEnabled),
                          bool_txt(sm->keyTxEnabled));
-       if (ret < 0 || (size_t) ret >= buflen - len)
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
 
@@ -2127,7 +2424,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
                          sm->dot1xAuthEapLengthErrorFramesRx,
                          sm->dot1xAuthLastEapolFrameVersion,
                          MAC2STR(sm->addr));
-       if (ret < 0 || (size_t) ret >= buflen - len)
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
 
@@ -2165,12 +2462,12 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
                          sm->backendOtherRequestsToSupplicant,
                          sm->backendAuthSuccesses,
                          sm->backendAuthFails);
-       if (ret < 0 || (size_t) ret >= buflen - len)
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
 
        /* dot1xAuthSessionStatsTable */
-       os_get_time(&t);
+       os_reltime_age(&sta->acct_session_start, &diff);
        ret = os_snprintf(buf + len, buflen - len,
                          /* TODO: dot1xAuthSessionOctetsRx */
                          /* TODO: dot1xAuthSessionOctetsTx */
@@ -2185,9 +2482,30 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
                          (wpa_key_mgmt_wpa_ieee8021x(
                                   wpa_auth_sta_key_mgmt(sta->wpa_sm))) ?
                          1 : 2,
-                         (unsigned int) (t.sec - sta->acct_session_start),
+                         (unsigned int) diff.sec,
                          sm->identity);
-       if (ret < 0 || (size_t) ret >= buflen - len)
+       if (os_snprintf_error(buflen - len, ret))
+               return len;
+       len += ret;
+
+       if (sm->acct_multi_session_id_hi) {
+               ret = os_snprintf(buf + len, buflen - len,
+                                 "authMultiSessionId=%08X+%08X\n",
+                                 sm->acct_multi_session_id_hi,
+                                 sm->acct_multi_session_id_lo);
+               if (os_snprintf_error(buflen - len, ret))
+                       return len;
+               len += ret;
+       }
+
+       name1 = eap_server_get_name(0, sm->eap_type_authsrv);
+       name2 = eap_server_get_name(0, sm->eap_type_supp);
+       ret = os_snprintf(buf + len, buflen - len,
+                         "last_eap_type_as=%d (%s)\n"
+                         "last_eap_type_sta=%d (%s)\n",
+                         sm->eap_type_authsrv, name1,
+                         sm->eap_type_supp, name2);
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
 
@@ -2196,39 +2514,61 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
 
 
 static void ieee802_1x_finished(struct hostapd_data *hapd,
-                               struct sta_info *sta, int success)
+                               struct sta_info *sta, int success,
+                               int remediation)
 {
        const u8 *key;
        size_t len;
        /* TODO: get PMKLifetime from WPA parameters */
        static const int dot11RSNAConfigPMKLifetime = 43200;
+       unsigned int session_timeout;
+
+#ifdef CONFIG_HS20
+       if (remediation && !sta->remediation) {
+               sta->remediation = 1;
+               os_free(sta->remediation_url);
+               sta->remediation_url =
+                       os_strdup(hapd->conf->subscr_remediation_url);
+               sta->remediation_method = 1; /* SOAP-XML SPP */
+       }
+
+       if (success) {
+               if (sta->remediation) {
+                       wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification "
+                                  "to " MACSTR " to indicate Subscription "
+                                  "Remediation",
+                                  MAC2STR(sta->addr));
+                       hs20_send_wnm_notification(hapd, sta->addr,
+                                                  sta->remediation_method,
+                                                  sta->remediation_url);
+                       os_free(sta->remediation_url);
+                       sta->remediation_url = NULL;
+               }
+
+               if (sta->hs20_deauth_req) {
+                       wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification "
+                                  "to " MACSTR " to indicate imminent "
+                                  "deauthentication", MAC2STR(sta->addr));
+                       hs20_send_wnm_notification_deauth_req(
+                               hapd, sta->addr, sta->hs20_deauth_req);
+               }
+       }
+#endif /* CONFIG_HS20 */
 
        key = ieee802_1x_get_key(sta->eapol_sm, &len);
-       if (success && key && len >= PMK_LEN &&
-           wpa_auth_pmksa_add(sta->wpa_sm, key, dot11RSNAConfigPMKLifetime,
+       if (sta->session_timeout_set)
+               session_timeout = sta->session_timeout;
+       else
+               session_timeout = dot11RSNAConfigPMKLifetime;
+       if (success && key && len >= PMK_LEN && !sta->remediation &&
+           !sta->hs20_deauth_requested &&
+           wpa_auth_pmksa_add(sta->wpa_sm, key, session_timeout,
                               sta->eapol_sm) == 0) {
                hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA,
                               HOSTAPD_LEVEL_DEBUG,
                               "Added PMKSA cache entry (IEEE 802.1X)");
        }
-#ifdef CONFIG_WPS
-       if (!success && (sta->flags & WLAN_STA_WPS)) {
-               /*
-                * Many devices require deauthentication after WPS provisioning
-                * and some may not be be able to do that themselves, so
-                * disconnect the client here.
-                */
-               wpa_printf(MSG_DEBUG, "WPS: Force disconnection after "
-                          "EAP-Failure");
-               /* Add a small sleep to increase likelihood of previously
-                * requested EAP-Failure TX getting out before this should the
-                * driver reorder operations.
-                */
-               os_sleep(0, 10000);
-               ap_sta_disconnect(hapd, sta, sta->addr,
-                                 WLAN_REASON_PREV_AUTH_NOT_VALID);
-       }
-#else /* CONFIG_WPS */
+
        if (!success) {
                /*
                 * Many devices require deauthentication after WPS provisioning
@@ -2249,6 +2589,6 @@ static void ieee802_1x_finished(struct hostapd_data *hapd,
                os_sleep(0, 10000);
                ap_sta_disconnect(hapd, sta, sta->addr,
                                  WLAN_REASON_IEEE_802_1X_AUTH_FAILED);
+               hostapd_wps_eap_completed(hapd);
        }
-#endif
 }
index e1df940..de6e0e7 100644 (file)
@@ -29,6 +29,7 @@ void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd,
                                   struct sta_info *sta, int authorized);
 void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta);
 int ieee802_1x_init(struct hostapd_data *hapd);
+void ieee802_1x_erp_flush(struct hostapd_data *hapd);
 void ieee802_1x_deinit(struct hostapd_data *hapd);
 int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta,
                         const u8 *buf, size_t len, int ack);
diff --git a/src/ap/ndisc_snoop.c b/src/ap/ndisc_snoop.c
new file mode 100644 (file)
index 0000000..b0d42dc
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Neighbor Discovery snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+#include <netinet/ip6.h>
+#include <netinet/icmp6.h>
+
+#include "utils/common.h"
+#include "l2_packet/l2_packet.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "list.h"
+#include "x_snoop.h"
+
+struct ip6addr {
+       struct in6_addr addr;
+       struct dl_list list;
+};
+
+struct icmpv6_ndmsg {
+       struct ip6_hdr ipv6h;
+       struct icmp6_hdr icmp6h;
+       struct in6_addr target_addr;
+       u8 opt_type;
+       u8 len;
+       u8 opt_lladdr[0];
+} STRUCT_PACKED;
+
+#define ROUTER_ADVERTISEMENT   134
+#define NEIGHBOR_SOLICITATION  135
+#define NEIGHBOR_ADVERTISEMENT 136
+#define SOURCE_LL_ADDR         1
+
+static int sta_ip6addr_add(struct sta_info *sta, struct in6_addr *addr)
+{
+       struct ip6addr *ip6addr;
+
+       ip6addr = os_zalloc(sizeof(*ip6addr));
+       if (!ip6addr)
+               return -1;
+
+       os_memcpy(&ip6addr->addr, addr, sizeof(*addr));
+
+       dl_list_add_tail(&sta->ip6addr, &ip6addr->list);
+
+       return 0;
+}
+
+
+void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta)
+{
+       struct ip6addr *ip6addr, *prev;
+
+       dl_list_for_each_safe(ip6addr, prev, &sta->ip6addr, struct ip6addr,
+                             list) {
+               hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) &ip6addr->addr);
+               os_free(ip6addr);
+       }
+}
+
+
+static int sta_has_ip6addr(struct sta_info *sta, struct in6_addr *addr)
+{
+       struct ip6addr *ip6addr;
+
+       dl_list_for_each(ip6addr, &sta->ip6addr, struct ip6addr, list) {
+               if (ip6addr->addr.s6_addr32[0] == addr->s6_addr32[0] &&
+                   ip6addr->addr.s6_addr32[1] == addr->s6_addr32[1] &&
+                   ip6addr->addr.s6_addr32[2] == addr->s6_addr32[2] &&
+                   ip6addr->addr.s6_addr32[3] == addr->s6_addr32[3])
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+static void handle_ndisc(void *ctx, const u8 *src_addr, const u8 *buf,
+                        size_t len)
+{
+       struct hostapd_data *hapd = ctx;
+       struct icmpv6_ndmsg *msg;
+       struct in6_addr *saddr;
+       struct sta_info *sta;
+       int res;
+       char addrtxt[INET6_ADDRSTRLEN + 1];
+
+       if (len < ETH_HLEN + sizeof(struct ip6_hdr) + sizeof(struct icmp6_hdr))
+               return;
+       msg = (struct icmpv6_ndmsg *) &buf[ETH_HLEN];
+       switch (msg->icmp6h.icmp6_type) {
+       case NEIGHBOR_SOLICITATION:
+               if (len < ETH_HLEN + sizeof(*msg))
+                       return;
+               if (msg->opt_type != SOURCE_LL_ADDR)
+                       return;
+
+               saddr = &msg->ipv6h.ip6_src;
+               if (!(saddr->s6_addr32[0] == 0 && saddr->s6_addr32[1] == 0 &&
+                     saddr->s6_addr32[2] == 0 && saddr->s6_addr32[3] == 0)) {
+                       if (len < ETH_HLEN + sizeof(*msg) + ETH_ALEN)
+                               return;
+                       sta = ap_get_sta(hapd, msg->opt_lladdr);
+                       if (!sta)
+                               return;
+
+                       if (sta_has_ip6addr(sta, saddr))
+                               return;
+
+                       if (inet_ntop(AF_INET6, saddr, addrtxt, sizeof(addrtxt))
+                           == NULL)
+                               addrtxt[0] = '\0';
+                       wpa_printf(MSG_DEBUG, "ndisc_snoop: Learned new IPv6 address %s for "
+                                  MACSTR, addrtxt, MAC2STR(sta->addr));
+                       hostapd_drv_br_delete_ip_neigh(hapd, 6, (u8 *) saddr);
+                       res = hostapd_drv_br_add_ip_neigh(hapd, 6, (u8 *) saddr,
+                                                         128, sta->addr);
+                       if (res) {
+                               wpa_printf(MSG_ERROR,
+                                          "ndisc_snoop: Adding ip neigh failed: %d",
+                                          res);
+                               return;
+                       }
+
+                       if (sta_ip6addr_add(sta, saddr))
+                               return;
+               }
+               break;
+       case ROUTER_ADVERTISEMENT:
+               if (!hapd->conf->disable_dgaf)
+                       return;
+               /* fall through */
+       case NEIGHBOR_ADVERTISEMENT:
+               for (sta = hapd->sta_list; sta; sta = sta->next) {
+                       if (!(sta->flags & WLAN_STA_AUTHORIZED))
+                               continue;
+                       x_snoop_mcast_to_ucast_convert_send(hapd, sta,
+                                                           (u8 *) buf, len);
+               }
+               break;
+       default:
+               break;
+       }
+}
+
+
+int ndisc_snoop_init(struct hostapd_data *hapd)
+{
+       hapd->sock_ndisc = x_snoop_get_l2_packet(hapd, handle_ndisc,
+                                                L2_PACKET_FILTER_NDISC);
+       if (hapd->sock_ndisc == NULL) {
+               wpa_printf(MSG_DEBUG,
+                          "ndisc_snoop: Failed to initialize L2 packet processing for NDISC packets: %s",
+                          strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}
+
+
+void ndisc_snoop_deinit(struct hostapd_data *hapd)
+{
+       l2_packet_deinit(hapd->sock_ndisc);
+}
diff --git a/src/ap/ndisc_snoop.h b/src/ap/ndisc_snoop.h
new file mode 100644 (file)
index 0000000..3cc9a55
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Neighbor Discovery snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef NDISC_SNOOP_H
+#define NDISC_SNOOP_H
+
+#if defined(CONFIG_PROXYARP) && defined(CONFIG_IPV6)
+
+int ndisc_snoop_init(struct hostapd_data *hapd);
+void ndisc_snoop_deinit(struct hostapd_data *hapd);
+void sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta);
+
+#else /* CONFIG_PROXYARP && CONFIG_IPV6 */
+
+static inline int ndisc_snoop_init(struct hostapd_data *hapd)
+{
+       return 0;
+}
+
+static inline void ndisc_snoop_deinit(struct hostapd_data *hapd)
+{
+}
+
+static inline void sta_ip6addr_del(struct hostapd_data *hapd,
+                                  struct sta_info *sta)
+{
+}
+
+#endif /* CONFIG_PROXYARP && CONFIG_IPV6 */
+
+#endif /* NDISC_SNOOP_H */
index 795d313..9be640c 100644 (file)
@@ -96,9 +96,8 @@ u8 * hostapd_eid_p2p_manage(struct hostapd_data *hapd, u8 *eid)
        u8 bitmap;
        *eid++ = WLAN_EID_VENDOR_SPECIFIC;
        *eid++ = 4 + 3 + 1;
-       WPA_PUT_BE24(eid, OUI_WFA);
-       eid += 3;
-       *eid++ = P2P_OUI_TYPE;
+       WPA_PUT_BE32(eid, P2P_IE_VENDOR_TYPE);
+       eid += 4;
 
        *eid++ = P2P_ATTR_MANAGEABILITY;
        WPA_PUT_LE16(eid, 1);
index ba5c606..efc1d7e 100644 (file)
@@ -79,15 +79,15 @@ static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth,
 
 
 void wpa_smk_m1(struct wpa_authenticator *wpa_auth,
-               struct wpa_state_machine *sm, struct wpa_eapol_key *key)
+               struct wpa_state_machine *sm, struct wpa_eapol_key *key,
+               const u8 *key_data, size_t key_data_len)
 {
        struct wpa_eapol_ie_parse kde;
        struct wpa_stsl_search search;
        u8 *buf, *pos;
        size_t buf_len;
 
-       if (wpa_parse_kde_ies((const u8 *) (key + 1),
-                             WPA_GET_BE16(key->key_data_length), &kde) < 0) {
+       if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
                wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1");
                return;
        }
@@ -221,8 +221,8 @@ static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth,
                return;
 
        /* Peer RSN IE */
-       os_memcpy(buf, kde->rsn_ie, kde->rsn_ie_len);
-       pos = buf + kde->rsn_ie_len;
+       os_memcpy(pos, kde->rsn_ie, kde->rsn_ie_len);
+       pos += kde->rsn_ie_len;
 
        /* Peer MAC Address */
        pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0);
@@ -253,14 +253,14 @@ static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth,
 
 
 void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
-               struct wpa_state_machine *sm, struct wpa_eapol_key *key)
+               struct wpa_state_machine *sm, struct wpa_eapol_key *key,
+               const u8 *key_data, size_t key_data_len)
 {
        struct wpa_eapol_ie_parse kde;
        struct wpa_stsl_search search;
        u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos;
 
-       if (wpa_parse_kde_ies((const u8 *) (key + 1),
-                             WPA_GET_BE16(key->key_data_length), &kde) < 0) {
+       if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
                wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3");
                return;
        }
@@ -324,15 +324,15 @@ void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
 
 
 void wpa_smk_error(struct wpa_authenticator *wpa_auth,
-                  struct wpa_state_machine *sm, struct wpa_eapol_key *key)
+                  struct wpa_state_machine *sm,
+                  const u8 *key_data, size_t key_data_len)
 {
        struct wpa_eapol_ie_parse kde;
        struct wpa_stsl_search search;
        struct rsn_error_kde error;
        u16 mui, error_type;
 
-       if (wpa_parse_kde_ies((const u8 *) (key + 1),
-                             WPA_GET_BE16(key->key_data_length), &kde) < 0) {
+       if (wpa_parse_kde_ies(key_data, key_data_len, &kde) < 0) {
                wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error");
                return;
        }
index 40972e9..877affe 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd - PMKSA cache for IEEE 802.11i RSN
- * Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2008, 2012-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -12,6 +12,7 @@
 #include "utils/eloop.h"
 #include "eapol_auth/eapol_auth_sm.h"
 #include "eapol_auth/eapol_auth_sm_i.h"
+#include "radius/radius_das.h"
 #include "sta_info.h"
 #include "ap_config.h"
 #include "pmksa_cache_auth.h"
@@ -37,14 +38,12 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
 
 static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
 {
-       if (entry == NULL)
-               return;
        os_free(entry->identity);
        wpabuf_free(entry->cui);
 #ifndef CONFIG_NO_RADIUS
        radius_free_class(&entry->radius_class);
 #endif /* CONFIG_NO_RADIUS */
-       os_free(entry);
+       bin_clear_free(entry, sizeof(*entry));
 }
 
 
@@ -52,38 +51,42 @@ void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
                            struct rsn_pmksa_cache_entry *entry)
 {
        struct rsn_pmksa_cache_entry *pos, *prev;
+       unsigned int hash;
 
        pmksa->pmksa_count--;
        pmksa->free_cb(entry, pmksa->ctx);
-       pos = pmksa->pmkid[PMKID_HASH(entry->pmkid)];
+
+       /* unlink from hash list */
+       hash = PMKID_HASH(entry->pmkid);
+       pos = pmksa->pmkid[hash];
        prev = NULL;
        while (pos) {
                if (pos == entry) {
-                       if (prev != NULL) {
-                               prev->hnext = pos->hnext;
-                       } else {
-                               pmksa->pmkid[PMKID_HASH(entry->pmkid)] =
-                                       pos->hnext;
-                       }
+                       if (prev != NULL)
+                               prev->hnext = entry->hnext;
+                       else
+                               pmksa->pmkid[hash] = entry->hnext;
                        break;
                }
                prev = pos;
                pos = pos->hnext;
        }
 
+       /* unlink from entry list */
        pos = pmksa->pmksa;
        prev = NULL;
        while (pos) {
                if (pos == entry) {
                        if (prev != NULL)
-                               prev->next = pos->next;
+                               prev->next = entry->next;
                        else
-                               pmksa->pmksa = pos->next;
+                               pmksa->pmksa = entry->next;
                        break;
                }
                prev = pos;
                pos = pos->next;
        }
+
        _pmksa_cache_free_entry(entry);
 }
 
@@ -91,9 +94,9 @@ void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
 static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
 {
        struct rsn_pmksa_cache *pmksa = eloop_ctx;
-       struct os_time now;
+       struct os_reltime now;
 
-       os_get_time(&now);
+       os_get_reltime(&now);
        while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
                wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
                           MACSTR, MAC2STR(pmksa->pmksa->spa));
@@ -107,12 +110,12 @@ static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
 static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
 {
        int sec;
-       struct os_time now;
+       struct os_reltime now;
 
        eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
        if (pmksa->pmksa == NULL)
                return;
-       os_get_time(&now);
+       os_get_reltime(&now);
        sec = pmksa->pmksa->expiration - now.sec;
        if (sec < 0)
                sec = 0;
@@ -144,6 +147,9 @@ static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
 
        entry->eap_type_authsrv = eapol->eap_type_authsrv;
        entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_id;
+
+       entry->acct_multi_session_id_hi = eapol->acct_multi_session_id_hi;
+       entry->acct_multi_session_id_lo = eapol->acct_multi_session_id_lo;
 }
 
 
@@ -181,6 +187,9 @@ void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
 
        eapol->eap_type_authsrv = entry->eap_type_authsrv;
        ((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id;
+
+       eapol->acct_multi_session_id_hi = entry->acct_multi_session_id_hi;
+       eapol->acct_multi_session_id_lo = entry->acct_multi_session_id_lo;
 }
 
 
@@ -188,6 +197,7 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
                                   struct rsn_pmksa_cache_entry *entry)
 {
        struct rsn_pmksa_cache_entry *pos, *prev;
+       int hash;
 
        /* Add the new entry; order by expiration time */
        pos = pmksa->pmksa;
@@ -205,8 +215,10 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
                entry->next = prev->next;
                prev->next = entry;
        }
-       entry->hnext = pmksa->pmkid[PMKID_HASH(entry->pmkid)];
-       pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry;
+
+       hash = PMKID_HASH(entry->pmkid);
+       entry->hnext = pmksa->pmkid[hash];
+       pmksa->pmkid[hash] = entry;
 
        pmksa->pmksa_count++;
        if (prev == NULL)
@@ -222,6 +234,8 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
  * @pmk: The new pairwise master key
  * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
+ * @kck: Key confirmation key or %NULL if not yet derived
+ * @kck_len: KCK length in bytes
  * @aa: Authenticator address
  * @spa: Supplicant address
  * @session_timeout: Session timeout
@@ -237,23 +251,32 @@ static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
 struct rsn_pmksa_cache_entry *
 pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
                     const u8 *pmk, size_t pmk_len,
-               const u8 *aa, const u8 *spa, int session_timeout,
-               struct eapol_state_machine *eapol, int akmp)
+                    const u8 *kck, size_t kck_len,
+                    const u8 *aa, const u8 *spa, int session_timeout,
+                    struct eapol_state_machine *eapol, int akmp)
 {
        struct rsn_pmksa_cache_entry *entry, *pos;
-       struct os_time now;
+       struct os_reltime now;
 
        if (pmk_len > PMK_LEN)
                return NULL;
 
+       if (wpa_key_mgmt_suite_b(akmp) && !kck)
+               return NULL;
+
        entry = os_zalloc(sizeof(*entry));
        if (entry == NULL)
                return NULL;
        os_memcpy(entry->pmk, pmk, pmk_len);
        entry->pmk_len = pmk_len;
-       rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
-                 wpa_key_mgmt_sha256(akmp));
-       os_get_time(&now);
+       if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+               rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid);
+       else if (wpa_key_mgmt_suite_b(akmp))
+               rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
+       else
+               rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
+                         wpa_key_mgmt_sha256(akmp));
+       os_get_reltime(&now);
        entry->expiration = now.sec;
        if (session_timeout > 0)
                entry->expiration += session_timeout;
@@ -342,6 +365,8 @@ void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa)
                _pmksa_cache_free_entry(prev);
        }
        eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
+       pmksa->pmksa_count = 0;
+       pmksa->pmksa = NULL;
        for (i = 0; i < PMKID_HASH_SIZE; i++)
                pmksa->pmkid[i] = NULL;
        os_free(pmksa);
@@ -361,18 +386,22 @@ pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
 {
        struct rsn_pmksa_cache_entry *entry;
 
-       if (pmkid)
-               entry = pmksa->pmkid[PMKID_HASH(pmkid)];
-       else
-               entry = pmksa->pmksa;
-       while (entry) {
-               if ((spa == NULL ||
-                    os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
-                   (pmkid == NULL ||
-                    os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0))
-                       return entry;
-               entry = pmkid ? entry->hnext : entry->next;
+       if (pmkid) {
+               for (entry = pmksa->pmkid[PMKID_HASH(pmkid)]; entry;
+                    entry = entry->hnext) {
+                       if ((spa == NULL ||
+                            os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
+                           os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)
+                               return entry;
+               }
+       } else {
+               for (entry = pmksa->pmksa; entry; entry = entry->next) {
+                       if (spa == NULL ||
+                           os_memcmp(entry->spa, spa, ETH_ALEN) == 0)
+                               return entry;
+               }
        }
+
        return NULL;
 }
 
@@ -394,15 +423,13 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
        struct rsn_pmksa_cache_entry *entry;
        u8 new_pmkid[PMKID_LEN];
 
-       entry = pmksa->pmksa;
-       while (entry) {
+       for (entry = pmksa->pmksa; entry; entry = entry->next) {
                if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0)
                        continue;
                rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid,
                          wpa_key_mgmt_sha256(entry->akmp));
                if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0)
                        return entry;
-               entry = entry->next;
        }
        return NULL;
 }
@@ -428,3 +455,74 @@ pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
 
        return pmksa;
 }
+
+
+static int das_attr_match(struct rsn_pmksa_cache_entry *entry,
+                         struct radius_das_attrs *attr)
+{
+       int match = 0;
+
+       if (attr->sta_addr) {
+               if (os_memcmp(attr->sta_addr, entry->spa, ETH_ALEN) != 0)
+                       return 0;
+               match++;
+       }
+
+       if (attr->acct_multi_session_id) {
+               char buf[20];
+
+               if (attr->acct_multi_session_id_len != 17)
+                       return 0;
+               os_snprintf(buf, sizeof(buf), "%08X+%08X",
+                           entry->acct_multi_session_id_hi,
+                           entry->acct_multi_session_id_lo);
+               if (os_memcmp(attr->acct_multi_session_id, buf, 17) != 0)
+                       return 0;
+               match++;
+       }
+
+       if (attr->cui) {
+               if (!entry->cui ||
+                   attr->cui_len != wpabuf_len(entry->cui) ||
+                   os_memcmp(attr->cui, wpabuf_head(entry->cui),
+                             attr->cui_len) != 0)
+                       return 0;
+               match++;
+       }
+
+       if (attr->user_name) {
+               if (!entry->identity ||
+                   attr->user_name_len != entry->identity_len ||
+                   os_memcmp(attr->user_name, entry->identity,
+                             attr->user_name_len) != 0)
+                       return 0;
+               match++;
+       }
+
+       return match;
+}
+
+
+int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
+                                          struct radius_das_attrs *attr)
+{
+       int found = 0;
+       struct rsn_pmksa_cache_entry *entry, *prev;
+
+       if (attr->acct_session_id)
+               return -1;
+
+       entry = pmksa->pmksa;
+       while (entry) {
+               if (das_attr_match(entry, attr)) {
+                       found++;
+                       prev = entry;
+                       entry = entry->next;
+                       pmksa_cache_free_entry(pmksa, prev);
+                       continue;
+               }
+               entry = entry->next;
+       }
+
+       return found ? 0 : -1;
+}
index aa90024..8b7be12 100644 (file)
@@ -30,6 +30,9 @@ struct rsn_pmksa_cache_entry {
        u8 eap_type_authsrv;
        int vlan_id;
        int opportunistic;
+
+       u32 acct_multi_session_id_hi;
+       u32 acct_multi_session_id_lo;
 };
 
 struct rsn_pmksa_cache;
@@ -47,6 +50,7 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
 struct rsn_pmksa_cache_entry *
 pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
                     const u8 *pmk, size_t pmk_len,
+                    const u8 *kck, size_t kck_len,
                     const u8 *aa, const u8 *spa, int session_timeout,
                     struct eapol_state_machine *eapol, int akmp);
 struct rsn_pmksa_cache_entry *
@@ -57,5 +61,7 @@ void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry,
                               struct eapol_state_machine *eapol);
 void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
                            struct rsn_pmksa_cache_entry *entry);
+int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
+                                          struct radius_das_attrs *attr);
 
 #endif /* PMKSA_CACHE_H */
index 833f1b2..7e75e1a 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / Station table
- * Copyright (c) 2002-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -15,7 +15,6 @@
 #include "common/sae.h"
 #include "radius/radius.h"
 #include "radius/radius_client.h"
-#include "drivers/driver.h"
 #include "p2p/p2p.h"
 #include "hostapd.h"
 #include "accounting.h"
 #include "p2p_hostapd.h"
 #include "ap_drv_ops.h"
 #include "gas_serv.h"
+#include "wnm_ap.h"
+#include "ndisc_snoop.h"
 #include "sta_info.h"
 
 static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd,
                                       struct sta_info *sta);
 static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx);
+static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx);
 static void ap_sta_deauth_cb_timeout(void *eloop_ctx, void *timeout_ctx);
 static void ap_sta_disassoc_cb_timeout(void *eloop_ctx, void *timeout_ctx);
 #ifdef CONFIG_IEEE80211W
@@ -70,6 +72,30 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta)
 }
 
 
+#ifdef CONFIG_P2P
+struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr)
+{
+       struct sta_info *sta;
+
+       for (sta = hapd->sta_list; sta; sta = sta->next) {
+               const u8 *p2p_dev_addr;
+
+               if (sta->p2p_ie == NULL)
+                       continue;
+
+               p2p_dev_addr = p2p_get_go_dev_addr(sta->p2p_ie);
+               if (p2p_dev_addr == NULL)
+                       continue;
+
+               if (os_memcmp(p2p_dev_addr, addr, ETH_ALEN) == 0)
+                       return sta;
+       }
+
+       return NULL;
+}
+#endif /* CONFIG_P2P */
+
+
 static void ap_sta_list_del(struct hostapd_data *hapd, struct sta_info *sta)
 {
        struct sta_info *tmp;
@@ -119,6 +145,12 @@ static void ap_sta_hash_del(struct hostapd_data *hapd, struct sta_info *sta)
 }
 
 
+void ap_sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta)
+{
+       sta_ip6addr_del(hapd, sta);
+}
+
+
 void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
 {
        int set_beacon = 0;
@@ -129,9 +161,14 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
        ap_sta_set_authorized(hapd, sta, 0);
 
        if (sta->flags & WLAN_STA_WDS)
-               hostapd_set_wds_sta(hapd, sta->addr, sta->aid, 0);
+               hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0);
+
+       if (sta->ipaddr)
+               hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
+       ap_sta_ip6addr_del(hapd, sta);
 
-       if (!(sta->flags & WLAN_STA_PREAUTH))
+       if (!hapd->iface->driver_ap_teardown &&
+           !(sta->flags & WLAN_STA_PREAUTH))
                hostapd_drv_sta_remove(hapd, sta->addr);
 
        ap_sta_hash_del(hapd, sta);
@@ -180,6 +217,10 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
                hapd->iface->num_sta_ht_20mhz--;
        }
 
+#ifdef CONFIG_IEEE80211N
+       ht40_intolerant_remove(hapd->iface, sta);
+#endif /* CONFIG_IEEE80211N */
+
 #ifdef CONFIG_P2P
        if (sta->no_p2p_set) {
                sta->no_p2p_set = 0;
@@ -194,6 +235,11 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
                set_beacon++;
 #endif /* NEED_AP_MLME && CONFIG_IEEE80211N */
 
+#ifdef CONFIG_MESH
+       if (hapd->mesh_sta_free_cb)
+               hapd->mesh_sta_free_cb(sta);
+#endif /* CONFIG_MESH */
+
        if (set_beacon)
                ieee802_11_set_beacons(hapd->iface);
 
@@ -201,17 +247,19 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
                   __func__, MAC2STR(sta->addr));
        eloop_cancel_timeout(ap_handle_timer, hapd, sta);
        eloop_cancel_timeout(ap_handle_session_timer, hapd, sta);
+       eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta);
        eloop_cancel_timeout(ap_sta_deauth_cb_timeout, hapd, sta);
        eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
+       sae_clear_retransmit_timer(hapd, sta);
 
        ieee802_1x_free_station(sta);
        wpa_auth_sta_deinit(sta->wpa_sm);
        rsn_preauth_free_station(hapd, sta);
 #ifndef CONFIG_NO_RADIUS
-       radius_client_flush_auth(hapd->radius, sta->addr);
+       if (hapd->radius)
+               radius_client_flush_auth(hapd->radius, sta->addr);
 #endif /* CONFIG_NO_RADIUS */
 
-       os_free(sta->last_assoc_req);
        os_free(sta->challenge);
 
 #ifdef CONFIG_IEEE80211W
@@ -237,9 +285,13 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
        wpabuf_free(sta->hs20_ie);
 
        os_free(sta->ht_capabilities);
+       os_free(sta->vht_capabilities);
        hostapd_free_psk_list(sta->psk);
        os_free(sta->identity);
        os_free(sta->radius_cui);
+       os_free(sta->remediation_url);
+       wpabuf_free(sta->hs20_deauth_req);
+       os_free(sta->hs20_session_info_url);
 
 #ifdef CONFIG_SAE
        sae_clear_data(sta->sae);
@@ -318,8 +370,15 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
                         * but do not disconnect the station now.
                         */
                        next_time = hapd->conf->ap_max_inactivity + fuzz;
-               } else if (inactive_sec < hapd->conf->ap_max_inactivity &&
-                          sta->flags & WLAN_STA_ASSOC) {
+               } else if (inactive_sec == -ENOENT) {
+                       wpa_msg(hapd->msg_ctx, MSG_DEBUG,
+                               "Station " MACSTR " has lost its driver entry",
+                               MAC2STR(sta->addr));
+
+                       /* Avoid sending client probe on removed client */
+                       sta->timeout_next = STA_DISASSOC;
+                       goto skip_poll;
+               } else if (inactive_sec < hapd->conf->ap_max_inactivity) {
                        /* station activity detected; reset timeout state */
                        wpa_msg(hapd->msg_ctx, MSG_DEBUG,
                                "Station " MACSTR " has been active %is ago",
@@ -351,6 +410,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx)
                next_time = hapd->conf->ap_max_inactivity;
        }
 
+skip_poll:
        if (next_time) {
                wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
                           "for " MACSTR " (%lu seconds)",
@@ -441,7 +501,6 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx)
 {
        struct hostapd_data *hapd = eloop_ctx;
        struct sta_info *sta = timeout_ctx;
-       u8 addr[ETH_ALEN];
 
        if (!(sta->flags & WLAN_STA_AUTH)) {
                if (sta->flags & WLAN_STA_GAS) {
@@ -452,6 +511,8 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx)
                return;
        }
 
+       hostapd_drv_sta_deauth(hapd, sta->addr,
+                              WLAN_REASON_PREV_AUTH_NOT_VALID);
        mlme_deauthenticate_indication(hapd, sta,
                                       WLAN_REASON_PREV_AUTH_NOT_VALID);
        hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
@@ -459,9 +520,19 @@ static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx)
                       "session timeout");
        sta->acct_terminate_cause =
                RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT;
-       os_memcpy(addr, sta->addr, ETH_ALEN);
        ap_free_sta(hapd, sta);
-       hostapd_drv_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID);
+}
+
+
+void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+                             u32 session_timeout)
+{
+       if (eloop_replenish_timeout(session_timeout, 0,
+                                   ap_handle_session_timer, hapd, sta) == 1) {
+               hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+                              HOSTAPD_LEVEL_DEBUG, "setting session timeout "
+                              "to %d seconds", session_timeout);
+       }
 }
 
 
@@ -483,6 +554,32 @@ void ap_sta_no_session_timeout(struct hostapd_data *hapd, struct sta_info *sta)
 }
 
 
+static void ap_handle_session_warning_timer(void *eloop_ctx, void *timeout_ctx)
+{
+#ifdef CONFIG_WNM
+       struct hostapd_data *hapd = eloop_ctx;
+       struct sta_info *sta = timeout_ctx;
+
+       wpa_printf(MSG_DEBUG, "WNM: Session warning time reached for " MACSTR,
+                  MAC2STR(sta->addr));
+       if (sta->hs20_session_info_url == NULL)
+               return;
+
+       wnm_send_ess_disassoc_imminent(hapd, sta, sta->hs20_session_info_url,
+                                      sta->hs20_disassoc_timer);
+#endif /* CONFIG_WNM */
+}
+
+
+void ap_sta_session_warning_timeout(struct hostapd_data *hapd,
+                                   struct sta_info *sta, int warning_time)
+{
+       eloop_cancel_timeout(ap_handle_session_warning_timer, hapd, sta);
+       eloop_register_timeout(warning_time, 0, ap_handle_session_warning_timer,
+                              hapd, sta);
+}
+
+
 struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
 {
        struct sta_info *sta;
@@ -507,13 +604,16 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
        sta->acct_interim_interval = hapd->conf->acct_interim_interval;
        accounting_sta_get_id(hapd, sta);
 
+       if (!(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)) {
+               wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
+                          "for " MACSTR " (%d seconds - ap_max_inactivity)",
+                          __func__, MAC2STR(addr),
+                          hapd->conf->ap_max_inactivity);
+               eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
+                                      ap_handle_timer, hapd, sta);
+       }
+
        /* initialize STA info data */
-       wpa_printf(MSG_DEBUG, "%s: register ap_handle_timer timeout "
-                  "for " MACSTR " (%d seconds - ap_max_inactivity)",
-                  __func__, MAC2STR(addr),
-                  hapd->conf->ap_max_inactivity);
-       eloop_register_timeout(hapd->conf->ap_max_inactivity, 0,
-                              ap_handle_timer, hapd, sta);
        os_memcpy(sta->addr, addr, ETH_ALEN);
        sta->next = hapd->sta_list;
        hapd->sta_list = sta;
@@ -521,6 +621,8 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
        ap_sta_hash_add(hapd, sta);
        sta->ssid = &hapd->conf->ssid;
        ap_sta_remove_in_other_bss(hapd, sta);
+       sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+       dl_list_init(&sta->ip6addr);
 
        return sta;
 }
@@ -530,6 +632,10 @@ static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta)
 {
        ieee802_1x_notify_port_enabled(sta->eapol_sm, 0);
 
+       if (sta->ipaddr)
+               hostapd_drv_br_delete_ip_neigh(hapd, 4, (u8 *) &sta->ipaddr);
+       ap_sta_ip6addr_del(hapd, sta);
+
        wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver",
                   MAC2STR(sta->addr));
        if (hostapd_drv_sta_remove(hapd, sta->addr) &&
@@ -582,6 +688,7 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
 {
        wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR,
                   hapd->conf->iface, MAC2STR(sta->addr));
+       sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
        sta->flags &= ~(WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
        ap_sta_set_authorized(hapd, sta, 0);
        sta->timeout_next = STA_DEAUTH;
@@ -620,7 +727,8 @@ void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta,
 {
        wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR,
                   hapd->conf->iface, MAC2STR(sta->addr));
-       sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC);
+       sta->last_seq_ctrl = WLAN_INVALID_MGMT_SEQ;
+       sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_ASSOC_REQ_OK);
        ap_sta_set_authorized(hapd, sta, 0);
        sta->timeout_next = STA_REMOVE;
        wpa_printf(MSG_DEBUG, "%s: reschedule ap_handle_timer timeout "
@@ -675,13 +783,6 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
        if (sta->vlan_id == old_vlanid)
                return 0;
 
-       /*
-        * During 1x reauth, if the vlan id changes, then remove the old id and
-        * proceed furthur to add the new one.
-        */
-       if (old_vlanid > 0)
-               vlan_remove_dynamic(hapd, old_vlanid);
-
        iface = hapd->conf->iface;
        if (sta->ssid->vlan[0])
                iface = sta->ssid->vlan;
@@ -689,15 +790,19 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
        if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED)
                sta->vlan_id = 0;
        else if (sta->vlan_id > 0) {
+               struct hostapd_vlan *wildcard_vlan = NULL;
                vlan = hapd->conf->vlan;
                while (vlan) {
-                       if (vlan->vlan_id == sta->vlan_id ||
-                           vlan->vlan_id == VLAN_ID_WILDCARD) {
-                               iface = vlan->ifname;
+                       if (vlan->vlan_id == sta->vlan_id)
                                break;
-                       }
+                       if (vlan->vlan_id == VLAN_ID_WILDCARD)
+                               wildcard_vlan = vlan;
                        vlan = vlan->next;
                }
+               if (!vlan)
+                       vlan = wildcard_vlan;
+               if (vlan)
+                       iface = vlan->ifname;
        }
 
        if (sta->vlan_id > 0 && vlan == NULL) {
@@ -705,7 +810,8 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
                               HOSTAPD_LEVEL_DEBUG, "could not find VLAN for "
                               "binding station to (vlan_id=%d)",
                               sta->vlan_id);
-               return -1;
+               ret = -1;
+               goto done;
        } else if (sta->vlan_id > 0 && vlan->vlan_id == VLAN_ID_WILDCARD) {
                vlan = vlan_add_dynamic(hapd, vlan, sta->vlan_id);
                if (vlan == NULL) {
@@ -714,7 +820,8 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
                                       HOSTAPD_LEVEL_DEBUG, "could not add "
                                       "dynamic VLAN interface for vlan_id=%d",
                                       sta->vlan_id);
-                       return -1;
+                       ret = -1;
+                       goto done;
                }
 
                iface = vlan->ifname;
@@ -768,6 +875,12 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
                               HOSTAPD_LEVEL_DEBUG, "could not bind the STA "
                               "entry to vlan_id=%d", sta->vlan_id);
        }
+
+done:
+       /* During 1x reauth, if the vlan id changes, then remove the old id. */
+       if (old_vlanid > 0)
+               vlan_remove_dynamic(hapd, old_vlanid);
+
        return ret;
 #else /* CONFIG_NO_VLAN */
        return 0;
@@ -780,9 +893,9 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
 int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta)
 {
        u32 tu;
-       struct os_time now, passed;
-       os_get_time(&now);
-       os_time_sub(&now, &sta->sa_query_start, &passed);
+       struct os_reltime now, passed;
+       os_get_reltime(&now);
+       os_reltime_sub(&now, &sta->sa_query_start, &passed);
        tu = (passed.sec * 1000000 + passed.usec) / 1024;
        if (hapd->conf->assoc_sa_query_max_timeout < tu) {
                hostapd_logger(hapd, sta->addr,
@@ -819,13 +932,21 @@ static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx)
                return;
        if (sta->sa_query_count == 0) {
                /* Starting a new SA Query procedure */
-               os_get_time(&sta->sa_query_start);
+               os_get_reltime(&sta->sa_query_start);
        }
        trans_id = nbuf + sta->sa_query_count * WLAN_SA_QUERY_TR_ID_LEN;
        sta->sa_query_trans_id = nbuf;
        sta->sa_query_count++;
 
-       os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+       if (os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0) {
+               /*
+                * We don't really care which ID is used here, so simply
+                * hardcode this if the mostly theoretical os_get_random()
+                * failure happens.
+                */
+               trans_id[0] = 0x12;
+               trans_id[1] = 0x34;
+       }
 
        timeout = hapd->conf->assoc_sa_query_retry_timeout;
        sec = ((timeout / 1000) * 1024) / 1000;
@@ -864,11 +985,17 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
        char buf[100];
 #ifdef CONFIG_P2P
        u8 addr[ETH_ALEN];
+       u8 ip_addr_buf[4];
 #endif /* CONFIG_P2P */
 
        if (!!authorized == !!(sta->flags & WLAN_STA_AUTHORIZED))
                return;
 
+       if (authorized)
+               sta->flags |= WLAN_STA_AUTHORIZED;
+       else
+               sta->flags &= ~WLAN_STA_AUTHORIZED;
+
 #ifdef CONFIG_P2P
        if (hapd->p2p_group == NULL) {
                if (sta->p2p_ie != NULL &&
@@ -876,23 +1003,38 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
                        dev_addr = addr;
        } else
                dev_addr = p2p_group_get_dev_addr(hapd->p2p_group, sta->addr);
-#endif /* CONFIG_P2P */
 
        if (dev_addr)
                os_snprintf(buf, sizeof(buf), MACSTR " p2p_dev_addr=" MACSTR,
                            MAC2STR(sta->addr), MAC2STR(dev_addr));
        else
+#endif /* CONFIG_P2P */
                os_snprintf(buf, sizeof(buf), MACSTR, MAC2STR(sta->addr));
 
+       if (hapd->sta_authorized_cb)
+               hapd->sta_authorized_cb(hapd->sta_authorized_cb_ctx,
+                                       sta->addr, authorized, dev_addr);
+
        if (authorized) {
-               wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s", buf);
+               char ip_addr[100];
+               ip_addr[0] = '\0';
+#ifdef CONFIG_P2P
+               if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) {
+                       os_snprintf(ip_addr, sizeof(ip_addr),
+                                   " ip_addr=%u.%u.%u.%u",
+                                   ip_addr_buf[0], ip_addr_buf[1],
+                                   ip_addr_buf[2], ip_addr_buf[3]);
+               }
+#endif /* CONFIG_P2P */
+
+               wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s",
+                       buf, ip_addr);
 
                if (hapd->msg_ctx_parent &&
                    hapd->msg_ctx_parent != hapd->msg_ctx)
                        wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
-                                         AP_STA_CONNECTED "%s", buf);
-
-               sta->flags |= WLAN_STA_AUTHORIZED;
+                                         AP_STA_CONNECTED "%s%s",
+                                         buf, ip_addr);
        } else {
                wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
 
@@ -900,13 +1042,7 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
                    hapd->msg_ctx_parent != hapd->msg_ctx)
                        wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
                                          AP_STA_DISCONNECTED "%s", buf);
-
-               sta->flags &= ~WLAN_STA_AUTHORIZED;
        }
-
-       if (hapd->sta_authorized_cb)
-               hapd->sta_authorized_cb(hapd->sta_authorized_cb_ctx,
-                                       sta->addr, authorized, dev_addr);
 }
 
 
@@ -967,3 +1103,36 @@ void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta)
        eloop_cancel_timeout(ap_sta_disassoc_cb_timeout, hapd, sta);
        ap_sta_disassoc_cb_timeout(hapd, sta);
 }
+
+
+int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen)
+{
+       int res;
+
+       buf[0] = '\0';
+       res = os_snprintf(buf, buflen, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
+                         (flags & WLAN_STA_AUTH ? "[AUTH]" : ""),
+                         (flags & WLAN_STA_ASSOC ? "[ASSOC]" : ""),
+                         (flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""),
+                         (flags & WLAN_STA_PENDING_POLL ? "[PENDING_POLL" :
+                          ""),
+                         (flags & WLAN_STA_SHORT_PREAMBLE ?
+                          "[SHORT_PREAMBLE]" : ""),
+                         (flags & WLAN_STA_PREAUTH ? "[PREAUTH]" : ""),
+                         (flags & WLAN_STA_WMM ? "[WMM]" : ""),
+                         (flags & WLAN_STA_MFP ? "[MFP]" : ""),
+                         (flags & WLAN_STA_WPS ? "[WPS]" : ""),
+                         (flags & WLAN_STA_MAYBE_WPS ? "[MAYBE_WPS]" : ""),
+                         (flags & WLAN_STA_WDS ? "[WDS]" : ""),
+                         (flags & WLAN_STA_NONERP ? "[NonERP]" : ""),
+                         (flags & WLAN_STA_WPS2 ? "[WPS2]" : ""),
+                         (flags & WLAN_STA_GAS ? "[GAS]" : ""),
+                         (flags & WLAN_STA_VHT ? "[VHT]" : ""),
+                         (flags & WLAN_STA_VENDOR_VHT ? "[VENDOR_VHT]" : ""),
+                         (flags & WLAN_STA_WNM_SLEEP_MODE ?
+                          "[WNM_SLEEP_MODE]" : ""));
+       if (os_snprintf_error(buflen, res))
+               res = -1;
+
+       return res;
+}
index f8f5a83..57551ab 100644 (file)
@@ -9,12 +9,16 @@
 #ifndef STA_INFO_H
 #define STA_INFO_H
 
+#ifdef CONFIG_MESH
+/* needed for mesh_plink_state enum */
+#include "common/defs.h"
+#endif /* CONFIG_MESH */
+
+#include "list.h"
+
 /* STA flags */
 #define WLAN_STA_AUTH BIT(0)
 #define WLAN_STA_ASSOC BIT(1)
-#define WLAN_STA_PS BIT(2)
-#define WLAN_STA_TIM BIT(3)
-#define WLAN_STA_PERM BIT(4)
 #define WLAN_STA_AUTHORIZED BIT(5)
 #define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */
 #define WLAN_STA_SHORT_PREAMBLE BIT(7)
@@ -29,6 +33,9 @@
 #define WLAN_STA_WPS2 BIT(16)
 #define WLAN_STA_GAS BIT(17)
 #define WLAN_STA_VHT BIT(18)
+#define WLAN_STA_WNM_SLEEP_MODE BIT(19)
+#define WLAN_STA_VHT_OPMODE_ENABLED BIT(20)
+#define WLAN_STA_VENDOR_VHT BIT(21)
 #define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
 #define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
 #define WLAN_STA_NONERP BIT(31)
@@ -42,6 +49,8 @@ struct sta_info {
        struct sta_info *next; /* next entry in sta list */
        struct sta_info *hnext; /* next entry in hash table list */
        u8 addr[6];
+       be32 ipaddr;
+       struct dl_list ip6addr; /* list head for struct ip6addr */
        u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */
        u32 flags; /* Bitfield of WLAN_STA_* */
        u16 capability;
@@ -50,16 +59,35 @@ struct sta_info {
        int supported_rates_len;
        u8 qosinfo; /* Valid when WLAN_STA_WMM is set */
 
+#ifdef CONFIG_MESH
+       enum mesh_plink_state plink_state;
+       u16 peer_lid;
+       u16 my_lid;
+       u16 mpm_close_reason;
+       int mpm_retries;
+       u8 my_nonce[32];
+       u8 peer_nonce[32];
+       u8 aek[32];     /* SHA256 digest length */
+       u8 mtk[16];
+       u8 mgtk[16];
+       u8 sae_auth_retry;
+#endif /* CONFIG_MESH */
+
        unsigned int nonerp_set:1;
        unsigned int no_short_slot_time_set:1;
        unsigned int no_short_preamble_set:1;
        unsigned int no_ht_gf_set:1;
        unsigned int no_ht_set:1;
+       unsigned int ht40_intolerant_set:1;
        unsigned int ht_20mhz_set:1;
        unsigned int no_p2p_set:1;
+       unsigned int qos_map_enabled:1;
+       unsigned int remediation:1;
+       unsigned int hs20_deauth_requested:1;
+       unsigned int session_timeout_set:1;
+       unsigned int radius_das_match:1;
 
        u16 auth_alg;
-       u8 previous_ap[6];
 
        enum {
                STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE,
@@ -72,12 +100,9 @@ struct sta_info {
        /* IEEE 802.1X related data */
        struct eapol_state_machine *eapol_sm;
 
-       /* IEEE 802.11f (IAPP) related data */
-       struct ieee80211_mgmt *last_assoc_req;
-
        u32 acct_session_id_hi;
        u32 acct_session_id_lo;
-       time_t acct_session_start;
+       struct os_reltime acct_session_start;
        int acct_session_started;
        int acct_terminate_cause; /* Acct-Terminate-Cause */
        int acct_interim_interval; /* Acct-Interim-Interval */
@@ -104,6 +129,7 @@ struct sta_info {
 
        struct ieee80211_ht_capabilities *ht_capabilities;
        struct ieee80211_vht_capabilities *vht_capabilities;
+       u8 vht_opmode;
 
 #ifdef CONFIG_IEEE80211W
        int sa_query_count; /* number of pending SA Query requests;
@@ -112,7 +138,7 @@ struct sta_info {
        u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN *
                                * sa_query_count octets of pending SA Query
                                * transaction identifiers */
-       struct os_time sa_query_start;
+       struct os_reltime sa_query_start;
 #endif /* CONFIG_IEEE80211W */
 
 #ifdef CONFIG_INTERWORKING
@@ -124,12 +150,25 @@ struct sta_info {
        struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
        struct wpabuf *p2p_ie; /* P2P IE from (Re)Association Request */
        struct wpabuf *hs20_ie; /* HS 2.0 IE from (Re)Association Request */
+       u8 remediation_method;
+       char *remediation_url; /* HS 2.0 Subscription Remediation Server URL */
+       struct wpabuf *hs20_deauth_req;
+       char *hs20_session_info_url;
+       int hs20_disassoc_timer;
 
-       struct os_time connected_time;
+       struct os_reltime connected_time;
 
 #ifdef CONFIG_SAE
        struct sae_data *sae;
 #endif /* CONFIG_SAE */
+
+       u32 session_timeout; /* valid only if session_timeout_set == 1 */
+
+       /* Last Authentication/(Re)Association Request/Action frame sequence
+        * control */
+       u16 last_seq_ctrl;
+       /* Last Authentication/(Re)Association Request/Action frame subtype */
+       u8 last_subtype;
 };
 
 
@@ -156,14 +195,20 @@ int ap_for_each_sta(struct hostapd_data *hapd,
                              void *ctx),
                    void *ctx);
 struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta);
+struct sta_info * ap_get_sta_p2p(struct hostapd_data *hapd, const u8 *addr);
 void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta);
 void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta);
+void ap_sta_ip6addr_del(struct hostapd_data *hapd, struct sta_info *sta);
 void hostapd_free_stas(struct hostapd_data *hapd);
 void ap_handle_timer(void *eloop_ctx, void *timeout_ctx);
+void ap_sta_replenish_timeout(struct hostapd_data *hapd, struct sta_info *sta,
+                             u32 session_timeout);
 void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta,
                            u32 session_timeout);
 void ap_sta_no_session_timeout(struct hostapd_data *hapd,
                               struct sta_info *sta);
+void ap_sta_session_warning_timeout(struct hostapd_data *hapd,
+                                   struct sta_info *sta, int warning_time);
 struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr);
 void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
                         u16 reason);
@@ -191,4 +236,6 @@ static inline int ap_sta_is_authorized(struct sta_info *sta)
 void ap_sta_deauth_cb(struct hostapd_data *hapd, struct sta_info *sta);
 void ap_sta_disassoc_cb(struct hostapd_data *hapd, struct sta_info *sta);
 
+int ap_sta_flags_txt(u32 flags, char *buf, size_t buflen);
+
 #endif /* STA_INFO_H */
index 4a2ea06..4725e2b 100644 (file)
@@ -68,7 +68,7 @@ void ieee80211_tkip_countermeasures_deinit(struct hostapd_data *hapd)
 
 int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
 {
-       struct os_time now;
+       struct os_reltime now;
        int ret = 0;
 
        if (addr && local) {
@@ -89,8 +89,8 @@ int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
                }
        }
 
-       os_get_time(&now);
-       if (now.sec > hapd->michael_mic_failure + 60) {
+       os_get_reltime(&now);
+       if (os_reltime_expired(&now, &hapd->michael_mic_failure, 60)) {
                hapd->michael_mic_failures = 1;
        } else {
                hapd->michael_mic_failures++;
@@ -99,7 +99,7 @@ int michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local)
                        ret = 1;
                }
        }
-       hapd->michael_mic_failure = now.sec;
+       hapd->michael_mic_failure = now;
 
        return ret;
 }
index 70affda..dc65019 100644 (file)
@@ -4,14 +4,8 @@
  * Copyright 2005-2006, Devicescape Software, Inc.
  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "utils/includes.h"
@@ -480,123 +474,6 @@ static int vlan_set_name_type(unsigned int name_type)
 #endif /* CONFIG_VLAN_NETLINK */
 
 
-/**
- * Increase the usage counter for given parent/ifname combination.
- * If create is set, then this iface is added to the global list.
- * Returns
- *     -1 on error
- *     0 if iface is not in list
- *     1 if iface is in list (was there or has been added)
- */
-static int hapd_get_dynamic_iface(const char *parent, const char *ifname,
-                                 int create, struct hostapd_data *hapd)
-{
-       size_t i;
-       struct hostapd_dynamic_iface *j = NULL, **tmp;
-       struct hapd_interfaces *hapd_global = hapd->iface->interfaces;
-
-       if (!parent)
-               parent = "";
-
-       for (i = 0; i < hapd_global->count_dynamic; i++) {
-               j = hapd_global->dynamic_iface[i];
-               if (os_strncmp(j->iface, ifname, sizeof(j->iface)) == 0 &&
-                   os_strncmp(j->parent, parent, sizeof(j->parent)) == 0)
-                       break;
-       }
-       if (i < hapd_global->count_dynamic) {
-               j->usage++;
-               return 1;
-       }
-
-       /* new entry required */
-       if (!create)
-               return 0;
-
-       j = os_zalloc(sizeof(*j));
-       if (!j)
-               return -1;
-       os_strlcpy(j->iface, ifname, sizeof(j->iface));
-       os_strlcpy(j->parent, parent, sizeof(j->parent));
-
-       tmp = os_realloc_array(hapd_global->dynamic_iface, i + 1,
-                              sizeof(*hapd_global->dynamic_iface));
-       if (!tmp) {
-               wpa_printf(MSG_ERROR, "VLAN: Failed to allocate memory in %s",
-                          __func__);
-               return -1;
-       }
-       hapd_global->count_dynamic++;
-       hapd_global->dynamic_iface = tmp;
-       hapd_global->dynamic_iface[i] = j;
-
-       return 1;
-}
-
-
-/**
- * Decrease the usage counter for given ifname.
- * Returns
- *     -1 on error or if iface was not found
- *     0 if iface was found and is still present
- *     1 if iface was removed from global list
- */
-static int hapd_put_dynamic_iface(const char *parent, const char *ifname,
-                                 struct hostapd_data *hapd)
-{
-       size_t i;
-       struct hostapd_dynamic_iface *j = NULL, **tmp;
-       struct hapd_interfaces *hapd_glob = hapd->iface->interfaces;
-
-       if (!parent)
-               parent = "";
-
-       for (i = 0; i < hapd_glob->count_dynamic; i++) {
-               j = hapd_glob->dynamic_iface[i];
-               if (os_strncmp(j->iface, ifname, sizeof(j->iface)) == 0 &&
-                   os_strncmp(j->parent, parent, sizeof(j->parent)) == 0)
-                       break;
-       }
-
-       if (i == hapd_glob->count_dynamic) {
-               /*
-                * Interface not in global list. This can happen if alloc in
-                * _get_ failed.
-                */
-               return -1;
-       }
-
-       if (j->usage > 0) {
-               j->usage--;
-               return 0;
-       }
-
-       os_free(j);
-       for (; i < hapd_glob->count_dynamic - 1; i++)
-               hapd_glob->dynamic_iface[i] = hapd_glob->dynamic_iface[i + 1];
-       hapd_glob->dynamic_iface[hapd_glob->count_dynamic - 1] = NULL;
-       hapd_glob->count_dynamic--;
-
-       if (hapd_glob->count_dynamic == 0) {
-               os_free(hapd_glob->dynamic_iface);
-               hapd_glob->dynamic_iface = NULL;
-               return 1;
-       }
-
-       tmp = os_realloc_array(hapd_glob->dynamic_iface,
-                              hapd_glob->count_dynamic,
-                              sizeof(*hapd_glob->dynamic_iface));
-       if (!tmp) {
-               wpa_printf(MSG_ERROR, "VLAN: Failed to release memory in %s",
-                          __func__);
-               return -1;
-       }
-       hapd_glob->dynamic_iface = tmp;
-
-       return 1;
-}
-
-
 static void vlan_newlink(char *ifname, struct hostapd_data *hapd)
 {
        char vlan_ifname[IFNAMSIZ];
@@ -604,7 +481,6 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd)
        struct hostapd_vlan *vlan = hapd->conf->vlan;
        char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
        int vlan_naming = hapd->conf->ssid.vlan_naming;
-       int ret;
 
        wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname);
 
@@ -624,9 +500,7 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd)
                                            "brvlan%d", vlan->vlan_id);
                        }
 
-                       ret = br_addbr(br_name);
-                       if (hapd_get_dynamic_iface(NULL, br_name, ret == 0,
-                                                  hapd))
+                       if (!br_addbr(br_name))
                                vlan->clean |= DVLAN_CLEAN_BR;
 
                        ifconfig_up(br_name);
@@ -644,24 +518,17 @@ static void vlan_newlink(char *ifname, struct hostapd_data *hapd)
                                                    "vlan%d", vlan->vlan_id);
 
                                ifconfig_up(tagged_interface);
-                               ret = vlan_add(tagged_interface, vlan->vlan_id,
-                                             vlan_ifname);
-                               if (hapd_get_dynamic_iface(NULL, vlan_ifname,
-                                                          ret == 0, hapd))
+                               if (!vlan_add(tagged_interface, vlan->vlan_id,
+                                             vlan_ifname))
                                        vlan->clean |= DVLAN_CLEAN_VLAN;
 
-                               ret = br_addif(br_name, vlan_ifname);
-                               if (hapd_get_dynamic_iface(br_name,
-                                                          vlan_ifname,
-                                                          ret == 0, hapd))
+                               if (!br_addif(br_name, vlan_ifname))
                                        vlan->clean |= DVLAN_CLEAN_VLAN_PORT;
 
                                ifconfig_up(vlan_ifname);
                        }
 
-                       ret = br_addif(br_name, ifname);
-                       if (hapd_get_dynamic_iface(br_name, ifname, ret == 0,
-                                                  hapd))
+                       if (!br_addif(br_name, ifname))
                                vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
 
                        ifconfig_up(ifname);
@@ -700,8 +567,7 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd)
                                            "brvlan%d", vlan->vlan_id);
                        }
 
-                       if ((vlan->clean & DVLAN_CLEAN_WLAN_PORT) &&
-                           hapd_put_dynamic_iface(br_name, vlan->ifname, hapd))
+                       if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
                                br_delif(br_name, vlan->ifname);
 
                        if (tagged_interface) {
@@ -715,20 +581,15 @@ static void vlan_dellink(char *ifname, struct hostapd_data *hapd)
                                        os_snprintf(vlan_ifname,
                                                    sizeof(vlan_ifname),
                                                    "vlan%d", vlan->vlan_id);
-                               if ((vlan->clean & DVLAN_CLEAN_VLAN_PORT) &&
-                                   hapd_put_dynamic_iface(br_name, vlan_ifname,
-                                                          hapd))
+                               if (vlan->clean & DVLAN_CLEAN_VLAN_PORT)
                                        br_delif(br_name, vlan_ifname);
                                ifconfig_down(vlan_ifname);
 
-                               if ((vlan->clean & DVLAN_CLEAN_VLAN) &&
-                                   hapd_put_dynamic_iface(NULL, vlan_ifname,
-                                                          hapd))
+                               if (vlan->clean & DVLAN_CLEAN_VLAN)
                                        vlan_rem(vlan_ifname);
                        }
 
                        if ((vlan->clean & DVLAN_CLEAN_BR) &&
-                           hapd_put_dynamic_iface(NULL, br_name, hapd) &&
                            br_getnumports(br_name) == 0) {
                                ifconfig_down(br_name);
                                br_delbr(br_name);
@@ -756,6 +617,7 @@ vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del,
        struct ifinfomsg *ifi;
        int attrlen, nlmsg_len, rta_len;
        struct rtattr *attr;
+       char ifname[IFNAMSIZ + 1];
 
        if (len < sizeof(*ifi))
                return;
@@ -770,29 +632,39 @@ vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del,
 
        attr = (struct rtattr *) (((char *) ifi) + nlmsg_len);
 
+       os_memset(ifname, 0, sizeof(ifname));
        rta_len = RTA_ALIGN(sizeof(struct rtattr));
        while (RTA_OK(attr, attrlen)) {
-               char ifname[IFNAMSIZ + 1];
-
                if (attr->rta_type == IFLA_IFNAME) {
                        int n = attr->rta_len - rta_len;
                        if (n < 0)
                                break;
 
-                       os_memset(ifname, 0, sizeof(ifname));
-
-                       if ((size_t) n > sizeof(ifname))
-                               n = sizeof(ifname);
+                       if ((size_t) n >= sizeof(ifname))
+                               n = sizeof(ifname) - 1;
                        os_memcpy(ifname, ((char *) attr) + rta_len, n);
 
-                       if (del)
-                               vlan_dellink(ifname, hapd);
-                       else
-                               vlan_newlink(ifname, hapd);
                }
 
                attr = RTA_NEXT(attr, attrlen);
        }
+
+       if (!ifname[0])
+               return;
+
+       wpa_printf(MSG_DEBUG,
+                  "VLAN: RTM_%sLINK: ifi_index=%d ifname=%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
+                  del ? "DEL" : "NEW",
+                  ifi->ifi_index, ifname, ifi->ifi_family, ifi->ifi_flags,
+                  (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
+                  (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
+                  (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
+                  (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+
+       if (del)
+               vlan_dellink(ifname, hapd);
+       else
+               vlan_newlink(ifname, hapd);
 }
 
 
@@ -816,7 +688,7 @@ static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx)
        }
 
        h = (struct nlmsghdr *) buf;
-       while (left >= (int) sizeof(*h)) {
+       while (NLMSG_OK(h, left)) {
                int len, plen;
 
                len = h->nlmsg_len;
@@ -837,9 +709,7 @@ static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx)
                        break;
                }
 
-               len = NLMSG_ALIGN(len);
-               left -= len;
-               h = (struct nlmsghdr *) ((char *) h + len);
+               h = NLMSG_NEXT(h, left);
        }
 
        if (left > 0) {
@@ -1004,11 +874,8 @@ int vlan_init(struct hostapd_data *hapd)
                vlan->vlan_id = VLAN_ID_WILDCARD;
                os_snprintf(vlan->ifname, sizeof(vlan->ifname), "%s.#",
                            hapd->conf->iface);
-               if (hapd->conf->vlan_tail)
-                       hapd->conf->vlan_tail->next = vlan;
-               else
-                       hapd->conf->vlan = vlan;
-               hapd->conf->vlan_tail = vlan;
+               vlan->next = hapd->conf->vlan;
+               hapd->conf->vlan = vlan;
        }
 
        if (vlan_dynamic_add(hapd, hapd->conf->vlan))
@@ -1024,6 +891,7 @@ void vlan_deinit(struct hostapd_data *hapd)
 
 #ifdef CONFIG_FULL_DYNAMIC_VLAN
        full_dynamic_vlan_deinit(hapd->full_dynamic_vlan);
+       hapd->full_dynamic_vlan = NULL;
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
 }
 
@@ -1032,7 +900,7 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
                                       struct hostapd_vlan *vlan,
                                       int vlan_id)
 {
-       struct hostapd_vlan *n;
+       struct hostapd_vlan *n = NULL;
        char *ifname, *pos;
 
        if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID ||
@@ -1045,28 +913,24 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
        if (ifname == NULL)
                return NULL;
        pos = os_strchr(ifname, '#');
-       if (pos == NULL) {
-               os_free(ifname);
-               return NULL;
-       }
+       if (pos == NULL)
+               goto free_ifname;
        *pos++ = '\0';
 
        n = os_zalloc(sizeof(*n));
-       if (n == NULL) {
-               os_free(ifname);
-               return NULL;
-       }
+       if (n == NULL)
+               goto free_ifname;
 
        n->vlan_id = vlan_id;
        n->dynamic_vlan = 1;
 
        os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id,
                    pos);
-       os_free(ifname);
 
        if (hostapd_vlan_if_add(hapd, n->ifname)) {
                os_free(n);
-               return NULL;
+               n = NULL;
+               goto free_ifname;
        }
 
        n->next = hapd->conf->vlan;
@@ -1076,6 +940,8 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
        ifconfig_up(n->ifname);
 #endif /* CONFIG_FULL_DYNAMIC_VLAN */
 
+free_ifname:
+       os_free(ifname);
        return n;
 }
 
index 382d5de..781eaac 100644 (file)
@@ -3,14 +3,8 @@
  * Copyright 2003, Instant802 Networks, Inc.
  * Copyright 2005, Devicescape Software, Inc.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #ifndef VLAN_INIT_H
index d21c82f..6d4177c 100644 (file)
@@ -4,14 +4,8 @@
  * Copyright 2005-2006, Devicescape Software, Inc.
  * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #include "utils/includes.h"
@@ -157,7 +151,7 @@ static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr,
        len = ((u8 *) (t + 1)) - buf;
 
        if (hostapd_drv_send_mlme(hapd, m, len, 0) < 0)
-               perror("wmm_send_action: send");
+               wpa_printf(MSG_INFO, "wmm_send_action: send failed");
 }
 
 
index 96b04e8..b70b863 100644 (file)
@@ -3,14 +3,8 @@
  * Copyright 2002-2003, Instant802 Networks, Inc.
  * Copyright 2005-2006, Devicescape Software, Inc.
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #ifndef WME_H
index 54a6b85..4c8bc10 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd - WNM
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -9,7 +9,9 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
 #include "ap/hostapd.h"
 #include "ap/sta_info.h"
 #include "ap/ap_config.h"
@@ -72,7 +74,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
        wnmsleep_ie.len = wnmsleep_ie_len - 2;
        wnmsleep_ie.action_type = action_type;
        wnmsleep_ie.status = WNM_STATUS_SLEEP_ACCEPT;
-       wnmsleep_ie.intval = intval;
+       wnmsleep_ie.intval = host_to_le16(intval);
 
        /* TFS IE(s) */
        wnmtfs_ie = os_zalloc(MAX_TFS_IE_LEN);
@@ -154,6 +156,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
                 */
                if (wnmsleep_ie.status == WNM_STATUS_SLEEP_ACCEPT &&
                    wnmsleep_ie.action_type == WNM_SLEEP_MODE_ENTER) {
+                       sta->flags |= WLAN_STA_WNM_SLEEP_MODE;
                        hostapd_drv_wnm_oper(hapd, WNM_SLEEP_ENTER_CONFIRM,
                                             addr, NULL, NULL);
                        wpa_set_wnmsleep(sta->wpa_sm, 1);
@@ -167,6 +170,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
                     wnmsleep_ie.status ==
                     WNM_STATUS_SLEEP_EXIT_ACCEPT_GTK_UPDATE) &&
                    wnmsleep_ie.action_type == WNM_SLEEP_MODE_EXIT) {
+                       sta->flags &= ~WLAN_STA_WNM_SLEEP_MODE;
                        wpa_set_wnmsleep(sta->wpa_sm, 0);
                        hostapd_drv_wnm_oper(hapd, WNM_SLEEP_EXIT_CONFIRM,
                                             addr, NULL, NULL);
@@ -233,7 +237,7 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
 
        ieee802_11_send_wnmsleep_resp(hapd, addr, dialog_token,
                                      wnmsleep_ie->action_type,
-                                     wnmsleep_ie->intval);
+                                     le_to_host16(wnmsleep_ie->intval));
 
        if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT) {
                /* clear the tfs after sending the resp frame */
@@ -243,29 +247,350 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
 }
 
 
+static int ieee802_11_send_bss_trans_mgmt_request(struct hostapd_data *hapd,
+                                                 const u8 *addr,
+                                                 u8 dialog_token,
+                                                 const char *url)
+{
+       struct ieee80211_mgmt *mgmt;
+       size_t url_len, len;
+       u8 *pos;
+       int res;
+
+       if (url)
+               url_len = os_strlen(url);
+       else
+               url_len = 0;
+
+       mgmt = os_zalloc(sizeof(*mgmt) + (url_len ? 1 + url_len : 0));
+       if (mgmt == NULL)
+               return -1;
+       os_memcpy(mgmt->da, addr, ETH_ALEN);
+       os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+       os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                          WLAN_FC_STYPE_ACTION);
+       mgmt->u.action.category = WLAN_ACTION_WNM;
+       mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+       mgmt->u.action.u.bss_tm_req.dialog_token = dialog_token;
+       mgmt->u.action.u.bss_tm_req.req_mode = 0;
+       mgmt->u.action.u.bss_tm_req.disassoc_timer = host_to_le16(0);
+       mgmt->u.action.u.bss_tm_req.validity_interval = 1;
+       pos = mgmt->u.action.u.bss_tm_req.variable;
+       if (url) {
+               *pos++ += url_len;
+               os_memcpy(pos, url, url_len);
+               pos += url_len;
+       }
+
+       wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
+                  MACSTR " dialog_token=%u req_mode=0x%x disassoc_timer=%u "
+                  "validity_interval=%u",
+                  MAC2STR(addr), dialog_token,
+                  mgmt->u.action.u.bss_tm_req.req_mode,
+                  le_to_host16(mgmt->u.action.u.bss_tm_req.disassoc_timer),
+                  mgmt->u.action.u.bss_tm_req.validity_interval);
+
+       len = pos - &mgmt->u.action.category;
+       res = hostapd_drv_send_action(hapd, hapd->iface->freq, 0,
+                                     mgmt->da, &mgmt->u.action.category, len);
+       os_free(mgmt);
+       return res;
+}
+
+
+static void ieee802_11_rx_bss_trans_mgmt_query(struct hostapd_data *hapd,
+                                              const u8 *addr, const u8 *frm,
+                                              size_t len)
+{
+       u8 dialog_token, reason;
+       const u8 *pos, *end;
+
+       if (len < 2) {
+               wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Query from "
+                          MACSTR, MAC2STR(addr));
+               return;
+       }
+
+       pos = frm;
+       end = pos + len;
+       dialog_token = *pos++;
+       reason = *pos++;
+
+       wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query from "
+                  MACSTR " dialog_token=%u reason=%u",
+                  MAC2STR(addr), dialog_token, reason);
+
+       wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
+                   pos, end - pos);
+
+       ieee802_11_send_bss_trans_mgmt_request(hapd, addr, dialog_token, NULL);
+}
+
+
+static void ieee802_11_rx_bss_trans_mgmt_resp(struct hostapd_data *hapd,
+                                             const u8 *addr, const u8 *frm,
+                                             size_t len)
+{
+       u8 dialog_token, status_code, bss_termination_delay;
+       const u8 *pos, *end;
+
+       if (len < 3) {
+               wpa_printf(MSG_DEBUG, "WNM: Ignore too short BSS Transition Management Response from "
+                          MACSTR, MAC2STR(addr));
+               return;
+       }
+
+       pos = frm;
+       end = pos + len;
+       dialog_token = *pos++;
+       status_code = *pos++;
+       bss_termination_delay = *pos++;
+
+       wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Response from "
+                  MACSTR " dialog_token=%u status_code=%u "
+                  "bss_termination_delay=%u", MAC2STR(addr), dialog_token,
+                  status_code, bss_termination_delay);
+
+       if (status_code == WNM_BSS_TM_ACCEPT) {
+               if (end - pos < ETH_ALEN) {
+                       wpa_printf(MSG_DEBUG, "WNM: not enough room for Target BSSID field");
+                       return;
+               }
+               wpa_printf(MSG_DEBUG, "WNM: Target BSSID: " MACSTR,
+                          MAC2STR(pos));
+               wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
+                       " status_code=%u bss_termination_delay=%u target_bssid="
+                       MACSTR,
+                       MAC2STR(addr), status_code, bss_termination_delay,
+                       MAC2STR(pos));
+               pos += ETH_ALEN;
+       } else {
+               wpa_msg(hapd->msg_ctx, MSG_INFO, BSS_TM_RESP MACSTR
+                       " status_code=%u bss_termination_delay=%u",
+                       MAC2STR(addr), status_code, bss_termination_delay);
+       }
+
+       wpa_hexdump(MSG_DEBUG, "WNM: BSS Transition Candidate List Entries",
+                   pos, end - pos);
+}
+
+
 int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
-                               struct rx_action *action)
+                               const struct ieee80211_mgmt *mgmt, size_t len)
 {
-       if (action->len < 1 || action->data == NULL)
+       u8 action;
+       const u8 *payload;
+       size_t plen;
+
+       if (len < IEEE80211_HDRLEN + 2)
                return -1;
 
-       switch (action->data[0]) {
+       payload = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
+       action = *payload++;
+       plen = len - IEEE80211_HDRLEN - 2;
+
+       switch (action) {
        case WNM_BSS_TRANS_MGMT_QUERY:
-               wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Query");
-               /* TODO */
-               return -1;
+               ieee802_11_rx_bss_trans_mgmt_query(hapd, mgmt->sa, payload,
+                                                  plen);
+               return 0;
        case WNM_BSS_TRANS_MGMT_RESP:
-               wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management "
-                          "Response");
-               /* TODO */
-               return -1;
+               ieee802_11_rx_bss_trans_mgmt_resp(hapd, mgmt->sa, payload,
+                                                 plen);
+               return 0;
        case WNM_SLEEP_MODE_REQ:
-               ieee802_11_rx_wnmsleep_req(hapd, action->sa, action->data + 1,
-                                          action->len - 1);
+               ieee802_11_rx_wnmsleep_req(hapd, mgmt->sa, payload, plen);
                return 0;
        }
 
        wpa_printf(MSG_DEBUG, "WNM: Unsupported WNM Action %u from " MACSTR,
-                  action->data[0], MAC2STR(action->sa));
+                  action, MAC2STR(mgmt->sa));
        return -1;
 }
+
+
+int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
+                              struct sta_info *sta, int disassoc_timer)
+{
+       u8 buf[1000], *pos;
+       struct ieee80211_mgmt *mgmt;
+
+       os_memset(buf, 0, sizeof(buf));
+       mgmt = (struct ieee80211_mgmt *) buf;
+       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                          WLAN_FC_STYPE_ACTION);
+       os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+       os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+       os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+       mgmt->u.action.category = WLAN_ACTION_WNM;
+       mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+       mgmt->u.action.u.bss_tm_req.dialog_token = 1;
+       mgmt->u.action.u.bss_tm_req.req_mode =
+               WNM_BSS_TM_REQ_DISASSOC_IMMINENT;
+       mgmt->u.action.u.bss_tm_req.disassoc_timer =
+               host_to_le16(disassoc_timer);
+       mgmt->u.action.u.bss_tm_req.validity_interval = 0;
+
+       pos = mgmt->u.action.u.bss_tm_req.variable;
+
+       wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request frame to indicate imminent disassociation (disassoc_timer=%d) to "
+                  MACSTR, disassoc_timer, MAC2STR(sta->addr));
+       if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
+               wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
+                          "Management Request frame");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static void set_disassoc_timer(struct hostapd_data *hapd, struct sta_info *sta,
+                              int disassoc_timer)
+{
+       int timeout, beacon_int;
+
+       /*
+        * Prevent STA from reconnecting using cached PMKSA to force
+        * full authentication with the authentication server (which may
+        * decide to reject the connection),
+        */
+       wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+
+       beacon_int = hapd->iconf->beacon_int;
+       if (beacon_int < 1)
+               beacon_int = 100; /* best guess */
+       /* Calculate timeout in ms based on beacon_int in TU */
+       timeout = disassoc_timer * beacon_int * 128 / 125;
+       wpa_printf(MSG_DEBUG, "Disassociation timer for " MACSTR
+                  " set to %d ms", MAC2STR(sta->addr), timeout);
+
+       sta->timeout_next = STA_DISASSOC_FROM_CLI;
+       eloop_cancel_timeout(ap_handle_timer, hapd, sta);
+       eloop_register_timeout(timeout / 1000,
+                              timeout % 1000 * 1000,
+                              ap_handle_timer, hapd, sta);
+}
+
+
+int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
+                                  struct sta_info *sta, const char *url,
+                                  int disassoc_timer)
+{
+       u8 buf[1000], *pos;
+       struct ieee80211_mgmt *mgmt;
+       size_t url_len;
+
+       os_memset(buf, 0, sizeof(buf));
+       mgmt = (struct ieee80211_mgmt *) buf;
+       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                          WLAN_FC_STYPE_ACTION);
+       os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+       os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+       os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+       mgmt->u.action.category = WLAN_ACTION_WNM;
+       mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+       mgmt->u.action.u.bss_tm_req.dialog_token = 1;
+       mgmt->u.action.u.bss_tm_req.req_mode =
+               WNM_BSS_TM_REQ_DISASSOC_IMMINENT |
+               WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT;
+       mgmt->u.action.u.bss_tm_req.disassoc_timer =
+               host_to_le16(disassoc_timer);
+       mgmt->u.action.u.bss_tm_req.validity_interval = 0x01;
+
+       pos = mgmt->u.action.u.bss_tm_req.variable;
+
+       /* Session Information URL */
+       url_len = os_strlen(url);
+       if (url_len > 255)
+               return -1;
+       *pos++ = url_len;
+       os_memcpy(pos, url, url_len);
+       pos += url_len;
+
+       if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
+               wpa_printf(MSG_DEBUG, "Failed to send BSS Transition "
+                          "Management Request frame");
+               return -1;
+       }
+
+       if (disassoc_timer) {
+               /* send disassociation frame after time-out */
+               set_disassoc_timer(hapd, sta, disassoc_timer);
+       }
+
+       return 0;
+}
+
+
+int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
+                       u8 req_mode, int disassoc_timer, u8 valid_int,
+                       const u8 *bss_term_dur, const char *url,
+                       const u8 *nei_rep, size_t nei_rep_len)
+{
+       u8 *buf, *pos;
+       struct ieee80211_mgmt *mgmt;
+       size_t url_len;
+
+       wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Request to "
+                  MACSTR " req_mode=0x%x disassoc_timer=%d valid_int=0x%x",
+                  MAC2STR(sta->addr), req_mode, disassoc_timer, valid_int);
+       buf = os_zalloc(1000 + nei_rep_len);
+       if (buf == NULL)
+               return -1;
+       mgmt = (struct ieee80211_mgmt *) buf;
+       mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                          WLAN_FC_STYPE_ACTION);
+       os_memcpy(mgmt->da, sta->addr, ETH_ALEN);
+       os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+       os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+       mgmt->u.action.category = WLAN_ACTION_WNM;
+       mgmt->u.action.u.bss_tm_req.action = WNM_BSS_TRANS_MGMT_REQ;
+       mgmt->u.action.u.bss_tm_req.dialog_token = 1;
+       mgmt->u.action.u.bss_tm_req.req_mode = req_mode;
+       mgmt->u.action.u.bss_tm_req.disassoc_timer =
+               host_to_le16(disassoc_timer);
+       mgmt->u.action.u.bss_tm_req.validity_interval = valid_int;
+
+       pos = mgmt->u.action.u.bss_tm_req.variable;
+
+       if ((req_mode & WNM_BSS_TM_REQ_BSS_TERMINATION_INCLUDED) &&
+           bss_term_dur) {
+               os_memcpy(pos, bss_term_dur, 12);
+               pos += 12;
+       }
+
+       if (url) {
+               /* Session Information URL */
+               url_len = os_strlen(url);
+               if (url_len > 255) {
+                       os_free(buf);
+                       return -1;
+               }
+
+               *pos++ = url_len;
+               os_memcpy(pos, url, url_len);
+               pos += url_len;
+       }
+
+       if (nei_rep) {
+               os_memcpy(pos, nei_rep, nei_rep_len);
+               pos += nei_rep_len;
+       }
+
+       if (hostapd_drv_send_mlme(hapd, buf, pos - buf, 0) < 0) {
+               wpa_printf(MSG_DEBUG,
+                          "Failed to send BSS Transition Management Request frame");
+               os_free(buf);
+               return -1;
+       }
+       os_free(buf);
+
+       if (disassoc_timer) {
+               /* send disassociation frame after time-out */
+               set_disassoc_timer(hapd, sta, disassoc_timer);
+       }
+
+       return 0;
+}
index f05726e..7789307 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.11v WNM related functions and structures
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -9,9 +9,18 @@
 #ifndef WNM_AP_H
 #define WNM_AP_H
 
-struct rx_action;
+struct sta_info;
 
 int ieee802_11_rx_wnm_action_ap(struct hostapd_data *hapd,
-                               struct rx_action *action);
+                               const struct ieee80211_mgmt *mgmt, size_t len);
+int wnm_send_disassoc_imminent(struct hostapd_data *hapd,
+                              struct sta_info *sta, int disassoc_timer);
+int wnm_send_ess_disassoc_imminent(struct hostapd_data *hapd,
+                                  struct sta_info *sta, const char *url,
+                                  int disassoc_timer);
+int wnm_send_bss_tm_req(struct hostapd_data *hapd, struct sta_info *sta,
+                       u8 req_mode, int disassoc_timer, u8 valid_int,
+                       const u8 *bss_term_dur, const char *url,
+                       const u8 *nei_rep, size_t nei_rep_len);
 
 #endif /* WNM_AP_H */
old mode 100644 (file)
new mode 100755 (executable)
index 83cc857..9c5f609
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.11 RSN / WPA Authenticator
- * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -11,6 +11,7 @@
 #include "utils/common.h"
 #include "utils/eloop.h"
 #include "utils/state_machine.h"
+#include "utils/bitfield.h"
 #include "common/ieee802_11_defs.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/crypto.h"
@@ -32,7 +33,8 @@
 
 static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx);
 static int wpa_sm_step(struct wpa_state_machine *sm);
-static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len);
+static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
+                             size_t data_len);
 static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx);
 static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
                              struct wpa_group *group);
@@ -41,6 +43,8 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
                          struct wpa_group *group);
 static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
                                       struct wpa_group *group);
+static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
+                         const u8 *pmk, struct wpa_ptk *ptk);
 
 static const u32 dot11RSNAConfigGroupUpdateCount = 4;
 static const u32 dot11RSNAConfigPairwiseUpdateCount = 4;
@@ -82,11 +86,14 @@ static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth,
 
 
 static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth,
-                                         const u8 *addr, const u8 *prev_psk)
+                                         const u8 *addr,
+                                         const u8 *p2p_dev_addr,
+                                         const u8 *prev_psk)
 {
        if (wpa_auth->cb.get_psk == NULL)
                return NULL;
-       return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, prev_psk);
+       return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, p2p_dev_addr,
+                                   prev_psk);
 }
 
 
@@ -131,6 +138,17 @@ wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr,
 }
 
 
+#ifdef CONFIG_MESH
+static inline int wpa_auth_start_ampe(struct wpa_authenticator *wpa_auth,
+                                     const u8 *addr)
+{
+       if (wpa_auth->cb.start_ampe == NULL)
+               return -1;
+       return wpa_auth->cb.start_ampe(wpa_auth->cb.ctx, addr);
+}
+#endif /* CONFIG_MESH */
+
+
 int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth,
                          int (*cb)(struct wpa_state_machine *sm, void *ctx),
                          void *cb_ctx)
@@ -207,6 +225,8 @@ static int wpa_use_aes_cmac(struct wpa_state_machine *sm)
        if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt))
                ret = 1;
 #endif /* CONFIG_IEEE80211W */
+       if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN)
+               ret = 1;
        return ret;
 }
 
@@ -395,6 +415,7 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
                                                wpa_auth);
        if (wpa_auth->pmksa == NULL) {
                wpa_printf(MSG_ERROR, "PMKSA cache initialization failed.");
+               os_free(wpa_auth->group);
                os_free(wpa_auth->wpa_ie);
                os_free(wpa_auth);
                return NULL;
@@ -404,6 +425,7 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
        wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init();
        if (wpa_auth->ft_pmk_cache == NULL) {
                wpa_printf(MSG_ERROR, "FT PMK cache initialization failed.");
+               os_free(wpa_auth->group);
                os_free(wpa_auth->wpa_ie);
                pmksa_cache_auth_deinit(wpa_auth->pmksa);
                os_free(wpa_auth);
@@ -421,6 +443,17 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
                                       wpa_rekey_gtk, wpa_auth, NULL);
        }
 
+#ifdef CONFIG_P2P
+       if (WPA_GET_BE32(conf->ip_addr_start)) {
+               int count = WPA_GET_BE32(conf->ip_addr_end) -
+                       WPA_GET_BE32(conf->ip_addr_start) + 1;
+               if (count > 1000)
+                       count = 1000;
+               if (count > 0)
+                       wpa_auth->ip_pool = bitfield_alloc(count);
+       }
+#endif /* CONFIG_P2P */
+
        return wpa_auth;
 }
 
@@ -434,6 +467,8 @@ int wpa_init_keys(struct wpa_authenticator *wpa_auth)
        wpa_group_sm_step(wpa_auth, group);
        group->GInit = FALSE;
        wpa_group_sm_step(wpa_auth, group);
+       if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+               return -1;
        return 0;
 }
 
@@ -461,6 +496,11 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth)
        wpa_auth->ft_pmk_cache = NULL;
 #endif /* CONFIG_IEEE80211R */
 
+#ifdef CONFIG_P2P
+       bitfield_free(wpa_auth->ip_pool);
+#endif /* CONFIG_P2P */
+
+
        os_free(wpa_auth->wpa_ie);
 
        group = wpa_auth->group;
@@ -508,14 +548,20 @@ int wpa_reconfig(struct wpa_authenticator *wpa_auth,
 
 
 struct wpa_state_machine *
-wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr)
+wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
+                 const u8 *p2p_dev_addr)
 {
        struct wpa_state_machine *sm;
 
+       if (wpa_auth->group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+               return NULL;
+
        sm = os_zalloc(sizeof(struct wpa_state_machine));
        if (sm == NULL)
                return NULL;
        os_memcpy(sm->addr, addr, ETH_ALEN);
+       if (p2p_dev_addr)
+               os_memcpy(sm->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
 
        sm->wpa_auth = wpa_auth;
        sm->group = wpa_auth->group;
@@ -535,6 +581,8 @@ int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
                wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
                                "FT authentication already completed - do not "
                                "start 4-way handshake");
+               /* Go to PTKINITDONE state to allow GTK rekeying */
+               sm->wpa_ptk_state = WPA_PTK_PTKINITDONE;
                return 0;
        }
 #endif /* CONFIG_IEEE80211R */
@@ -572,12 +620,26 @@ void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm)
 
 static void wpa_free_sta_sm(struct wpa_state_machine *sm)
 {
+#ifdef CONFIG_P2P
+       if (WPA_GET_BE32(sm->ip_addr)) {
+               u32 start;
+               wpa_printf(MSG_DEBUG, "P2P: Free assigned IP "
+                          "address %u.%u.%u.%u from " MACSTR,
+                          sm->ip_addr[0], sm->ip_addr[1],
+                          sm->ip_addr[2], sm->ip_addr[3],
+                          MAC2STR(sm->addr));
+               start = WPA_GET_BE32(sm->wpa_auth->conf.ip_addr_start);
+               bitfield_clear(sm->wpa_auth->ip_pool,
+                              WPA_GET_BE32(sm->ip_addr) - start);
+       }
+#endif /* CONFIG_P2P */
        if (sm->GUpdateStationKeys) {
                sm->group->GKeyDoneStations--;
                sm->GUpdateStationKeys = FALSE;
        }
 #ifdef CONFIG_IEEE80211R
        os_free(sm->assoc_resp_ftie);
+       wpabuf_free(sm->ft_pending_req_ies);
 #endif /* CONFIG_IEEE80211R */
        os_free(sm->last_rx_eapol_key);
        os_free(sm->wpa_ie);
@@ -736,40 +798,96 @@ static int wpa_receive_error_report(struct wpa_authenticator *wpa_auth,
 }
 
 
+static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
+                             size_t data_len)
+{
+       struct wpa_ptk PTK;
+       int ok = 0;
+       const u8 *pmk = NULL;
+
+       for (;;) {
+               if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
+                       pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
+                                              sm->p2p_dev_addr, pmk);
+                       if (pmk == NULL)
+                               break;
+               } else
+                       pmk = sm->PMK;
+
+               wpa_derive_ptk(sm, sm->alt_SNonce, pmk, &PTK);
+
+               if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK, data, data_len)
+                   == 0) {
+                       ok = 1;
+                       break;
+               }
+
+               if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt))
+                       break;
+       }
+
+       if (!ok) {
+               wpa_printf(MSG_DEBUG,
+                          "WPA: Earlier SNonce did not result in matching MIC");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG,
+                  "WPA: Earlier SNonce resulted in matching MIC");
+       sm->alt_snonce_valid = 0;
+       os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN);
+       os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
+       sm->PTK_valid = TRUE;
+
+       return 0;
+}
+
+
 void wpa_receive(struct wpa_authenticator *wpa_auth,
                 struct wpa_state_machine *sm,
                 u8 *data, size_t data_len)
 {
        struct ieee802_1x_hdr *hdr;
        struct wpa_eapol_key *key;
+       struct wpa_eapol_key_192 *key192;
        u16 key_info, key_data_length;
        enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST,
               SMK_M1, SMK_M3, SMK_ERROR } msg;
        char *msgtxt;
        struct wpa_eapol_ie_parse kde;
        int ft;
-       const u8 *eapol_key_ie;
-       size_t eapol_key_ie_len;
+       const u8 *eapol_key_ie, *key_data;
+       size_t eapol_key_ie_len, keyhdrlen, mic_len;
 
        if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL)
                return;
 
-       if (data_len < sizeof(*hdr) + sizeof(*key))
+       mic_len = wpa_mic_len(sm->wpa_key_mgmt);
+       keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+
+       if (data_len < sizeof(*hdr) + keyhdrlen)
                return;
 
        hdr = (struct ieee802_1x_hdr *) data;
        key = (struct wpa_eapol_key *) (hdr + 1);
+       key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
        key_info = WPA_GET_BE16(key->key_info);
-       key_data_length = WPA_GET_BE16(key->key_data_length);
+       if (mic_len == 24) {
+               key_data = (const u8 *) (key192 + 1);
+               key_data_length = WPA_GET_BE16(key192->key_data_length);
+       } else {
+               key_data = (const u8 *) (key + 1);
+               key_data_length = WPA_GET_BE16(key->key_data_length);
+       }
        wpa_printf(MSG_DEBUG, "WPA: Received EAPOL-Key from " MACSTR
                   " key_info=0x%x type=%u key_data_length=%u",
                   MAC2STR(sm->addr), key_info, key->type, key_data_length);
-       if (key_data_length > data_len - sizeof(*hdr) - sizeof(*key)) {
+       if (key_data_length > data_len - sizeof(*hdr) - keyhdrlen) {
                wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - "
                           "key_data overflow (%d > %lu)",
                           key_data_length,
                           (unsigned long) (data_len - sizeof(*hdr) -
-                                           sizeof(*key)));
+                                           keyhdrlen));
                return;
        }
 
@@ -837,6 +955,8 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                if (sm->pairwise == WPA_CIPHER_CCMP ||
                    sm->pairwise == WPA_CIPHER_GCMP) {
                        if (wpa_use_aes_cmac(sm) &&
+                           sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN &&
+                           !wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
                            ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
                                wpa_auth_logger(wpa_auth, sm->addr,
                                                LOGGER_WARNING,
@@ -855,6 +975,13 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                                return;
                        }
                }
+
+               if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
+                   ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+                       wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING,
+                                       "did not use EAPOL-Key descriptor version 0 as required for AKM-defined cases");
+                       return;
+               }
        }
 
        if (key_info & WPA_KEY_INFO_REQUEST) {
@@ -890,8 +1017,25 @@ void wpa_receive(struct wpa_authenticator *wpa_auth,
                                         "based on retransmitted EAPOL-Key "
                                         "1/4");
                        sm->update_snonce = 1;
-                       wpa_replay_counter_mark_invalid(sm->prev_key_replay,
-                                                       key->replay_counter);
+                       os_memcpy(sm->alt_SNonce, sm->SNonce, WPA_NONCE_LEN);
+                       sm->alt_snonce_valid = TRUE;
+                       os_memcpy(sm->alt_replay_counter,
+                                 sm->key_replay[0].counter,
+                                 WPA_REPLAY_COUNTER_LEN);
+                       goto continue_processing;
+               }
+
+               if (msg == PAIRWISE_4 && sm->alt_snonce_valid &&
+                   sm->wpa_ptk_state == WPA_PTK_PTKINITNEGOTIATING &&
+                   os_memcmp(key->replay_counter, sm->alt_replay_counter,
+                             WPA_REPLAY_COUNTER_LEN) == 0) {
+                       /*
+                        * Supplicant may still be using the old SNonce since
+                        * there was two EAPOL-Key 2/4 messages and they had
+                        * different SNonce values.
+                        */
+                       wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+                                        "Try to process received EAPOL-Key 4/4 based on old Replay Counter and SNonce from an earlier EAPOL-Key 1/4");
                        goto continue_processing;
                }
 
@@ -950,8 +1094,7 @@ continue_processing:
                        wpa_sta_disconnect(wpa_auth, sm->addr);
                        return;
                }
-               if (wpa_parse_kde_ies((u8 *) (key + 1), key_data_length,
-                                     &kde) < 0) {
+               if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
                        wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
                                         "received EAPOL-Key msg 2/4 with "
                                         "invalid Key Data contents");
@@ -960,6 +1103,9 @@ continue_processing:
                if (kde.rsn_ie) {
                        eapol_key_ie = kde.rsn_ie;
                        eapol_key_ie_len = kde.rsn_ie_len;
+               } else if (kde.osen) {
+                       eapol_key_ie = kde.osen;
+                       eapol_key_ie_len = kde.osen_len;
                } else {
                        eapol_key_ie = kde.wpa_ie;
                        eapol_key_ie_len = kde.wpa_ie_len;
@@ -989,6 +1135,26 @@ continue_processing:
                        return;
                }
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+               if (kde.ip_addr_req && kde.ip_addr_req[0] &&
+                   wpa_auth->ip_pool && WPA_GET_BE32(sm->ip_addr) == 0) {
+                       int idx;
+                       wpa_printf(MSG_DEBUG, "P2P: IP address requested in "
+                                  "EAPOL-Key exchange");
+                       idx = bitfield_get_first_zero(wpa_auth->ip_pool);
+                       if (idx >= 0) {
+                               u32 start = WPA_GET_BE32(wpa_auth->conf.
+                                                        ip_addr_start);
+                               bitfield_set(wpa_auth->ip_pool, idx);
+                               WPA_PUT_BE32(sm->ip_addr, start + idx);
+                               wpa_printf(MSG_DEBUG, "P2P: Assigned IP "
+                                          "address %u.%u.%u.%u to " MACSTR,
+                                          sm->ip_addr[0], sm->ip_addr[1],
+                                          sm->ip_addr[2], sm->ip_addr[3],
+                                          MAC2STR(sm->addr));
+                       }
+               }
+#endif /* CONFIG_P2P */
                break;
        case PAIRWISE_4:
                if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING ||
@@ -1053,7 +1219,10 @@ continue_processing:
 
        sm->MICVerified = FALSE;
        if (sm->PTK_valid && !sm->update_snonce) {
-               if (wpa_verify_key_mic(&sm->PTK, data, data_len)) {
+               if (wpa_verify_key_mic(sm->wpa_key_mgmt, &sm->PTK, data,
+                                      data_len) &&
+                   (msg != PAIRWISE_4 || !sm->alt_snonce_valid ||
+                    wpa_try_alt_snonce(sm, data, data_len))) {
                        wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
                                        "received EAPOL-Key with invalid MIC");
                        return;
@@ -1082,7 +1251,7 @@ continue_processing:
                 */
                if (msg == SMK_ERROR) {
 #ifdef CONFIG_PEERKEY
-                       wpa_smk_error(wpa_auth, sm, key);
+                       wpa_smk_error(wpa_auth, sm, key_data, key_data_length);
 #endif /* CONFIG_PEERKEY */
                        return;
                } else if (key_info & WPA_KEY_INFO_ERROR) {
@@ -1097,11 +1266,12 @@ continue_processing:
                        wpa_request_new_ptk(sm);
 #ifdef CONFIG_PEERKEY
                } else if (msg == SMK_M1) {
-                       wpa_smk_m1(wpa_auth, sm, key);
+                       wpa_smk_m1(wpa_auth, sm, key, key_data,
+                                  key_data_length);
 #endif /* CONFIG_PEERKEY */
                } else if (key_data_length > 0 &&
-                          wpa_parse_kde_ies((const u8 *) (key + 1),
-                                            key_data_length, &kde) == 0 &&
+                          wpa_parse_kde_ies(key_data, key_data_length,
+                                            &kde) == 0 &&
                           kde.mac_addr) {
                } else {
                        wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
@@ -1139,7 +1309,7 @@ continue_processing:
 
 #ifdef CONFIG_PEERKEY
        if (msg == SMK_M3) {
-               wpa_smk_m3(wpa_auth, sm, key);
+               wpa_smk_m3(wpa_auth, sm, key, key_data, key_data_length);
                return;
        }
 #endif /* CONFIG_PEERKEY */
@@ -1214,17 +1384,25 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
 {
        struct ieee802_1x_hdr *hdr;
        struct wpa_eapol_key *key;
-       size_t len;
+       struct wpa_eapol_key_192 *key192;
+       size_t len, mic_len, keyhdrlen;
        int alg;
        int key_data_len, pad_len = 0;
        u8 *buf, *pos;
        int version, pairwise;
        int i;
+       u8 *key_data;
 
-       len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key);
+       mic_len = wpa_mic_len(sm->wpa_key_mgmt);
+       keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+
+       len = sizeof(struct ieee802_1x_hdr) + keyhdrlen;
 
        if (force_version)
                version = force_version;
+       else if (sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
+                wpa_key_mgmt_suite_b(sm->wpa_key_mgmt))
+               version = WPA_KEY_INFO_TYPE_AKM_DEFINED;
        else if (wpa_use_aes_cmac(sm))
                version = WPA_KEY_INFO_TYPE_AES_128_CMAC;
        else if (sm->pairwise != WPA_CIPHER_TKIP)
@@ -1247,6 +1425,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
        key_data_len = kde_len;
 
        if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+            sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
+            wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
             version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) {
                pad_len = key_data_len % 8;
                if (pad_len)
@@ -1263,6 +1443,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
        hdr->type = IEEE802_1X_TYPE_EAPOL_KEY;
        hdr->length = host_to_be16(len  - sizeof(*hdr));
        key = (struct wpa_eapol_key *) (hdr + 1);
+       key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
+       key_data = ((u8 *) (hdr + 1)) + keyhdrlen;
 
        key->type = sm->wpa == WPA_VERSION_WPA2 ?
                EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
@@ -1288,6 +1470,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
        inc_byte_array(sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN);
        os_memcpy(key->replay_counter, sm->key_replay[0].counter,
                  WPA_REPLAY_COUNTER_LEN);
+       wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter",
+                   key->replay_counter, WPA_REPLAY_COUNTER_LEN);
        sm->key_replay[0].valid = TRUE;
 
        if (nonce)
@@ -1297,8 +1481,11 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
                os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN);
 
        if (kde && !encr) {
-               os_memcpy(key + 1, kde, kde_len);
-               WPA_PUT_BE16(key->key_data_length, kde_len);
+               os_memcpy(key_data, kde, kde_len);
+               if (mic_len == 24)
+                       WPA_PUT_BE16(key192->key_data_length, kde_len);
+               else
+                       WPA_PUT_BE16(key->key_data_length, kde_len);
        } else if (encr && kde) {
                buf = os_zalloc(key_data_len);
                if (buf == NULL) {
@@ -1315,29 +1502,47 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
                wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
                                buf, key_data_len);
                if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
+                   sm->wpa_key_mgmt == WPA_KEY_MGMT_OSEN ||
+                   wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) ||
                    version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
-                       if (aes_wrap(sm->PTK.kek, (key_data_len - 8) / 8, buf,
-                                    (u8 *) (key + 1))) {
+                       if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len,
+                                    (key_data_len - 8) / 8, buf, key_data)) {
                                os_free(hdr);
                                os_free(buf);
                                return;
                        }
-                       WPA_PUT_BE16(key->key_data_length, key_data_len);
-               } else {
+                       if (mic_len == 24)
+                               WPA_PUT_BE16(key192->key_data_length,
+                                            key_data_len);
+                       else
+                               WPA_PUT_BE16(key->key_data_length,
+                                            key_data_len);
+               } else if (sm->PTK.kek_len == 16) {
                        u8 ek[32];
                        os_memcpy(key->key_iv,
                                  sm->group->Counter + WPA_NONCE_LEN - 16, 16);
                        inc_byte_array(sm->group->Counter, WPA_NONCE_LEN);
                        os_memcpy(ek, key->key_iv, 16);
-                       os_memcpy(ek + 16, sm->PTK.kek, 16);
-                       os_memcpy(key + 1, buf, key_data_len);
-                       rc4_skip(ek, 32, 256, (u8 *) (key + 1), key_data_len);
-                       WPA_PUT_BE16(key->key_data_length, key_data_len);
+                       os_memcpy(ek + 16, sm->PTK.kek, sm->PTK.kek_len);
+                       os_memcpy(key_data, buf, key_data_len);
+                       rc4_skip(ek, 32, 256, key_data, key_data_len);
+                       if (mic_len == 24)
+                               WPA_PUT_BE16(key192->key_data_length,
+                                            key_data_len);
+                       else
+                               WPA_PUT_BE16(key->key_data_length,
+                                            key_data_len);
+               } else {
+                       os_free(hdr);
+                       os_free(buf);
+                       return;
                }
                os_free(buf);
        }
 
        if (key_info & WPA_KEY_INFO_MIC) {
+               u8 *key_mic;
+
                if (!sm->PTK_valid) {
                        wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG,
                                        "PTK not valid when sending EAPOL-Key "
@@ -1345,16 +1550,19 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
                        os_free(hdr);
                        return;
                }
-               wpa_eapol_key_mic(sm->PTK.kck, version, (u8 *) hdr, len,
-                                 key->key_mic);
+
+               key_mic = key192->key_mic; /* same offset for key and key192 */
+               wpa_eapol_key_mic(sm->PTK.kck, sm->PTK.kck_len,
+                                 sm->wpa_key_mgmt, version,
+                                 (u8 *) hdr, len, key_mic);
 #ifdef CONFIG_TESTING_OPTIONS
                if (!pairwise &&
-                   wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0d &&
+                   wpa_auth->conf.corrupt_gtk_rekey_mic_probability > 0.0 &&
                    drand48() <
                    wpa_auth->conf.corrupt_gtk_rekey_mic_probability) {
                        wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
                                        "Corrupting group EAPOL-Key Key MIC");
-                       key->key_mic[0]++;
+                       key_mic[0]++;
                }
 #endif /* CONFIG_TESTING_OPTIONS */
        }
@@ -1398,27 +1606,32 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth,
 }
 
 
-static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len)
+static int wpa_verify_key_mic(int akmp, struct wpa_ptk *PTK, u8 *data,
+                             size_t data_len)
 {
        struct ieee802_1x_hdr *hdr;
        struct wpa_eapol_key *key;
+       struct wpa_eapol_key_192 *key192;
        u16 key_info;
        int ret = 0;
-       u8 mic[16];
+       u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+       size_t mic_len = wpa_mic_len(akmp);
 
        if (data_len < sizeof(*hdr) + sizeof(*key))
                return -1;
 
        hdr = (struct ieee802_1x_hdr *) data;
        key = (struct wpa_eapol_key *) (hdr + 1);
+       key192 = (struct wpa_eapol_key_192 *) (hdr + 1);
        key_info = WPA_GET_BE16(key->key_info);
-       os_memcpy(mic, key->key_mic, 16);
-       os_memset(key->key_mic, 0, 16);
-       if (wpa_eapol_key_mic(PTK->kck, key_info & WPA_KEY_INFO_TYPE_MASK,
-                             data, data_len, key->key_mic) ||
-           os_memcmp(mic, key->key_mic, 16) != 0)
+       os_memcpy(mic, key192->key_mic, mic_len);
+       os_memset(key192->key_mic, 0, mic_len);
+       if (wpa_eapol_key_mic(PTK->kck, PTK->kck_len, akmp,
+                             key_info & WPA_KEY_INFO_TYPE_MASK,
+                             data, data_len, key192->key_mic) ||
+           os_memcmp_const(mic, key192->key_mic, mic_len) != 0)
                ret = -1;
-       os_memcpy(key->key_mic, mic, 16);
+       os_memcpy(key192->key_mic, mic, mic_len);
        return ret;
 }
 
@@ -1445,6 +1658,14 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event)
 
        switch (event) {
        case WPA_AUTH:
+#ifdef CONFIG_MESH
+               /* PTKs are derived through AMPE */
+               if (wpa_auth_start_ampe(sm->wpa_auth, sm->addr)) {
+                       /* not mesh */
+                       break;
+               }
+               return 0;
+#endif /* CONFIG_MESH */
        case WPA_ASSOC:
                break;
        case WPA_DEAUTH:
@@ -1658,8 +1879,12 @@ SM_STATE(WPA_PTK, INITPMK)
                }
 #endif /* CONFIG_IEEE80211R */
        } else {
-               wpa_printf(MSG_DEBUG, "WPA: Could not get PMK");
+               wpa_printf(MSG_DEBUG, "WPA: Could not get PMK, get_msk: %p",
+                          sm->wpa_auth->cb.get_msk);
+               sm->Disconnect = TRUE;
+               return;
        }
+       os_memset(msk, 0, sizeof(msk));
 
        sm->req_replay_counter_used = 0;
        /* IEEE 802.11i does not set keyRun to FALSE, but not doing this
@@ -1678,7 +1903,7 @@ SM_STATE(WPA_PTK, INITPSK)
 {
        const u8 *psk;
        SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk);
-       psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL);
+       psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL);
        if (psk) {
                os_memcpy(sm->PMK, psk, PMK_LEN);
 #ifdef CONFIG_IEEE80211R
@@ -1698,6 +1923,7 @@ SM_STATE(WPA_PTK, PTKSTART)
        SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk);
        sm->PTKRequest = FALSE;
        sm->TimeoutEvt = FALSE;
+       sm->alt_snonce_valid = FALSE;
 
        sm->TimeoutCtr++;
        if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) {
@@ -1713,16 +1939,20 @@ SM_STATE(WPA_PTK, PTKSTART)
         * one possible PSK for this STA.
         */
        if (sm->wpa == WPA_VERSION_WPA2 &&
-           wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt)) {
+           wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) &&
+           sm->wpa_key_mgmt != WPA_KEY_MGMT_OSEN) {
                pmkid = buf;
                pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
                pmkid[0] = WLAN_EID_VENDOR_SPECIFIC;
                pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN;
                RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID);
-               if (sm->pmksa)
+               if (sm->pmksa) {
                        os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
                                  sm->pmksa->pmkid, PMKID_LEN);
-               else {
+               } else if (wpa_key_mgmt_suite_b(sm->wpa_key_mgmt)) {
+                       /* No KCK available to derive PMKID */
+                       pmkid = NULL;
+               } else {
                        /*
                         * Calculate PMKID since no PMKSA cache entry was
                         * available with pre-calculated PMKID.
@@ -1738,21 +1968,17 @@ SM_STATE(WPA_PTK, PTKSTART)
 }
 
 
-static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk,
-                         struct wpa_ptk *ptk)
+static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
+                         const u8 *pmk, struct wpa_ptk *ptk)
 {
-       size_t ptk_len = sm->pairwise != WPA_CIPHER_TKIP ? 48 : 64;
 #ifdef CONFIG_IEEE80211R
        if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
-               return wpa_auth_derive_ptk_ft(sm, pmk, ptk, ptk_len);
+               return wpa_auth_derive_ptk_ft(sm, pmk, ptk);
 #endif /* CONFIG_IEEE80211R */
 
-       wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion",
-                      sm->wpa_auth->addr, sm->addr, sm->ANonce, sm->SNonce,
-                      (u8 *) ptk, ptk_len,
-                      wpa_key_mgmt_sha256(sm->wpa_key_mgmt));
-
-       return 0;
+       return wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion",
+                             sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce,
+                             ptk, sm->wpa_key_mgmt, sm->pairwise);
 }
 
 
@@ -1771,15 +1997,17 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
         * the packet */
        for (;;) {
                if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
-                       pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, pmk);
+                       pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
+                                              sm->p2p_dev_addr, pmk);
                        if (pmk == NULL)
                                break;
                } else
                        pmk = sm->PMK;
 
-               wpa_derive_ptk(sm, pmk, &PTK);
+               wpa_derive_ptk(sm, sm->SNonce, pmk, &PTK);
 
-               if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key,
+               if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
+                                      sm->last_rx_eapol_key,
                                       sm->last_rx_eapol_key_len) == 0) {
                        ok = 1;
                        break;
@@ -1801,8 +2029,8 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
                 * Verify that PMKR1Name from EAPOL-Key message 2/4 matches
                 * with the value we derived.
                 */
-               if (os_memcmp(sm->sup_pmk_r1_name, sm->pmk_r1_name,
-                             WPA_PMK_NAME_LEN) != 0) {
+               if (os_memcmp_const(sm->sup_pmk_r1_name, sm->pmk_r1_name,
+                                   WPA_PMK_NAME_LEN) != 0) {
                        wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
                                        "PMKR1Name mismatch in FT 4-way "
                                        "handshake");
@@ -1845,7 +2073,9 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2)
 static int ieee80211w_kde_len(struct wpa_state_machine *sm)
 {
        if (sm->mgmt_frame_prot) {
-               return 2 + RSN_SELECTOR_LEN + sizeof(struct wpa_igtk_kde);
+               size_t len;
+               len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
+               return 2 + RSN_SELECTOR_LEN + WPA_IGTK_KDE_PREFIX_LEN + len;
        }
 
        return 0;
@@ -1856,6 +2086,8 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
 {
        struct wpa_igtk_kde igtk;
        struct wpa_group *gsm = sm->group;
+       u8 rsc[WPA_KEY_RSC_LEN];
+       size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
 
        if (!sm->mgmt_frame_prot)
                return pos;
@@ -1863,19 +2095,22 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
        igtk.keyid[0] = gsm->GN_igtk;
        igtk.keyid[1] = 0;
        if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE ||
-           wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, igtk.pn) < 0)
+           wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, rsc) < 0)
                os_memset(igtk.pn, 0, sizeof(igtk.pn));
-       os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
+       else
+               os_memcpy(igtk.pn, rsc, sizeof(igtk.pn));
+       os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], len);
        if (sm->wpa_auth->conf.disable_gtk) {
                /*
                 * Provide unique random IGTK to each STA to prevent use of
                 * IGTK in the BSS.
                 */
-               if (random_get_bytes(igtk.igtk, WPA_IGTK_LEN) < 0)
+               if (random_get_bytes(igtk.igtk, len) < 0)
                        return pos;
        }
        pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK,
-                         (const u8 *) &igtk, sizeof(igtk), NULL, 0);
+                         (const u8 *) &igtk, WPA_IGTK_KDE_PREFIX_LEN + len,
+                         NULL, 0);
 
        return pos;
 }
@@ -1925,8 +2160,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
        if (sm->wpa == WPA_VERSION_WPA &&
            (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) &&
            wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) {
-               /* WPA-only STA, remove RSN IE */
+               /* WPA-only STA, remove RSN IE and possible MDIE */
                wpa_ie = wpa_ie + wpa_ie[1] + 2;
+               if (wpa_ie[0] == WLAN_EID_MOBILITY_DOMAIN)
+                       wpa_ie = wpa_ie + wpa_ie[1] + 2;
                wpa_ie_len = wpa_ie[1] + 2;
        }
        wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
@@ -1980,6 +2217,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
                kde_len += 300; /* FTIE + 2 * TIE */
        }
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+       if (WPA_GET_BE32(sm->ip_addr) > 0)
+               kde_len += 2 + RSN_SELECTOR_LEN + 3 * 4;
+#endif /* CONFIG_P2P */
        kde = os_malloc(kde_len);
        if (kde == NULL)
                return;
@@ -2041,6 +2282,16 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
                pos += 4;
        }
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+       if (WPA_GET_BE32(sm->ip_addr) > 0) {
+               u8 addr[3 * 4];
+               os_memcpy(addr, sm->ip_addr, 4);
+               os_memcpy(addr + 4, sm->wpa_auth->conf.ip_addr_mask, 4);
+               os_memcpy(addr + 8, sm->wpa_auth->conf.ip_addr_go, 4);
+               pos = wpa_add_kde(pos, WFA_KEY_DATA_IP_ADDR_ALLOC,
+                                 addr, sizeof(addr), NULL, 0);
+       }
+#endif /* CONFIG_P2P */
 
        wpa_send_eapol(sm->wpa_auth, sm,
                       (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC |
@@ -2059,7 +2310,7 @@ SM_STATE(WPA_PTK, PTKINITDONE)
                enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise);
                int klen = wpa_cipher_key_len(sm->pairwise);
                if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
-                                    sm->PTK.tk1, klen)) {
+                                    sm->PTK.tk, klen)) {
                        wpa_sta_disconnect(sm->wpa_auth, sm->addr);
                        return;
                }
@@ -2158,7 +2409,8 @@ SM_STEP(WPA_PTK)
                }
                break;
        case WPA_PTK_INITPSK:
-               if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL))
+               if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr,
+                                    NULL))
                        SM_ENTER(WPA_PTK, PTKSTART);
                else {
                        wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO,
@@ -2232,7 +2484,8 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
 {
        u8 rsc[WPA_KEY_RSC_LEN];
        struct wpa_group *gsm = sm->group;
-       u8 *kde, *pos, hdr[2];
+       const u8 *kde;
+       u8 *kde_buf = NULL, *pos, hdr[2];
        size_t kde_len;
        u8 *gtk, dummy_gtk[32];
 
@@ -2268,28 +2521,29 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
        if (sm->wpa == WPA_VERSION_WPA2) {
                kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
                        ieee80211w_kde_len(sm);
-               kde = os_malloc(kde_len);
-               if (kde == NULL)
+               kde_buf = os_malloc(kde_len);
+               if (kde_buf == NULL)
                        return;
 
-               pos = kde;
+               kde = pos = kde_buf;
                hdr[0] = gsm->GN & 0x03;
                hdr[1] = 0;
                pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
                                  gtk, gsm->GTK_len);
                pos = ieee80211w_kde_add(sm, pos);
+               kde_len = pos - kde;
        } else {
                kde = gtk;
-               pos = kde + gsm->GTK_len;
+               kde_len = gsm->GTK_len;
        }
 
        wpa_send_eapol(sm->wpa_auth, sm,
                       WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC |
                       WPA_KEY_INFO_ACK |
                       (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0),
-                      rsc, gsm->GNonce, kde, pos - kde, gsm->GN, 1);
-       if (sm->wpa == WPA_VERSION_WPA2)
-               os_free(kde);
+                      rsc, gsm->GNonce, kde, kde_len, gsm->GN, 1);
+
+       os_free(kde_buf);
 }
 
 
@@ -2366,15 +2620,16 @@ static int wpa_gtk_update(struct wpa_authenticator *wpa_auth,
 
 #ifdef CONFIG_IEEE80211W
        if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+               size_t len;
+               len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
                os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN);
                inc_byte_array(group->Counter, WPA_NONCE_LEN);
                if (wpa_gmk_to_gtk(group->GMK, "IGTK key expansion",
                                   wpa_auth->addr, group->GNonce,
-                                  group->IGTK[group->GN_igtk - 4],
-                                  WPA_IGTK_LEN) < 0)
+                                  group->IGTK[group->GN_igtk - 4], len) < 0)
                        ret = -1;
                wpa_hexdump_key(MSG_DEBUG, "IGTK",
-                               group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN);
+                               group->IGTK[group->GN_igtk - 4], len);
        }
 #endif /* CONFIG_IEEE80211W */
 
@@ -2441,7 +2696,7 @@ static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx)
 /* update GTK when exiting WNM-Sleep Mode */
 void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
 {
-       if (sm->is_wnmsleep)
+       if (sm == NULL || sm->is_wnmsleep)
                return;
 
        wpa_group_update_sta(sm, NULL);
@@ -2450,7 +2705,8 @@ void wpa_wnmsleep_rekey_gtk(struct wpa_state_machine *sm)
 
 void wpa_set_wnmsleep(struct wpa_state_machine *sm, int flag)
 {
-       sm->is_wnmsleep = !!flag;
+       if (sm)
+               sm->is_wnmsleep = !!flag;
 }
 
 
@@ -2490,26 +2746,27 @@ int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos)
 {
        struct wpa_group *gsm = sm->group;
        u8 *start = pos;
+       size_t len = wpa_cipher_key_len(sm->wpa_auth->conf.group_mgmt_cipher);
 
        /*
         * IGTK subelement:
         * Sub-elem ID[1] | Length[1] | KeyID[2] | PN[6] | Key[16]
         */
        *pos++ = WNM_SLEEP_SUBELEM_IGTK;
-       *pos++ = 2 + 6 + WPA_IGTK_LEN;
+       *pos++ = 2 + 6 + len;
        WPA_PUT_LE16(pos, gsm->GN_igtk);
        pos += 2;
        if (wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos) != 0)
                return 0;
        pos += 6;
 
-       os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
-       pos += WPA_IGTK_LEN;
+       os_memcpy(pos, gsm->IGTK[gsm->GN_igtk - 4], len);
+       pos += len;
 
        wpa_printf(MSG_DEBUG, "WNM: IGTK Key ID %u in WNM-Sleep Mode exit",
                   gsm->GN_igtk);
        wpa_hexdump_key(MSG_DEBUG, "WNM: IGTK in WNM-Sleep Mode exit",
-                       gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN);
+                       gsm->IGTK[gsm->GN_igtk - 4], len);
 
        return pos - start;
 }
@@ -2564,18 +2821,48 @@ static int wpa_group_config_group_keys(struct wpa_authenticator *wpa_auth,
                ret = -1;
 
 #ifdef CONFIG_IEEE80211W
-       if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION &&
-           wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK,
-                            broadcast_ether_addr, group->GN_igtk,
-                            group->IGTK[group->GN_igtk - 4],
-                            WPA_IGTK_LEN) < 0)
-               ret = -1;
+       if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+               enum wpa_alg alg;
+               size_t len;
+
+               alg = wpa_cipher_to_alg(wpa_auth->conf.group_mgmt_cipher);
+               len = wpa_cipher_key_len(wpa_auth->conf.group_mgmt_cipher);
+
+               if (ret == 0 &&
+                   wpa_auth_set_key(wpa_auth, group->vlan_id, alg,
+                                    broadcast_ether_addr, group->GN_igtk,
+                                    group->IGTK[group->GN_igtk - 4], len) < 0)
+                       ret = -1;
+       }
 #endif /* CONFIG_IEEE80211W */
 
        return ret;
 }
 
 
+static int wpa_group_disconnect_cb(struct wpa_state_machine *sm, void *ctx)
+{
+       if (sm->group == ctx) {
+               wpa_printf(MSG_DEBUG, "WPA: Mark STA " MACSTR
+                          " for discconnection due to fatal failure",
+                          MAC2STR(sm->addr));
+               sm->Disconnect = TRUE;
+       }
+
+       return 0;
+}
+
+
+static void wpa_group_fatal_failure(struct wpa_authenticator *wpa_auth,
+                                   struct wpa_group *group)
+{
+       wpa_printf(MSG_DEBUG, "WPA: group state machine entering state FATAL_FAILURE");
+       group->changed = TRUE;
+       group->wpa_group_state = WPA_GROUP_FATAL_FAILURE;
+       wpa_auth_for_each_sta(wpa_auth, wpa_group_disconnect_cb, group);
+}
+
+
 static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth,
                                 struct wpa_group *group)
 {
@@ -2584,8 +2871,10 @@ static int wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth,
        group->changed = TRUE;
        group->wpa_group_state = WPA_GROUP_SETKEYSDONE;
 
-       if (wpa_group_config_group_keys(wpa_auth, group) < 0)
+       if (wpa_group_config_group_keys(wpa_auth, group) < 0) {
+               wpa_group_fatal_failure(wpa_auth, group);
                return -1;
+       }
 
        return 0;
 }
@@ -2596,6 +2885,8 @@ static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth,
 {
        if (group->GInit) {
                wpa_group_gtk_init(wpa_auth, group);
+       } else if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE) {
+               /* Do not allow group operations */
        } else if (group->wpa_group_state == WPA_GROUP_GTK_INIT &&
                   group->GTKAuthenticator) {
                wpa_group_setkeysdone(wpa_auth, group);
@@ -2723,7 +3014,7 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen)
                          wpa_bool_txt(preauth),
                          wpa_bool_txt(wpa_auth->conf.wpa & WPA_PROTO_RSN),
                          wpa_bool_txt(wpa_auth->conf.rsn_preauth));
-       if (ret < 0 || (size_t) ret >= buflen - len)
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
 
@@ -2773,7 +3064,7 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen)
                RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested),
                wpa_auth->dot11RSNATKIPCounterMeasuresInvoked,
                wpa_auth->dot11RSNA4WayHandshakeFailures);
-       if (ret < 0 || (size_t) ret >= buflen - len)
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
 
@@ -2783,7 +3074,7 @@ int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen)
        /* Private MIB */
        ret = os_snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n",
                          wpa_auth->group->wpa_group_state);
-       if (ret < 0 || (size_t) ret >= buflen - len)
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
 
@@ -2825,7 +3116,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
                RSN_SUITE_ARG(pairwise),
                sm->dot11RSNAStatsTKIPLocalMICFailures,
                sm->dot11RSNAStatsTKIPRemoteMICFailures);
-       if (ret < 0 || (size_t) ret >= buflen - len)
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
 
@@ -2835,7 +3126,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen)
                          "hostapdWPAPTKGroupState=%d\n",
                          sm->wpa_ptk_state,
                          sm->wpa_ptk_group_state);
-       if (ret < 0 || (size_t) ret >= buflen - len)
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
 
@@ -2919,6 +3210,7 @@ int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk,
                return -1;
 
        if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN,
+                                sm->PTK.kck, sm->PTK.kck_len,
                                 sm->wpa_auth->addr, sm->addr, session_timeout,
                                 eapol, sm->wpa_key_mgmt))
                return 0;
@@ -2935,7 +3227,9 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
        if (wpa_auth == NULL)
                return -1;
 
-       if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, wpa_auth->addr,
+       if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len,
+                                NULL, 0,
+                                wpa_auth->addr,
                                 sta_addr, session_timeout, eapol,
                                 WPA_KEY_MGMT_IEEE8021X))
                return 0;
@@ -2944,6 +3238,22 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
 }
 
 
+int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
+                          const u8 *pmk)
+{
+       if (wpa_auth->conf.disable_pmksa_caching)
+               return -1;
+
+       if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN,
+                                NULL, 0,
+                                wpa_auth->addr, addr, 0, NULL,
+                                WPA_KEY_MGMT_SAE))
+               return 0;
+
+       return -1;
+}
+
+
 void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
                           const u8 *sta_addr)
 {
@@ -3004,6 +3314,9 @@ int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id)
        if (sm->group == group)
                return 0;
 
+       if (group->wpa_group_state == WPA_GROUP_FATAL_FAILURE)
+               return -1;
+
        wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state "
                   "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id);
 
@@ -3048,3 +3361,40 @@ int wpa_auth_uses_sae(struct wpa_state_machine *sm)
                return 0;
        return wpa_key_mgmt_sae(sm->wpa_key_mgmt);
 }
+
+
+int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm)
+{
+       if (sm == NULL)
+               return 0;
+       return sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_SAE;
+}
+
+
+#ifdef CONFIG_P2P
+int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr)
+{
+       if (sm == NULL || WPA_GET_BE32(sm->ip_addr) == 0)
+               return -1;
+       os_memcpy(addr, sm->ip_addr, 4);
+       return 0;
+}
+#endif /* CONFIG_P2P */
+
+
+int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth,
+                                        struct radius_das_attrs *attr)
+{
+       return pmksa_cache_auth_radius_das_disconnect(wpa_auth->pmksa, attr);
+}
+
+
+void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth)
+{
+       struct wpa_group *group;
+
+       if (!wpa_auth)
+               return;
+       for (group = wpa_auth->group; group; group = group->next)
+               wpa_group_config_group_keys(wpa_auth, group);
+}
index ebfe86f..2788e65 100644 (file)
@@ -42,6 +42,7 @@ struct ft_rrb_frame {
 #define FT_R0KH_R1KH_PULL_DATA_LEN 44
 #define FT_R0KH_R1KH_RESP_DATA_LEN 76
 #define FT_R0KH_R1KH_PUSH_DATA_LEN 88
+#define FT_R0KH_R1KH_PULL_NONCE_LEN 16
 
 struct ft_r0kh_r1kh_pull_frame {
        u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */
@@ -49,7 +50,7 @@ struct ft_r0kh_r1kh_pull_frame {
        le16 data_length; /* little endian length of data (44) */
        u8 ap_address[ETH_ALEN];
 
-       u8 nonce[16];
+       u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
        u8 pmk_r0_name[WPA_PMK_NAME_LEN];
        u8 r1kh_id[FT_R1KH_ID_LEN];
        u8 s1kh_id[ETH_ALEN];
@@ -63,7 +64,7 @@ struct ft_r0kh_r1kh_resp_frame {
        le16 data_length; /* little endian length of data (76) */
        u8 ap_address[ETH_ALEN];
 
-       u8 nonce[16]; /* copied from pull */
+       u8 nonce[FT_R0KH_R1KH_PULL_NONCE_LEN]; /* copied from pull */
        u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */
        u8 s1kh_id[ETH_ALEN]; /* copied from pull */
        u8 pmk_r1[PMK_LEN];
@@ -142,6 +143,7 @@ struct wpa_auth_config {
        int tx_status;
 #ifdef CONFIG_IEEE80211W
        enum mfp_options ieee80211w;
+       int group_mgmt_cipher;
 #endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_IEEE80211R
 #define SSID_LEN 32
@@ -163,6 +165,12 @@ struct wpa_auth_config {
 #ifdef CONFIG_TESTING_OPTIONS
        double corrupt_gtk_rekey_mic_probability;
 #endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_P2P
+       u8 ip_addr_go[4];
+       u8 ip_addr_mask[4];
+       u8 ip_addr_start[4];
+       u8 ip_addr_end[4];
+#endif /* CONFIG_P2P */
 };
 
 typedef enum {
@@ -184,7 +192,8 @@ struct wpa_auth_callbacks {
        void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var,
                          int value);
        int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var);
-       const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *prev_psk);
+       const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr,
+                             const u8 *prev_psk);
        int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len);
        int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg,
                       const u8 *addr, int idx, u8 *key, size_t key_len);
@@ -202,8 +211,11 @@ struct wpa_auth_callbacks {
        int (*send_ft_action)(void *ctx, const u8 *dst,
                              const u8 *data, size_t data_len);
        int (*add_tspec)(void *ctx, const u8 *sta_addr, u8 *tspec_ie,
-                         size_t tspec_ielen);
+                        size_t tspec_ielen);
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_MESH
+       int (*start_ampe)(void *ctx, const u8 *sta_addr);
+#endif /* CONFIG_MESH */
 };
 
 struct wpa_authenticator * wpa_init(const u8 *addr,
@@ -225,9 +237,13 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
                        struct wpa_state_machine *sm,
                        const u8 *wpa_ie, size_t wpa_ie_len,
                        const u8 *mdie, size_t mdie_len);
+int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
+                     struct wpa_state_machine *sm,
+                     const u8 *osen_ie, size_t osen_ie_len);
 int wpa_auth_uses_mfp(struct wpa_state_machine *sm);
 struct wpa_state_machine *
-wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr);
+wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
+                 const u8 *p2p_dev_addr);
 int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth,
                            struct wpa_state_machine *sm);
 void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm);
@@ -263,6 +279,8 @@ int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth,
                               const u8 *pmk, size_t len, const u8 *sta_addr,
                               int session_timeout,
                               struct eapol_state_machine *eapol);
+int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
+                          const u8 *pmk);
 void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
                           const u8 *sta_addr);
 int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id);
@@ -293,5 +311,13 @@ int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos);
 int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos);
 
 int wpa_auth_uses_sae(struct wpa_state_machine *sm);
+int wpa_auth_uses_ft_sae(struct wpa_state_machine *sm);
+
+int wpa_auth_get_ip_addr(struct wpa_state_machine *sm, u8 *addr);
+
+struct radius_das_attrs;
+int wpa_auth_radius_das_disconnect_pmksa(struct wpa_authenticator *wpa_auth,
+                                        struct radius_das_attrs *attr);
+void wpa_auth_reconfig_group_keys(struct wpa_authenticator *wpa_auth);
 
 #endif /* WPA_AUTH_H */
index 1bb5d97..ef3249a 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd - IEEE 802.11r - Fast BSS Transition
- * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -9,6 +9,8 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/list.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "crypto/aes_wrap.h"
 
 #ifdef CONFIG_IEEE80211R
 
+static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
+                                    const u8 *current_ap, const u8 *sta_addr,
+                                    u16 status, const u8 *resp_ies,
+                                    size_t resp_ies_len);
+
+
 static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst,
                           const u8 *data, size_t data_len)
 {
@@ -57,7 +65,7 @@ static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth,
                            u8 *tspec_ie, size_t tspec_ielen)
 {
        if (wpa_auth->cb.add_tspec == NULL) {
-               wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized");
+               wpa_printf(MSG_DEBUG, "FT: add_tspec is not initialized");
                return -1;
        }
        return wpa_auth->cb.add_tspec(wpa_auth->cb.ctx, sta_addr, tspec_ie,
@@ -228,8 +236,8 @@ static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth,
        r0 = cache->pmk_r0;
        while (r0) {
                if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 &&
-                   os_memcmp(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN)
-                   == 0) {
+                   os_memcmp_const(r0->pmk_r0_name, pmk_r0_name,
+                                   WPA_PMK_NAME_LEN) == 0) {
                        os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN);
                        if (pairwise)
                                *pairwise = r0->pairwise;
@@ -278,8 +286,8 @@ static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
        r1 = cache->pmk_r1;
        while (r1) {
                if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 &&
-                   os_memcmp(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN)
-                   == 0) {
+                   os_memcmp_const(r1->pmk_r1_name, pmk_r1_name,
+                                   WPA_PMK_NAME_LEN) == 0) {
                        os_memcpy(pmk_r1, r1->pmk_r1, PMK_LEN);
                        if (pairwise)
                                *pairwise = r1->pairwise;
@@ -293,22 +301,26 @@ static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth,
 }
 
 
-static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth,
-                             const u8 *s1kh_id, const u8 *r0kh_id,
-                             size_t r0kh_id_len, const u8 *pmk_r0_name)
+static int wpa_ft_pull_pmk_r1(struct wpa_state_machine *sm,
+                             const u8 *ies, size_t ies_len,
+                             const u8 *pmk_r0_name)
 {
        struct ft_remote_r0kh *r0kh;
        struct ft_r0kh_r1kh_pull_frame frame, f;
 
-       r0kh = wpa_auth->conf.r0kh_list;
+       r0kh = sm->wpa_auth->conf.r0kh_list;
        while (r0kh) {
-               if (r0kh->id_len == r0kh_id_len &&
-                   os_memcmp(r0kh->id, r0kh_id, r0kh_id_len) == 0)
+               if (r0kh->id_len == sm->r0kh_id_len &&
+                   os_memcmp_const(r0kh->id, sm->r0kh_id, sm->r0kh_id_len) ==
+                   0)
                        break;
                r0kh = r0kh->next;
        }
-       if (r0kh == NULL)
+       if (r0kh == NULL) {
+               wpa_hexdump(MSG_DEBUG, "FT: Did not find R0KH-ID",
+                           sm->r0kh_id, sm->r0kh_id_len);
                return -1;
+       }
 
        wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH "
                   "address " MACSTR, MAC2STR(r0kh->addr));
@@ -317,31 +329,40 @@ static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth,
        frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
        frame.packet_type = FT_PACKET_R0KH_R1KH_PULL;
        frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN);
-       os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN);
+       os_memcpy(frame.ap_address, sm->wpa_auth->addr, ETH_ALEN);
 
        /* aes_wrap() does not support inplace encryption, so use a temporary
         * buffer for the data. */
-       if (random_get_bytes(f.nonce, sizeof(f.nonce))) {
+       if (random_get_bytes(f.nonce, FT_R0KH_R1KH_PULL_NONCE_LEN)) {
                wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
                           "nonce");
                return -1;
        }
+       os_memcpy(sm->ft_pending_pull_nonce, f.nonce,
+                 FT_R0KH_R1KH_PULL_NONCE_LEN);
        os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN);
-       os_memcpy(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
-       os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN);
+       os_memcpy(f.r1kh_id, sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN);
+       os_memcpy(f.s1kh_id, sm->addr, ETH_ALEN);
+       os_memset(f.pad, 0, sizeof(f.pad));
 
-       if (aes_wrap(r0kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
+       if (aes_wrap(r0kh->key, sizeof(r0kh->key),
+                    (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
                     f.nonce, frame.nonce) < 0)
                return -1;
 
-       wpa_ft_rrb_send(wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame));
+       wpabuf_free(sm->ft_pending_req_ies);
+       sm->ft_pending_req_ies = wpabuf_alloc_copy(ies, ies_len);
+       if (sm->ft_pending_req_ies == NULL)
+               return -1;
+
+       wpa_ft_rrb_send(sm->wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame));
 
        return 0;
 }
 
 
 int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
-                          struct wpa_ptk *ptk, size_t ptk_len)
+                          struct wpa_ptk *ptk)
 {
        u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN];
        u8 pmk_r1[PMK_LEN];
@@ -353,7 +374,6 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
        const u8 *ssid = sm->wpa_auth->conf.ssid;
        size_t ssid_len = sm->wpa_auth->conf.ssid_len;
 
-
        if (sm->xxkey_len == 0) {
                wpa_printf(MSG_DEBUG, "FT: XXKey not available for key "
                           "derivation");
@@ -375,13 +395,9 @@ int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
        wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name,
                            sm->pairwise);
 
-       wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
-                         sm->wpa_auth->addr, sm->pmk_r1_name,
-                         (u8 *) ptk, ptk_len, ptk_name);
-       wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len);
-       wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
-
-       return 0;
+       return wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
+                                sm->wpa_auth->addr, sm->pmk_r1_name,
+                                ptk, ptk_name, sm->wpa_key_mgmt, sm->pairwise);
 }
 
 
@@ -440,7 +456,8 @@ static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len)
        WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03);
        subelem[4] = gsm->GTK_len;
        wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5);
-       if (aes_wrap(sm->PTK.kek, key_len / 8, key, subelem + 13)) {
+       if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, key_len / 8, key,
+                    subelem + 13)) {
                os_free(subelem);
                return NULL;
        }
@@ -472,7 +489,7 @@ static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len)
        wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos);
        pos += 6;
        *pos++ = WPA_IGTK_LEN;
-       if (aes_wrap(sm->PTK.kek, WPA_IGTK_LEN / 8,
+       if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, WPA_IGTK_LEN / 8,
                     gsm->IGTK[gsm->GN_igtk - 4], pos)) {
                os_free(subelem);
                return NULL;
@@ -569,8 +586,8 @@ static u8 * wpa_ft_process_rdie(struct wpa_state_machine *sm,
                        else {
                                /* TSPEC accepted; include updated TSPEC in
                                 * response */
-                               rdie->descr_count = 1;
-                               pos += sizeof(*tspec);
+                               rdie->descr_count = 1;
+                               pos += sizeof(*tspec);
                        }
                        return pos;
                }
@@ -632,8 +649,7 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
 
        conf = &sm->wpa_auth->conf;
 
-       if (sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
-           sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_PSK)
+       if (!wpa_key_mgmt_ft(sm->wpa_key_mgmt))
                return pos;
 
        end = pos + max_len;
@@ -725,7 +741,8 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
                ric_start = NULL;
 
        if (auth_alg == WLAN_AUTH_FT &&
-           wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 6,
+           wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr,
+                      sm->wpa_auth->addr, 6,
                       mdie, mdie_len, ftie, ftie_len,
                       rsnie, rsnie_len,
                       ric_start, ric_start ? pos - ric_start : 0,
@@ -769,7 +786,7 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm)
         * optimized by adding the STA entry earlier.
         */
        if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
-                            sm->PTK.tk1, klen))
+                            sm->PTK.tk, klen))
                return;
 
        /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
@@ -777,7 +794,7 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm)
 }
 
 
-static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm,
+static int wpa_ft_process_auth_req(struct wpa_state_machine *sm,
                                   const u8 *ies, size_t ies_len,
                                   u8 **resp_ies, size_t *resp_ies_len)
 {
@@ -787,7 +804,7 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm,
        u8 ptk_name[WPA_PMK_NAME_LEN];
        struct wpa_auth_config *conf;
        struct wpa_ft_ies parse;
-       size_t buflen, ptk_len;
+       size_t buflen;
        int ret;
        u8 *pos, *end;
        int pairwise;
@@ -848,19 +865,13 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm,
 
        if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1,
                    &pairwise) < 0) {
-               if (wpa_ft_pull_pmk_r1(sm->wpa_auth, sm->addr, sm->r0kh_id,
-                                      sm->r0kh_id_len, parse.rsn_pmkid) < 0) {
+               if (wpa_ft_pull_pmk_r1(sm, ies, ies_len, parse.rsn_pmkid) < 0) {
                        wpa_printf(MSG_DEBUG, "FT: Did not have matching "
                                   "PMK-R1 and unknown R0KH-ID");
                        return WLAN_STATUS_INVALID_PMKID;
                }
 
-               /*
-                * TODO: Should return "status pending" (and the caller should
-                * not send out response now). The real response will be sent
-                * once the response from R0KH is received.
-                */
-               return WLAN_STATUS_INVALID_PMKID;
+               return -1; /* Status pending */
        }
 
        wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN);
@@ -878,15 +889,14 @@ static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm,
        wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce",
                    sm->ANonce, WPA_NONCE_LEN);
 
-       ptk_len = pairwise == WPA_CIPHER_TKIP ? 64 : 48;
-       wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
-                         sm->wpa_auth->addr, pmk_r1_name,
-                         (u8 *) &sm->PTK, ptk_len, ptk_name);
-       wpa_hexdump_key(MSG_DEBUG, "FT: PTK",
-                       (u8 *) &sm->PTK, ptk_len);
-       wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
+       if (wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr,
+                             sm->wpa_auth->addr, pmk_r1_name,
+                             &sm->PTK, ptk_name, sm->wpa_key_mgmt,
+                             pairwise) < 0)
+               return WLAN_STATUS_UNSPECIFIED_FAILURE;
 
        sm->pairwise = pairwise;
+       sm->PTK_valid = TRUE;
        wpa_ft_install_ptk(sm);
 
        buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) +
@@ -940,6 +950,7 @@ void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
        u16 status;
        u8 *resp_ies;
        size_t resp_ies_len;
+       int res;
 
        if (sm == NULL) {
                wpa_printf(MSG_DEBUG, "FT: Received authentication frame, but "
@@ -950,8 +961,16 @@ void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid,
        wpa_printf(MSG_DEBUG, "FT: Received authentication frame: STA=" MACSTR
                   " BSSID=" MACSTR " transaction=%d",
                   MAC2STR(sm->addr), MAC2STR(bssid), auth_transaction);
-       status = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies,
-                                        &resp_ies_len);
+       sm->ft_pending_cb = cb;
+       sm->ft_pending_cb_ctx = ctx;
+       sm->ft_pending_auth_transaction = auth_transaction;
+       res = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies,
+                                     &resp_ies_len);
+       if (res < 0) {
+               wpa_printf(MSG_DEBUG, "FT: Callback postponed until response is available");
+               return;
+       }
+       status = res;
 
        wpa_printf(MSG_DEBUG, "FT: FT authentication response: dst=" MACSTR
                   " auth_transaction=%d status=%d",
@@ -969,7 +988,8 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
        struct wpa_ft_ies parse;
        struct rsn_mdie *mdie;
        struct rsn_ftie *ftie;
-       u8 mic[16];
+       u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+       size_t mic_len = 16;
        unsigned int count;
 
        if (sm == NULL)
@@ -992,8 +1012,8 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
                return WLAN_STATUS_INVALID_PMKID;
        }
 
-       if (os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0)
-       {
+       if (os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)
+           != 0) {
                wpa_printf(MSG_DEBUG, "FT: PMKID in Reassoc Req did not match "
                           "with the PMKR1Name derived from auth request");
                return WLAN_STATUS_INVALID_PMKID;
@@ -1039,7 +1059,8 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
        }
 
        if (parse.r0kh_id_len != sm->r0kh_id_len ||
-           os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) {
+           os_memcmp_const(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0)
+       {
                wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with "
                           "the current R0KH-ID");
                wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
@@ -1054,8 +1075,8 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
                return -1;
        }
 
-       if (os_memcmp(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder,
-                     FT_R1KH_ID_LEN) != 0) {
+       if (os_memcmp_const(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder,
+                           FT_R1KH_ID_LEN) != 0) {
                wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in "
                           "ReassocReq");
                wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID in FTIE",
@@ -1066,7 +1087,8 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
        }
 
        if (parse.rsn_pmkid == NULL ||
-           os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) {
+           os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN))
+       {
                wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
                           "RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
                return -1;
@@ -1082,7 +1104,8 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
                return -1;
        }
 
-       if (wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 5,
+       if (wpa_ft_mic(sm->PTK.kck, sm->PTK.kck_len, sm->addr,
+                      sm->wpa_auth->addr, 5,
                       parse.mdie - 2, parse.mdie_len + 2,
                       parse.ftie - 2, parse.ftie_len + 2,
                       parse.rsn - 2, parse.rsn_len + 2,
@@ -1092,12 +1115,13 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
                return WLAN_STATUS_UNSPECIFIED_FAILURE;
        }
 
-       if (os_memcmp(mic, ftie->mic, 16) != 0) {
+       if (os_memcmp_const(mic, ftie->mic, mic_len) != 0) {
                wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
                wpa_printf(MSG_DEBUG, "FT: addr=" MACSTR " auth_addr=" MACSTR,
                           MAC2STR(sm->addr), MAC2STR(sm->wpa_auth->addr));
-               wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16);
-               wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16);
+               wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC",
+                           ftie->mic, mic_len);
+               wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, mic_len);
                wpa_hexdump(MSG_MSGDUMP, "FT: MDIE",
                            parse.mdie - 2, parse.mdie_len + 2);
                wpa_hexdump(MSG_MSGDUMP, "FT: FTIE",
@@ -1182,15 +1206,27 @@ int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len)
 }
 
 
+static void wpa_ft_rrb_rx_request_cb(void *ctx, const u8 *dst, const u8 *bssid,
+                                    u16 auth_transaction, u16 resp,
+                                    const u8 *ies, size_t ies_len)
+{
+       struct wpa_state_machine *sm = ctx;
+       wpa_printf(MSG_DEBUG, "FT: Over-the-DS RX request cb for " MACSTR,
+                  MAC2STR(sm->addr));
+       wpa_ft_send_rrb_auth_resp(sm, sm->ft_pending_current_ap, sm->addr,
+                                 WLAN_STATUS_SUCCESS, ies, ies_len);
+}
+
+
 static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth,
                                 const u8 *current_ap, const u8 *sta_addr,
                                 const u8 *body, size_t len)
 {
        struct wpa_state_machine *sm;
        u16 status;
-       u8 *resp_ies, *pos;
-       size_t resp_ies_len, rlen;
-       struct ft_rrb_frame *frame;
+       u8 *resp_ies;
+       size_t resp_ies_len;
+       int res;
 
        sm = wpa_ft_add_sta(wpa_auth, sta_addr);
        if (sm == NULL) {
@@ -1201,8 +1237,33 @@ static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth,
 
        wpa_hexdump(MSG_MSGDUMP, "FT: RRB Request Frame body", body, len);
 
-       status = wpa_ft_process_auth_req(sm, body, len, &resp_ies,
-                                        &resp_ies_len);
+       sm->ft_pending_cb = wpa_ft_rrb_rx_request_cb;
+       sm->ft_pending_cb_ctx = sm;
+       os_memcpy(sm->ft_pending_current_ap, current_ap, ETH_ALEN);
+       res = wpa_ft_process_auth_req(sm, body, len, &resp_ies,
+                                     &resp_ies_len);
+       if (res < 0) {
+               wpa_printf(MSG_DEBUG, "FT: No immediate response available - wait for pull response");
+               return 0;
+       }
+       status = res;
+
+       res = wpa_ft_send_rrb_auth_resp(sm, current_ap, sta_addr, status,
+                                       resp_ies, resp_ies_len);
+       os_free(resp_ies);
+       return res;
+}
+
+
+static int wpa_ft_send_rrb_auth_resp(struct wpa_state_machine *sm,
+                                    const u8 *current_ap, const u8 *sta_addr,
+                                    u16 status, const u8 *resp_ies,
+                                    size_t resp_ies_len)
+{
+       struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+       size_t rlen;
+       struct ft_rrb_frame *frame;
+       u8 *pos;
 
        wpa_printf(MSG_DEBUG, "FT: RRB authentication response: STA=" MACSTR
                   " CurrentAP=" MACSTR " status=%d",
@@ -1218,10 +1279,8 @@ static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth,
        rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len;
 
        frame = os_malloc(sizeof(*frame) + rlen);
-       if (frame == NULL) {
-               os_free(resp_ies);
+       if (frame == NULL)
                return -1;
-       }
        frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
        frame->packet_type = FT_PACKET_RESPONSE;
        frame->action_length = host_to_le16(rlen);
@@ -1235,10 +1294,8 @@ static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth,
        pos += ETH_ALEN;
        WPA_PUT_LE16(pos, status);
        pos += 2;
-       if (resp_ies) {
+       if (resp_ies)
                os_memcpy(pos, resp_ies, resp_ies_len);
-               os_free(resp_ies);
-       }
 
        wpa_ft_rrb_send(wpa_auth, current_ap, (u8 *) frame,
                        sizeof(*frame) + rlen);
@@ -1252,7 +1309,9 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
                              const u8 *src_addr,
                              const u8 *data, size_t data_len)
 {
-       struct ft_r0kh_r1kh_pull_frame *frame, f;
+       struct ft_r0kh_r1kh_pull_frame f;
+       const u8 *crypt;
+       u8 *plain;
        struct ft_remote_r1kh *r1kh;
        struct ft_r0kh_r1kh_resp_frame resp, r;
        u8 pmk_r0[PMK_LEN];
@@ -1260,7 +1319,7 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
 
        wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull");
 
-       if (data_len < sizeof(*frame))
+       if (data_len < sizeof(f))
                return -1;
 
        r1kh = wpa_auth->conf.r1kh_list;
@@ -1276,11 +1335,14 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
                return -1;
        }
 
-       frame = (struct ft_r0kh_r1kh_pull_frame *) data;
+       crypt = data + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
+       os_memset(&f, 0, sizeof(f));
+       plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_pull_frame, nonce);
        /* aes_unwrap() does not support inplace decryption, so use a temporary
         * buffer for the data. */
-       if (aes_unwrap(r1kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
-                      frame->nonce, f.nonce) < 0) {
+       if (aes_unwrap(r1kh->key, sizeof(r1kh->key),
+                      (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8,
+                      crypt, plain) < 0) {
                wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
                           "request from " MACSTR, MAC2STR(src_addr));
                return -1;
@@ -1290,7 +1352,7 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
                    f.nonce, sizeof(f.nonce));
        wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name",
                    f.pmk_r0_name, WPA_PMK_NAME_LEN);
-       wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID="
+       wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID="
                   MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id));
 
        os_memset(&resp, 0, sizeof(resp));
@@ -1317,8 +1379,10 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
        wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name,
                    WPA_PMK_NAME_LEN);
        r.pairwise = host_to_le16(pairwise);
+       os_memset(r.pad, 0, sizeof(r.pad));
 
-       if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
+       if (aes_wrap(r1kh->key, sizeof(r1kh->key),
+                    (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
                     r.nonce, resp.nonce) < 0) {
                os_memset(pmk_r0, 0, PMK_LEN);
                return -1;
@@ -1332,17 +1396,64 @@ static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth,
 }
 
 
+static void ft_pull_resp_cb_finish(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_state_machine *sm = eloop_ctx;
+       int res;
+       u8 *resp_ies;
+       size_t resp_ies_len;
+       u16 status;
+
+       res = wpa_ft_process_auth_req(sm, wpabuf_head(sm->ft_pending_req_ies),
+                                     wpabuf_len(sm->ft_pending_req_ies),
+                                     &resp_ies, &resp_ies_len);
+       wpabuf_free(sm->ft_pending_req_ies);
+       sm->ft_pending_req_ies = NULL;
+       if (res < 0)
+               res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+       status = res;
+       wpa_printf(MSG_DEBUG, "FT: Postponed auth callback result for " MACSTR
+                  " - status %u", MAC2STR(sm->addr), status);
+
+       sm->ft_pending_cb(sm->ft_pending_cb_ctx, sm->addr, sm->wpa_auth->addr,
+                         sm->ft_pending_auth_transaction + 1, status,
+                         resp_ies, resp_ies_len);
+       os_free(resp_ies);
+}
+
+
+static int ft_pull_resp_cb(struct wpa_state_machine *sm, void *ctx)
+{
+       struct ft_r0kh_r1kh_resp_frame *frame = ctx;
+
+       if (os_memcmp(frame->s1kh_id, sm->addr, ETH_ALEN) != 0)
+               return 0;
+       if (os_memcmp(frame->nonce, sm->ft_pending_pull_nonce,
+                     FT_R0KH_R1KH_PULL_NONCE_LEN) != 0)
+               return 0;
+       if (sm->ft_pending_cb == NULL || sm->ft_pending_req_ies == NULL)
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "FT: Response to a pending pull request for "
+                  MACSTR " - process from timeout", MAC2STR(sm->addr));
+       eloop_register_timeout(0, 0, ft_pull_resp_cb_finish, sm, NULL);
+       return 1;
+}
+
+
 static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
                              const u8 *src_addr,
                              const u8 *data, size_t data_len)
 {
-       struct ft_r0kh_r1kh_resp_frame *frame, f;
+       struct ft_r0kh_r1kh_resp_frame f;
+       const u8 *crypt;
+       u8 *plain;
        struct ft_remote_r0kh *r0kh;
-       int pairwise;
+       int pairwise, res;
 
        wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response");
 
-       if (data_len < sizeof(*frame))
+       if (data_len < sizeof(f))
                return -1;
 
        r0kh = wpa_auth->conf.r0kh_list;
@@ -1358,31 +1469,30 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
                return -1;
        }
 
-       frame = (struct ft_r0kh_r1kh_resp_frame *) data;
+       crypt = data + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
+       os_memset(&f, 0, sizeof(f));
+       plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_resp_frame, nonce);
        /* aes_unwrap() does not support inplace decryption, so use a temporary
         * buffer for the data. */
-       if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
-                      frame->nonce, f.nonce) < 0) {
+       if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
+                      (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8,
+                      crypt, plain) < 0) {
                wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull "
                           "response from " MACSTR, MAC2STR(src_addr));
                return -1;
        }
 
-       if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN)
-           != 0) {
+       if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder,
+                           FT_R1KH_ID_LEN) != 0) {
                wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a "
                           "matching R1KH-ID");
                return -1;
        }
 
-       /* TODO: verify that <nonce,s1kh_id> matches with a pending request
-        * and call this requests callback function to finish request
-        * processing */
-
        pairwise = le_to_host16(f.pairwise);
        wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce",
                    f.nonce, sizeof(f.nonce));
-       wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID="
+       wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR " S1KH-ID="
                   MACSTR " pairwise=0x%x",
                   MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise);
        wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1",
@@ -1390,11 +1500,13 @@ static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth,
        wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name",
                        f.pmk_r1_name, WPA_PMK_NAME_LEN);
 
-       wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
-                           pairwise);
+       res = wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name,
+                                 pairwise);
+       wpa_printf(MSG_DEBUG, "FT: Look for pending pull request");
+       wpa_auth_for_each_sta(wpa_auth, ft_pull_resp_cb, &f);
        os_memset(f.pmk_r1, 0, PMK_LEN);
 
-       return 0;
+       return res ? 0 : -1;
 }
 
 
@@ -1402,7 +1514,9 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
                              const u8 *src_addr,
                              const u8 *data, size_t data_len)
 {
-       struct ft_r0kh_r1kh_push_frame *frame, f;
+       struct ft_r0kh_r1kh_push_frame f;
+       const u8 *crypt;
+       u8 *plain;
        struct ft_remote_r0kh *r0kh;
        struct os_time now;
        os_time_t tsend;
@@ -1410,7 +1524,7 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
 
        wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push");
 
-       if (data_len < sizeof(*frame))
+       if (data_len < sizeof(f))
                return -1;
 
        r0kh = wpa_auth->conf.r0kh_list;
@@ -1426,11 +1540,15 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
                return -1;
        }
 
-       frame = (struct ft_r0kh_r1kh_push_frame *) data;
+       crypt = data + offsetof(struct ft_r0kh_r1kh_push_frame, timestamp);
+       os_memset(&f, 0, sizeof(f));
+       plain = ((u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
+                                      timestamp);
        /* aes_unwrap() does not support inplace decryption, so use a temporary
         * buffer for the data. */
-       if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
-                      frame->timestamp, f.timestamp) < 0) {
+       if (aes_unwrap(r0kh->key, sizeof(r0kh->key),
+                      (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
+                      crypt, plain) < 0) {
                wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from "
                           MACSTR, MAC2STR(src_addr));
                return -1;
@@ -1446,8 +1564,8 @@ static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth,
                return -1;
        }
 
-       if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN)
-           != 0) {
+       if (os_memcmp_const(f.r1kh_id, wpa_auth->conf.r1_key_holder,
+                           FT_R1KH_ID_LEN) != 0) {
                wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching "
                           "R1KH-ID (received " MACSTR " own " MACSTR ")",
                           MAC2STR(f.r1kh_id),
@@ -1588,6 +1706,11 @@ int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
                return -1;
        }
 
+       if (end > pos) {
+               wpa_hexdump(MSG_DEBUG, "FT: Ignore extra data in end",
+                           pos, end - pos);
+       }
+
        return 0;
 }
 
@@ -1599,6 +1722,8 @@ static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
 {
        struct ft_r0kh_r1kh_push_frame frame, f;
        struct os_time now;
+       const u8 *plain;
+       u8 *crypt;
 
        os_memset(&frame, 0, sizeof(frame));
        frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB;
@@ -1620,8 +1745,14 @@ static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth,
        os_get_time(&now);
        WPA_PUT_LE32(f.timestamp, now.sec);
        f.pairwise = host_to_le16(pairwise);
-       if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
-                    f.timestamp, frame.timestamp) < 0)
+       os_memset(f.pad, 0, sizeof(f.pad));
+       plain = ((const u8 *) &f) + offsetof(struct ft_r0kh_r1kh_push_frame,
+                                            timestamp);
+       crypt = ((u8 *) &frame) + offsetof(struct ft_r0kh_r1kh_push_frame,
+                                          timestamp);
+       if (aes_wrap(r1kh->key, sizeof(r1kh->key),
+                    (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8,
+                    plain, crypt) < 0)
                return;
 
        wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame));
index e2be1ea..7f83207 100644 (file)
@@ -15,7 +15,6 @@
 #include "eapol_auth/eapol_auth_sm_i.h"
 #include "eap_server/eap.h"
 #include "l2_packet/l2_packet.h"
-#include "drivers/driver.h"
 #include "hostapd.h"
 #include "ieee802_1x.h"
 #include "preauth_auth.h"
@@ -50,6 +49,7 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
        wconf->okc = conf->okc;
 #ifdef CONFIG_IEEE80211W
        wconf->ieee80211w = conf->ieee80211w;
+       wconf->group_mgmt_cipher = conf->group_mgmt_cipher;
 #endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_IEEE80211R
        wconf->ssid_len = conf->ssid.ssid_len;
@@ -74,11 +74,30 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_HS20
        wconf->disable_gtk = conf->disable_dgaf;
+       if (conf->osen) {
+               wconf->disable_gtk = 1;
+               wconf->wpa = WPA_PROTO_OSEN;
+               wconf->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
+               wconf->wpa_pairwise = 0;
+               wconf->wpa_group = WPA_CIPHER_CCMP;
+               wconf->rsn_pairwise = WPA_CIPHER_CCMP;
+               wconf->rsn_preauth = 0;
+               wconf->disable_pmksa_caching = 1;
+#ifdef CONFIG_IEEE80211W
+               wconf->ieee80211w = 1;
+#endif /* CONFIG_IEEE80211W */
+       }
 #endif /* CONFIG_HS20 */
 #ifdef CONFIG_TESTING_OPTIONS
        wconf->corrupt_gtk_rekey_mic_probability =
                iconf->corrupt_gtk_rekey_mic_probability;
 #endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_P2P
+       os_memcpy(wconf->ip_addr_go, conf->ip_addr_go, 4);
+       os_memcpy(wconf->ip_addr_mask, conf->ip_addr_mask, 4);
+       os_memcpy(wconf->ip_addr_start, conf->ip_addr_start, 4);
+       os_memcpy(wconf->ip_addr_end, conf->ip_addr_end, 4);
+#endif /* CONFIG_P2P */
 }
 
 
@@ -186,6 +205,7 @@ static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr,
 
 
 static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
+                                          const u8 *p2p_dev_addr,
                                           const u8 *prev_psk)
 {
        struct hostapd_data *hapd = ctx;
@@ -200,7 +220,7 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
        }
 #endif /* CONFIG_SAE */
 
-       psk = hostapd_get_psk(hapd->conf, addr, prev_psk);
+       psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk);
        /*
         * This is about to iterate over all psks, prev_psk gives the last
         * returned psk which should not be returned again.
@@ -229,12 +249,17 @@ static int hostapd_wpa_auth_get_msk(void *ctx, const u8 *addr, u8 *msk,
        struct sta_info *sta;
 
        sta = ap_get_sta(hapd, addr);
-       if (sta == NULL)
+       if (sta == NULL) {
+               wpa_printf(MSG_DEBUG, "AUTH_GET_MSK: Cannot find STA");
                return -1;
+       }
 
        key = ieee802_1x_get_key(sta->eapol_sm, &keylen);
-       if (key == NULL)
+       if (key == NULL) {
+               wpa_printf(MSG_DEBUG, "AUTH_GET_MSK: Key is null, eapol_sm: %p",
+                          sta->eapol_sm);
                return -1;
+       }
 
        if (keylen > *len)
                keylen = *len;
@@ -279,6 +304,21 @@ static int hostapd_wpa_auth_send_eapol(void *ctx, const u8 *addr,
        struct sta_info *sta;
        u32 flags = 0;
 
+#ifdef CONFIG_TESTING_OPTIONS
+       if (hapd->ext_eapol_frame_io) {
+               size_t hex_len = 2 * data_len + 1;
+               char *hex = os_malloc(hex_len);
+
+               if (hex == NULL)
+                       return -1;
+               wpa_snprintf_hex(hex, hex_len, data, data_len);
+               wpa_msg(hapd->msg_ctx, MSG_INFO, "EAPOL-TX " MACSTR " %s",
+                       MAC2STR(addr), hex);
+               os_free(hex);
+               return 0;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
        sta = ap_get_sta(hapd, addr);
        if (sta)
                flags = hostapd_sta_flags_to_drv(sta->flags);
@@ -384,6 +424,21 @@ static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto,
        struct l2_ethhdr *buf;
        int ret;
 
+#ifdef CONFIG_TESTING_OPTIONS
+       if (hapd->ext_eapol_frame_io && proto == ETH_P_EAPOL) {
+               size_t hex_len = 2 * data_len + 1;
+               char *hex = os_malloc(hex_len);
+
+               if (hex == NULL)
+                       return -1;
+               wpa_snprintf_hex(hex, hex_len, data, data_len);
+               wpa_msg(hapd->msg_ctx, MSG_INFO, "EAPOL-TX " MACSTR " %s",
+                       MAC2STR(dst), hex);
+               os_free(hex);
+               return 0;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
 #ifdef CONFIG_IEEE80211R
        if (proto == ETH_P_RRB && hapd->iface->interfaces &&
            hapd->iface->interfaces->for_each_interface) {
@@ -471,7 +526,7 @@ hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr)
                return sta->wpa_sm;
        }
 
-       sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr);
+       sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr, NULL);
        if (sta->wpa_sm == NULL) {
                ap_free_sta(hapd, sta);
                return NULL;
@@ -617,5 +672,6 @@ void hostapd_deinit_wpa(struct hostapd_data *hapd)
 
 #ifdef CONFIG_IEEE80211R
        l2_packet_deinit(hapd->l2);
+       hapd->l2 = NULL;
 #endif /* CONFIG_IEEE80211R */
 }
index 97489d3..7b2cd3e 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -26,6 +26,7 @@ struct wpa_state_machine {
        struct wpa_group *group;
 
        u8 addr[ETH_ALEN];
+       u8 p2p_dev_addr[ETH_ALEN];
 
        enum {
                WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED,
@@ -57,6 +58,8 @@ struct wpa_state_machine {
        Boolean GUpdateStationKeys;
        u8 ANonce[WPA_NONCE_LEN];
        u8 SNonce[WPA_NONCE_LEN];
+       u8 alt_SNonce[WPA_NONCE_LEN];
+       u8 alt_replay_counter[WPA_REPLAY_COUNTER_LEN];
        u8 PMK[PMK_LEN];
        struct wpa_ptk PTK;
        Boolean PTK_valid;
@@ -83,6 +86,7 @@ struct wpa_state_machine {
        unsigned int mgmt_frame_prot:1;
        unsigned int rx_eapol_key_secure:1;
        unsigned int update_snonce:1;
+       unsigned int alt_snonce_valid:1;
 #ifdef CONFIG_IEEE80211R
        unsigned int ft_completed:1;
        unsigned int pmk_r1_name_valid:1;
@@ -117,9 +121,22 @@ struct wpa_state_machine {
        u8 sup_pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name from EAPOL-Key
                                               * message 2/4 */
        u8 *assoc_resp_ftie;
+
+       void (*ft_pending_cb)(void *ctx, const u8 *dst, const u8 *bssid,
+                             u16 auth_transaction, u16 status,
+                             const u8 *ies, size_t ies_len);
+       void *ft_pending_cb_ctx;
+       struct wpabuf *ft_pending_req_ies;
+       u8 ft_pending_pull_nonce[FT_R0KH_R1KH_PULL_NONCE_LEN];
+       u8 ft_pending_auth_transaction;
+       u8 ft_pending_current_ap[ETH_ALEN];
 #endif /* CONFIG_IEEE80211R */
 
        int pending_1_of_4_timeout;
+
+#ifdef CONFIG_P2P
+       u8 ip_addr[4];
+#endif /* CONFIG_P2P */
 };
 
 
@@ -138,7 +155,8 @@ struct wpa_group {
 
        enum {
                WPA_GROUP_GTK_INIT = 0,
-               WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE
+               WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE,
+               WPA_GROUP_FATAL_FAILURE
        } wpa_group_state;
 
        u8 GMK[WPA_GMK_LEN];
@@ -148,7 +166,7 @@ struct wpa_group {
        Boolean first_sta_seen;
        Boolean reject_4way_hs_for_entropy;
 #ifdef CONFIG_IEEE80211W
-       u8 IGTK[2][WPA_IGTK_LEN];
+       u8 IGTK[2][WPA_IGTK_MAX_LEN];
        int GN_igtk, GM_igtk;
 #endif /* CONFIG_IEEE80211W */
 };
@@ -183,6 +201,10 @@ struct wpa_authenticator {
 
        struct rsn_pmksa_cache *pmksa;
        struct wpa_ft_pmk_cache *ft_pmk_cache;
+
+#ifdef CONFIG_P2P
+       struct bitfield *ip_pool;
+#endif /* CONFIG_P2P */
 };
 
 
@@ -208,11 +230,14 @@ int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth,
 int wpa_stsl_remove(struct wpa_authenticator *wpa_auth,
                    struct wpa_stsl_negotiation *neg);
 void wpa_smk_error(struct wpa_authenticator *wpa_auth,
-                  struct wpa_state_machine *sm, struct wpa_eapol_key *key);
+                  struct wpa_state_machine *sm,
+                  const u8 *key_data, size_t key_data_len);
 void wpa_smk_m1(struct wpa_authenticator *wpa_auth,
-               struct wpa_state_machine *sm, struct wpa_eapol_key *key);
+               struct wpa_state_machine *sm, struct wpa_eapol_key *key,
+               const u8 *key_data, size_t key_data_len);
 void wpa_smk_m3(struct wpa_authenticator *wpa_auth,
-               struct wpa_state_machine *sm, struct wpa_eapol_key *key);
+               struct wpa_state_machine *sm, struct wpa_eapol_key *key,
+               const u8 *key_data, size_t key_data_len);
 #endif /* CONFIG_PEERKEY */
 
 #ifdef CONFIG_IEEE80211R
@@ -223,7 +248,7 @@ int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id,
                   u8 *buf, size_t len, const u8 *subelem,
                   size_t subelem_len);
 int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
-                          struct wpa_ptk *ptk, size_t ptk_len);
+                          struct wpa_ptk *ptk);
 struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void);
 void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache);
 void wpa_ft_install_ptk(struct wpa_state_machine *sm);
index cdfcca1..f287297 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd - WPA/RSN IE and KDE definitions
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -200,6 +200,16 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
                num_suites++;
        }
 #endif /* CONFIG_SAE */
+       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+               RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B);
+               pos += RSN_SELECTOR_LEN;
+               num_suites++;
+       }
+       if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+               RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192);
+               pos += RSN_SELECTOR_LEN;
+               num_suites++;
+       }
 
 #ifdef CONFIG_RSN_TESTING
        if (rsn_testing) {
@@ -261,7 +271,25 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
                }
 
                /* Management Group Cipher Suite */
-               RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+               switch (conf->group_mgmt_cipher) {
+               case WPA_CIPHER_AES_128_CMAC:
+                       RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+                       break;
+               case WPA_CIPHER_BIP_GMAC_128:
+                       RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_128);
+                       break;
+               case WPA_CIPHER_BIP_GMAC_256:
+                       RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_GMAC_256);
+                       break;
+               case WPA_CIPHER_BIP_CMAC_256:
+                       RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_BIP_CMAC_256);
+                       break;
+               default:
+                       wpa_printf(MSG_DEBUG,
+                                  "Invalid group management cipher (0x%x)",
+                                  conf->group_mgmt_cipher);
+                       return -1;
+               }
                pos += RSN_SELECTOR_LEN;
        }
 #endif /* CONFIG_IEEE80211W */
@@ -295,6 +323,55 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
 }
 
 
+static u8 * wpa_write_osen(struct wpa_auth_config *conf, u8 *eid)
+{
+       u8 *len;
+       u16 capab;
+
+       *eid++ = WLAN_EID_VENDOR_SPECIFIC;
+       len = eid++; /* to be filled */
+       WPA_PUT_BE24(eid, OUI_WFA);
+       eid += 3;
+       *eid++ = HS20_OSEN_OUI_TYPE;
+
+       /* Group Data Cipher Suite */
+       RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+       eid += RSN_SELECTOR_LEN;
+
+       /* Pairwise Cipher Suite Count and List */
+       WPA_PUT_LE16(eid, 1);
+       eid += 2;
+       RSN_SELECTOR_PUT(eid, RSN_CIPHER_SUITE_CCMP);
+       eid += RSN_SELECTOR_LEN;
+
+       /* AKM Suite Count and List */
+       WPA_PUT_LE16(eid, 1);
+       eid += 2;
+       RSN_SELECTOR_PUT(eid, RSN_AUTH_KEY_MGMT_OSEN);
+       eid += RSN_SELECTOR_LEN;
+
+       /* RSN Capabilities */
+       capab = 0;
+       if (conf->wmm_enabled) {
+               /* 4 PTKSA replay counters when using WMM */
+               capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2);
+       }
+#ifdef CONFIG_IEEE80211W
+       if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) {
+               capab |= WPA_CAPABILITY_MFPC;
+               if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED)
+                       capab |= WPA_CAPABILITY_MFPR;
+       }
+#endif /* CONFIG_IEEE80211W */
+       WPA_PUT_LE16(eid, capab);
+       eid += 2;
+
+       *len = eid - len - 1;
+
+       return eid;
+}
+
+
 int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
 {
        u8 *pos, buf[128];
@@ -302,6 +379,9 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
 
        pos = buf;
 
+       if (wpa_auth->conf.wpa == WPA_PROTO_OSEN) {
+               pos = wpa_write_osen(&wpa_auth->conf, pos);
+       }
        if (wpa_auth->conf.wpa & WPA_PROTO_RSN) {
                res = wpa_write_rsn_ie(&wpa_auth->conf,
                                       pos, buf + sizeof(buf) - pos, NULL);
@@ -407,6 +487,10 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
                selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
                if (0) {
                }
+               else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+                       selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
+               else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+                       selector = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
 #ifdef CONFIG_IEEE80211R
                else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
                        selector = RSN_AUTH_KEY_MGMT_FT_802_1X;
@@ -485,6 +569,10 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
        }
        if (0) {
        }
+       else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+               sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+       else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+               sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B;
 #ifdef CONFIG_IEEE80211R
        else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
                sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
@@ -534,7 +622,8 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
                        return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
                }
 
-               if (data.mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) {
+               if (data.mgmt_group_cipher != wpa_auth->conf.group_mgmt_cipher)
+               {
                        wpa_printf(MSG_DEBUG, "Unsupported management group "
                                   "cipher %d", data.mgmt_group_cipher);
                        return WPA_INVALID_MGMT_GROUP_CIPHER;
@@ -604,7 +693,7 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
                        break;
                }
        }
-       if (sm->pmksa) {
+       if (sm->pmksa && pmkid) {
                wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
                                 "PMKID found from PMKSA cache "
                                 "eap_type=%d vlan_id=%d",
@@ -626,6 +715,36 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
 }
 
 
+#ifdef CONFIG_HS20
+int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
+                     struct wpa_state_machine *sm,
+                     const u8 *osen_ie, size_t osen_ie_len)
+{
+       if (wpa_auth == NULL || sm == NULL)
+               return -1;
+
+       /* TODO: parse OSEN element */
+       sm->wpa_key_mgmt = WPA_KEY_MGMT_OSEN;
+       sm->mgmt_frame_prot = 1;
+       sm->pairwise = WPA_CIPHER_CCMP;
+       sm->wpa = WPA_VERSION_WPA2;
+
+       if (sm->wpa_ie == NULL || sm->wpa_ie_len < osen_ie_len) {
+               os_free(sm->wpa_ie);
+               sm->wpa_ie = os_malloc(osen_ie_len);
+               if (sm->wpa_ie == NULL)
+                       return -1;
+       }
+
+       os_memcpy(sm->wpa_ie, osen_ie, osen_ie_len);
+       sm->wpa_ie_len = osen_ie_len;
+
+       return 0;
+}
+
+#endif /* CONFIG_HS20 */
+
+
 /**
  * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs
  * @pos: Pointer to the IE header
@@ -648,6 +767,12 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end,
                return 0;
        }
 
+       if (pos[1] >= 4 && WPA_GET_BE32(pos + 2) == OSEN_IE_VENDOR_TYPE) {
+               ie->osen = pos;
+               ie->osen_len = pos[1] + 2;
+               return 0;
+       }
+
        if (pos + 1 + RSN_SELECTOR_LEN < end &&
            pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN &&
            RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) {
@@ -708,6 +833,25 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end,
        }
 #endif /* CONFIG_IEEE80211W */
 
+#ifdef CONFIG_P2P
+       if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
+           RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) {
+               ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN;
+               wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key",
+                           ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN);
+               return 0;
+       }
+
+       if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 &&
+           RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) {
+               ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN;
+               wpa_hexdump(MSG_DEBUG,
+                           "WPA: IP Address Allocation in EAPOL-Key",
+                           ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN);
+               return 0;
+       }
+#endif /* CONFIG_P2P */
+
        return 0;
 }
 
index 4999139..d2067ba 100644 (file)
@@ -39,6 +39,13 @@ struct wpa_eapol_ie_parse {
        const u8 *ftie;
        size_t ftie_len;
 #endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_P2P
+       const u8 *ip_addr_req;
+       const u8 *ip_addr_alloc;
+#endif /* CONFIG_P2P */
+
+       const u8 *osen;
+       size_t osen_len;
 };
 
 int wpa_parse_kde_ies(const u8 *buf, size_t len,
index 69b34fe..b0e8b0b 100644 (file)
@@ -40,6 +40,7 @@ static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, const u8 *da,
                                    const u8 *ie, size_t ie_len,
                                    int ssi_signal);
 static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
+static void hostapd_wps_nfc_clear(struct wps_context *wps);
 
 
 struct wps_for_each_data {
@@ -91,15 +92,24 @@ static int hostapd_wps_for_each(struct hostapd_data *hapd,
 }
 
 
-static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk,
+static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr,
+                                 const u8 *p2p_dev_addr, const u8 *psk,
                                  size_t psk_len)
 {
        struct hostapd_data *hapd = ctx;
        struct hostapd_wpa_psk *p;
        struct hostapd_ssid *ssid = &hapd->conf->ssid;
 
-       wpa_printf(MSG_DEBUG, "Received new WPA/WPA2-PSK from WPS for STA "
-                  MACSTR, MAC2STR(mac_addr));
+       if (is_zero_ether_addr(p2p_dev_addr)) {
+               wpa_printf(MSG_DEBUG,
+                          "Received new WPA/WPA2-PSK from WPS for STA " MACSTR,
+                          MAC2STR(mac_addr));
+       } else {
+               wpa_printf(MSG_DEBUG,
+                          "Received new WPA/WPA2-PSK from WPS for STA " MACSTR
+                          " P2P Device Addr " MACSTR,
+                          MAC2STR(mac_addr), MAC2STR(p2p_dev_addr));
+       }
        wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len);
 
        if (psk_len != PMK_LEN) {
@@ -113,8 +123,14 @@ static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk,
        if (p == NULL)
                return -1;
        os_memcpy(p->addr, mac_addr, ETH_ALEN);
+       os_memcpy(p->p2p_dev_addr, p2p_dev_addr, ETH_ALEN);
        os_memcpy(p->psk, psk, PMK_LEN);
 
+       if (hapd->new_psk_cb) {
+               hapd->new_psk_cb(hapd->new_psk_cb_ctx, mac_addr, p2p_dev_addr,
+                                psk, psk_len);
+       }
+
        p->next = ssid->wpa_psk;
        ssid->wpa_psk = p;
 
@@ -169,7 +185,7 @@ static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
                          dev->model_number, dev->serial_number,
                          wps_dev_type_bin2str(dev->pri_dev_type, devtype,
                                               sizeof(devtype)));
-       if (len > 0 && len < (int) sizeof(txt))
+       if (!os_snprintf_error(sizeof(txt), len))
                wpa_msg(hapd->msg_ctx, MSG_INFO, "%s", txt);
 
        if (hapd->conf->wps_pin_requests) {
@@ -272,6 +288,20 @@ static void wps_reload_config(void *eloop_data, void *user_ctx)
 }
 
 
+void hostapd_wps_eap_completed(struct hostapd_data *hapd)
+{
+       /*
+        * Reduce race condition of the station trying to reconnect immediately
+        * after AP reconfiguration through WPS by rescheduling the reload
+        * timeout to happen after EAP completion rather than the originally
+        * scheduled 100 ms after new configuration became known.
+        */
+       if (eloop_deplete_timeout(0, 0, wps_reload_config, hapd->iface, NULL) ==
+           1)
+               wpa_printf(MSG_DEBUG, "WPS: Reschedule immediate configuration reload");
+}
+
+
 static void hapd_new_ap_event(struct hostapd_data *hapd, const u8 *attr,
                              size_t attr_len)
 {
@@ -332,10 +362,9 @@ static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd,
                        if (bss->ssid.wpa_passphrase)
                                os_memcpy(bss->ssid.wpa_passphrase, cred->key,
                                          cred->key_len);
-                       os_free(bss->ssid.wpa_psk);
-                       bss->ssid.wpa_psk = NULL;
+                       hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
                } else if (cred->key_len == 64) {
-                       os_free(bss->ssid.wpa_psk);
+                       hostapd_config_clear_wpa_psk(&bss->ssid.wpa_psk);
                        bss->ssid.wpa_psk =
                                os_zalloc(sizeof(struct hostapd_wpa_psk));
                        if (bss->ssid.wpa_psk &&
@@ -348,40 +377,11 @@ static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd,
                }
                bss->auth_algs = 1;
        } else {
-               if ((cred->auth_type & WPS_AUTH_OPEN) &&
-                   (cred->auth_type & WPS_AUTH_SHARED))
-                       bss->auth_algs = 3;
-               else if (cred->auth_type & WPS_AUTH_SHARED)
-                       bss->auth_algs = 2;
-               else
-                       bss->auth_algs = 1;
-               if (cred->encr_type & WPS_ENCR_WEP && cred->key_idx > 0 &&
-                   cred->key_idx <= 4) {
-                       struct hostapd_wep_keys *wep = &bss->ssid.wep;
-                       int idx = cred->key_idx;
-                       if (idx)
-                               idx--;
-                       wep->idx = idx;
-                       if (cred->key_len == 10 || cred->key_len == 26) {
-                               os_free(wep->key[idx]);
-                               wep->key[idx] = os_malloc(cred->key_len / 2);
-                               if (wep->key[idx] == NULL ||
-                                   hexstr2bin((const char *) cred->key,
-                                              wep->key[idx],
-                                              cred->key_len / 2))
-                                       return -1;
-                               wep->len[idx] = cred->key_len / 2;
-                       } else {
-                               os_free(wep->key[idx]);
-                               wep->key[idx] = os_malloc(cred->key_len);
-                               if (wep->key[idx] == NULL)
-                                       return -1;
-                               os_memcpy(wep->key[idx], cred->key,
-                                         cred->key_len);
-                               wep->len[idx] = cred->key_len;
-                       }
-                       wep->keys_set = 1;
-               }
+               /*
+                * WPS 2.0 does not allow WEP to be configured, so no need to
+                * process that option here either.
+                */
+               bss->auth_algs = 1;
        }
 
        /* Schedule configuration reload after short period of time to allow
@@ -442,6 +442,8 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx)
        hapd->wps->ssid_len = cred->ssid_len;
        hapd->wps->encr_types = cred->encr_type;
        hapd->wps->auth_types = cred->auth_type;
+       hapd->wps->ap_encr_type = cred->encr_type;
+       hapd->wps->ap_auth_type = cred->auth_type;
        if (cred->key_len == 0) {
                os_free(hapd->wps->network_key);
                hapd->wps->network_key = NULL;
@@ -554,31 +556,11 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx)
 
                fprintf(nconf, "auth_algs=1\n");
        } else {
-               if ((cred->auth_type & WPS_AUTH_OPEN) &&
-                   (cred->auth_type & WPS_AUTH_SHARED))
-                       fprintf(nconf, "auth_algs=3\n");
-               else if (cred->auth_type & WPS_AUTH_SHARED)
-                       fprintf(nconf, "auth_algs=2\n");
-               else
-                       fprintf(nconf, "auth_algs=1\n");
-
-               if (cred->encr_type & WPS_ENCR_WEP && cred->key_idx <= 4) {
-                       int key_idx = cred->key_idx;
-                       if (key_idx)
-                               key_idx--;
-                       fprintf(nconf, "wep_default_key=%d\n", key_idx);
-                       fprintf(nconf, "wep_key%d=", key_idx);
-                       if (cred->key_len == 10 || cred->key_len == 26) {
-                               /* WEP key as a hex string */
-                               for (i = 0; i < cred->key_len; i++)
-                                       fputc(cred->key[i], nconf);
-                       } else {
-                               /* Raw WEP key; convert to hex */
-                               for (i = 0; i < cred->key_len; i++)
-                                       fprintf(nconf, "%02x", cred->key[i]);
-                       }
-                       fprintf(nconf, "\n");
-               }
+               /*
+                * WPS 2.0 does not allow WEP to be configured, so no need to
+                * process that option here either.
+                */
+               fprintf(nconf, "auth_algs=1\n");
        }
 
        fprintf(nconf, "# WPS configuration - END\n");
@@ -707,6 +689,12 @@ static int wps_pwd_auth_fail(struct hostapd_data *hapd, void *ctx)
 static void hostapd_pwd_auth_fail(struct hostapd_data *hapd,
                                  struct wps_event_pwd_auth_fail *data)
 {
+       /* Update WPS Status - Authentication Failure */
+       wpa_printf(MSG_DEBUG, "WPS: Authentication failure update");
+       hapd->wps_stats.status = WPS_STATUS_FAILURE;
+       hapd->wps_stats.failure_reason = WPS_EI_AUTH_FAILURE;
+       os_memcpy(hapd->wps_stats.peer_addr, data->peer_macaddr, ETH_ALEN);
+
        hostapd_wps_for_each(hapd, wps_pwd_auth_fail, data);
 }
 
@@ -734,21 +722,59 @@ static void hostapd_wps_ap_pin_success(struct hostapd_data *hapd)
 }
 
 
-static const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = {
-       "No Error", /* WPS_EI_NO_ERROR */
-       "TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */
-       "WEP Prohibited" /* WPS_EI_SECURITY_WEP_PROHIBITED */
-};
+static void hostapd_wps_event_pbc_overlap(struct hostapd_data *hapd)
+{
+       /* Update WPS Status - PBC Overlap */
+       hapd->wps_stats.pbc_status = WPS_PBC_STATUS_OVERLAP;
+}
+
+
+static void hostapd_wps_event_pbc_timeout(struct hostapd_data *hapd)
+{
+       /* Update WPS PBC Status:PBC Timeout */
+       hapd->wps_stats.pbc_status = WPS_PBC_STATUS_TIMEOUT;
+}
+
+
+static void hostapd_wps_event_pbc_active(struct hostapd_data *hapd)
+{
+       /* Update WPS PBC status - Active */
+       hapd->wps_stats.pbc_status = WPS_PBC_STATUS_ACTIVE;
+}
+
+
+static void hostapd_wps_event_pbc_disable(struct hostapd_data *hapd)
+{
+       /* Update WPS PBC status - Active */
+       hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE;
+}
+
+
+static void hostapd_wps_event_success(struct hostapd_data *hapd,
+                                     struct wps_event_success *success)
+{
+       /* Update WPS status - Success */
+       hapd->wps_stats.pbc_status = WPS_PBC_STATUS_DISABLE;
+       hapd->wps_stats.status = WPS_STATUS_SUCCESS;
+       os_memcpy(hapd->wps_stats.peer_addr, success->peer_macaddr, ETH_ALEN);
+}
+
 
 static void hostapd_wps_event_fail(struct hostapd_data *hapd,
                                   struct wps_event_fail *fail)
 {
+       /* Update WPS status - Failure */
+       hapd->wps_stats.status = WPS_STATUS_FAILURE;
+       os_memcpy(hapd->wps_stats.peer_addr, fail->peer_macaddr, ETH_ALEN);
+
+       hapd->wps_stats.failure_reason = fail->error_indication;
+
        if (fail->error_indication > 0 &&
            fail->error_indication < NUM_WPS_EI_VALUES) {
                wpa_msg(hapd->msg_ctx, MSG_INFO,
                        WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)",
                        fail->msg, fail->config_error, fail->error_indication,
-                       wps_event_fail_reason[fail->error_indication]);
+                       wps_ei_str(fail->error_indication));
        } else {
                wpa_msg(hapd->msg_ctx, MSG_INFO,
                        WPS_EVENT_FAIL "msg=%d config_error=%d",
@@ -770,17 +796,28 @@ static void hostapd_wps_event_cb(void *ctx, enum wps_event event,
                hostapd_wps_event_fail(hapd, &data->fail);
                break;
        case WPS_EV_SUCCESS:
+               hostapd_wps_event_success(hapd, &data->success);
                wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_SUCCESS);
                break;
        case WPS_EV_PWD_AUTH_FAIL:
                hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail);
                break;
        case WPS_EV_PBC_OVERLAP:
+               hostapd_wps_event_pbc_overlap(hapd);
                wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_OVERLAP);
                break;
        case WPS_EV_PBC_TIMEOUT:
+               hostapd_wps_event_pbc_timeout(hapd);
                wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_TIMEOUT);
                break;
+       case WPS_EV_PBC_ACTIVE:
+               hostapd_wps_event_pbc_active(hapd);
+               wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ACTIVE);
+               break;
+       case WPS_EV_PBC_DISABLE:
+               hostapd_wps_event_pbc_disable(hapd);
+               wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_DISABLE);
+               break;
        case WPS_EV_ER_AP_ADD:
                break;
        case WPS_EV_ER_AP_REMOVE:
@@ -802,7 +839,16 @@ static void hostapd_wps_event_cb(void *ctx, enum wps_event event,
 }
 
 
-static void hostapd_wps_clear_ies(struct hostapd_data *hapd)
+static int hostapd_wps_rf_band_cb(void *ctx)
+{
+       struct hostapd_data *hapd = ctx;
+
+       return hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ?
+               WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */
+}
+
+
+static void hostapd_wps_clear_ies(struct hostapd_data *hapd, int deinit_only)
 {
        wpabuf_free(hapd->wps_beacon_ie);
        hapd->wps_beacon_ie = NULL;
@@ -810,6 +856,9 @@ static void hostapd_wps_clear_ies(struct hostapd_data *hapd)
        wpabuf_free(hapd->wps_probe_resp_ie);
        hapd->wps_probe_resp_ie = NULL;
 
+       if (deinit_only)
+               return;
+
        hostapd_set_ap_wps_ie(hapd);
 }
 
@@ -892,6 +941,21 @@ static int hostapd_wps_set_vendor_ext(struct hostapd_data *hapd,
 }
 
 
+static void hostapd_free_wps(struct wps_context *wps)
+{
+       int i;
+
+       for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
+               wpabuf_free(wps->dev.vendor_ext[i]);
+       wps_device_data_free(&wps->dev);
+       os_free(wps->network_key);
+       hostapd_wps_nfc_clear(wps);
+       wpabuf_free(wps->dh_pubkey);
+       wpabuf_free(wps->dh_privkey);
+       os_free(wps);
+}
+
+
 int hostapd_init_wps(struct hostapd_data *hapd,
                     struct hostapd_bss_config *conf)
 {
@@ -899,7 +963,7 @@ int hostapd_init_wps(struct hostapd_data *hapd,
        struct wps_registrar_config cfg;
 
        if (conf->wps_state == 0) {
-               hostapd_wps_clear_ies(hapd);
+               hostapd_wps_clear_ies(hapd, 0);
                return 0;
        }
 
@@ -909,6 +973,7 @@ int hostapd_init_wps(struct hostapd_data *hapd,
 
        wps->cred_cb = hostapd_wps_cred_cb;
        wps->event_cb = hostapd_wps_event_cb;
+       wps->rf_band_cb = hostapd_wps_rf_band_cb;
        wps->cb_ctx = hapd;
 
        os_memset(&cfg, 0, sizeof(cfg));
@@ -947,7 +1012,6 @@ int hostapd_init_wps(struct hostapd_data *hapd,
                os_strdup(hapd->conf->serial_number) : NULL;
        wps->config_methods =
                wps_config_methods_str2bin(hapd->conf->config_methods);
-#ifdef CONFIG_WPS2
        if ((wps->config_methods &
             (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
              WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
@@ -962,14 +1026,11 @@ int hostapd_init_wps(struct hostapd_data *hapd,
                           "virtual_push_button for WPS 2.0 compliance");
                wps->config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
        }
-#endif /* CONFIG_WPS2 */
        os_memcpy(wps->dev.pri_dev_type, hapd->conf->device_type,
                  WPS_DEV_TYPE_LEN);
 
-       if (hostapd_wps_set_vendor_ext(hapd, wps) < 0) {
-               os_free(wps);
-               return -1;
-       }
+       if (hostapd_wps_set_vendor_ext(hapd, wps) < 0)
+               goto fail;
 
        wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version);
 
@@ -987,7 +1048,7 @@ int hostapd_init_wps(struct hostapd_data *hapd,
                if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X)
                        wps->auth_types |= WPS_AUTH_WPA2;
 
-               if (conf->rsn_pairwise & WPA_CIPHER_CCMP)
+               if (conf->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP))
                        wps->encr_types |= WPS_ENCR_AES;
                if (conf->rsn_pairwise & WPA_CIPHER_TKIP)
                        wps->encr_types |= WPS_ENCR_TKIP;
@@ -1008,18 +1069,6 @@ int hostapd_init_wps(struct hostapd_data *hapd,
        if (conf->ssid.security_policy == SECURITY_PLAINTEXT) {
                wps->encr_types |= WPS_ENCR_NONE;
                wps->auth_types |= WPS_AUTH_OPEN;
-       } else if (conf->ssid.security_policy == SECURITY_STATIC_WEP) {
-               wps->encr_types |= WPS_ENCR_WEP;
-               if (conf->auth_algs & WPA_AUTH_ALG_OPEN)
-                       wps->auth_types |= WPS_AUTH_OPEN;
-               if (conf->auth_algs & WPA_AUTH_ALG_SHARED)
-                       wps->auth_types |= WPS_AUTH_SHARED;
-       } else if (conf->ssid.security_policy == SECURITY_IEEE_802_1X) {
-               wps->auth_types |= WPS_AUTH_OPEN;
-               if (conf->default_wep_key_len)
-                       wps->encr_types |= WPS_ENCR_WEP;
-               else
-                       wps->encr_types |= WPS_ENCR_NONE;
        }
 
        if (conf->ssid.wpa_psk_file) {
@@ -1029,19 +1078,15 @@ int hostapd_init_wps(struct hostapd_data *hapd,
                wps->network_key_len = os_strlen(conf->ssid.wpa_passphrase);
        } else if (conf->ssid.wpa_psk) {
                wps->network_key = os_malloc(2 * PMK_LEN + 1);
-               if (wps->network_key == NULL) {
-                       os_free(wps);
-                       return -1;
-               }
+               if (wps->network_key == NULL)
+                       goto fail;
                wpa_snprintf_hex((char *) wps->network_key, 2 * PMK_LEN + 1,
                                 conf->ssid.wpa_psk->psk, PMK_LEN);
                wps->network_key_len = 2 * PMK_LEN;
        } else if (conf->ssid.wep.keys_set && conf->ssid.wep.key[0]) {
                wps->network_key = os_malloc(conf->ssid.wep.len[0]);
-               if (wps->network_key == NULL) {
-                       os_free(wps);
-                       return -1;
-               }
+               if (wps->network_key == NULL)
+                       goto fail;
                os_memcpy(wps->network_key, conf->ssid.wep.key[0],
                          conf->ssid.wep.len[0]);
                wps->network_key_len = conf->ssid.wep.len[0];
@@ -1052,6 +1097,8 @@ int hostapd_init_wps(struct hostapd_data *hapd,
                wps->psk_set = 1;
        }
 
+       wps->ap_auth_type = wps->auth_types;
+       wps->ap_encr_type = wps->encr_types;
        if (conf->wps_state == WPS_STATE_NOT_CONFIGURED) {
                /* Override parameters to enable security by default */
                wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK;
@@ -1080,13 +1127,12 @@ int hostapd_init_wps(struct hostapd_data *hapd,
                cfg.dualband = 1;
        if (cfg.dualband)
                wpa_printf(MSG_DEBUG, "WPS: Dualband AP");
+       cfg.force_per_enrollee_psk = conf->force_per_enrollee_psk;
 
        wps->registrar = wps_registrar_init(wps, &cfg);
        if (wps->registrar == NULL) {
                wpa_printf(MSG_ERROR, "Failed to initialize WPS Registrar");
-               os_free(wps->network_key);
-               os_free(wps);
-               return -1;
+               goto fail;
        }
 
 #ifdef CONFIG_WPS_UPNP
@@ -1102,6 +1148,10 @@ int hostapd_init_wps(struct hostapd_data *hapd,
        hapd->wps = wps;
 
        return 0;
+
+fail:
+       hostapd_free_wps(wps);
+       return -1;
 }
 
 
@@ -1116,8 +1166,7 @@ int hostapd_init_wps_complete(struct hostapd_data *hapd)
        if (hostapd_wps_upnp_init(hapd, wps) < 0) {
                wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP");
                wps_registrar_deinit(wps->registrar);
-               os_free(wps->network_key);
-               os_free(wps);
+               hostapd_free_wps(wps);
                hapd->wps = NULL;
                return -1;
        }
@@ -1130,6 +1179,7 @@ int hostapd_init_wps_complete(struct hostapd_data *hapd)
 static void hostapd_wps_nfc_clear(struct wps_context *wps)
 {
 #ifdef CONFIG_WPS_NFC
+       wpa_printf(MSG_DEBUG, "WPS: Clear NFC Tag context %p", wps);
        wps->ap_nfc_dev_pw_id = 0;
        wpabuf_free(wps->ap_nfc_dh_pubkey);
        wps->ap_nfc_dh_pubkey = NULL;
@@ -1145,21 +1195,19 @@ void hostapd_deinit_wps(struct hostapd_data *hapd)
 {
        eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL);
        eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL);
-       if (hapd->wps == NULL)
+       eloop_cancel_timeout(wps_reload_config, hapd->iface, NULL);
+       if (hapd->wps == NULL) {
+               hostapd_wps_clear_ies(hapd, 1);
                return;
+       }
 #ifdef CONFIG_WPS_UPNP
        hostapd_wps_upnp_deinit(hapd);
 #endif /* CONFIG_WPS_UPNP */
        wps_registrar_deinit(hapd->wps->registrar);
-       os_free(hapd->wps->network_key);
-       wps_device_data_free(&hapd->wps->dev);
-       wpabuf_free(hapd->wps->dh_pubkey);
-       wpabuf_free(hapd->wps->dh_privkey);
        wps_free_pending_msgs(hapd->wps->upnp_msgs);
-       hostapd_wps_nfc_clear(hapd->wps);
-       os_free(hapd->wps);
+       hostapd_free_wps(hapd->wps);
        hapd->wps = NULL;
-       hostapd_wps_clear_ies(hapd);
+       hostapd_wps_clear_ies(hapd, 1);
 }
 
 
@@ -1241,7 +1289,7 @@ static int wps_button_pushed(struct hostapd_data *hapd, void *ctx)
 {
        const u8 *p2p_dev_addr = ctx;
        if (hapd->wps == NULL)
-               return 0;
+               return -1;
        return wps_registrar_button_pushed(hapd->wps->registrar, p2p_dev_addr);
 }
 
@@ -1257,7 +1305,7 @@ int hostapd_wps_button_pushed(struct hostapd_data *hapd,
 static int wps_cancel(struct hostapd_data *hapd, void *ctx)
 {
        if (hapd->wps == NULL)
-               return 0;
+               return -1;
 
        wps_registrar_wps_cancel(hapd->wps->registrar);
        ap_for_each_sta(hapd, ap_sta_wps_cancel, NULL);
@@ -1378,6 +1426,16 @@ static int hostapd_rx_req_put_wlan_response(
                return 0;
        }
 
+       if (!sta->eapol_sm) {
+               /*
+                * This can happen, e.g., if an ER sends an extra message after
+                * the station has disassociated (but not fully
+                * deauthenticated).
+                */
+               wpa_printf(MSG_DEBUG, "WPS UPnP: Matching STA did not have EAPOL state machine initialized");
+               return 0;
+       }
+
        p = os_zalloc(sizeof(*p));
        if (p == NULL)
                return -1;
@@ -1524,7 +1582,7 @@ int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin,
        int ret;
 
        ret = os_snprintf(data.pin_txt, sizeof(data.pin_txt), "%s", pin);
-       if (ret < 0 || ret >= (int) sizeof(data.pin_txt))
+       if (os_snprintf_error(sizeof(data.pin_txt), ret))
                return -1;
        data.timeout = timeout;
        return hostapd_wps_for_each(hapd, wps_ap_pin_set, &data);
@@ -1571,8 +1629,6 @@ int hostapd_wps_config_ap(struct hostapd_data *hapd, const char *ssid,
        if (encr) {
                if (os_strncmp(encr, "NONE", 4) == 0)
                        cred.encr_type = WPS_ENCR_NONE;
-               else if (os_strncmp(encr, "WEP", 3) == 0)
-                       cred.encr_type = WPS_ENCR_WEP;
                else if (os_strncmp(encr, "TKIP", 4) == 0)
                        cred.encr_type = WPS_ENCR_TKIP;
                else if (os_strncmp(encr, "CCMP", 4) == 0)
@@ -1687,7 +1743,8 @@ struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
        if (hapd->wps == NULL)
                return NULL;
 
-       ret = wps_get_oob_cred(hapd->wps);
+       ret = wps_get_oob_cred(hapd->wps, hostapd_wps_rf_band_cb(hapd),
+                              hapd->iconf->channel);
        if (ndef && ret) {
                struct wpabuf *tmp;
                tmp = ndef_build_wifi(ret);
@@ -1703,11 +1760,136 @@ struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
 
 struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef)
 {
+       struct wpabuf *ret;
+
+       if (hapd->wps == NULL)
+               return NULL;
+
+       if (hapd->conf->wps_nfc_dh_pubkey == NULL) {
+               struct wps_context *wps = hapd->wps;
+               if (wps_nfc_gen_dh(&hapd->conf->wps_nfc_dh_pubkey,
+                                  &hapd->conf->wps_nfc_dh_privkey) < 0)
+                       return NULL;
+               hostapd_wps_nfc_clear(wps);
+               wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
+               wps->ap_nfc_dh_pubkey =
+                       wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey);
+               wps->ap_nfc_dh_privkey =
+                       wpabuf_dup(hapd->conf->wps_nfc_dh_privkey);
+               if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) {
+                       hostapd_wps_nfc_clear(wps);
+                       return NULL;
+               }
+       }
+
+       ret = wps_build_nfc_handover_sel(hapd->wps,
+                                        hapd->conf->wps_nfc_dh_pubkey,
+                                        hapd->own_addr, hapd->iface->freq);
+
+       if (ndef && ret) {
+               struct wpabuf *tmp;
+               tmp = ndef_build_wifi(ret);
+               wpabuf_free(ret);
+               if (tmp == NULL)
+                       return NULL;
+               ret = tmp;
+       }
+
+       return ret;
+}
+
+
+int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd,
+                                   const struct wpabuf *req,
+                                   const struct wpabuf *sel)
+{
+       struct wpabuf *wps;
+       int ret = -1;
+       u16 wsc_len;
+       const u8 *pos;
+       struct wpabuf msg;
+       struct wps_parse_attr attr;
+       u16 dev_pw_id;
+
        /*
-        * Handover Select carrier record for WPS uses the same format as
-        * configuration token.
+        * Enrollee/station is always initiator of the NFC connection handover,
+        * so use the request message here to find Enrollee public key hash.
         */
-       return hostapd_wps_nfc_config_token(hapd, ndef);
+       wps = ndef_parse_wifi(req);
+       if (wps == NULL)
+               return -1;
+       wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
+                  "payload from NFC connection handover");
+       wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
+       if (wpabuf_len(wps) < 2) {
+               wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request "
+                          "Message");
+               goto out;
+       }
+       pos = wpabuf_head(wps);
+       wsc_len = WPA_GET_BE16(pos);
+       if (wsc_len > wpabuf_len(wps) - 2) {
+               wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) "
+                          "in rt Wi-Fi Handover Request Message", wsc_len);
+               goto out;
+       }
+       pos += 2;
+
+       wpa_hexdump(MSG_DEBUG,
+                   "WPS: WSC attributes in Wi-Fi Handover Request Message",
+                   pos, wsc_len);
+       if (wsc_len < wpabuf_len(wps) - 2) {
+               wpa_hexdump(MSG_DEBUG,
+                           "WPS: Ignore extra data after WSC attributes",
+                           pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len);
+       }
+
+       wpabuf_set(&msg, pos, wsc_len);
+       ret = wps_parse_msg(&msg, &attr);
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in "
+                          "Wi-Fi Handover Request Message");
+               goto out;
+       }
+
+       if (attr.oob_dev_password == NULL ||
+           attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+               wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password "
+                          "included in Wi-Fi Handover Request Message");
+               ret = -1;
+               goto out;
+       }
+
+       if (attr.uuid_e == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi "
+                          "Handover Request Message");
+               ret = -1;
+               goto out;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN);
+
+       wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password",
+                   attr.oob_dev_password, attr.oob_dev_password_len);
+       dev_pw_id = WPA_GET_BE16(attr.oob_dev_password +
+                                WPS_OOB_PUBKEY_HASH_LEN);
+       if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) {
+               wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID "
+                          "%u in Wi-Fi Handover Request Message", dev_pw_id);
+               ret = -1;
+               goto out;
+       }
+       wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash",
+                   attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
+
+       ret = wps_registrar_add_nfc_pw_token(hapd->wps->registrar,
+                                            attr.oob_dev_password,
+                                            DEV_PW_NFC_CONNECTION_HANDOVER,
+                                            NULL, 0, 1);
+
+out:
+       wpabuf_free(wps);
+       return ret;
 }
 
 
@@ -1742,6 +1924,9 @@ int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd)
                return -1;
 
        hostapd_wps_nfc_clear(wps);
+       wpa_printf(MSG_DEBUG,
+                  "WPS: Enable NFC Tag (Dev Pw Id %u) for AP interface %s (context %p)",
+                  hapd->conf->wps_nfc_dev_pw_id, hapd->conf->iface, wps);
        wps->ap_nfc_dev_pw_id = hapd->conf->wps_nfc_dev_pw_id;
        wps->ap_nfc_dh_pubkey = wpabuf_dup(hapd->conf->wps_nfc_dh_pubkey);
        wps->ap_nfc_dh_privkey = wpabuf_dup(hapd->conf->wps_nfc_dh_privkey);
@@ -1768,6 +1953,8 @@ int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd)
 
 void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd)
 {
+       wpa_printf(MSG_DEBUG, "WPS: Disable NFC token for AP interface %s",
+                  hapd->conf->iface);
        hostapd_wps_nfc_clear(hapd->wps);
 }
 
index a2c2cf0..204bd82 100644 (file)
@@ -16,6 +16,7 @@ int hostapd_init_wps(struct hostapd_data *hapd,
 int hostapd_init_wps_complete(struct hostapd_data *hapd);
 void hostapd_deinit_wps(struct hostapd_data *hapd);
 void hostapd_update_wps(struct hostapd_data *hapd);
+void hostapd_wps_eap_completed(struct hostapd_data *hapd);
 int hostapd_wps_add_pin(struct hostapd_data *hapd, const u8 *addr,
                        const char *uuid, const char *pin, int timeout);
 int hostapd_wps_button_pushed(struct hostapd_data *hapd,
@@ -36,6 +37,9 @@ int hostapd_wps_nfc_tag_read(struct hostapd_data *hapd,
 struct wpabuf * hostapd_wps_nfc_config_token(struct hostapd_data *hapd,
                                             int ndef);
 struct wpabuf * hostapd_wps_nfc_hs_cr(struct hostapd_data *hapd, int ndef);
+int hostapd_wps_nfc_report_handover(struct hostapd_data *hapd,
+                                   const struct wpabuf *req,
+                                   const struct wpabuf *sel);
 struct wpabuf * hostapd_wps_nfc_token_gen(struct hostapd_data *hapd, int ndef);
 int hostapd_wps_nfc_token_enable(struct hostapd_data *hapd);
 void hostapd_wps_nfc_token_disable(struct hostapd_data *hapd);
@@ -61,6 +65,10 @@ static inline void hostapd_update_wps(struct hostapd_data *hapd)
 {
 }
 
+static inline void hostapd_wps_eap_completed(struct hostapd_data *hapd)
+{
+}
+
 static inline int hostapd_wps_get_mib_sta(struct hostapd_data *hapd,
                                          const u8 *addr,
                                          char *buf, size_t buflen)
diff --git a/src/ap/x_snoop.c b/src/ap/x_snoop.c
new file mode 100755 (executable)
index 0000000..8f77015
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Generic Snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "hostapd.h"
+#include "sta_info.h"
+#include "ap_drv_ops.h"
+#include "x_snoop.h"
+
+
+int x_snoop_init(struct hostapd_data *hapd)
+{
+       struct hostapd_bss_config *conf = hapd->conf;
+
+       if (!conf->isolate) {
+               wpa_printf(MSG_DEBUG,
+                          "x_snoop: ap_isolate must be enabled for x_snoop");
+               return -1;
+       }
+
+       if (conf->bridge[0] == '\0') {
+               wpa_printf(MSG_DEBUG,
+                          "x_snoop: Bridge must be configured for x_snoop");
+               return -1;
+       }
+
+       if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE,
+                                        1)) {
+               wpa_printf(MSG_DEBUG,
+                          "x_snoop: Failed to enable hairpin_mode on the bridge port");
+               return -1;
+       }
+
+       if (hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 1)) {
+               wpa_printf(MSG_DEBUG,
+                          "x_snoop: Failed to enable proxyarp on the bridge port");
+               return -1;
+       }
+
+       if (hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT,
+                                        1)) {
+               wpa_printf(MSG_DEBUG,
+                          "x_snoop: Failed to enable accepting gratuitous ARP on the bridge");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+struct l2_packet_data *
+x_snoop_get_l2_packet(struct hostapd_data *hapd,
+                     void (*handler)(void *ctx, const u8 *src_addr,
+                                     const u8 *buf, size_t len),
+                     enum l2_packet_filter_type type)
+{
+       struct hostapd_bss_config *conf = hapd->conf;
+       struct l2_packet_data *l2;
+
+       l2 = l2_packet_init(conf->bridge, NULL, ETH_P_ALL, handler, hapd, 1);
+       if (l2 == NULL) {
+               wpa_printf(MSG_DEBUG,
+                          "x_snoop: Failed to initialize L2 packet processing %s",
+                          strerror(errno));
+               return NULL;
+       }
+
+       if (l2_packet_set_packet_filter(l2, type)) {
+               wpa_printf(MSG_DEBUG,
+                          "x_snoop: Failed to set L2 packet filter for type: %d",
+                          type);
+               l2_packet_deinit(l2);
+               return NULL;
+       }
+
+       return l2;
+}
+
+
+void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
+                                        struct sta_info *sta, u8 *buf,
+                                        size_t len)
+{
+       int res;
+       u8 addr[ETH_ALEN];
+       u8 *dst_addr = buf;
+
+       if (!(dst_addr[0] & 0x01))
+               return;
+
+       wpa_printf(MSG_EXCESSIVE, "x_snoop: Multicast-to-unicast conversion "
+                  MACSTR " -> " MACSTR " (len %u)",
+                  MAC2STR(dst_addr), MAC2STR(sta->addr), (unsigned int) len);
+
+       /* save the multicast destination address for restoring it later */
+       os_memcpy(addr, buf, ETH_ALEN);
+
+       os_memcpy(buf, sta->addr, ETH_ALEN);
+       res = l2_packet_send(hapd->sock_dhcp, NULL, 0, buf, len);
+       if (res < 0) {
+               wpa_printf(MSG_DEBUG,
+                          "x_snoop: Failed to send mcast to ucast converted packet to "
+                          MACSTR, MAC2STR(sta->addr));
+       }
+
+       /* restore the multicast destination address */
+       os_memcpy(buf, addr, ETH_ALEN);
+}
+
+
+void x_snoop_deinit(struct hostapd_data *hapd)
+{
+       hostapd_drv_br_set_net_param(hapd, DRV_BR_NET_PARAM_GARP_ACCEPT, 0);
+       hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_PROXYARP, 0);
+       hostapd_drv_br_port_set_attr(hapd, DRV_BR_PORT_ATTR_HAIRPIN_MODE, 0);
+}
diff --git a/src/ap/x_snoop.h b/src/ap/x_snoop.h
new file mode 100755 (executable)
index 0000000..e43a78d
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Generic Snooping for Proxy ARP
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef X_SNOOP_H
+#define X_SNOOP_H
+
+#include "l2_packet/l2_packet.h"
+
+#ifdef CONFIG_PROXYARP
+
+int x_snoop_init(struct hostapd_data *hapd);
+struct l2_packet_data *
+x_snoop_get_l2_packet(struct hostapd_data *hapd,
+                     void (*handler)(void *ctx, const u8 *src_addr,
+                                     const u8 *buf, size_t len),
+                     enum l2_packet_filter_type type);
+void x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
+                                        struct sta_info *sta, u8 *buf,
+                                        size_t len);
+void x_snoop_deinit(struct hostapd_data *hapd);
+
+#else /* CONFIG_PROXYARP */
+
+static inline int x_snoop_init(struct hostapd_data *hapd)
+{
+       return 0;
+}
+
+static inline struct l2_packet_data *
+x_snoop_get_l2_packet(struct hostapd_data *hapd,
+                     void (*handler)(void *ctx, const u8 *src_addr,
+                                     const u8 *buf, size_t len),
+                     enum l2_packet_filter_type type)
+{
+       return NULL;
+}
+
+static inline void
+x_snoop_mcast_to_ucast_convert_send(struct hostapd_data *hapd,
+                                   struct sta_info *sta, void *buf,
+                                   size_t len)
+{
+}
+
+static inline void x_snoop_deinit(struct hostapd_data *hapd)
+{
+}
+
+#endif /* CONFIG_PROXYARP */
+
+#endif /* X_SNOOP_H */
index 9c41962..adfd3df 100644 (file)
@@ -2,7 +2,7 @@ all:
        @echo Nothing to be made.
 
 clean:
-       rm -f *~ *.o *.d
+       rm -f *~ *.o *.d *.gcno *.gcda *.gcov
 
 install:
        @echo Nothing to be made.
diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c
new file mode 100644 (file)
index 0000000..56b1122
--- /dev/null
@@ -0,0 +1,172 @@
+/*
+ * common 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.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "ieee802_11_common.h"
+#include "wpa_common.h"
+
+
+struct ieee802_11_parse_test_data {
+       u8 *data;
+       size_t len;
+       ParseRes result;
+       int count;
+};
+
+static const struct ieee802_11_parse_test_data parse_tests[] = {
+       { (u8 *) "", 0, ParseOK, 0 },
+       { (u8 *) " ", 1, ParseFailed, 0 },
+       { (u8 *) "\xff\x00", 2, ParseUnknown, 1 },
+       { (u8 *) "\xff\x01", 2, ParseFailed, 0 },
+       { (u8 *) "\xdd\x03\x01\x02\x03", 5, ParseUnknown, 1 },
+       { (u8 *) "\xdd\x04\x01\x02\x03\x04", 6, ParseUnknown, 1 },
+       { (u8 *) "\xdd\x04\x00\x50\xf2\x02", 6, ParseUnknown, 1 },
+       { (u8 *) "\xdd\x05\x00\x50\xf2\x02\x02", 7, ParseOK, 1 },
+       { (u8 *) "\xdd\x05\x00\x50\xf2\x02\xff", 7, ParseUnknown, 1 },
+       { (u8 *) "\xdd\x04\x00\x50\xf2\xff", 6, ParseUnknown, 1 },
+       { (u8 *) "\xdd\x04\x50\x6f\x9a\xff", 6, ParseUnknown, 1 },
+       { (u8 *) "\xdd\x04\x00\x90\x4c\x33", 6, ParseOK, 1 },
+       { (u8 *) "\xdd\x04\x00\x90\x4c\xff\xdd\x04\x00\x90\x4c\x33", 12,
+         ParseUnknown, 2 },
+       { (u8 *) "\x10\x01\x00\x21\x00", 5, ParseOK, 2 },
+       { (u8 *) "\x24\x00", 2, ParseOK, 1 },
+       { (u8 *) "\x38\x00", 2, ParseOK, 1 },
+       { (u8 *) "\x54\x00", 2, ParseOK, 1 },
+       { (u8 *) "\x5a\x00", 2, ParseOK, 1 },
+       { (u8 *) "\x65\x00", 2, ParseOK, 1 },
+       { (u8 *) "\x65\x12\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11",
+         20, ParseOK, 1 },
+       { (u8 *) "\x6e\x00", 2, ParseOK, 1 },
+       { (u8 *) "\xc7\x00", 2, ParseOK, 1 },
+       { (u8 *) "\xc7\x01\x00", 3, ParseOK, 1 },
+       { NULL, 0, ParseOK, 0 }
+};
+
+static int ieee802_11_parse_tests(void)
+{
+       int i, ret = 0;
+
+       wpa_printf(MSG_INFO, "ieee802_11_parse tests");
+
+       for (i = 0; parse_tests[i].data; i++) {
+               const struct ieee802_11_parse_test_data *test;
+               struct ieee802_11_elems elems;
+               ParseRes res;
+
+               test = &parse_tests[i];
+               res = ieee802_11_parse_elems(test->data, test->len, &elems, 1);
+               if (res != test->result ||
+                   ieee802_11_ie_count(test->data, test->len) != test->count) {
+                       wpa_printf(MSG_ERROR, "ieee802_11_parse test %d failed",
+                                  i);
+                       ret = -1;
+               }
+       }
+
+       if (ieee802_11_vendor_ie_concat((const u8 *) "\x00\x01", 2, 0) != NULL)
+       {
+               wpa_printf(MSG_ERROR,
+                          "ieee802_11_vendor_ie_concat test failed");
+               ret = -1;
+       }
+
+       return ret;
+}
+
+
+struct rsn_ie_parse_test_data {
+       u8 *data;
+       size_t len;
+       int result;
+};
+
+static const struct rsn_ie_parse_test_data rsn_parse_tests[] = {
+       { (u8 *) "", 0, -1 },
+       { (u8 *) "\x30\x00", 2, -1 },
+       { (u8 *) "\x30\x02\x01\x00", 4, 0 },
+       { (u8 *) "\x30\x02\x00\x00", 4, -2 },
+       { (u8 *) "\x30\x02\x02\x00", 4, -2 },
+       { (u8 *) "\x30\x02\x00\x01", 4, -2 },
+       { (u8 *) "\x30\x02\x00\x00\x00", 5, -2 },
+       { (u8 *) "\x30\x03\x01\x00\x00", 5, -3 },
+       { (u8 *) "\x30\x06\x01\x00\x00\x00\x00\x00", 8, -1 },
+       { (u8 *) "\x30\x06\x01\x00\x00\x0f\xac\x04", 8, 0 },
+       { (u8 *) "\x30\x07\x01\x00\x00\x0f\xac\x04\x00", 9, -5 },
+       { (u8 *) "\x30\x08\x01\x00\x00\x0f\xac\x04\x00\x00", 10, -4 },
+       { (u8 *) "\x30\x08\x01\x00\x00\x0f\xac\x04\x00\x01", 10, -4 },
+       { (u8 *) "\x30\x0c\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04",
+         14, 0 },
+       { (u8 *) "\x30\x0c\x01\x00\x00\x0f\xac\x04\x00\x01\x00\x0f\xac\x04",
+         14, -4 },
+       { (u8 *) "\x30\x0c\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x06",
+         14, -1 },
+       { (u8 *) "\x30\x10\x01\x00\x00\x0f\xac\x04\x02\x00\x00\x0f\xac\x04\x00\x0f\xac\x08",
+         18, 0 },
+       { (u8 *) "\x30\x0d\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x00",
+         15, -7 },
+       { (u8 *) "\x30\x0e\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x00\x00",
+         16, -6 },
+       { (u8 *) "\x30\x0e\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x00\x01",
+         16, -6 },
+       { (u8 *) "\x30\x12\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01",
+         20, 0 },
+       { (u8 *) "\x30\x16\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x02\x00\x00\x0f\xac\x01\x00\x0f\xac\x02",
+         24, 0 },
+       { (u8 *) "\x30\x13\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00",
+         21, 0 },
+       { (u8 *) "\x30\x14\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00",
+         22, 0 },
+       { (u8 *) "\x30\x16\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x00",
+         24, 0 },
+       { (u8 *) "\x30\x16\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x01",
+         24, -9 },
+       { (u8 *) "\x30\x1a\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x00\x00\x00\x00\x00",
+         28, -10 },
+       { (u8 *) "\x30\x1a\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x00\x00\x0f\xac\x06",
+         28, 0 },
+       { (u8 *) "\x30\x1c\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x00\x00\x00\x00\x00\x0f\xac\x06\x01\x02",
+         30, 0 },
+       { NULL, 0, 0 }
+};
+
+static int rsn_ie_parse_tests(void)
+{
+       int i, ret = 0;
+
+       wpa_printf(MSG_INFO, "rsn_ie_parse tests");
+
+       for (i = 0; rsn_parse_tests[i].data; i++) {
+               const struct rsn_ie_parse_test_data *test;
+               struct wpa_ie_data data;
+
+               test = &rsn_parse_tests[i];
+               if (wpa_parse_wpa_ie_rsn(test->data, test->len, &data) !=
+                   test->result) {
+                       wpa_printf(MSG_ERROR, "rsn_ie_parse test %d failed", i);
+                       ret = -1;
+               }
+       }
+
+       return ret;
+}
+
+
+int common_module_tests(void)
+{
+       int ret = 0;
+
+       wpa_printf(MSG_INFO, "common module tests");
+
+       if (ieee802_11_parse_tests() < 0 ||
+           rsn_ie_parse_tests() < 0)
+               ret = -1;
+
+       return ret;
+}
index 281dd8a..b5f4f80 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - Common definitions
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -23,11 +23,15 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean;
 #define WPA_CIPHER_WEP104 BIT(2)
 #define WPA_CIPHER_TKIP BIT(3)
 #define WPA_CIPHER_CCMP BIT(4)
-#ifdef CONFIG_IEEE80211W
 #define WPA_CIPHER_AES_128_CMAC BIT(5)
-#endif /* CONFIG_IEEE80211W */
 #define WPA_CIPHER_GCMP BIT(6)
 #define WPA_CIPHER_SMS4 BIT(7)
+#define WPA_CIPHER_GCMP_256 BIT(8)
+#define WPA_CIPHER_CCMP_256 BIT(9)
+#define WPA_CIPHER_BIP_GMAC_128 BIT(11)
+#define WPA_CIPHER_BIP_GMAC_256 BIT(12)
+#define WPA_CIPHER_BIP_CMAC_256 BIT(13)
+#define WPA_CIPHER_GTK_NOT_USED BIT(14)
 
 #define WPA_KEY_MGMT_IEEE8021X BIT(0)
 #define WPA_KEY_MGMT_PSK BIT(1)
@@ -44,13 +48,19 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean;
 #define WPA_KEY_MGMT_WAPI_PSK BIT(12)
 #define WPA_KEY_MGMT_WAPI_CERT BIT(13)
 #define WPA_KEY_MGMT_CCKM BIT(14)
+#define WPA_KEY_MGMT_OSEN BIT(15)
+#define WPA_KEY_MGMT_IEEE8021X_SUITE_B BIT(16)
+#define WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 BIT(17)
 
 static inline int wpa_key_mgmt_wpa_ieee8021x(int akm)
 {
        return !!(akm & (WPA_KEY_MGMT_IEEE8021X |
                         WPA_KEY_MGMT_FT_IEEE8021X |
                         WPA_KEY_MGMT_CCKM |
-                        WPA_KEY_MGMT_IEEE8021X_SHA256));
+                        WPA_KEY_MGMT_OSEN |
+                        WPA_KEY_MGMT_IEEE8021X_SHA256 |
+                        WPA_KEY_MGMT_IEEE8021X_SUITE_B |
+                        WPA_KEY_MGMT_IEEE8021X_SUITE_B_192));
 }
 
 static inline int wpa_key_mgmt_wpa_psk(int akm)
@@ -58,7 +68,8 @@ static inline int wpa_key_mgmt_wpa_psk(int akm)
        return !!(akm & (WPA_KEY_MGMT_PSK |
                         WPA_KEY_MGMT_FT_PSK |
                         WPA_KEY_MGMT_PSK_SHA256 |
-                        WPA_KEY_MGMT_SAE));
+                        WPA_KEY_MGMT_SAE |
+                        WPA_KEY_MGMT_FT_SAE));
 }
 
 static inline int wpa_key_mgmt_ft(int akm)
@@ -77,13 +88,27 @@ static inline int wpa_key_mgmt_sae(int akm)
 static inline int wpa_key_mgmt_sha256(int akm)
 {
        return !!(akm & (WPA_KEY_MGMT_PSK_SHA256 |
-                        WPA_KEY_MGMT_IEEE8021X_SHA256));
+                        WPA_KEY_MGMT_IEEE8021X_SHA256 |
+                        WPA_KEY_MGMT_OSEN |
+                        WPA_KEY_MGMT_IEEE8021X_SUITE_B));
+}
+
+static inline int wpa_key_mgmt_sha384(int akm)
+{
+       return !!(akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192);
+}
+
+static inline int wpa_key_mgmt_suite_b(int akm)
+{
+       return !!(akm & (WPA_KEY_MGMT_IEEE8021X_SUITE_B |
+                        WPA_KEY_MGMT_IEEE8021X_SUITE_B_192));
 }
 
 static inline int wpa_key_mgmt_wpa(int akm)
 {
        return wpa_key_mgmt_wpa_ieee8021x(akm) ||
-               wpa_key_mgmt_wpa_psk(akm);
+               wpa_key_mgmt_wpa_psk(akm) ||
+               wpa_key_mgmt_sae(akm);
 }
 
 static inline int wpa_key_mgmt_wpa_any(int akm)
@@ -100,6 +125,7 @@ static inline int wpa_key_mgmt_cckm(int akm)
 #define WPA_PROTO_WPA BIT(0)
 #define WPA_PROTO_RSN BIT(1)
 #define WPA_PROTO_WAPI BIT(2)
+#define WPA_PROTO_OSEN BIT(3)
 
 #define WPA_AUTH_ALG_OPEN BIT(0)
 #define WPA_AUTH_ALG_SHARED BIT(1)
@@ -117,41 +143,12 @@ enum wpa_alg {
        WPA_ALG_PMK,
        WPA_ALG_GCMP,
        WPA_ALG_SMS4,
-       WPA_ALG_KRK
-};
-
-/**
- * enum wpa_cipher - Cipher suites
- */
-enum wpa_cipher {
-       CIPHER_NONE,
-       CIPHER_WEP40,
-       CIPHER_TKIP,
-       CIPHER_CCMP,
-       CIPHER_WEP104,
-       CIPHER_GCMP,
-       CIPHER_SMS4
-};
-
-/**
- * enum wpa_key_mgmt - Key management suites
- */
-enum wpa_key_mgmt {
-       KEY_MGMT_802_1X,
-       KEY_MGMT_PSK,
-       KEY_MGMT_NONE,
-       KEY_MGMT_802_1X_NO_WPA,
-       KEY_MGMT_WPA_NONE,
-       KEY_MGMT_FT_802_1X,
-       KEY_MGMT_FT_PSK,
-       KEY_MGMT_802_1X_SHA256,
-       KEY_MGMT_PSK_SHA256,
-       KEY_MGMT_WPS,
-       KEY_MGMT_SAE,
-       KEY_MGMT_FT_SAE,
-       KEY_MGMT_WAPI_PSK,
-       KEY_MGMT_WAPI_CERT,
-       KEY_MGMT_CCKM
+       WPA_ALG_KRK,
+       WPA_ALG_GCMP_256,
+       WPA_ALG_CCMP_256,
+       WPA_ALG_BIP_GMAC_128,
+       WPA_ALG_BIP_GMAC_256,
+       WPA_ALG_BIP_CMAC_256
 };
 
 /**
@@ -312,10 +309,21 @@ enum wpa_ctrl_req_type {
        WPA_CTRL_REQ_EAP_PIN,
        WPA_CTRL_REQ_EAP_OTP,
        WPA_CTRL_REQ_EAP_PASSPHRASE,
+       WPA_CTRL_REQ_SIM,
        NUM_WPA_CTRL_REQS
 };
 
 /* Maximum number of EAP methods to store for EAP server user information */
 #define EAP_MAX_METHODS 8
 
+enum mesh_plink_state {
+       PLINK_LISTEN = 1,
+       PLINK_OPEN_SENT,
+       PLINK_OPEN_RCVD,
+       PLINK_CNF_RCVD,
+       PLINK_ESTAB,
+       PLINK_HOLDING,
+       PLINK_BLOCKED,
+};
+
 #endif /* DEFS_H */
index 4811f38..6958661 100644 (file)
@@ -22,17 +22,28 @@ struct ieee802_1x_hdr {
        /* followed by length octets of data */
 } STRUCT_PACKED;
 
+struct ieee8023_hdr {
+       u8 dest[ETH_ALEN];
+       u8 src[ETH_ALEN];
+       u16 ethertype;
+} STRUCT_PACKED;
+
 #ifdef _MSC_VER
 #pragma pack(pop)
 #endif /* _MSC_VER */
 
+#ifdef CONFIG_MACSEC
+#define EAPOL_VERSION 3
+#else /* CONFIG_MACSEC */
 #define EAPOL_VERSION 2
+#endif /* CONFIG_MACSEC */
 
 enum { IEEE802_1X_TYPE_EAP_PACKET = 0,
        IEEE802_1X_TYPE_EAPOL_START = 1,
        IEEE802_1X_TYPE_EAPOL_LOGOFF = 2,
        IEEE802_1X_TYPE_EAPOL_KEY = 3,
-       IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT = 4
+       IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT = 4,
+       IEEE802_1X_TYPE_EAPOL_MKA = 5,
 };
 
 enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2,
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
new file mode 100755 (executable)
index 0000000..e8babb5
--- /dev/null
@@ -0,0 +1,438 @@
+/*
+ * Common hostapd/wpa_supplicant HW features
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2015, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "defs.h"
+#include "ieee802_11_defs.h"
+#include "ieee802_11_common.h"
+#include "hw_features_common.h"
+
+
+struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode,
+                                                 int chan, int *freq)
+{
+       int i;
+
+       if (freq)
+               *freq = 0;
+
+       if (!mode)
+               return NULL;
+
+       for (i = 0; i < mode->num_channels; i++) {
+               struct hostapd_channel_data *ch = &mode->channels[i];
+               if (ch->chan == chan) {
+                       if (freq)
+                               *freq = ch->freq;
+                       return ch;
+               }
+       }
+
+       return NULL;
+}
+
+
+struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode,
+                                                 int freq, int *chan)
+{
+       int i;
+
+       if (chan)
+               *chan = 0;
+
+       if (!mode)
+               return NULL;
+
+       for (i = 0; i < mode->num_channels; i++) {
+               struct hostapd_channel_data *ch = &mode->channels[i];
+               if (ch->freq == freq) {
+                       if (chan)
+                               *chan = ch->chan;
+                       return ch;
+               }
+       }
+
+       return NULL;
+}
+
+
+int hw_get_freq(struct hostapd_hw_modes *mode, int chan)
+{
+       int freq;
+
+       hw_get_channel_chan(mode, chan, &freq);
+
+       return freq;
+}
+
+
+int hw_get_chan(struct hostapd_hw_modes *mode, int freq)
+{
+       int chan;
+
+       hw_get_channel_freq(mode, freq, &chan);
+
+       return chan;
+}
+
+
+int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
+                             int sec_chan)
+{
+       int ok, j, first;
+       int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
+                         184, 192 };
+       size_t k;
+
+       if (pri_chan == sec_chan || !sec_chan)
+               return 1; /* HT40 not used */
+
+       wpa_printf(MSG_DEBUG,
+                  "HT40: control channel: %d  secondary channel: %d",
+                  pri_chan, sec_chan);
+
+       /* Verify that HT40 secondary channel is an allowed 20 MHz
+        * channel */
+       ok = 0;
+       for (j = 0; j < mode->num_channels; j++) {
+               struct hostapd_channel_data *chan = &mode->channels[j];
+               if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+                   chan->chan == sec_chan) {
+                       ok = 1;
+                       break;
+               }
+       }
+       if (!ok) {
+               wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
+                          sec_chan);
+               return 0;
+       }
+
+       /*
+        * Verify that HT40 primary,secondary channel pair is allowed per
+        * IEEE 802.11n Annex J. This is only needed for 5 GHz band since
+        * 2.4 GHz rules allow all cases where the secondary channel fits into
+        * the list of allowed channels (already checked above).
+        */
+       if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+               return 1;
+
+       first = pri_chan < sec_chan ? pri_chan : sec_chan;
+
+       ok = 0;
+       for (k = 0; k < ARRAY_SIZE(allowed); k++) {
+               if (first == allowed[k]) {
+                       ok = 1;
+                       break;
+               }
+       }
+       if (!ok) {
+               wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed",
+                          pri_chan, sec_chan);
+               return 0;
+       }
+
+       return 1;
+}
+
+
+void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan)
+{
+       struct ieee80211_ht_operation *oper;
+       struct ieee802_11_elems elems;
+
+       *pri_chan = *sec_chan = 0;
+
+       ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
+       if (elems.ht_operation &&
+           elems.ht_operation_len >= sizeof(*oper)) {
+               oper = (struct ieee80211_ht_operation *) elems.ht_operation;
+               *pri_chan = oper->primary_chan;
+               if (oper->ht_param & HT_INFO_HT_PARAM_STA_CHNL_WIDTH) {
+                       int sec = oper->ht_param &
+                               HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
+                       if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+                               *sec_chan = *pri_chan + 4;
+                       else if (sec == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+                               *sec_chan = *pri_chan - 4;
+               }
+       }
+}
+
+
+int check_40mhz_5g(struct hostapd_hw_modes *mode,
+                  struct wpa_scan_results *scan_res, int pri_chan,
+                  int sec_chan)
+{
+       int pri_freq, sec_freq, pri_bss, sec_bss;
+       int bss_pri_chan, bss_sec_chan;
+       size_t i;
+       int match;
+
+       if (!mode || !scan_res || !pri_chan || !sec_chan)
+               return 0;
+
+       if (pri_chan == sec_chan)
+               return 0;
+
+       pri_freq = hw_get_freq(mode, pri_chan);
+       sec_freq = hw_get_freq(mode, sec_chan);
+
+       /*
+        * Switch PRI/SEC channels if Beacons were detected on selected SEC
+        * channel, but not on selected PRI channel.
+        */
+       pri_bss = sec_bss = 0;
+       for (i = 0; i < scan_res->num; i++) {
+               struct wpa_scan_res *bss = scan_res->res[i];
+               if (bss->freq == pri_freq)
+                       pri_bss++;
+               else if (bss->freq == sec_freq)
+                       sec_bss++;
+       }
+       if (sec_bss && !pri_bss) {
+               wpa_printf(MSG_INFO,
+                          "Switch own primary and secondary channel to get secondary channel with no Beacons from other BSSes");
+               return 2;
+       }
+
+       /*
+        * Match PRI/SEC channel with any existing HT40 BSS on the same
+        * channels that we are about to use (if already mixed order in
+        * existing BSSes, use own preference).
+        */
+       match = 0;
+       for (i = 0; i < scan_res->num; i++) {
+               struct wpa_scan_res *bss = scan_res->res[i];
+               get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
+               if (pri_chan == bss_pri_chan &&
+                   sec_chan == bss_sec_chan) {
+                       match = 1;
+                       break;
+               }
+       }
+       if (!match) {
+               for (i = 0; i < scan_res->num; i++) {
+                       struct wpa_scan_res *bss = scan_res->res[i];
+                       get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan);
+                       if (pri_chan == bss_sec_chan &&
+                           sec_chan == bss_pri_chan) {
+                               wpa_printf(MSG_INFO, "Switch own primary and "
+                                          "secondary channel due to BSS "
+                                          "overlap with " MACSTR,
+                                          MAC2STR(bss->bssid));
+                               return 2;
+                       }
+               }
+       }
+
+       return 1;
+}
+
+
+int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, int end)
+{
+       struct ieee802_11_elems elems;
+       struct ieee80211_ht_operation *oper;
+
+       if (bss->freq < start || bss->freq > end || bss->freq == pri_freq)
+               return 0;
+
+       ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0);
+       if (!elems.ht_capabilities) {
+               wpa_printf(MSG_DEBUG, "Found overlapping legacy BSS: "
+                          MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
+               return 1;
+       }
+
+       if (elems.ht_operation &&
+           elems.ht_operation_len >= sizeof(*oper)) {
+               oper = (struct ieee80211_ht_operation *) elems.ht_operation;
+               if (oper->ht_param & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)
+                       return 0;
+
+               wpa_printf(MSG_DEBUG, "Found overlapping 20 MHz HT BSS: "
+                          MACSTR " freq=%d", MAC2STR(bss->bssid), bss->freq);
+               return 1;
+       }
+       return 0;
+}
+
+
+int check_40mhz_2g4(struct hostapd_hw_modes *mode,
+                   struct wpa_scan_results *scan_res, int pri_chan,
+                   int sec_chan)
+{
+       int pri_freq, sec_freq;
+       int affected_start, affected_end;
+       size_t i;
+
+       if (!mode || !scan_res || !pri_chan || !sec_chan)
+               return 0;
+
+       if (pri_chan == sec_chan)
+               return 0;
+
+       pri_freq = hw_get_freq(mode, pri_chan);
+       sec_freq = hw_get_freq(mode, sec_chan);
+
+       affected_start = (pri_freq + sec_freq) / 2 - 25;
+       affected_end = (pri_freq + sec_freq) / 2 + 25;
+       wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz",
+                  affected_start, affected_end);
+       for (i = 0; i < scan_res->num; i++) {
+               struct wpa_scan_res *bss = scan_res->res[i];
+               int pri = bss->freq;
+               int sec = pri;
+               struct ieee802_11_elems elems;
+
+               /* Check for overlapping 20 MHz BSS */
+               if (check_20mhz_bss(bss, pri_freq, affected_start,
+                                   affected_end)) {
+                       wpa_printf(MSG_DEBUG,
+                                  "Overlapping 20 MHz BSS is found");
+                       return 0;
+               }
+
+               get_pri_sec_chan(bss, &pri_chan, &sec_chan);
+
+               if (sec_chan) {
+                       if (sec_chan < pri_chan)
+                               sec = pri - 20;
+                       else
+                               sec = pri + 20;
+               }
+
+               if ((pri < affected_start || pri > affected_end) &&
+                   (sec < affected_start || sec > affected_end))
+                       continue; /* not within affected channel range */
+
+               wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR
+                          " freq=%d pri=%d sec=%d",
+                          MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan);
+
+               if (sec_chan) {
+                       if (pri_freq != pri || sec_freq != sec) {
+                               wpa_printf(MSG_DEBUG,
+                                          "40 MHz pri/sec mismatch with BSS "
+                                          MACSTR
+                                          " <%d,%d> (chan=%d%c) vs. <%d,%d>",
+                                          MAC2STR(bss->bssid),
+                                          pri, sec, pri_chan,
+                                          sec > pri ? '+' : '-',
+                                          pri_freq, sec_freq);
+                               return 0;
+                       }
+               }
+
+               ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems,
+                                      0);
+               if (elems.ht_capabilities &&
+                   elems.ht_capabilities_len >=
+                   sizeof(struct ieee80211_ht_capabilities)) {
+                       struct ieee80211_ht_capabilities *ht_cap =
+                               (struct ieee80211_ht_capabilities *)
+                               elems.ht_capabilities;
+
+                       if (le_to_host16(ht_cap->ht_capabilities_info) &
+                           HT_CAP_INFO_40MHZ_INTOLERANT) {
+                               wpa_printf(MSG_DEBUG,
+                                          "40 MHz Intolerant is set on channel %d in BSS "
+                                          MACSTR, pri, MAC2STR(bss->bssid));
+                               return 0;
+                       }
+               }
+       }
+
+       return 1;
+}
+
+
+int hostapd_set_freq_params(struct hostapd_freq_params *data,
+                           enum hostapd_hw_mode mode,
+                           int freq, int channel, int ht_enabled,
+                           int vht_enabled, int sec_channel_offset,
+                           int vht_oper_chwidth, int center_segment0,
+                           int center_segment1, u32 vht_caps)
+{
+       int tmp;
+
+       os_memset(data, 0, sizeof(*data));
+       data->mode = mode;
+       data->freq = freq;
+       data->channel = channel;
+       data->ht_enabled = ht_enabled;
+       data->vht_enabled = vht_enabled;
+       data->sec_channel_offset = sec_channel_offset;
+       data->center_freq1 = freq + sec_channel_offset * 10;
+       data->center_freq2 = 0;
+       data->bandwidth = sec_channel_offset ? 40 : 20;
+
+       if (data->vht_enabled) switch (vht_oper_chwidth) {
+       case VHT_CHANWIDTH_USE_HT:
+               if (center_segment1)
+                       return -1;
+               if (center_segment0 != 0 &&
+                   5000 + center_segment0 * 5 != data->center_freq1 &&
+                   2407 + center_segment0 * 5 != data->center_freq1)
+                       return -1;
+               break;
+       case VHT_CHANWIDTH_80P80MHZ:
+               if (!(vht_caps & VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ)) {
+                       wpa_printf(MSG_ERROR,
+                                  "80+80 channel width is not supported!");
+                       return -1;
+               }
+               if (center_segment1 == center_segment0 + 4 ||
+                   center_segment1 == center_segment0 - 4)
+                       return -1;
+               data->center_freq2 = 5000 + center_segment1 * 5;
+               /* fall through */
+       case VHT_CHANWIDTH_80MHZ:
+               data->bandwidth = 80;
+               if (vht_oper_chwidth == 1 && center_segment1)
+                       return -1;
+               if (vht_oper_chwidth == 3 && !center_segment1)
+                       return -1;
+               if (!sec_channel_offset)
+                       return -1;
+               /* primary 40 part must match the HT configuration */
+               tmp = (30 + freq - 5000 - center_segment0 * 5) / 20;
+               tmp /= 2;
+               if (data->center_freq1 != 5000 +
+                   center_segment0 * 5 - 20 + 40 * tmp)
+                       return -1;
+               data->center_freq1 = 5000 + center_segment0 * 5;
+               break;
+       case VHT_CHANWIDTH_160MHZ:
+               data->bandwidth = 160;
+               if (!(vht_caps & (VHT_CAP_SUPP_CHAN_WIDTH_160MHZ |
+                                 VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ))) {
+                       wpa_printf(MSG_ERROR,
+                                  "160MHZ channel width is not supported!");
+                       return -1;
+               }
+               if (center_segment1)
+                       return -1;
+               if (!sec_channel_offset)
+                       return -1;
+               /* primary 40 part must match the HT configuration */
+               tmp = (70 + freq - 5000 - center_segment0 * 5) / 20;
+               tmp /= 2;
+               if (data->center_freq1 != 5000 +
+                   center_segment0 * 5 - 60 + 40 * tmp)
+                       return -1;
+               data->center_freq1 = 5000 + center_segment0 * 5;
+               break;
+       }
+
+       return 0;
+}
diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h
new file mode 100755 (executable)
index 0000000..7f43d00
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Common hostapd/wpa_supplicant HW features
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2015, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HW_FEATURES_COMMON_H
+#define HW_FEATURES_COMMON_H
+
+#include "drivers/driver.h"
+
+struct hostapd_channel_data * hw_get_channel_chan(struct hostapd_hw_modes *mode,
+                                                 int chan, int *freq);
+struct hostapd_channel_data * hw_get_channel_freq(struct hostapd_hw_modes *mode,
+                                                 int freq, int *chan);
+
+int hw_get_freq(struct hostapd_hw_modes *mode, int chan);
+int hw_get_chan(struct hostapd_hw_modes *mode, int freq);
+
+int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
+                             int sec_chan);
+void get_pri_sec_chan(struct wpa_scan_res *bss, int *pri_chan, int *sec_chan);
+int check_40mhz_5g(struct hostapd_hw_modes *mode,
+                  struct wpa_scan_results *scan_res, int pri_chan,
+                  int sec_chan);
+int check_20mhz_bss(struct wpa_scan_res *bss, int pri_freq, int start, int end);
+int check_40mhz_2g4(struct hostapd_hw_modes *mode,
+                   struct wpa_scan_results *scan_res, int pri_chan,
+                   int sec_chan);
+int hostapd_set_freq_params(struct hostapd_freq_params *data,
+                           enum hostapd_hw_mode mode,
+                           int freq, int channel, int ht_enabled,
+                           int vht_enabled, int sec_channel_offset,
+                           int vht_oper_chwidth, int center_segment0,
+                           int center_segment1, u32 vht_caps);
+
+#endif /* HW_FEATURES_COMMON_H */
index aab8ac6..aca0b73 100644 (file)
@@ -108,10 +108,15 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
                        elems->hs20 = pos;
                        elems->hs20_len = elen;
                        break;
+               case HS20_OSEN_OUI_TYPE:
+                       /* Hotspot 2.0 OSEN */
+                       elems->osen = pos;
+                       elems->osen_len = elen;
+                       break;
                default:
                        wpa_printf(MSG_MSGDUMP, "Unknown WFA "
                                   "information element ignored "
-                                  "(type=%d len=%lu)\n",
+                                  "(type=%d len=%lu)",
                                   pos[3], (unsigned long) elen);
                        return -1;
                }
@@ -123,6 +128,15 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
                        elems->vendor_ht_cap = pos;
                        elems->vendor_ht_cap_len = elen;
                        break;
+               case VENDOR_VHT_TYPE:
+                       if (elen > 4 &&
+                           (pos[4] == VENDOR_VHT_SUBTYPE ||
+                            pos[4] == VENDOR_VHT_SUBTYPE2)) {
+                               elems->vendor_vht = pos;
+                               elems->vendor_vht_len = elen;
+                       } else
+                               return -1;
+                       break;
                default:
                        wpa_printf(MSG_EXCESSIVE, "Unknown Broadcom "
                                   "information element ignored "
@@ -189,25 +203,12 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
                        elems->supp_rates = pos;
                        elems->supp_rates_len = elen;
                        break;
-               case WLAN_EID_FH_PARAMS:
-                       elems->fh_params = pos;
-                       elems->fh_params_len = elen;
-                       break;
                case WLAN_EID_DS_PARAMS:
                        elems->ds_params = pos;
                        elems->ds_params_len = elen;
                        break;
                case WLAN_EID_CF_PARAMS:
-                       elems->cf_params = pos;
-                       elems->cf_params_len = elen;
-                       break;
                case WLAN_EID_TIM:
-                       elems->tim = pos;
-                       elems->tim_len = elen;
-                       break;
-               case WLAN_EID_IBSS_PARAMS:
-                       elems->ibss_params = pos;
-                       elems->ibss_params_len = elen;
                        break;
                case WLAN_EID_CHALLENGE:
                        elems->challenge = pos;
@@ -232,8 +233,6 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
                        elems->rsn_ie_len = elen;
                        break;
                case WLAN_EID_PWR_CAPABILITY:
-                       elems->power_cap = pos;
-                       elems->power_cap_len = elen;
                        break;
                case WLAN_EID_SUPPORTED_CHANNELS:
                        elems->supp_channels = pos;
@@ -259,6 +258,18 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
                        elems->ht_operation = pos;
                        elems->ht_operation_len = elen;
                        break;
+               case WLAN_EID_MESH_CONFIG:
+                       elems->mesh_config = pos;
+                       elems->mesh_config_len = elen;
+                       break;
+               case WLAN_EID_MESH_ID:
+                       elems->mesh_id = pos;
+                       elems->mesh_id_len = elen;
+                       break;
+               case WLAN_EID_PEER_MGMT:
+                       elems->peer_mgmt = pos;
+                       elems->peer_mgmt_len = elen;
+                       break;
                case WLAN_EID_VHT_CAP:
                        elems->vht_capabilities = pos;
                        elems->vht_capabilities_len = elen;
@@ -267,6 +278,11 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
                        elems->vht_operation = pos;
                        elems->vht_operation_len = elen;
                        break;
+               case WLAN_EID_VHT_OPERATING_MODE_NOTIFICATION:
+                       if (elen != 1)
+                               break;
+                       elems->vht_opmode_notif = pos;
+                       break;
                case WLAN_EID_LINK_ID:
                        if (elen < 18)
                                break;
@@ -276,6 +292,12 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
                        elems->interworking = pos;
                        elems->interworking_len = elen;
                        break;
+               case WLAN_EID_QOS_MAP_SET:
+                       if (elen < 16)
+                               break;
+                       elems->qos_map_set = pos;
+                       elems->qos_map_set_len = elen;
+                       break;
                case WLAN_EID_EXT_CAPAB:
                        elems->ext_capab = pos;
                        elems->ext_capab_len = elen;
@@ -289,6 +311,16 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
                        elems->ssid_list = pos;
                        elems->ssid_list_len = elen;
                        break;
+               case WLAN_EID_AMPE:
+                       elems->ampe = pos;
+                       elems->ampe_len = elen;
+                       break;
+               case WLAN_EID_MIC:
+                       elems->mic = pos;
+                       elems->mic_len = elen;
+                       /* after mic everything is encrypted, so stop. */
+                       left = elen;
+                       break;
                default:
                        unknown++;
                        if (!show_errors)
@@ -514,6 +546,293 @@ enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel)
 }
 
 
+static const char *us_op_class_cc[] = {
+       "US", "CA", NULL
+};
+
+static const char *eu_op_class_cc[] = {
+       "AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE",
+       "DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT",
+       "LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT",
+       "RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK", NULL
+};
+
+static const char *jp_op_class_cc[] = {
+       "JP", NULL
+};
+
+static const char *cn_op_class_cc[] = {
+       "CN", "CA", NULL
+};
+
+
+static int country_match(const char *cc[], const char *country)
+{
+       int i;
+
+       if (country == NULL)
+               return 0;
+       for (i = 0; cc[i]; i++) {
+               if (cc[i][0] == country[0] && cc[i][1] == country[1])
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+static int ieee80211_chan_to_freq_us(u8 op_class, u8 chan)
+{
+       switch (op_class) {
+       case 12: /* channels 1..11 */
+       case 32: /* channels 1..7; 40 MHz */
+       case 33: /* channels 5..11; 40 MHz */
+               if (chan < 1 || chan > 11)
+                       return -1;
+               return 2407 + 5 * chan;
+       case 1: /* channels 36,40,44,48 */
+       case 2: /* channels 52,56,60,64; dfs */
+       case 22: /* channels 36,44; 40 MHz */
+       case 23: /* channels 52,60; 40 MHz */
+       case 27: /* channels 40,48; 40 MHz */
+       case 28: /* channels 56,64; 40 MHz */
+               if (chan < 36 || chan > 64)
+                       return -1;
+               return 5000 + 5 * chan;
+       case 4: /* channels 100-144 */
+       case 24: /* channels 100-140; 40 MHz */
+               if (chan < 100 || chan > 144)
+                       return -1;
+               return 5000 + 5 * chan;
+       case 3: /* channels 149,153,157,161 */
+       case 25: /* channels 149,157; 40 MHz */
+       case 26: /* channels 149,157; 40 MHz */
+       case 30: /* channels 153,161; 40 MHz */
+       case 31: /* channels 153,161; 40 MHz */
+               if (chan < 149 || chan > 161)
+                       return -1;
+               return 5000 + 5 * chan;
+       case 34: /* 60 GHz band, channels 1..3 */
+               if (chan < 1 || chan > 3)
+                       return -1;
+               return 56160 + 2160 * chan;
+       }
+       return -1;
+}
+
+
+static int ieee80211_chan_to_freq_eu(u8 op_class, u8 chan)
+{
+       switch (op_class) {
+       case 4: /* channels 1..13 */
+       case 11: /* channels 1..9; 40 MHz */
+       case 12: /* channels 5..13; 40 MHz */
+               if (chan < 1 || chan > 13)
+                       return -1;
+               return 2407 + 5 * chan;
+       case 1: /* channels 36,40,44,48 */
+       case 2: /* channels 52,56,60,64; dfs */
+       case 5: /* channels 36,44; 40 MHz */
+       case 6: /* channels 52,60; 40 MHz */
+       case 8: /* channels 40,48; 40 MHz */
+       case 9: /* channels 56,64; 40 MHz */
+               if (chan < 36 || chan > 64)
+                       return -1;
+               return 5000 + 5 * chan;
+       case 3: /* channels 100-140 */
+       case 7: /* channels 100-132; 40 MHz */
+       case 10: /* channels 104-136; 40 MHz */
+       case 16: /* channels 100-140 */
+               if (chan < 100 || chan > 140)
+                       return -1;
+               return 5000 + 5 * chan;
+       case 17: /* channels 149,153,157,161,165,169 */
+               if (chan < 149 || chan > 169)
+                       return -1;
+               return 5000 + 5 * chan;
+       case 18: /* 60 GHz band, channels 1..4 */
+               if (chan < 1 || chan > 4)
+                       return -1;
+               return 56160 + 2160 * chan;
+       }
+       return -1;
+}
+
+
+static int ieee80211_chan_to_freq_jp(u8 op_class, u8 chan)
+{
+       switch (op_class) {
+       case 30: /* channels 1..13 */
+       case 56: /* channels 1..9; 40 MHz */
+       case 57: /* channels 5..13; 40 MHz */
+               if (chan < 1 || chan > 13)
+                       return -1;
+               return 2407 + 5 * chan;
+       case 31: /* channel 14 */
+               if (chan != 14)
+                       return -1;
+               return 2414 + 5 * chan;
+       case 1: /* channels 34,38,42,46(old) or 36,40,44,48 */
+       case 32: /* channels 52,56,60,64 */
+       case 33: /* channels 52,56,60,64 */
+       case 36: /* channels 36,44; 40 MHz */
+       case 37: /* channels 52,60; 40 MHz */
+       case 38: /* channels 52,60; 40 MHz */
+       case 41: /* channels 40,48; 40 MHz */
+       case 42: /* channels 56,64; 40 MHz */
+       case 43: /* channels 56,64; 40 MHz */
+               if (chan < 34 || chan > 64)
+                       return -1;
+               return 5000 + 5 * chan;
+       case 34: /* channels 100-140 */
+       case 35: /* channels 100-140 */
+       case 39: /* channels 100-132; 40 MHz */
+       case 40: /* channels 100-132; 40 MHz */
+       case 44: /* channels 104-136; 40 MHz */
+       case 45: /* channels 104-136; 40 MHz */
+       case 58: /* channels 100-140 */
+               if (chan < 100 || chan > 140)
+                       return -1;
+               return 5000 + 5 * chan;
+       case 59: /* 60 GHz band, channels 1..4 */
+               if (chan < 1 || chan > 3)
+                       return -1;
+               return 56160 + 2160 * chan;
+       }
+       return -1;
+}
+
+
+static int ieee80211_chan_to_freq_cn(u8 op_class, u8 chan)
+{
+       switch (op_class) {
+       case 7: /* channels 1..13 */
+       case 8: /* channels 1..9; 40 MHz */
+       case 9: /* channels 5..13; 40 MHz */
+               if (chan < 1 || chan > 13)
+                       return -1;
+               return 2407 + 5 * chan;
+       case 1: /* channels 36,40,44,48 */
+       case 2: /* channels 52,56,60,64; dfs */
+       case 4: /* channels 36,44; 40 MHz */
+       case 5: /* channels 52,60; 40 MHz */
+               if (chan < 36 || chan > 64)
+                       return -1;
+               return 5000 + 5 * chan;
+       case 3: /* channels 149,153,157,161,165 */
+       case 6: /* channels 149,157; 40 MHz */
+               if (chan < 149 || chan > 165)
+                       return -1;
+               return 5000 + 5 * chan;
+       }
+       return -1;
+}
+
+
+static int ieee80211_chan_to_freq_global(u8 op_class, u8 chan)
+{
+       /* Table E-4 in IEEE Std 802.11-2012 - Global operating classes */
+       switch (op_class) {
+       case 81:
+               /* channels 1..13 */
+               if (chan < 1 || chan > 13)
+                       return -1;
+               return 2407 + 5 * chan;
+       case 82:
+               /* channel 14 */
+               if (chan != 14)
+                       return -1;
+               return 2414 + 5 * chan;
+       case 83: /* channels 1..9; 40 MHz */
+       case 84: /* channels 5..13; 40 MHz */
+               if (chan < 1 || chan > 13)
+                       return -1;
+               return 2407 + 5 * chan;
+       case 115: /* channels 36,40,44,48; indoor only */
+       case 116: /* channels 36,44; 40 MHz; indoor only */
+       case 117: /* channels 40,48; 40 MHz; indoor only */
+       case 118: /* channels 52,56,60,64; dfs */
+       case 119: /* channels 52,60; 40 MHz; dfs */
+       case 120: /* channels 56,64; 40 MHz; dfs */
+               if (chan < 36 || chan > 64)
+                       return -1;
+               return 5000 + 5 * chan;
+       case 121: /* channels 100-140 */
+       case 122: /* channels 100-142; 40 MHz */
+       case 123: /* channels 104-136; 40 MHz */
+               if (chan < 100 || chan > 140)
+                       return -1;
+               return 5000 + 5 * chan;
+       case 124: /* channels 149,153,157,161 */
+       case 125: /* channels 149,153,157,161,165,169 */
+       case 126: /* channels 149,157; 40 MHz */
+       case 127: /* channels 153,161; 40 MHz */
+               if (chan < 149 || chan > 161)
+                       return -1;
+               return 5000 + 5 * chan;
+       case 128: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
+       case 130: /* center freqs 42, 58, 106, 122, 138, 155; 80 MHz */
+               if (chan < 36 || chan > 161)
+                       return -1;
+               return 5000 + 5 * chan;
+       case 129: /* center freqs 50, 114; 160 MHz */
+               if (chan < 50 || chan > 114)
+                       return -1;
+               return 5000 + 5 * chan;
+       case 180: /* 60 GHz band, channels 1..4 */
+               if (chan < 1 || chan > 4)
+                       return -1;
+               return 56160 + 2160 * chan;
+       }
+       return -1;
+}
+
+/**
+ * ieee80211_chan_to_freq - Convert channel info to frequency
+ * @country: Country code, if known; otherwise, global operating class is used
+ * @op_class: Operating class
+ * @chan: Channel number
+ * Returns: Frequency in MHz or -1 if the specified channel is unknown
+ */
+int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan)
+{
+       int freq;
+
+       if (country_match(us_op_class_cc, country)) {
+               freq = ieee80211_chan_to_freq_us(op_class, chan);
+               if (freq > 0)
+                       return freq;
+       }
+
+       if (country_match(eu_op_class_cc, country)) {
+               freq = ieee80211_chan_to_freq_eu(op_class, chan);
+               if (freq > 0)
+                       return freq;
+       }
+
+       if (country_match(jp_op_class_cc, country)) {
+               freq = ieee80211_chan_to_freq_jp(op_class, chan);
+               if (freq > 0)
+                       return freq;
+       }
+
+       if (country_match(cn_op_class_cc, country)) {
+               freq = ieee80211_chan_to_freq_cn(op_class, chan);
+               if (freq > 0)
+                       return freq;
+       }
+
+       return ieee80211_chan_to_freq_global(op_class, chan);
+}
+
+
+int ieee80211_is_dfs(int freq)
+{
+       /* TODO: this could be more accurate to better cover all domains */
+       return (freq >= 5260 && freq <= 5320) || (freq >= 5500 && freq <= 5700);
+}
+
+
 static int is_11b(u8 rate)
 {
        return rate == 0x02 || rate == 0x04 || rate == 0x0b || rate == 0x16;
@@ -545,3 +864,60 @@ int supp_rates_11b_only(struct ieee802_11_elems *elems)
 
        return num_11b > 0 && num_others == 0;
 }
+
+
+const char * fc2str(u16 fc)
+{
+       u16 stype = WLAN_FC_GET_STYPE(fc);
+#define C2S(x) case x: return #x;
+
+       switch (WLAN_FC_GET_TYPE(fc)) {
+       case WLAN_FC_TYPE_MGMT:
+               switch (stype) {
+               C2S(WLAN_FC_STYPE_ASSOC_REQ)
+               C2S(WLAN_FC_STYPE_ASSOC_RESP)
+               C2S(WLAN_FC_STYPE_REASSOC_REQ)
+               C2S(WLAN_FC_STYPE_REASSOC_RESP)
+               C2S(WLAN_FC_STYPE_PROBE_REQ)
+               C2S(WLAN_FC_STYPE_PROBE_RESP)
+               C2S(WLAN_FC_STYPE_BEACON)
+               C2S(WLAN_FC_STYPE_ATIM)
+               C2S(WLAN_FC_STYPE_DISASSOC)
+               C2S(WLAN_FC_STYPE_AUTH)
+               C2S(WLAN_FC_STYPE_DEAUTH)
+               C2S(WLAN_FC_STYPE_ACTION)
+               }
+               break;
+       case WLAN_FC_TYPE_CTRL:
+               switch (stype) {
+               C2S(WLAN_FC_STYPE_PSPOLL)
+               C2S(WLAN_FC_STYPE_RTS)
+               C2S(WLAN_FC_STYPE_CTS)
+               C2S(WLAN_FC_STYPE_ACK)
+               C2S(WLAN_FC_STYPE_CFEND)
+               C2S(WLAN_FC_STYPE_CFENDACK)
+               }
+               break;
+       case WLAN_FC_TYPE_DATA:
+               switch (stype) {
+               C2S(WLAN_FC_STYPE_DATA)
+               C2S(WLAN_FC_STYPE_DATA_CFACK)
+               C2S(WLAN_FC_STYPE_DATA_CFPOLL)
+               C2S(WLAN_FC_STYPE_DATA_CFACKPOLL)
+               C2S(WLAN_FC_STYPE_NULLFUNC)
+               C2S(WLAN_FC_STYPE_CFACK)
+               C2S(WLAN_FC_STYPE_CFPOLL)
+               C2S(WLAN_FC_STYPE_CFACKPOLL)
+               C2S(WLAN_FC_STYPE_QOS_DATA)
+               C2S(WLAN_FC_STYPE_QOS_DATA_CFACK)
+               C2S(WLAN_FC_STYPE_QOS_DATA_CFPOLL)
+               C2S(WLAN_FC_STYPE_QOS_DATA_CFACKPOLL)
+               C2S(WLAN_FC_STYPE_QOS_NULL)
+               C2S(WLAN_FC_STYPE_QOS_CFPOLL)
+               C2S(WLAN_FC_STYPE_QOS_CFACKPOLL)
+               }
+               break;
+       }
+       return "WLAN_FC_TYPE_UNKNOWN";
+#undef C2S
+}
index 68c6b96..7f0b296 100644 (file)
 struct ieee802_11_elems {
        const u8 *ssid;
        const u8 *supp_rates;
-       const u8 *fh_params;
        const u8 *ds_params;
-       const u8 *cf_params;
-       const u8 *tim;
-       const u8 *ibss_params;
        const u8 *challenge;
        const u8 *erp_info;
        const u8 *ext_supp_rates;
@@ -26,32 +22,36 @@ struct ieee802_11_elems {
        const u8 *wmm; /* WMM Information or Parameter Element */
        const u8 *wmm_tspec;
        const u8 *wps_ie;
-       const u8 *power_cap;
        const u8 *supp_channels;
        const u8 *mdie;
        const u8 *ftie;
        const u8 *timeout_int;
        const u8 *ht_capabilities;
        const u8 *ht_operation;
+       const u8 *mesh_config;
+       const u8 *mesh_id;
+       const u8 *peer_mgmt;
        const u8 *vht_capabilities;
        const u8 *vht_operation;
+       const u8 *vht_opmode_notif;
        const u8 *vendor_ht_cap;
+       const u8 *vendor_vht;
        const u8 *p2p;
        const u8 *wfd;
        const u8 *link_id;
        const u8 *interworking;
+       const u8 *qos_map_set;
        const u8 *hs20;
        const u8 *ext_capab;
        const u8 *bss_max_idle_period;
        const u8 *ssid_list;
+       const u8 *osen;
+       const u8 *ampe;
+       const u8 *mic;
 
        u8 ssid_len;
        u8 supp_rates_len;
-       u8 fh_params_len;
        u8 ds_params_len;
-       u8 cf_params_len;
-       u8 tim_len;
-       u8 ibss_params_len;
        u8 challenge_len;
        u8 erp_info_len;
        u8 ext_supp_rates_len;
@@ -60,22 +60,29 @@ struct ieee802_11_elems {
        u8 wmm_len; /* 7 = WMM Information; 24 = WMM Parameter */
        u8 wmm_tspec_len;
        u8 wps_ie_len;
-       u8 power_cap_len;
        u8 supp_channels_len;
        u8 mdie_len;
        u8 ftie_len;
        u8 timeout_int_len;
        u8 ht_capabilities_len;
        u8 ht_operation_len;
+       u8 mesh_config_len;
+       u8 mesh_id_len;
+       u8 peer_mgmt_len;
        u8 vht_capabilities_len;
        u8 vht_operation_len;
        u8 vendor_ht_cap_len;
+       u8 vendor_vht_len;
        u8 p2p_len;
        u8 wfd_len;
        u8 interworking_len;
+       u8 qos_map_set_len;
        u8 hs20_len;
        u8 ext_capab_len;
        u8 ssid_list_len;
+       u8 osen_len;
+       u8 ampe_len;
+       u8 mic_len;
 };
 
 typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes;
@@ -100,7 +107,10 @@ struct hostapd_wmm_ac_params {
 int hostapd_config_wmm_ac(struct hostapd_wmm_ac_params wmm_ac_params[],
                          const char *name, const char *val);
 enum hostapd_hw_mode ieee80211_freq_to_chan(int freq, u8 *channel);
+int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan);
+int ieee80211_is_dfs(int freq);
 
 int supp_rates_11b_only(struct ieee802_11_elems *elems);
 
+const char * fc2str(u16 fc);
 #endif /* IEEE802_11_COMMON_H */
index 137c309..2e51935 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.11 Frame type definitions
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2007-2008 Intel Corporation
  *
  * This software may be distributed under the terms of the BSD license.
@@ -25,6 +25,8 @@
 #define WLAN_FC_GET_TYPE(fc)   (((fc) & 0x000c) >> 2)
 #define WLAN_FC_GET_STYPE(fc)  (((fc) & 0x00f0) >> 4)
 
+#define WLAN_INVALID_MGMT_SEQ   0xFFFF
+
 #define WLAN_GET_SEQ_FRAG(seq) ((seq) & (BIT(3) | BIT(2) | BIT(1) | BIT(0)))
 #define WLAN_GET_SEQ_SEQ(seq) \
        (((seq) & (~(BIT(3) | BIT(2) | BIT(1) | BIT(0)))) >> 4)
 #define WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ 76
 #define WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED 77
 #define WLAN_STATUS_TRANSMISSION_FAILURE 79
+#define WLAN_STATUS_QUERY_RESP_OUTSTANDING 95
+#define WLAN_STATUS_ASSOC_DENIED_NO_VHT 104
 
 /* Reason codes (IEEE 802.11-2007, 7.3.1.7, Table 7-22) */
 #define WLAN_REASON_UNSPECIFIED 1
 #define WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED 26
 /* IEEE 802.11e */
 #define WLAN_REASON_DISASSOC_LOW_ACK 34
+/* IEEE 802.11s */
+#define WLAN_REASON_MESH_PEERING_CANCELLED 52
+#define WLAN_REASON_MESH_MAX_PEERS 53
+#define WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION 54
+#define WLAN_REASON_MESH_CLOSE_RCVD 55
+#define WLAN_REASON_MESH_MAX_RETRIES 56
+#define WLAN_REASON_MESH_CONFIRM_TIMEOUT 57
+#define WLAN_REASON_MESH_INVALID_GTK 58
+#define WLAN_REASON_MESH_INCONSISTENT_PARAMS 59
+#define WLAN_REASON_MESH_INVALID_SECURITY_CAP 60
 
 
 /* Information Element IDs */
 #define WLAN_EID_TIM 5
 #define WLAN_EID_IBSS_PARAMS 6
 #define WLAN_EID_COUNTRY 7
+#define WLAN_EID_BSS_LOAD 11
 #define WLAN_EID_CHALLENGE 16
 /* EIDs defined by IEEE 802.11h - START */
 #define WLAN_EID_PWR_CONSTRAINT 32
 #define WLAN_EID_QOS 46
 #define WLAN_EID_RSN 48
 #define WLAN_EID_EXT_SUPP_RATES 50
+#define WLAN_EID_NEIGHBOR_REPORT 52
 #define WLAN_EID_MOBILITY_DOMAIN 54
 #define WLAN_EID_FAST_BSS_TRANSITION 55
 #define WLAN_EID_TIMEOUT_INTERVAL 56
 #define WLAN_EID_RIC_DATA 57
+#define WLAN_EID_SUPPORTED_OPERATING_CLASSES 59
 #define WLAN_EID_HT_OPERATION 61
 #define WLAN_EID_SECONDARY_CHANNEL_OFFSET 62
 #define WLAN_EID_WAPI 68
 #define WLAN_EID_TIME_ADVERTISEMENT 69
+#define WLAN_EID_RRM_ENABLED_CAPABILITIES 70
 #define WLAN_EID_20_40_BSS_COEXISTENCE 72
 #define WLAN_EID_20_40_BSS_INTOLERANT 73
 #define WLAN_EID_OVERLAPPING_BSS_SCAN_PARAMS 74
 #define WLAN_EID_LINK_ID 101
 #define WLAN_EID_INTERWORKING 107
 #define WLAN_EID_ADV_PROTO 108
+#define WLAN_EID_QOS_MAP_SET 110
 #define WLAN_EID_ROAMING_CONSORTIUM 111
+#define WLAN_EID_MESH_CONFIG 113
+#define WLAN_EID_MESH_ID 114
+#define WLAN_EID_PEER_MGMT 117
 #define WLAN_EID_EXT_CAPAB 127
+#define WLAN_EID_AMPE 139
+#define WLAN_EID_MIC 140
 #define WLAN_EID_CCKM 156
 #define WLAN_EID_VHT_CAP 191
 #define WLAN_EID_VHT_OPERATION 192
 #define WLAN_ACTION_FT 6
 #define WLAN_ACTION_HT 7
 #define WLAN_ACTION_SA_QUERY 8
+#define WLAN_ACTION_PROTECTED_DUAL 9
 #define WLAN_ACTION_WNM 10
 #define WLAN_ACTION_UNPROTECTED_WNM 11
 #define WLAN_ACTION_TDLS 12
+#define WLAN_ACTION_SELF_PROTECTED 15
 #define WLAN_ACTION_WMM 17 /* WMM Specification 1.1 */
 #define WLAN_ACTION_VENDOR_SPECIFIC 127
 
 #define WLAN_PA_GAS_COMEBACK_RESP 13
 #define WLAN_TDLS_DISCOVERY_RESPONSE 14
 
+/* Protected Dual of Public Action frames */
+#define WLAN_PROT_DSE_ENABLEMENT 1
+#define WLAN_PROT_DSE_DEENABLEMENT 2
+#define WLAN_PROT_EXT_CSA 4
+#define WLAN_PROT_MEASUREMENT_REQ 5
+#define WLAN_PROT_MEASUREMENT_REPORT 6
+#define WLAN_PROT_DSE_POWER_CONSTRAINT 8
+#define WLAN_PROT_VENDOR_SPECIFIC 9
+#define WLAN_PROT_GAS_INITIAL_REQ 10
+#define WLAN_PROT_GAS_INITIAL_RESP 11
+#define WLAN_PROT_GAS_COMEBACK_REQ 12
+#define WLAN_PROT_GAS_COMEBACK_RESP 13
+
 /* SA Query Action frame (IEEE 802.11w/D8.0, 7.4.9) */
 #define WLAN_SA_QUERY_REQUEST 0
 #define WLAN_SA_QUERY_RESPONSE 1
 #define WLAN_TDLS_PEER_TRAFFIC_RESPONSE 9
 #define WLAN_TDLS_DISCOVERY_REQUEST 10
 
+/* Radio Measurement Action codes */
+#define WLAN_RRM_RADIO_MEASUREMENT_REQUEST 0
+#define WLAN_RRM_RADIO_MEASUREMENT_REPORT 1
+#define WLAN_RRM_LINK_MEASUREMENT_REQUEST 2
+#define WLAN_RRM_LINK_MEASUREMENT_REPORT 3
+#define WLAN_RRM_NEIGHBOR_REPORT_REQUEST 4
+#define WLAN_RRM_NEIGHBOR_REPORT_RESPONSE 5
+
+/* Radio Measurement capabilities (from RRM Capabilities IE) */
+/* byte 1 (out of 5) */
+#define WLAN_RRM_CAPS_LINK_MEASUREMENT BIT(0)
+#define WLAN_RRM_CAPS_NEIGHBOR_REPORT BIT(1)
+
 /* Timeout Interval Type */
 #define WLAN_TIMEOUT_REASSOC_DEADLINE 1
 #define WLAN_TIMEOUT_KEY_LIFETIME 2
@@ -557,6 +609,10 @@ struct ieee80211_mgmt {
                                         * Entries (optional) */
                                        u8 variable[0];
                                } STRUCT_PACKED bss_tm_query;
+                               struct {
+                                       u8 action; /* 15 */
+                                       u8 variable[0];
+                               } STRUCT_PACKED slf_prot_action;
                        } u;
                } STRUCT_PACKED action;
        } u;
@@ -566,9 +622,12 @@ struct ieee80211_mgmt {
 /* Rx MCS bitmask is in the first 77 bits of supported_mcs_set */
 #define IEEE80211_HT_MCS_MASK_LEN 10
 
+/* HT Capabilities element */
 struct ieee80211_ht_capabilities {
        le16 ht_capabilities_info;
-       u8 a_mpdu_params;
+       u8 a_mpdu_params; /* Maximum A-MPDU Length Exponent B0..B1
+                          * Minimum MPDU Start Spacing B2..B4
+                          * Reserved B5..B7 */
        u8 supported_mcs_set[16];
        le16 ht_extended_capabilities;
        le32 tx_bf_capability_info;
@@ -576,12 +635,25 @@ struct ieee80211_ht_capabilities {
 } STRUCT_PACKED;
 
 
+/* HT Operation element */
 struct ieee80211_ht_operation {
-       u8 control_chan;
-       u8 ht_param;
-       le16 operation_mode;
-       le16 stbc_param;
-       u8 basic_set[16];
+       u8 primary_chan;
+       /* Five octets of HT Operation Information */
+       u8 ht_param; /* B0..B7 */
+       le16 operation_mode; /* B8..B23 */
+       le16 param; /* B24..B39 */
+       u8 basic_mcs_set[16];
+} STRUCT_PACKED;
+
+
+struct ieee80211_obss_scan_parameters {
+       le16 scan_passive_dwell;
+       le16 scan_active_dwell;
+       le16 width_trigger_scan_interval;
+       le16 scan_passive_total_per_channel;
+       le16 scan_active_total_per_channel;
+       le16 channel_transition_delay_factor;
+       le16 scan_activity_threshold;
 } STRUCT_PACKED;
 
 
@@ -602,6 +674,15 @@ struct ieee80211_vht_operation {
        le16 vht_basic_mcs_set;
 } STRUCT_PACKED;
 
+struct ieee80211_ampe_ie {
+       u8 selected_pairwise_suite[4];
+       u8 local_nonce[32];
+       u8 peer_nonce[32];
+       u8 mgtk[16];
+       u8 key_rsc[8];
+       u8 key_expiration[4];
+} STRUCT_PACKED;
+
 #ifdef _MSC_VER
 #pragma pack(pop)
 #endif /* _MSC_VER */
@@ -610,7 +691,9 @@ struct ieee80211_vht_operation {
 #define ERP_INFO_USE_PROTECTION BIT(1)
 #define ERP_INFO_BARKER_PREAMBLE_MODE BIT(2)
 
+#define OVERLAPPING_BSS_TRANS_DELAY_FACTOR 5
 
+/* HT Capabilities Info field within HT Capabilities element */
 #define HT_CAP_INFO_LDPC_CODING_CAP            ((u16) BIT(0))
 #define HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET     ((u16) BIT(1))
 #define HT_CAP_INFO_SMPS_MASK                  ((u16) (BIT(2) | BIT(3)))
@@ -628,73 +711,86 @@ struct ieee80211_vht_operation {
 #define HT_CAP_INFO_DELAYED_BA                 ((u16) BIT(10))
 #define HT_CAP_INFO_MAX_AMSDU_SIZE             ((u16) BIT(11))
 #define HT_CAP_INFO_DSSS_CCK40MHZ              ((u16) BIT(12))
-#define HT_CAP_INFO_PSMP_SUPP                  ((u16) BIT(13))
+/* B13 - Reserved (was PSMP support during P802.11n development) */
 #define HT_CAP_INFO_40MHZ_INTOLERANT           ((u16) BIT(14))
 #define HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT  ((u16) BIT(15))
 
-
+/* HT Extended Capabilities field within HT Capabilities element */
 #define EXT_HT_CAP_INFO_PCO                    ((u16) BIT(0))
+#define EXT_HT_CAP_INFO_PCO_TRANS_TIME_MASK    ((u16) (BIT(1) | BIT(2)))
 #define EXT_HT_CAP_INFO_TRANS_TIME_OFFSET      1
+/* B3..B7 - Reserved */
+#define EXT_HT_CAP_INFO_MCS_FEEDBACK_MASK      ((u16) (BIT(8) | BIT(9)))
 #define EXT_HT_CAP_INFO_MCS_FEEDBACK_OFFSET    8
-#define EXT_HT_CAP_INFO_HTC_SUPPORTED          ((u16) BIT(10))
+#define EXT_HT_CAP_INFO_HTC_SUPPORT            ((u16) BIT(10))
 #define EXT_HT_CAP_INFO_RD_RESPONDER           ((u16) BIT(11))
-
-
-#define TX_BEAMFORM_CAP_TXBF_CAP ((u32) BIT(0))
-#define TX_BEAMFORM_CAP_RX_STAGGERED_SOUNDING_CAP ((u32) BIT(1))
-#define TX_BEAMFORM_CAP_TX_STAGGERED_SOUNDING_CAP ((u32) BIT(2))
-#define TX_BEAMFORM_CAP_RX_ZLF_CAP ((u32) BIT(3))
-#define TX_BEAMFORM_CAP_TX_ZLF_CAP ((u32) BIT(4))
-#define TX_BEAMFORM_CAP_IMPLICIT_ZLF_CAP ((u32) BIT(5))
-#define TX_BEAMFORM_CAP_CALIB_OFFSET 6
-#define TX_BEAMFORM_CAP_EXPLICIT_CSI_TXBF_CAP ((u32) BIT(8))
-#define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_CAP ((u32) BIT(9))
-#define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_CAP ((u32) BIT(10))
-#define TX_BEAMFORM_CAP_EXPLICIT_BF_CSI_FEEDBACK_OFFSET 11
-#define TX_BEAMFORM_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_FEEDBACK_OFFSET 13
-#define TX_BEAMFORM_CAP_EXPLICIT_COMPRESSED_STEERING_MATRIX_FEEDBACK_OFFSET 15
-#define TX_BEAMFORM_CAP_MINIMAL_GROUPING_OFFSET 17
-#define TX_BEAMFORM_CAP_CSI_NUM_BEAMFORMER_ANT_OFFSET 19
-#define TX_BEAMFORM_CAP_UNCOMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 21
-#define TX_BEAMFORM_CAP_COMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 23
-#define TX_BEAMFORM_CAP_SCI_MAX_OF_ROWS_BEANFORMER_SUPPORTED_OFFSET 25
-
-
-#define ASEL_CAPABILITY_ASEL_CAPABLE ((u8) BIT(0))
-#define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(1))
-#define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(2))
-#define ASEL_CAPABILITY_EXPLICIT_CSI_FEEDBACK_CAP ((u8) BIT(3))
-#define ASEL_CAPABILITY_ANT_INDICES_FEEDBACK_CAP ((u8) BIT(4))
-#define ASEL_CAPABILITY_RX_AS_CAP ((u8) BIT(5))
-#define ASEL_CAPABILITY_TX_SOUND_PPDUS_CAP ((u8) BIT(6))
-
+/* B12..B15 - Reserved */
+
+/* Transmit Beanforming Capabilities within HT Capabilities element */
+#define TX_BF_CAP_IMPLICIT_TXBF_RX_CAP ((u32) BIT(0))
+#define TX_BF_CAP_RX_STAGGERED_SOUNDING_CAP ((u32) BIT(1))
+#define TX_BF_CAP_TX_STAGGERED_SOUNDING_CAP ((u32) BIT(2))
+#define TX_BF_CAP_RX_NDP_CAP ((u32) BIT(3))
+#define TX_BF_CAP_TX_NDP_CAP ((u32) BIT(4))
+#define TX_BF_CAP_IMPLICIT_TX_BF_CAP ((u32) BIT(5))
+#define TX_BF_CAP_CALIBRATION_MASK ((u32) (BIT(6) | BIT(7))
+#define TX_BF_CAP_CALIB_OFFSET 6
+#define TX_BF_CAP_EXPLICIT_CSI_TXBF_CAP ((u32) BIT(8))
+#define TX_BF_CAP_EXPLICIT_NONCOMPR_STEERING_CAP ((u32) BIT(9))
+#define TX_BF_CAP_EXPLICIT_COMPR_STEERING_CAP ((u32) BIT(10))
+#define TX_BF_CAP_EXPLICIT_TX_BF_CSI_FEEDBACK_MASK ((u32) (BIT(10) | BIT(11)))
+#define TX_BF_CAP_EXPLICIT_BF_CSI_FEEDBACK_OFFSET 11
+#define TX_BF_CAP_EXPLICIT_UNCOMPR_STEERING_MATRIX_FEEDBACK_OFFSET 13
+#define TX_BF_CAP_EXPLICIT_COMPRESSED_STEERING_MATRIX_FEEDBACK_OFFSET 15
+#define TX_BF_CAP_MINIMAL_GROUPING_OFFSET 17
+#define TX_BF_CAP_CSI_NUM_BEAMFORMER_ANT_OFFSET 19
+#define TX_BF_CAP_UNCOMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 21
+#define TX_BF_CAP_COMPRESSED_STEERING_MATRIX_BEAMFORMER_ANT_OFFSET 23
+#define TX_BF_CAP_SCI_MAX_OF_ROWS_BEANFORMER_SUPPORTED_OFFSET 25
+#define TX_BF_CAP_CHANNEL_ESTIMATION_CAP_MASK ((u32) (BIT(27) | BIT(28)))
+#define TX_BF_CAP_CHANNEL_ESTIMATION_CAP_OFFSET 27
+/* B29..B31 - Reserved */
+
+/* ASEL Capability field within HT Capabilities element */
+#define ASEL_CAP_ASEL_CAPABLE ((u8) BIT(0))
+#define ASEL_CAP_EXPLICIT_CSI_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(1))
+#define ASEL_CAP_ANT_INDICES_FEEDBACK_BASED_TX_AS_CAP ((u8) BIT(2))
+#define ASEL_CAP_EXPLICIT_CSI_FEEDBACK_CAP ((u8) BIT(3))
+#define ASEL_CAP_ANT_INDICES_FEEDBACK_CAP ((u8) BIT(4))
+#define ASEL_CAP_RX_AS_CAP ((u8) BIT(5))
+#define ASEL_CAP_TX_SOUNDING_PPDUS_CAP ((u8) BIT(6))
+/* B7 - Reserved */
+
+/* First octet of HT Operation Information within HT Operation element */
 #define HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK       ((u8) BIT(0) | BIT(1))
 #define HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE          ((u8) BIT(0))
 #define HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW          ((u8) BIT(0) | BIT(1))
-#define HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH          ((u8) BIT(2))
+#define HT_INFO_HT_PARAM_STA_CHNL_WIDTH                        ((u8) BIT(2))
 #define HT_INFO_HT_PARAM_RIFS_MODE                     ((u8) BIT(3))
-#define HT_INFO_HT_PARAM_CTRL_ACCESS_ONLY              ((u8) BIT(4))
-#define HT_INFO_HT_PARAM_SRV_INTERVAL_GRANULARITY      ((u8) BIT(5))
-
-
-#define OP_MODE_PURE                    0
-#define OP_MODE_MAY_BE_LEGACY_STAS      1
-#define OP_MODE_20MHZ_HT_STA_ASSOCED    2
-#define OP_MODE_MIXED                   3
-
-#define HT_INFO_OPERATION_MODE_OP_MODE_MASK    \
-               (0x0001 | 0x0002)
-#define HT_INFO_OPERATION_MODE_OP_MODE_OFFSET          0
-#define HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT     ((u8) BIT(2))
-#define HT_INFO_OPERATION_MODE_TRANSMIT_BURST_LIMIT    ((u8) BIT(3))
-#define HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT      ((u8) BIT(4))
-
-#define HT_INFO_STBC_PARAM_DUAL_BEACON                 ((u16) BIT(6))
-#define HT_INFO_STBC_PARAM_DUAL_STBC_PROTECT           ((u16) BIT(7))
-#define HT_INFO_STBC_PARAM_SECONDARY_BCN               ((u16) BIT(8))
-#define HT_INFO_STBC_PARAM_LSIG_TXOP_PROTECT_ALLOWED   ((u16) BIT(9))
-#define HT_INFO_STBC_PARAM_PCO_ACTIVE                  ((u16) BIT(10))
-#define HT_INFO_STBC_PARAM_PCO_PHASE                   ((u16) BIT(11))
+/* B4..B7 - Reserved */
+
+/* HT Protection (B8..B9 of HT Operation Information) */
+#define HT_PROT_NO_PROTECTION           0
+#define HT_PROT_NONMEMBER_PROTECTION    1
+#define HT_PROT_20MHZ_PROTECTION        2
+#define HT_PROT_NON_HT_MIXED            3
+/* Bits within ieee80211_ht_operation::operation_mode (BIT(0) maps to B8 in
+ * HT Operation Information) */
+#define HT_OPER_OP_MODE_HT_PROT_MASK ((u16) (BIT(0) | BIT(1))) /* B8..B9 */
+#define HT_OPER_OP_MODE_NON_GF_HT_STAS_PRESENT ((u16) BIT(2)) /* B10 */
+/* BIT(3), i.e., B11 in HT Operation Information field - Reserved */
+#define HT_OPER_OP_MODE_OBSS_NON_HT_STAS_PRESENT       ((u16) BIT(4)) /* B12 */
+/* BIT(5)..BIT(15), i.e., B13..B23 - Reserved */
+
+/* Last two octets of HT Operation Information (BIT(0) = B24) */
+/* B24..B29 - Reserved */
+#define HT_OPER_PARAM_DUAL_BEACON                      ((u16) BIT(6))
+#define HT_OPER_PARAM_DUAL_CTS_PROTECTION              ((u16) BIT(7))
+#define HT_OPER_PARAM_STBC_BEACON                      ((u16) BIT(8))
+#define HT_OPER_PARAM_LSIG_TXOP_PROT_FULL_SUPP         ((u16) BIT(9))
+#define HT_OPER_PARAM_PCO_ACTIVE                       ((u16) BIT(10))
+#define HT_OPER_PARAM_PCO_PHASE                                ((u16) BIT(11))
+/* B36..B39 - Reserved */
 
 #define BSS_MEMBERSHIP_SELECTOR_VHT_PHY 126
 #define BSS_MEMBERSHIP_SELECTOR_HT_PHY 127
@@ -702,8 +798,11 @@ struct ieee80211_vht_operation {
 /* VHT Defines */
 #define VHT_CAP_MAX_MPDU_LENGTH_7991                ((u32) BIT(0))
 #define VHT_CAP_MAX_MPDU_LENGTH_11454               ((u32) BIT(1))
+#define VHT_CAP_MAX_MPDU_LENGTH_MASK                ((u32) BIT(0) | BIT(1))
+#define VHT_CAP_MAX_MPDU_LENGTH_MASK_SHIFT          0
 #define VHT_CAP_SUPP_CHAN_WIDTH_160MHZ              ((u32) BIT(2))
 #define VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ     ((u32) BIT(3))
+#define VHT_CAP_SUPP_CHAN_WIDTH_MASK                ((u32) BIT(2) | BIT(3))
 #define VHT_CAP_RXLDPC                              ((u32) BIT(4))
 #define VHT_CAP_SHORT_GI_80                         ((u32) BIT(5))
 #define VHT_CAP_SHORT_GI_160                        ((u32) BIT(6))
@@ -712,20 +811,45 @@ struct ieee80211_vht_operation {
 #define VHT_CAP_RXSTBC_2                            ((u32) BIT(9))
 #define VHT_CAP_RXSTBC_3                            ((u32) BIT(8) | BIT(9))
 #define VHT_CAP_RXSTBC_4                            ((u32) BIT(10))
+#define VHT_CAP_RXSTBC_MASK                         ((u32) BIT(8) | BIT(9) | \
+                                                          BIT(10))
+#define VHT_CAP_RXSTBC_MASK_SHIFT                   8
 #define VHT_CAP_SU_BEAMFORMER_CAPABLE               ((u32) BIT(11))
 #define VHT_CAP_SU_BEAMFORMEE_CAPABLE               ((u32) BIT(12))
-#define VHT_CAP_BEAMFORMER_ANTENNAS_MAX             ((u32) BIT(13) | BIT(14))
-#define VHT_CAP_SOUNDING_DIMENTION_MAX              ((u32) BIT(16) | BIT(17))
+#define VHT_CAP_BEAMFORMEE_STS_MAX                  ((u32) BIT(13) | \
+                                                          BIT(14) | BIT(15))
+#define VHT_CAP_BEAMFORMEE_STS_MAX_SHIFT            13
+#define VHT_CAP_BEAMFORMEE_STS_OFFSET               13
+#define VHT_CAP_SOUNDING_DIMENSION_MAX              ((u32) BIT(16) | \
+                                                          BIT(17) | BIT(18))
+#define VHT_CAP_SOUNDING_DIMENSION_MAX_SHIFT        16
+#define VHT_CAP_SOUNDING_DIMENSION_OFFSET           16
 #define VHT_CAP_MU_BEAMFORMER_CAPABLE               ((u32) BIT(19))
 #define VHT_CAP_MU_BEAMFORMEE_CAPABLE               ((u32) BIT(20))
 #define VHT_CAP_VHT_TXOP_PS                         ((u32) BIT(21))
 #define VHT_CAP_HTC_VHT                             ((u32) BIT(22))
-#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT          ((u32) BIT(23))
+
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_1        ((u32) BIT(23))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_2        ((u32) BIT(24))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_3        ((u32) BIT(23) | BIT(24))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_4        ((u32) BIT(25))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_5        ((u32) BIT(23) | BIT(25))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_6        ((u32) BIT(24) | BIT(25))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX      ((u32) BIT(23) | \
+                                                          BIT(24) | BIT(25))
+#define VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX_SHIFT 23
 #define VHT_CAP_VHT_LINK_ADAPTATION_VHT_UNSOL_MFB   ((u32) BIT(27))
 #define VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB     ((u32) BIT(26) | BIT(27))
 #define VHT_CAP_RX_ANTENNA_PATTERN                  ((u32) BIT(28))
 #define VHT_CAP_TX_ANTENNA_PATTERN                  ((u32) BIT(29))
 
+#define VHT_OPMODE_CHANNEL_WIDTH_MASK              ((u8) BIT(0) | BIT(1))
+#define VHT_OPMODE_CHANNEL_RxNSS_MASK              ((u8) BIT(4) | BIT(5) | \
+                                                    BIT(6))
+#define VHT_OPMODE_NOTIF_RX_NSS_SHIFT              4
+
+#define VHT_RX_NSS_MAX_STREAMS                     8
+
 /* VHT channel widths */
 #define VHT_CHANWIDTH_USE_HT   0
 #define VHT_CHANWIDTH_80MHZ    1
@@ -735,12 +859,14 @@ struct ieee80211_vht_operation {
 #define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs)
                                * 00:50:F2 */
 #define WPA_IE_VENDOR_TYPE 0x0050f201
+#define WMM_IE_VENDOR_TYPE 0x0050f202
 #define WPS_IE_VENDOR_TYPE 0x0050f204
 #define OUI_WFA 0x506f9a
 #define P2P_IE_VENDOR_TYPE 0x506f9a09
 #define WFD_IE_VENDOR_TYPE 0x506f9a0a
 #define WFD_OUI_TYPE 10
 #define HS20_IE_VENDOR_TYPE 0x506f9a10
+#define OSEN_IE_VENDOR_TYPE 0x506f9a12
 
 #define WMM_OUI_TYPE 2
 #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0
@@ -779,6 +905,8 @@ struct wmm_information_element {
 
 } STRUCT_PACKED;
 
+#define WMM_QOSINFO_AP_UAPSD 0x80
+
 #define WMM_QOSINFO_STA_AC_MASK 0x0f
 #define WMM_QOSINFO_STA_SP_MASK 0x03
 #define WMM_QOSINFO_STA_SP_SHIFT 5
@@ -846,16 +974,18 @@ struct wmm_tspec_element {
 
 
 /* Access Categories / ACI to AC coding */
-enum {
+enum wmm_ac {
        WMM_AC_BE = 0 /* Best Effort */,
        WMM_AC_BK = 1 /* Background */,
        WMM_AC_VI = 2 /* Video */,
-       WMM_AC_VO = 3 /* Voice */
+       WMM_AC_VO = 3 /* Voice */,
+       WMM_AC_NUM = 4
 };
 
 
 #define HS20_INDICATION_OUI_TYPE 16
 #define HS20_ANQP_OUI_TYPE 17
+#define HS20_OSEN_OUI_TYPE 18
 #define HS20_STYPE_QUERY_LIST 1
 #define HS20_STYPE_CAPABILITY_LIST 2
 #define HS20_STYPE_OPERATOR_FRIENDLY_NAME 3
@@ -863,6 +993,21 @@ enum {
 #define HS20_STYPE_CONNECTION_CAPABILITY 5
 #define HS20_STYPE_NAI_HOME_REALM_QUERY 6
 #define HS20_STYPE_OPERATING_CLASS 7
+#define HS20_STYPE_OSU_PROVIDERS_LIST 8
+#define HS20_STYPE_ICON_REQUEST 10
+#define HS20_STYPE_ICON_BINARY_FILE 11
+
+#define HS20_DGAF_DISABLED 0x01
+#define HS20_PPS_MO_ID_PRESENT 0x02
+#define HS20_ANQP_DOMAIN_ID_PRESENT 0x04
+#define HS20_VERSION 0x10 /* Release 2 */
+
+/* WNM-Notification WFA vendors specific subtypes */
+#define HS20_WNM_SUB_REM_NEEDED 0
+#define HS20_WNM_DEAUTH_IMMINENT_NOTICE 1
+
+#define HS20_DEAUTH_REASON_CODE_BSS 0
+#define HS20_DEAUTH_REASON_CODE_ESS 1
 
 /* Wi-Fi Direct (P2P) */
 
@@ -888,6 +1033,15 @@ enum p2p_attr_id {
        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
 };
 
@@ -909,6 +1063,7 @@ enum p2p_attr_id {
 #define P2P_GROUP_CAPAB_CROSS_CONN BIT(4)
 #define P2P_GROUP_CAPAB_PERSISTENT_RECONN BIT(5)
 #define P2P_GROUP_CAPAB_GROUP_FORMATION BIT(6)
+#define P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION BIT(7)
 
 /* Invitation Flags */
 #define P2P_INVITATION_FLAGS_TYPE BIT(0)
@@ -931,6 +1086,13 @@ enum p2p_status_code {
        P2P_SC_FAIL_BOTH_GO_INTENT_15 = 9,
        P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD = 10,
        P2P_SC_FAIL_REJECTED_BY_USER = 11,
+       P2P_SC_SUCCESS_DEFERRED = 12,
+};
+
+enum p2p_role_indication {
+       P2P_DEVICE_NOT_IN_GROUP = 0x00,
+       P2P_CLIENT_IN_A_GROUP = 0x01,
+       P2P_GO_IN_A_GROUP = 0x02,
 };
 
 #define P2P_WILDCARD_SSID "DIRECT-"
@@ -963,6 +1125,7 @@ enum p2p_service_protocol_type {
        P2P_SERV_UPNP = 2,
        P2P_SERV_WS_DISCOVERY = 3,
        P2P_SERV_WIFI_DISPLAY = 4,
+       P2P_SERV_P2PS = 11,
        P2P_SERV_VENDOR_SPECIFIC = 255
 };
 
@@ -987,8 +1150,24 @@ enum wifi_display_subelem {
        WFD_SUBELEM_SESSION_INFO = 9
 };
 
+/* 802.11s */
+#define MESH_SYNC_METHOD_NEIGHBOR_OFFSET 1
+#define MESH_SYNC_METHOD_VENDOR                255
+#define MESH_PATH_PROTOCOL_HWMP                1
+#define MESH_PATH_PROTOCOL_VENDOR      255
+#define MESH_PATH_METRIC_AIRTIME       1
+#define MESH_PATH_METRIC_VENDOR                255
+
+enum plink_action_field {
+       PLINK_OPEN = 1,
+       PLINK_CONFIRM,
+       PLINK_CLOSE
+};
 
 #define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */
+#define VENDOR_VHT_TYPE                0x04
+#define VENDOR_VHT_SUBTYPE     0x08
+#define VENDOR_VHT_SUBTYPE2    0x00
 
 #define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */
 
@@ -1002,6 +1181,11 @@ enum wifi_display_subelem {
 #define WLAN_CIPHER_SUITE_AES_CMAC     0x000FAC06
 #define WLAN_CIPHER_SUITE_NO_GROUP_ADDR        0x000FAC07
 #define WLAN_CIPHER_SUITE_GCMP         0x000FAC08
+#define WLAN_CIPHER_SUITE_GCMP_256     0x000FAC09
+#define WLAN_CIPHER_SUITE_CCMP_256     0x000FAC0A
+#define WLAN_CIPHER_SUITE_BIP_GMAC_128 0x000FAC0B
+#define WLAN_CIPHER_SUITE_BIP_GMAC_256 0x000FAC0C
+#define WLAN_CIPHER_SUITE_BIP_CMAC_256 0x000FAC0D
 
 #define WLAN_CIPHER_SUITE_SMS4         0x00147201
 
@@ -1015,7 +1199,12 @@ enum wifi_display_subelem {
 #define WLAN_AKM_SUITE_PSK             0x000FAC02
 #define WLAN_AKM_SUITE_FT_8021X                0x000FAC03
 #define WLAN_AKM_SUITE_FT_PSK          0x000FAC04
+#define WLAN_AKM_SUITE_8021X_SHA256    0x000FAC05
+#define WLAN_AKM_SUITE_PSK_SHA256      0x000FAC06
+#define WLAN_AKM_SUITE_8021X_SUITE_B   0x000FAC11
+#define WLAN_AKM_SUITE_8021X_SUITE_B_192       0x000FAC12
 #define WLAN_AKM_SUITE_CCKM            0x00409600
+#define WLAN_AKM_SUITE_OSEN            0x506f9a01
 
 
 /* IEEE 802.11v - WNM Action field values */
@@ -1079,6 +1268,15 @@ enum bss_trans_mgmt_status_code {
 #define WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES   70
 #define WNM_NEIGHBOR_MULTIPLE_BSSID             71
 
+/* QoS action */
+enum qos_action {
+       QOS_ADDTS_REQ = 0,
+       QOS_ADDTS_RESP = 1,
+       QOS_DELTS = 2,
+       QOS_SCHEDULE = 3,
+       QOS_QOS_MAP_CONFIG = 4,
+};
+
 /* IEEE Std 802.11-2012, 8.4.2.62 20/40 BSS Coexistence element */
 #define WLAN_20_40_BSS_COEX_INFO_REQ            BIT(0)
 #define WLAN_20_40_BSS_COEX_40MHZ_INTOL         BIT(1)
@@ -1126,4 +1324,34 @@ enum wnm_sleep_mode_subelement_id {
        WNM_SLEEP_SUBELEM_IGTK = 1
 };
 
+/* Channel Switch modes (802.11h) */
+#define CHAN_SWITCH_MODE_ALLOW_TX      0
+#define CHAN_SWITCH_MODE_BLOCK_TX      1
+
+struct tpc_report {
+       u8 eid;
+       u8 len;
+       u8 tx_power;
+       u8 link_margin;
+} STRUCT_PACKED;
+
+/* IEEE Std 802.11-2012, 8.5.7.4 - Link Measurement Request frame format */
+struct rrm_link_measurement_request {
+       u8 dialog_token;
+       s8 tx_power;
+       s8 max_tp;
+       u8 variable[0];
+} STRUCT_PACKED;
+
+/* IEEE Std 802.11-2012, 8.5.7.5 - Link Measurement Report frame format */
+struct rrm_link_measurement_report {
+       u8 dialog_token;
+       struct tpc_report tpc;
+       u8 rx_ant_id;
+       u8 tx_ant_id;
+       u8 rcpi;
+       u8 rsni;
+       u8 variable[0];
+} STRUCT_PACKED;
+
 #endif /* IEEE802_11_DEFS_H */
diff --git a/src/common/ieee802_1x_defs.h b/src/common/ieee802_1x_defs.h
new file mode 100644 (file)
index 0000000..cc88caa
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * IEEE Std 802.1X-2010 definitions
+ * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_1X_DEFS_H
+#define IEEE802_1X_DEFS_H
+
+#define CS_ID_LEN              8
+#define CS_ID_GCM_AES_128      {0x00, 0x80, 0x02, 0x00, 0x01, 0x00, 0x00, 0x01}
+#define CS_NAME_GCM_AES_128    "GCM-AES-128"
+
+enum macsec_policy {
+       /**
+        * Should secure sessions.
+        * This accepts key server's advice to determine whether to secure the
+        * session or not.
+        */
+       SHOULD_SECURE,
+
+       /**
+        * Disabled MACsec - do not secure sessions.
+        */
+       DO_NOT_SECURE,
+};
+
+
+/* IEEE Std 802.1X-2010 - Table 11-6 - MACsec Capability */
+enum macsec_cap {
+       /**
+        * MACsec is not implemented
+        */
+       MACSEC_CAP_NOT_IMPLEMENTED,
+
+       /**
+        * 'Integrity without confidentiality'
+        */
+       MACSEC_CAP_INTEGRITY,
+
+       /**
+        * 'Integrity without confidentiality' and
+        * 'Integrity and confidentiality' with a confidentiality offset of 0
+        */
+       MACSEC_CAP_INTEG_AND_CONF,
+
+       /**
+        * 'Integrity without confidentiality' and
+        * 'Integrity and confidentiality' with a confidentiality offset of 0,
+        * 30, 50
+        */
+       MACSEC_CAP_INTEG_AND_CONF_0_30_50,
+};
+
+enum validate_frames {
+       Disabled,
+       Checked,
+       Strict,
+};
+
+/* IEEE Std 802.1X-2010 - Table 11-6 - Confidentiality Offset */
+enum confidentiality_offset {
+       CONFIDENTIALITY_NONE      = 0,
+       CONFIDENTIALITY_OFFSET_0  = 1,
+       CONFIDENTIALITY_OFFSET_30 = 2,
+       CONFIDENTIALITY_OFFSET_50 = 3,
+};
+
+/* IEEE Std 802.1X-2010 - Table 9-2 */
+#define DEFAULT_PRIO_INFRA_PORT        0x10
+#define DEFAULT_PRIO_PRIMRAY_AP        0x30
+#define DEFAULT_PRIO_SECONDARY_AP      0x50
+#define DEFAULT_PRIO_GROUP_CA_MEMBER   0x70
+#define DEFAULT_PRIO_NOT_KEY_SERVER    0xFF
+
+#endif /* IEEE802_1X_DEFS_H */
index 858b51d..4dc34c4 100644 (file)
@@ -31,7 +31,9 @@ struct privsep_cmd_associate
        u8 bssid[ETH_ALEN];
        u8 ssid[32];
        size_t ssid_len;
+       int hwmode;
        int freq;
+       int channel;
        int pairwise_suite;
        int group_suite;
        int key_mgmt_suite;
diff --git a/src/common/qca-vendor-attr.h b/src/common/qca-vendor-attr.h
new file mode 100644 (file)
index 0000000..6f51803
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Qualcomm Atheros vendor specific attribute definitions
+ * Copyright (c) 2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef QCA_VENDOR_ATTR_H
+#define QCA_VENDOR_ATTR_H
+
+/*
+ * This file defines some of the attributes used with Qualcomm Atheros OUI
+ * 00:13:74 in a way that is not suitable for qca-vendor.h, e.g., due to
+ * compiler dependencies.
+ */
+
+struct qca_avoid_freq_range {
+       u32 start_freq;
+       u32 end_freq;
+} __attribute__ ((packed));
+
+struct qca_avoid_freq_list {
+       u32 count;
+       struct qca_avoid_freq_range range[0];
+} __attribute__ ((packed));
+
+#endif /* QCA_VENDOR_ATTR_H */
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
new file mode 100644 (file)
index 0000000..2117ee7
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ * Qualcomm Atheros OUI and vendor specific assignments
+ * Copyright (c) 2014-2015, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef QCA_VENDOR_H
+#define QCA_VENDOR_H
+
+/*
+ * This file is a registry of identifier assignments from the Qualcomm Atheros
+ * OUI 00:13:74 for purposes other than MAC address assignment. New identifiers
+ * can be assigned through normal review process for changes to the upstream
+ * hostap.git repository.
+ */
+
+#define OUI_QCA 0x001374
+
+/**
+ * enum qca_radiotap_vendor_ids - QCA radiotap vendor namespace IDs
+ */
+enum qca_radiotap_vendor_ids {
+       QCA_RADIOTAP_VID_WLANTEST = 0,
+};
+
+/**
+ * enum qca_nl80211_vendor_subcmds - QCA nl80211 vendor command identifiers
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_UNSPEC: Reserved value 0
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_TEST: Test command/event
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_ROAMING: Set roaming policy for drivers that use
+ *     internal BSS-selection. This command uses
+ *     @QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY to specify the new roaming policy
+ *     for the current connection (i.e., changes policy set by the nl80211
+ *     Connect command). @QCA_WLAN_VENDOR_ATTR_MAC_ADDR may optionally be
+ *     included to indicate which BSS to use in case roaming is disabled.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: Recommendation of frequency
+ *     ranges to avoid to reduce issues due to interference or internal
+ *     co-existence information in the driver. The event data structure is
+ *     defined in struct qca_avoid_freq_list.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY: Command to check driver support
+ *     for DFS offloading.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_NAN: NAN command/event which is used to pass
+ *     NAN Request/Response and NAN Indication messages. These messages are
+ *     interpreted between the framework and the firmware component.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY: Set key operation that can be
+ *     used to configure PMK to the driver even when not connected. This can
+ *     be used to request offloading of key management operations. Only used
+ *     if device supports QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH: An extended version of
+ *     NL80211_CMD_ROAM event with optional attributes including information
+ *     from offloaded key management operation. Uses
+ *     enum qca_wlan_vendor_attr_roam_auth attributes. Only used
+ *     if device supports QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DO_ACS: ACS command/event which is used to
+ *     invoke the ACS function in device and pass selected channels to
+ *     hostapd.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES: Command to get the features
+ *     supported by the driver. enum qca_wlan_vendor_features defines
+ *     the possible features.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED: Event used by driver,
+ *     which supports DFS offloading, to indicate a channel availability check
+ *     start.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED: Event used by driver,
+ *     which supports DFS offloading, to indicate a channel availability check
+ *     completion.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED: Event used by driver,
+ *     which supports DFS offloading, to indicate that the channel availability
+ *     check aborted, no change to the channel status.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED: Event used by
+ *     driver, which supports DFS offloading, to indicate that the
+ *     Non-Occupancy Period for this channel is over, channel becomes usable.
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED: Event used by driver,
+ *     which supports DFS offloading, to indicate a radar pattern has been
+ *     detected. The channel is now unusable.
+ */
+enum qca_nl80211_vendor_subcmds {
+       QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
+       QCA_NL80211_VENDOR_SUBCMD_TEST = 1,
+       /* subcmds 2..8 not yet allocated */
+       QCA_NL80211_VENDOR_SUBCMD_ROAMING = 9,
+       QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY = 10,
+       QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY =  11,
+       QCA_NL80211_VENDOR_SUBCMD_NAN =  12,
+       QCA_NL80211_VENDOR_SUBMCD_STATS_EXT = 13,
+       QCA_NL80211_VENDOR_SUBCMD_LL_STATS_SET = 14,
+       QCA_NL80211_VENDOR_SUBCMD_LL_STATS_GET = 15,
+       QCA_NL80211_VENDOR_SUBCMD_LL_STATS_CLR = 16,
+       QCA_NL80211_VENDOR_SUBCMD_LL_STATS_RADIO_RESULTS = 17,
+       QCA_NL80211_VENDOR_SUBCMD_LL_STATS_IFACE_RESULTS = 18,
+       QCA_NL80211_VENDOR_SUBCMD_LL_STATS_PEERS_RESULTS = 19,
+       QCA_NL80211_VENDOR_SUBCMD_GSCAN_START = 20,
+       QCA_NL80211_VENDOR_SUBCMD_GSCAN_STOP = 21,
+       QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_VALID_CHANNELS = 22,
+       QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CAPABILITIES = 23,
+       QCA_NL80211_VENDOR_SUBCMD_GSCAN_GET_CACHED_RESULTS = 24,
+       QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_RESULTS_AVAILABLE = 25,
+       QCA_NL80211_VENDOR_SUBCMD_GSCAN_FULL_SCAN_RESULT = 26,
+       QCA_NL80211_VENDOR_SUBCMD_GSCAN_SCAN_EVENT = 27,
+       QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_FOUND = 28,
+       QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_BSSID_HOTLIST = 29,
+       QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_BSSID_HOTLIST = 30,
+       QCA_NL80211_VENDOR_SUBCMD_GSCAN_SIGNIFICANT_CHANGE = 31,
+       QCA_NL80211_VENDOR_SUBCMD_GSCAN_SET_SIGNIFICANT_CHANGE = 32,
+       QCA_NL80211_VENDOR_SUBCMD_GSCAN_RESET_SIGNIFICANT_CHANGE = 33,
+       QCA_NL80211_VENDOR_SUBCMD_TDLS_ENABLE = 34,
+       QCA_NL80211_VENDOR_SUBCMD_TDLS_DISABLE = 35,
+       QCA_NL80211_VENDOR_SUBCMD_TDLS_GET_STATUS = 36,
+       QCA_NL80211_VENDOR_SUBCMD_TDLS_STATE = 37,
+       QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_FEATURES = 38,
+       QCA_NL80211_VENDOR_SUBCMD_SCANNING_MAC_OUI = 39,
+       QCA_NL80211_VENDOR_SUBCMD_NO_DFS_FLAG = 40,
+       QCA_NL80211_VENDOR_SUBCMD_GSCAN_HOTLIST_AP_LOST = 41,
+       QCA_NL80211_VENDOR_SUBCMD_GET_CONCURRENCY_MATRIX = 42,
+       /* 43..49 - reserved for QCA */
+       QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY = 50,
+       QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH = 51,
+       QCA_NL80211_VENDOR_SUBCMD_APFIND = 52,
+       /* 53 - reserved for QCA */
+       QCA_NL80211_VENDOR_SUBCMD_DO_ACS = 54,
+       QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES = 55,
+       QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED = 56,
+       QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED = 57,
+       QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED = 58,
+       QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED = 59,
+       QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED = 60,
+       /* 61-90 - reserved for QCA */
+       QCA_NL80211_VENDOR_SUBCMD_DATA_OFFLOAD = 91,
+};
+
+
+enum qca_wlan_vendor_attr {
+       QCA_WLAN_VENDOR_ATTR_INVALID = 0,
+       /* used by QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY */
+       QCA_WLAN_VENDOR_ATTR_DFS     = 1,
+       /* used by QCA_NL80211_VENDOR_SUBCMD_NAN */
+       QCA_WLAN_VENDOR_ATTR_NAN     = 2,
+       /* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */
+       QCA_WLAN_VENDOR_ATTR_STATS_EXT     = 3,
+       /* used by QCA_NL80211_VENDOR_SUBCMD_STATS_EXT */
+       QCA_WLAN_VENDOR_ATTR_IFINDEX     = 4,
+       /* used by QCA_NL80211_VENDOR_SUBCMD_ROAMING, u32 with values defined
+        * by enum qca_roaming_policy. */
+       QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY = 5,
+       QCA_WLAN_VENDOR_ATTR_MAC_ADDR = 6,
+       /* used by QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES */
+       QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS = 7,
+       QCA_WLAN_VENDOR_ATTR_TEST = 8,
+       /* keep last */
+       QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
+       QCA_WLAN_VENDOR_ATTR_MAX        = QCA_WLAN_VENDOR_ATTR_AFTER_LAST - 1,
+};
+
+
+enum qca_roaming_policy {
+       QCA_ROAMING_NOT_ALLOWED,
+       QCA_ROAMING_ALLOWED_WITHIN_ESS,
+};
+
+enum qca_wlan_vendor_attr_roam_auth {
+       QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_INVALID = 0,
+       QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID,
+       QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE,
+       QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE,
+       QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED,
+       QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR,
+       QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK,
+       QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK,
+       /* keep last */
+       QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST,
+       QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX =
+       QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST - 1
+};
+
+enum qca_wlan_vendor_attr_acs_offload {
+       QCA_WLAN_VENDOR_ATTR_ACS_CHANNEL_INVALID = 0,
+       QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL,
+       QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL,
+       QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE,
+       QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED,
+       QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED,
+       /* keep last */
+       QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST,
+       QCA_WLAN_VENDOR_ATTR_ACS_MAX =
+       QCA_WLAN_VENDOR_ATTR_ACS_AFTER_LAST - 1
+};
+
+enum qca_wlan_vendor_acs_hw_mode {
+       QCA_ACS_MODE_IEEE80211B,
+       QCA_ACS_MODE_IEEE80211G,
+       QCA_ACS_MODE_IEEE80211A,
+       QCA_ACS_MODE_IEEE80211AD,
+};
+
+/**
+ * enum qca_wlan_vendor_features - Vendor device/driver feature flags
+ *
+ * @QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD: Device supports key
+ *     management offload, a mechanism where the station's firmware
+ *     does the exchange with the AP to establish the temporal keys
+ *     after roaming, rather than having the user space wpa_supplicant do it.
+ * @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
+ */
+enum qca_wlan_vendor_features {
+       QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD        = 0,
+       NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
+};
+
+/**
+ * enum qca_wlan_vendor_attr_data_offload_ind - Vendor Data Offload Indication
+ *
+ * @QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_SESSION: Session corresponding to
+ *     the offloaded data.
+ * @QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_PROTOCOL: Protocol of the offloaded
+ *     data.
+ * @QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_EVENT: Event type for the data offload
+ *     indication.
+ */
+enum qca_wlan_vendor_attr_data_offload_ind {
+       QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_INVALID = 0,
+       QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_SESSION,
+       QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_PROTOCOL,
+       QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_EVENT,
+
+       /* keep last */
+       QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST,
+       QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_MAX =
+       QCA_WLAN_VENDOR_ATTR_DATA_OFFLOAD_IND_AFTER_LAST - 1
+};
+#endif /* QCA_VENDOR_H */
index bce60a3..5888958 100644 (file)
@@ -87,7 +87,8 @@ void sae_clear_temp_data(struct sae_data *sae)
        crypto_ec_point_deinit(tmp->pwe_ecc, 1);
        crypto_ec_point_deinit(tmp->own_commit_element_ecc, 0);
        crypto_ec_point_deinit(tmp->peer_commit_element_ecc, 0);
-       os_free(sae->tmp);
+       wpabuf_free(tmp->anti_clogging_token);
+       bin_clear_free(tmp, sizeof(*tmp));
        sae->tmp = NULL;
 }
 
@@ -134,8 +135,10 @@ static struct crypto_bignum * sae_get_rand(struct sae_data *sae)
                        return NULL;
                if (crypto_bignum_is_zero(bn) ||
                    crypto_bignum_is_one(bn) ||
-                   crypto_bignum_cmp(bn, sae->tmp->order) >= 0)
+                   crypto_bignum_cmp(bn, sae->tmp->order) >= 0) {
+                       crypto_bignum_deinit(bn, 0);
                        continue;
+               }
                break;
        }
 
@@ -503,6 +506,8 @@ int sae_prepare_commit(const u8 *addr1, const u8 *addr2,
                       const u8 *password, size_t password_len,
                       struct sae_data *sae)
 {
+       if (sae->tmp == NULL)
+               return -1;
        if (sae->tmp->ec && sae_derive_pwe_ecc(sae, addr1, addr2, password,
                                          password_len) < 0)
                return -1;
@@ -619,8 +624,10 @@ static int sae_derive_keys(struct sae_data *sae, const u8 *k)
        wpa_hexdump(MSG_DEBUG, "SAE: PMKID", val, SAE_PMKID_LEN);
        sha256_prf(keyseed, sizeof(keyseed), "SAE KCK and PMK",
                   val, sae->tmp->prime_len, keys, sizeof(keys));
+       os_memset(keyseed, 0, sizeof(keyseed));
        os_memcpy(sae->tmp->kck, keys, SAE_KCK_LEN);
        os_memcpy(sae->pmk, keys + SAE_KCK_LEN, SAE_PMK_LEN);
+       os_memset(keys, 0, sizeof(keys));
        wpa_hexdump_key(MSG_DEBUG, "SAE: KCK", sae->tmp->kck, SAE_KCK_LEN);
        wpa_hexdump_key(MSG_DEBUG, "SAE: PMK", sae->pmk, SAE_PMK_LEN);
 
@@ -634,7 +641,8 @@ fail:
 int sae_process_commit(struct sae_data *sae)
 {
        u8 k[SAE_MAX_PRIME_LEN];
-       if ((sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) ||
+       if (sae->tmp == NULL ||
+           (sae->tmp->ec && sae_derive_k_ecc(sae, k) < 0) ||
            (sae->tmp->dh && sae_derive_k_ffc(sae, k) < 0) ||
            sae_derive_keys(sae, k) < 0)
                return -1;
@@ -646,9 +654,16 @@ void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
                      const struct wpabuf *token)
 {
        u8 *pos;
+
+       if (sae->tmp == NULL)
+               return;
+
        wpabuf_put_le16(buf, sae->group); /* Finite Cyclic Group */
-       if (token)
+       if (token) {
                wpabuf_put_buf(buf, token);
+               wpa_hexdump(MSG_DEBUG, "SAE: Anti-clogging token",
+                           wpabuf_head(token), wpabuf_len(token));
+       }
        pos = wpabuf_put(buf, sae->tmp->prime_len);
        crypto_bignum_to_bin(sae->tmp->own_commit_scalar, pos,
                             sae->tmp->prime_len, sae->tmp->prime_len);
@@ -673,12 +688,11 @@ void sae_write_commit(struct sae_data *sae, struct wpabuf *buf,
 }
 
 
-static u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups,
-                            u16 group)
+u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group)
 {
        if (allowed_groups) {
                int i;
-               for (i = 0; allowed_groups[i] >= 0; i++) {
+               for (i = 0; allowed_groups[i] > 0; i++) {
                        if (allowed_groups[i] == group)
                                break;
                }
@@ -701,6 +715,11 @@ static u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups,
                return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
        }
 
+       if (sae->tmp == NULL) {
+               wpa_printf(MSG_DEBUG, "SAE: Group information not yet initialized");
+               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+       }
+
        if (sae->tmp->dh && !allowed_groups) {
                wpa_printf(MSG_DEBUG, "SAE: Do not allow FFC group %u without "
                           "explicit configuration enabling it", group);
@@ -797,7 +816,7 @@ static u16 sae_parse_commit_element_ecc(struct sae_data *sae, const u8 *pos,
 
        /* element x and y coordinates < p */
        if (os_memcmp(pos, prime, sae->tmp->prime_len) >= 0 ||
-           os_memcmp(pos + sae->tmp->prime_len + sae->tmp->prime_len, prime,
+           os_memcmp(pos + sae->tmp->prime_len, prime,
                      sae->tmp->prime_len) >= 0) {
                wpa_printf(MSG_DEBUG, "SAE: Invalid coordinates in peer "
                           "element");
@@ -985,6 +1004,9 @@ void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf)
 {
        const u8 *sc;
 
+       if (sae->tmp == NULL)
+               return;
+
        /* Send-Confirm */
        sc = wpabuf_put(buf, 0);
        wpabuf_put_le16(buf, sae->send_confirm);
@@ -1016,6 +1038,11 @@ int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len)
 
        wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data));
 
+       if (sae->tmp == NULL) {
+               wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available");
+               return -1;
+       }
+
        if (sae->tmp->ec)
                sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar,
                                   sae->tmp->peer_commit_element_ecc,
@@ -1029,7 +1056,7 @@ int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len)
                                   sae->tmp->own_commit_element_ffc,
                                   verifier);
 
-       if (os_memcmp(verifier, data + 2, SHA256_MAC_LEN) != 0) {
+       if (os_memcmp_const(verifier, data + 2, SHA256_MAC_LEN) != 0) {
                wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch");
                wpa_hexdump(MSG_DEBUG, "SAE: Received confirm",
                            data + 2, SHA256_MAC_LEN);
index d82a98e..3ebf40c 100644 (file)
@@ -35,6 +35,7 @@ struct sae_temporary_data {
        const struct crypto_bignum *order;
        struct crypto_bignum *prime_buf;
        struct crypto_bignum *order_buf;
+       struct wpabuf *anti_clogging_token;
 };
 
 struct sae_data {
@@ -43,6 +44,7 @@ struct sae_data {
        u8 pmk[SAE_PMK_LEN];
        struct crypto_bignum *peer_commit_scalar;
        int group;
+       int sync;
        struct sae_temporary_data *tmp;
 };
 
@@ -60,5 +62,6 @@ u16 sae_parse_commit(struct sae_data *sae, const u8 *data, size_t len,
                     const u8 **token, size_t *token_len, int *allowed_groups);
 void sae_write_confirm(struct sae_data *sae, struct wpabuf *buf);
 int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len);
+u16 sae_group_allowed(struct sae_data *sae, int *allowed_groups, u16 group);
 
 #endif /* SAE_H */
diff --git a/src/common/tnc.h b/src/common/tnc.h
new file mode 100644 (file)
index 0000000..108acf9
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * TNC - Common defines
+ * Copyright (c) 2007-2014, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef TNC_H
+#define TNC_H
+
+typedef unsigned long TNC_UInt32;
+typedef unsigned char *TNC_BufferReference;
+
+typedef TNC_UInt32 TNC_IMVID;
+typedef TNC_UInt32 TNC_IMCID;
+typedef TNC_UInt32 TNC_ConnectionID;
+typedef TNC_UInt32 TNC_ConnectionState;
+typedef TNC_UInt32 TNC_RetryReason;
+typedef TNC_UInt32 TNC_IMV_Action_Recommendation;
+typedef TNC_UInt32 TNC_IMV_Evaluation_Result;
+typedef TNC_UInt32 TNC_MessageType;
+typedef TNC_MessageType *TNC_MessageTypeList;
+typedef TNC_UInt32 TNC_VendorID;
+typedef TNC_UInt32 TNC_Subtype;
+typedef TNC_UInt32 TNC_MessageSubtype;
+typedef TNC_UInt32 TNC_Version;
+typedef TNC_UInt32 TNC_Result;
+typedef TNC_UInt32 TNC_AttributeID;
+
+typedef TNC_Result (*TNC_TNCS_BindFunctionPointer)(
+       TNC_IMVID imvID,
+       char *functionName,
+       void **pOutfunctionPointer);
+typedef TNC_Result (*TNC_TNCS_ReportMessageTypesPointer)(
+       TNC_IMVID imvID,
+       TNC_MessageTypeList supportedTypes,
+       TNC_UInt32 typeCount);
+typedef TNC_Result (*TNC_TNCS_SendMessagePointer)(
+       TNC_IMVID imvID,
+       TNC_ConnectionID connectionID,
+       TNC_BufferReference message,
+       TNC_UInt32 messageLength,
+       TNC_MessageType messageType);
+typedef TNC_Result (*TNC_TNCS_RequestHandshakeRetryPointer)(
+       TNC_IMVID imvID,
+       TNC_ConnectionID connectionID,
+       TNC_RetryReason reason);
+typedef TNC_Result (*TNC_TNCS_ProvideRecommendationPointer)(
+       TNC_IMVID imvID,
+       TNC_ConnectionID connectionID,
+       TNC_IMV_Action_Recommendation recommendation,
+       TNC_IMV_Evaluation_Result evaluation);
+typedef TNC_Result (*TNC_TNCC_BindFunctionPointer)(
+       TNC_IMCID imcID,
+       char *functionName,
+       void **pOutfunctionPointer);
+typedef TNC_Result (*TNC_TNCC_SendMessagePointer)(
+       TNC_IMCID imcID,
+       TNC_ConnectionID connectionID,
+       TNC_BufferReference message,
+       TNC_UInt32 messageLength,
+       TNC_MessageType messageType);
+typedef TNC_Result (*TNC_TNCC_ReportMessageTypesPointer)(
+       TNC_IMCID imcID,
+       TNC_MessageTypeList supportedTypes,
+       TNC_UInt32 typeCount);
+typedef TNC_Result (*TNC_TNCC_RequestHandshakeRetryPointer)(
+       TNC_IMCID imcID,
+       TNC_ConnectionID connectionID,
+       TNC_RetryReason reason);
+
+#define TNC_IFIMV_VERSION_1 1
+#define TNC_IFIMC_VERSION_1 1
+
+#define TNC_RESULT_SUCCESS 0
+#define TNC_RESULT_NOT_INITIALIZED 1
+#define TNC_RESULT_ALREADY_INITIALIZED 2
+#define TNC_RESULT_NO_COMMON_VERSION 3
+#define TNC_RESULT_CANT_RETRY 4
+#define TNC_RESULT_WONT_RETRY 5
+#define TNC_RESULT_INVALID_PARAMETER 6
+#define TNC_RESULT_CANT_RESPOND 7
+#define TNC_RESULT_ILLEGAL_OPERATION 8
+#define TNC_RESULT_OTHER 9
+#define TNC_RESULT_FATAL 10
+
+#define TNC_CONNECTION_STATE_CREATE 0
+#define TNC_CONNECTION_STATE_HANDSHAKE 1
+#define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2
+#define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3
+#define TNC_CONNECTION_STATE_ACCESS_NONE 4
+#define TNC_CONNECTION_STATE_DELETE 5
+
+#define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff)
+#define TNC_SUBTYPE_ANY ((TNC_Subtype) 0xff)
+
+/* TNCC-TNCS Message Types */
+#define TNC_TNCCS_RECOMMENDATION               0x00000001
+#define TNC_TNCCS_ERROR                                0x00000002
+#define TNC_TNCCS_PREFERREDLANGUAGE            0x00000003
+#define TNC_TNCCS_REASONSTRINGS                        0x00000004
+
+/* Possible TNC_IMV_Action_Recommendation values: */
+enum IMV_Action_Recommendation {
+       TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
+       TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS,
+       TNC_IMV_ACTION_RECOMMENDATION_ISOLATE,
+       TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
+};
+
+/* Possible TNC_IMV_Evaluation_Result values: */
+enum IMV_Evaluation_Result {
+       TNC_IMV_EVALUATION_RESULT_COMPLIANT,
+       TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR,
+       TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR,
+       TNC_IMV_EVALUATION_RESULT_ERROR,
+       TNC_IMV_EVALUATION_RESULT_DONT_KNOW
+};
+
+#endif /* TNC_H */
index 2faa8c7..e39a8db 100644 (file)
@@ -5,6 +5,6 @@
 #define VERSION_STR_POSTFIX ""
 #endif /* VERSION_STR_POSTFIX */
 
-#define VERSION_STR "2.1-devel" VERSION_STR_POSTFIX
+#define VERSION_STR "2.4" VERSION_STR_POSTFIX
 
 #endif /* VERSION_H */
index c3afbfd..de81d53 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * WPA/RSN - Shared functions for supplicant and authenticator
- * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -12,6 +12,7 @@
 #include "crypto/md5.h"
 #include "crypto/sha1.h"
 #include "crypto/sha256.h"
+#include "crypto/sha384.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/crypto.h"
 #include "ieee802_11_defs.h"
 #include "wpa_common.h"
 
 
+static unsigned int wpa_kck_len(int akmp)
+{
+       if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+               return 24;
+       return 16;
+}
+
+
+static unsigned int wpa_kek_len(int akmp)
+{
+       if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+               return 32;
+       return 16;
+}
+
+
+unsigned int wpa_mic_len(int akmp)
+{
+       if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+               return 24;
+       return 16;
+}
+
+
 /**
  * wpa_eapol_key_mic - Calculate EAPOL-Key MIC
  * @key: EAPOL-Key Key Confirmation Key (KCK)
+ * @key_len: KCK length in octets
+ * @akmp: WPA_KEY_MGMT_* used in key derivation
  * @ver: Key descriptor version (WPA_KEY_INFO_TYPE_*)
  * @buf: Pointer to the beginning of the EAPOL header (version field)
  * @len: Length of the EAPOL frame (from EAPOL header to the end of the frame)
  * happened during final editing of the standard and the correct behavior is
  * defined in the last draft (IEEE 802.11i/D10).
  */
-int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len,
-                     u8 *mic)
+int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
+                     const u8 *buf, size_t len, u8 *mic)
 {
-       u8 hash[SHA1_MAC_LEN];
+       u8 hash[SHA384_MAC_LEN];
 
        switch (ver) {
 #ifndef CONFIG_FIPS
        case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
-               return hmac_md5(key, 16, buf, len, mic);
+               return hmac_md5(key, key_len, buf, len, mic);
 #endif /* CONFIG_FIPS */
        case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES:
-               if (hmac_sha1(key, 16, buf, len, hash))
+               if (hmac_sha1(key, key_len, buf, len, hash))
                        return -1;
                os_memcpy(mic, hash, MD5_MAC_LEN);
                break;
@@ -56,6 +83,30 @@ int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len,
        case WPA_KEY_INFO_TYPE_AES_128_CMAC:
                return omac1_aes_128(key, buf, len, mic);
 #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
+       case WPA_KEY_INFO_TYPE_AKM_DEFINED:
+               switch (akmp) {
+#ifdef CONFIG_HS20
+               case WPA_KEY_MGMT_OSEN:
+                       return omac1_aes_128(key, buf, len, mic);
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_SUITEB
+               case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
+                       if (hmac_sha256(key, key_len, buf, len, hash))
+                               return -1;
+                       os_memcpy(mic, hash, MD5_MAC_LEN);
+                       break;
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SUITEB192
+               case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+                       if (hmac_sha384(key, key_len, buf, len, hash))
+                               return -1;
+                       os_memcpy(mic, hash, 24);
+                       break;
+#endif /* CONFIG_SUITEB192 */
+               default:
+                       return -1;
+               }
+               break;
        default:
                return -1;
        }
@@ -74,8 +125,9 @@ int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len,
  * @nonce1: ANonce or SNonce
  * @nonce2: SNonce or ANonce
  * @ptk: Buffer for pairwise transient key
- * @ptk_len: Length of PTK
- * @use_sha256: Whether to use SHA256-based KDF
+ * @akmp: Negotiated AKM
+ * @cipher: Negotiated pairwise cipher
+ * Returns: 0 on success, -1 on failure
  *
  * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
  * PTK = PRF-X(PMK, "Pairwise key expansion",
@@ -86,12 +138,14 @@ int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len,
  *             Min(MAC_I, MAC_P) || Max(MAC_I, MAC_P) ||
  *             Min(INonce, PNonce) || Max(INonce, PNonce))
  */
-void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
-                   const u8 *addr1, const u8 *addr2,
-                   const u8 *nonce1, const u8 *nonce2,
-                   u8 *ptk, size_t ptk_len, int use_sha256)
+int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
+                  const u8 *addr1, const u8 *addr2,
+                  const u8 *nonce1, const u8 *nonce2,
+                  struct wpa_ptk *ptk, int akmp, int cipher)
 {
        u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN];
+       u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
+       size_t ptk_len;
 
        if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) {
                os_memcpy(data, addr1, ETH_ALEN);
@@ -111,27 +165,44 @@ void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
                          WPA_NONCE_LEN);
        }
 
+       ptk->kck_len = wpa_kck_len(akmp);
+       ptk->kek_len = wpa_kek_len(akmp);
+       ptk->tk_len = wpa_cipher_key_len(cipher);
+       ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
+
 #ifdef CONFIG_IEEE80211W
-       if (use_sha256)
+       if (wpa_key_mgmt_sha256(akmp))
                sha256_prf(pmk, pmk_len, label, data, sizeof(data),
-                          ptk, ptk_len);
+                          tmp, ptk_len);
        else
 #endif /* CONFIG_IEEE80211W */
-               sha1_prf(pmk, pmk_len, label, data, sizeof(data), ptk,
-                        ptk_len);
+               sha1_prf(pmk, pmk_len, label, data, sizeof(data), tmp, ptk_len);
 
        wpa_printf(MSG_DEBUG, "WPA: PTK derivation - A1=" MACSTR " A2=" MACSTR,
                   MAC2STR(addr1), MAC2STR(addr2));
        wpa_hexdump(MSG_DEBUG, "WPA: Nonce1", nonce1, WPA_NONCE_LEN);
        wpa_hexdump(MSG_DEBUG, "WPA: Nonce2", nonce2, WPA_NONCE_LEN);
        wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len);
-       wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", ptk, ptk_len);
+       wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", tmp, ptk_len);
+
+       os_memcpy(ptk->kck, tmp, ptk->kck_len);
+       wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", ptk->kck, ptk->kck_len);
+
+       os_memcpy(ptk->kek, tmp + ptk->kck_len, ptk->kek_len);
+       wpa_hexdump_key(MSG_DEBUG, "WPA: KEK", ptk->kek, ptk->kek_len);
+
+       os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len);
+       wpa_hexdump_key(MSG_DEBUG, "WPA: TK", ptk->tk, ptk->tk_len);
+
+       os_memset(tmp, 0, sizeof(tmp));
+       return 0;
 }
 
 
 #ifdef CONFIG_IEEE80211R
-int wpa_ft_mic(const u8 *kck, const u8 *sta_addr, const u8 *ap_addr,
-              u8 transaction_seqnum, const u8 *mdie, size_t mdie_len,
+int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
+              const u8 *ap_addr, u8 transaction_seqnum,
+              const u8 *mdie, size_t mdie_len,
               const u8 *ftie, size_t ftie_len,
               const u8 *rsnie, size_t rsnie_len,
               const u8 *ric, size_t ric_len, u8 *mic)
@@ -139,6 +210,12 @@ int wpa_ft_mic(const u8 *kck, const u8 *sta_addr, const u8 *ap_addr,
        u8 *buf, *pos;
        size_t buf_len;
 
+       if (kck_len != 16) {
+               wpa_printf(MSG_WARNING, "FT: Unsupported KCK length %u",
+                          (unsigned int) kck_len);
+               return -1;
+       }
+
        buf_len = 2 * ETH_ALEN + 1 + mdie_len + ftie_len + rsnie_len + ric_len;
        buf = os_malloc(buf_len);
        if (buf == NULL)
@@ -353,6 +430,18 @@ static int rsn_selector_to_bitfield(const u8 *s)
 #endif /* CONFIG_IEEE80211W */
        if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP)
                return WPA_CIPHER_GCMP;
+       if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_CCMP_256)
+               return WPA_CIPHER_CCMP_256;
+       if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_GCMP_256)
+               return WPA_CIPHER_GCMP_256;
+       if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_GMAC_128)
+               return WPA_CIPHER_BIP_GMAC_128;
+       if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_GMAC_256)
+               return WPA_CIPHER_BIP_GMAC_256;
+       if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_BIP_CMAC_256)
+               return WPA_CIPHER_BIP_CMAC_256;
+       if (RSN_SELECTOR_GET(s) == RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED)
+               return WPA_CIPHER_GTK_NOT_USED;
        return 0;
 }
 
@@ -381,10 +470,34 @@ static int rsn_key_mgmt_to_bitfield(const u8 *s)
        if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_FT_SAE)
                return WPA_KEY_MGMT_FT_SAE;
 #endif /* CONFIG_SAE */
+       if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B)
+               return WPA_KEY_MGMT_IEEE8021X_SUITE_B;
+       if (RSN_SELECTOR_GET(s) == RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192)
+               return WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
        return 0;
 }
 
 
+static int wpa_cipher_valid_group(int cipher)
+{
+       return wpa_cipher_valid_pairwise(cipher) ||
+               cipher == WPA_CIPHER_WEP104 ||
+               cipher == WPA_CIPHER_WEP40 ||
+               cipher == WPA_CIPHER_GTK_NOT_USED;
+}
+
+
+#ifdef CONFIG_IEEE80211W
+int wpa_cipher_valid_mgmt_group(int cipher)
+{
+       return cipher == WPA_CIPHER_AES_128_CMAC ||
+               cipher == WPA_CIPHER_BIP_GMAC_128 ||
+               cipher == WPA_CIPHER_BIP_GMAC_256 ||
+               cipher == WPA_CIPHER_BIP_CMAC_256;
+}
+#endif /* CONFIG_IEEE80211W */
+
+
 /**
  * wpa_parse_wpa_ie_rsn - Parse RSN IE
  * @rsn_ie: Buffer containing RSN IE
@@ -440,13 +553,11 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
 
        if (left >= RSN_SELECTOR_LEN) {
                data->group_cipher = rsn_selector_to_bitfield(pos);
-#ifdef CONFIG_IEEE80211W
-               if (data->group_cipher == WPA_CIPHER_AES_128_CMAC) {
-                       wpa_printf(MSG_DEBUG, "%s: AES-128-CMAC used as group "
-                                  "cipher", __func__);
+               if (!wpa_cipher_valid_group(data->group_cipher)) {
+                       wpa_printf(MSG_DEBUG, "%s: invalid group cipher 0x%x",
+                                  __func__, data->group_cipher);
                        return -1;
                }
-#endif /* CONFIG_IEEE80211W */
                pos += RSN_SELECTOR_LEN;
                left -= RSN_SELECTOR_LEN;
        } else if (left > 0) {
@@ -460,7 +571,7 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
                count = WPA_GET_LE16(pos);
                pos += 2;
                left -= 2;
-               if (count == 0 || left < count * RSN_SELECTOR_LEN) {
+               if (count == 0 || count > left / RSN_SELECTOR_LEN) {
                        wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
                                   "count %u left %u", __func__, count, left);
                        return -4;
@@ -488,7 +599,7 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
                count = WPA_GET_LE16(pos);
                pos += 2;
                left -= 2;
-               if (count == 0 || left < count * RSN_SELECTOR_LEN) {
+               if (count == 0 || count > left / RSN_SELECTOR_LEN) {
                        wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
                                   "count %u left %u", __func__, count, left);
                        return -6;
@@ -511,17 +622,17 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
        }
 
        if (left >= 2) {
-               data->num_pmkid = WPA_GET_LE16(pos);
+               u16 num_pmkid = WPA_GET_LE16(pos);
                pos += 2;
                left -= 2;
-               if (left < (int) data->num_pmkid * PMKID_LEN) {
+               if (num_pmkid > (unsigned int) left / PMKID_LEN) {
                        wpa_printf(MSG_DEBUG, "%s: PMKID underflow "
-                                  "(num_pmkid=%lu left=%d)",
-                                  __func__, (unsigned long) data->num_pmkid,
-                                  left);
+                                  "(num_pmkid=%u left=%d)",
+                                  __func__, num_pmkid, left);
                        data->num_pmkid = 0;
                        return -9;
                } else {
+                       data->num_pmkid = num_pmkid;
                        data->pmkid = pos;
                        pos += data->num_pmkid * PMKID_LEN;
                        left -= data->num_pmkid * PMKID_LEN;
@@ -531,7 +642,7 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
 #ifdef CONFIG_IEEE80211W
        if (left >= 4) {
                data->mgmt_group_cipher = rsn_selector_to_bitfield(pos);
-               if (data->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) {
+               if (!wpa_cipher_valid_mgmt_group(data->mgmt_group_cipher)) {
                        wpa_printf(MSG_DEBUG, "%s: Unsupported management "
                                   "group cipher 0x%x", __func__,
                                   data->mgmt_group_cipher);
@@ -543,8 +654,9 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
 #endif /* CONFIG_IEEE80211W */
 
        if (left > 0) {
-               wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored",
-                          __func__, left);
+               wpa_hexdump(MSG_DEBUG,
+                           "wpa_parse_wpa_ie_rsn: ignore trailing bytes",
+                           pos, left);
        }
 
        return 0;
@@ -637,7 +749,7 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
                count = WPA_GET_LE16(pos);
                pos += 2;
                left -= 2;
-               if (count == 0 || left < count * WPA_SELECTOR_LEN) {
+               if (count == 0 || count > left / WPA_SELECTOR_LEN) {
                        wpa_printf(MSG_DEBUG, "%s: ie count botch (pairwise), "
                                   "count %u left %u", __func__, count, left);
                        return -4;
@@ -658,7 +770,7 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
                count = WPA_GET_LE16(pos);
                pos += 2;
                left -= 2;
-               if (count == 0 || left < count * WPA_SELECTOR_LEN) {
+               if (count == 0 || count > left / WPA_SELECTOR_LEN) {
                        wpa_printf(MSG_DEBUG, "%s: ie count botch (key mgmt), "
                                   "count %u left %u", __func__, count, left);
                        return -6;
@@ -681,8 +793,9 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
        }
 
        if (left > 0) {
-               wpa_printf(MSG_DEBUG, "%s: ie has %u trailing bytes - ignored",
-                          __func__, left);
+               wpa_hexdump(MSG_DEBUG,
+                           "wpa_parse_wpa_ie_wpa: ignore trailing bytes",
+                           pos, left);
        }
 
        return 0;
@@ -806,15 +919,17 @@ void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name,
  *
  * IEEE Std 802.11r-2008 - 8.5.1.5.5
  */
-void wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
-                      const u8 *sta_addr, const u8 *bssid,
-                      const u8 *pmk_r1_name,
-                      u8 *ptk, size_t ptk_len, u8 *ptk_name)
+int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
+                     const u8 *sta_addr, const u8 *bssid,
+                     const u8 *pmk_r1_name,
+                     struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher)
 {
        u8 buf[2 * WPA_NONCE_LEN + 2 * ETH_ALEN];
        u8 *pos, hash[32];
        const u8 *addr[6];
        size_t len[6];
+       u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
+       size_t ptk_len;
 
        /*
         * PTK = KDF-PTKLen(PMK-R1, "FT-PTK", SNonce || ANonce ||
@@ -830,7 +945,12 @@ void wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
        os_memcpy(pos, sta_addr, ETH_ALEN);
        pos += ETH_ALEN;
 
-       sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf, ptk, ptk_len);
+       ptk->kck_len = wpa_kck_len(akmp);
+       ptk->kek_len = wpa_kek_len(akmp);
+       ptk->tk_len = wpa_cipher_key_len(cipher);
+       ptk_len = ptk->kck_len + ptk->kek_len + ptk->tk_len;
+
+       sha256_prf(pmk_r1, PMK_LEN, "FT-PTK", buf, pos - buf, tmp, ptk_len);
 
        /*
         * PTKName = Truncate-128(SHA-256(PMKR1Name || "FT-PTKN" || SNonce ||
@@ -851,6 +971,19 @@ void wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
 
        sha256_vector(6, addr, len, hash);
        os_memcpy(ptk_name, hash, WPA_PMK_NAME_LEN);
+
+       os_memcpy(ptk->kck, tmp, ptk->kck_len);
+       os_memcpy(ptk->kek, tmp + ptk->kck_len, ptk->kek_len);
+       os_memcpy(ptk->tk, tmp + ptk->kck_len + ptk->kek_len, ptk->tk_len);
+
+       wpa_hexdump_key(MSG_DEBUG, "FT: KCK", ptk->kck, ptk->kck_len);
+       wpa_hexdump_key(MSG_DEBUG, "FT: KEK", ptk->kek, ptk->kek_len);
+       wpa_hexdump_key(MSG_DEBUG, "FT: TK", ptk->tk, ptk->tk_len);
+       wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
+
+       os_memset(tmp, 0, sizeof(tmp));
+
+       return 0;
 }
 
 #endif /* CONFIG_IEEE80211R */
@@ -890,6 +1023,72 @@ void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
 }
 
 
+#ifdef CONFIG_SUITEB
+/**
+ * rsn_pmkid_suite_b - Calculate PMK identifier for Suite B AKM
+ * @kck: Key confirmation key
+ * @kck_len: Length of kck in bytes
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @pmkid: Buffer for PMKID
+ * Returns: 0 on success, -1 on failure
+ *
+ * IEEE Std 802.11ac-2013 - 11.6.1.3 Pairwise key hierarchy
+ * PMKID = Truncate(HMAC-SHA-256(KCK, "PMK Name" || AA || SPA))
+ */
+int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa,
+                     const u8 *spa, u8 *pmkid)
+{
+       char *title = "PMK Name";
+       const u8 *addr[3];
+       const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
+       unsigned char hash[SHA256_MAC_LEN];
+
+       addr[0] = (u8 *) title;
+       addr[1] = aa;
+       addr[2] = spa;
+
+       if (hmac_sha256_vector(kck, kck_len, 3, addr, len, hash) < 0)
+               return -1;
+       os_memcpy(pmkid, hash, PMKID_LEN);
+       return 0;
+}
+#endif /* CONFIG_SUITEB */
+
+
+#ifdef CONFIG_SUITEB192
+/**
+ * rsn_pmkid_suite_b_192 - Calculate PMK identifier for Suite B AKM
+ * @kck: Key confirmation key
+ * @kck_len: Length of kck in bytes
+ * @aa: Authenticator address
+ * @spa: Supplicant address
+ * @pmkid: Buffer for PMKID
+ * Returns: 0 on success, -1 on failure
+ *
+ * IEEE Std 802.11ac-2013 - 11.6.1.3 Pairwise key hierarchy
+ * PMKID = Truncate(HMAC-SHA-384(KCK, "PMK Name" || AA || SPA))
+ */
+int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len, const u8 *aa,
+                         const u8 *spa, u8 *pmkid)
+{
+       char *title = "PMK Name";
+       const u8 *addr[3];
+       const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN };
+       unsigned char hash[SHA384_MAC_LEN];
+
+       addr[0] = (u8 *) title;
+       addr[1] = aa;
+       addr[2] = spa;
+
+       if (hmac_sha384_vector(kck, kck_len, 3, addr, len, hash) < 0)
+               return -1;
+       os_memcpy(pmkid, hash, PMKID_LEN);
+       return 0;
+}
+#endif /* CONFIG_SUITEB192 */
+
+
 /**
  * wpa_cipher_txt - Convert cipher suite to a text string
  * @cipher: Cipher suite (WPA_CIPHER_* enum)
@@ -912,6 +1111,12 @@ const char * wpa_cipher_txt(int cipher)
                return "CCMP+TKIP";
        case WPA_CIPHER_GCMP:
                return "GCMP";
+       case WPA_CIPHER_GCMP_256:
+               return "GCMP-256";
+       case WPA_CIPHER_CCMP_256:
+               return "CCMP-256";
+       case WPA_CIPHER_GTK_NOT_USED:
+               return "GTK_NOT_USED";
        default:
                return "UNKNOWN";
        }
@@ -953,12 +1158,52 @@ const char * wpa_key_mgmt_txt(int key_mgmt, int proto)
        case WPA_KEY_MGMT_PSK_SHA256:
                return "WPA2-PSK-SHA256";
 #endif /* CONFIG_IEEE80211W */
+       case WPA_KEY_MGMT_WPS:
+               return "WPS";
+       case WPA_KEY_MGMT_SAE:
+               return "SAE";
+       case WPA_KEY_MGMT_FT_SAE:
+               return "FT-SAE";
+       case WPA_KEY_MGMT_OSEN:
+               return "OSEN";
+       case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
+               return "WPA2-EAP-SUITE-B";
+       case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+               return "WPA2-EAP-SUITE-B-192";
        default:
                return "UNKNOWN";
        }
 }
 
 
+u32 wpa_akm_to_suite(int akm)
+{
+       if (akm & WPA_KEY_MGMT_FT_IEEE8021X)
+               return WLAN_AKM_SUITE_FT_8021X;
+       if (akm & WPA_KEY_MGMT_FT_PSK)
+               return WLAN_AKM_SUITE_FT_PSK;
+       if (akm & WPA_KEY_MGMT_IEEE8021X)
+               return WLAN_AKM_SUITE_8021X;
+       if (akm & WPA_KEY_MGMT_IEEE8021X_SHA256)
+               return WLAN_AKM_SUITE_8021X_SHA256;
+       if (akm & WPA_KEY_MGMT_IEEE8021X)
+               return WLAN_AKM_SUITE_8021X;
+       if (akm & WPA_KEY_MGMT_PSK_SHA256)
+               return WLAN_AKM_SUITE_PSK_SHA256;
+       if (akm & WPA_KEY_MGMT_PSK)
+               return WLAN_AKM_SUITE_PSK;
+       if (akm & WPA_KEY_MGMT_CCKM)
+               return WLAN_AKM_SUITE_CCKM;
+       if (akm & WPA_KEY_MGMT_OSEN)
+               return WLAN_AKM_SUITE_OSEN;
+       if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+               return WLAN_AKM_SUITE_8021X_SUITE_B;
+       if (akm & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+               return WLAN_AKM_SUITE_8021X_SUITE_B_192;
+       return 0;
+}
+
+
 int wpa_compare_rsn_ie(int ft_initial_assoc,
                       const u8 *ie1, size_t ie1len,
                       const u8 *ie2, size_t ie2len)
@@ -1078,8 +1323,15 @@ int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid)
 int wpa_cipher_key_len(int cipher)
 {
        switch (cipher) {
+       case WPA_CIPHER_CCMP_256:
+       case WPA_CIPHER_GCMP_256:
+       case WPA_CIPHER_BIP_GMAC_256:
+       case WPA_CIPHER_BIP_CMAC_256:
+               return 32;
        case WPA_CIPHER_CCMP:
        case WPA_CIPHER_GCMP:
+       case WPA_CIPHER_AES_128_CMAC:
+       case WPA_CIPHER_BIP_GMAC_128:
                return 16;
        case WPA_CIPHER_TKIP:
                return 32;
@@ -1096,6 +1348,8 @@ int wpa_cipher_key_len(int cipher)
 int wpa_cipher_rsc_len(int cipher)
 {
        switch (cipher) {
+       case WPA_CIPHER_CCMP_256:
+       case WPA_CIPHER_GCMP_256:
        case WPA_CIPHER_CCMP:
        case WPA_CIPHER_GCMP:
        case WPA_CIPHER_TKIP:
@@ -1112,6 +1366,10 @@ int wpa_cipher_rsc_len(int cipher)
 int wpa_cipher_to_alg(int cipher)
 {
        switch (cipher) {
+       case WPA_CIPHER_CCMP_256:
+               return WPA_ALG_CCMP_256;
+       case WPA_CIPHER_GCMP_256:
+               return WPA_ALG_GCMP_256;
        case WPA_CIPHER_CCMP:
                return WPA_ALG_CCMP;
        case WPA_CIPHER_GCMP:
@@ -1121,34 +1379,24 @@ int wpa_cipher_to_alg(int cipher)
        case WPA_CIPHER_WEP104:
        case WPA_CIPHER_WEP40:
                return WPA_ALG_WEP;
+       case WPA_CIPHER_AES_128_CMAC:
+               return WPA_ALG_IGTK;
+       case WPA_CIPHER_BIP_GMAC_128:
+               return WPA_ALG_BIP_GMAC_128;
+       case WPA_CIPHER_BIP_GMAC_256:
+               return WPA_ALG_BIP_GMAC_256;
+       case WPA_CIPHER_BIP_CMAC_256:
+               return WPA_ALG_BIP_CMAC_256;
        }
        return WPA_ALG_NONE;
 }
 
 
-enum wpa_cipher wpa_cipher_to_suite_driver(int cipher)
-{
-       switch (cipher) {
-       case WPA_CIPHER_NONE:
-               return CIPHER_NONE;
-       case WPA_CIPHER_WEP40:
-               return CIPHER_WEP40;
-       case WPA_CIPHER_WEP104:
-               return CIPHER_WEP104;
-       case WPA_CIPHER_CCMP:
-               return CIPHER_CCMP;
-       case WPA_CIPHER_GCMP:
-               return CIPHER_GCMP;
-       case WPA_CIPHER_TKIP:
-       default:
-               return CIPHER_TKIP;
-       }
-}
-
-
 int wpa_cipher_valid_pairwise(int cipher)
 {
-       return cipher == WPA_CIPHER_CCMP ||
+       return cipher == WPA_CIPHER_CCMP_256 ||
+               cipher == WPA_CIPHER_GCMP_256 ||
+               cipher == WPA_CIPHER_CCMP ||
                cipher == WPA_CIPHER_GCMP ||
                cipher == WPA_CIPHER_TKIP;
 }
@@ -1156,6 +1404,10 @@ int wpa_cipher_valid_pairwise(int cipher)
 
 u32 wpa_cipher_to_suite(int proto, int cipher)
 {
+       if (cipher & WPA_CIPHER_CCMP_256)
+               return RSN_CIPHER_SUITE_CCMP_256;
+       if (cipher & WPA_CIPHER_GCMP_256)
+               return RSN_CIPHER_SUITE_GCMP_256;
        if (cipher & WPA_CIPHER_CCMP)
                return (proto == WPA_PROTO_RSN ?
                        RSN_CIPHER_SUITE_CCMP : WPA_CIPHER_SUITE_CCMP);
@@ -1173,65 +1425,80 @@ u32 wpa_cipher_to_suite(int proto, int cipher)
        if (cipher & WPA_CIPHER_NONE)
                return (proto == WPA_PROTO_RSN ?
                        RSN_CIPHER_SUITE_NONE : WPA_CIPHER_SUITE_NONE);
+       if (cipher & WPA_CIPHER_GTK_NOT_USED)
+               return RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED;
+       if (cipher & WPA_CIPHER_AES_128_CMAC)
+               return RSN_CIPHER_SUITE_AES_128_CMAC;
+       if (cipher & WPA_CIPHER_BIP_GMAC_128)
+               return RSN_CIPHER_SUITE_BIP_GMAC_128;
+       if (cipher & WPA_CIPHER_BIP_GMAC_256)
+               return RSN_CIPHER_SUITE_BIP_GMAC_256;
+       if (cipher & WPA_CIPHER_BIP_CMAC_256)
+               return RSN_CIPHER_SUITE_BIP_CMAC_256;
        return 0;
 }
 
 
-int rsn_cipher_put_suites(u8 *pos, int ciphers)
+int rsn_cipher_put_suites(u8 *start, int ciphers)
 {
-       int num_suites = 0;
+       u8 *pos = start;
 
+       if (ciphers & WPA_CIPHER_CCMP_256) {
+               RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP_256);
+               pos += RSN_SELECTOR_LEN;
+       }
+       if (ciphers & WPA_CIPHER_GCMP_256) {
+               RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP_256);
+               pos += RSN_SELECTOR_LEN;
+       }
        if (ciphers & WPA_CIPHER_CCMP) {
                RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
                pos += RSN_SELECTOR_LEN;
-               num_suites++;
        }
        if (ciphers & WPA_CIPHER_GCMP) {
                RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_GCMP);
                pos += RSN_SELECTOR_LEN;
-               num_suites++;
        }
        if (ciphers & WPA_CIPHER_TKIP) {
                RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP);
                pos += RSN_SELECTOR_LEN;
-               num_suites++;
        }
        if (ciphers & WPA_CIPHER_NONE) {
                RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE);
                pos += RSN_SELECTOR_LEN;
-               num_suites++;
        }
 
-       return num_suites;
+       return (pos - start) / RSN_SELECTOR_LEN;
 }
 
 
-int wpa_cipher_put_suites(u8 *pos, int ciphers)
+int wpa_cipher_put_suites(u8 *start, int ciphers)
 {
-       int num_suites = 0;
+       u8 *pos = start;
 
        if (ciphers & WPA_CIPHER_CCMP) {
                RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP);
                pos += WPA_SELECTOR_LEN;
-               num_suites++;
        }
        if (ciphers & WPA_CIPHER_TKIP) {
                RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP);
                pos += WPA_SELECTOR_LEN;
-               num_suites++;
        }
        if (ciphers & WPA_CIPHER_NONE) {
                RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE);
                pos += WPA_SELECTOR_LEN;
-               num_suites++;
        }
 
-       return num_suites;
+       return (pos - start) / RSN_SELECTOR_LEN;
 }
 
 
 int wpa_pick_pairwise_cipher(int ciphers, int none_allowed)
 {
+       if (ciphers & WPA_CIPHER_CCMP_256)
+               return WPA_CIPHER_CCMP_256;
+       if (ciphers & WPA_CIPHER_GCMP_256)
+               return WPA_CIPHER_GCMP_256;
        if (ciphers & WPA_CIPHER_CCMP)
                return WPA_CIPHER_CCMP;
        if (ciphers & WPA_CIPHER_GCMP)
@@ -1246,10 +1513,16 @@ int wpa_pick_pairwise_cipher(int ciphers, int none_allowed)
 
 int wpa_pick_group_cipher(int ciphers)
 {
+       if (ciphers & WPA_CIPHER_CCMP_256)
+               return WPA_CIPHER_CCMP_256;
+       if (ciphers & WPA_CIPHER_GCMP_256)
+               return WPA_CIPHER_GCMP_256;
        if (ciphers & WPA_CIPHER_CCMP)
                return WPA_CIPHER_CCMP;
        if (ciphers & WPA_CIPHER_GCMP)
                return WPA_CIPHER_GCMP;
+       if (ciphers & WPA_CIPHER_GTK_NOT_USED)
+               return WPA_CIPHER_GTK_NOT_USED;
        if (ciphers & WPA_CIPHER_TKIP)
                return WPA_CIPHER_TKIP;
        if (ciphers & WPA_CIPHER_WEP104)
@@ -1280,7 +1553,11 @@ int wpa_parse_cipher(const char *value)
                        end++;
                last = *end == '\0';
                *end = '\0';
-               if (os_strcmp(start, "CCMP") == 0)
+               if (os_strcmp(start, "CCMP-256") == 0)
+                       val |= WPA_CIPHER_CCMP_256;
+               else if (os_strcmp(start, "GCMP-256") == 0)
+                       val |= WPA_CIPHER_GCMP_256;
+               else if (os_strcmp(start, "CCMP") == 0)
                        val |= WPA_CIPHER_CCMP;
                else if (os_strcmp(start, "GCMP") == 0)
                        val |= WPA_CIPHER_GCMP;
@@ -1292,6 +1569,8 @@ int wpa_parse_cipher(const char *value)
                        val |= WPA_CIPHER_WEP40;
                else if (os_strcmp(start, "NONE") == 0)
                        val |= WPA_CIPHER_NONE;
+               else if (os_strcmp(start, "GTK_NOT_USED") == 0)
+                       val |= WPA_CIPHER_GTK_NOT_USED;
                else {
                        os_free(buf);
                        return -1;
@@ -1312,45 +1591,59 @@ int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim)
        char *pos = start;
        int ret;
 
+       if (ciphers & WPA_CIPHER_CCMP_256) {
+               ret = os_snprintf(pos, end - pos, "%sCCMP-256",
+                                 pos == start ? "" : delim);
+               if (os_snprintf_error(end - pos, ret))
+                       return -1;
+               pos += ret;
+       }
+       if (ciphers & WPA_CIPHER_GCMP_256) {
+               ret = os_snprintf(pos, end - pos, "%sGCMP-256",
+                                 pos == start ? "" : delim);
+               if (os_snprintf_error(end - pos, ret))
+                       return -1;
+               pos += ret;
+       }
        if (ciphers & WPA_CIPHER_CCMP) {
                ret = os_snprintf(pos, end - pos, "%sCCMP",
                                  pos == start ? "" : delim);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return -1;
                pos += ret;
        }
        if (ciphers & WPA_CIPHER_GCMP) {
                ret = os_snprintf(pos, end - pos, "%sGCMP",
                                  pos == start ? "" : delim);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return -1;
                pos += ret;
        }
        if (ciphers & WPA_CIPHER_TKIP) {
                ret = os_snprintf(pos, end - pos, "%sTKIP",
                                  pos == start ? "" : delim);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return -1;
                pos += ret;
        }
        if (ciphers & WPA_CIPHER_WEP104) {
                ret = os_snprintf(pos, end - pos, "%sWEP104",
                                  pos == start ? "" : delim);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return -1;
                pos += ret;
        }
        if (ciphers & WPA_CIPHER_WEP40) {
                ret = os_snprintf(pos, end - pos, "%sWEP40",
                                  pos == start ? "" : delim);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return -1;
                pos += ret;
        }
        if (ciphers & WPA_CIPHER_NONE) {
                ret = os_snprintf(pos, end - pos, "%sNONE",
                                  pos == start ? "" : delim);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return -1;
                pos += ret;
        }
@@ -1373,5 +1666,11 @@ int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise)
                return WPA_CIPHER_TKIP;
        if ((pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) == WPA_CIPHER_GCMP)
                return WPA_CIPHER_GCMP;
+       if ((pairwise & (WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP |
+                        WPA_CIPHER_GCMP)) == WPA_CIPHER_GCMP_256)
+               return WPA_CIPHER_GCMP_256;
+       if ((pairwise & (WPA_CIPHER_CCMP_256 | WPA_CIPHER_CCMP |
+                        WPA_CIPHER_GCMP)) == WPA_CIPHER_CCMP_256)
+               return WPA_CIPHER_CCMP_256;
        return WPA_CIPHER_CCMP;
 }
index 2d63662..091e317 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * WPA definitions shared between hostapd and wpa_supplicant
- * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 #define WPA_GTK_MAX_LEN 32
 
 #define WPA_ALLOWED_PAIRWISE_CIPHERS \
-(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE)
+(WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_NONE | \
+WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256)
 #define WPA_ALLOWED_GROUP_CIPHERS \
 (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP | WPA_CIPHER_TKIP | WPA_CIPHER_WEP104 | \
-WPA_CIPHER_WEP40)
+WPA_CIPHER_WEP40 | WPA_CIPHER_GCMP_256 | WPA_CIPHER_CCMP_256 | \
+WPA_CIPHER_GTK_NOT_USED)
 
 #define WPA_SELECTOR_LEN 4
 #define WPA_VERSION 1
@@ -60,7 +62,12 @@ WPA_CIPHER_WEP40)
 #define RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
 #define RSN_AUTH_KEY_MGMT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
 #define RSN_AUTH_KEY_MGMT_FT_SAE RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
+#define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
+#define RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192 RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
+#define RSN_AUTH_KEY_MGMT_FT_802_1X_SUITE_B_192 \
+RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
 #define RSN_AUTH_KEY_MGMT_CCKM RSN_SELECTOR(0x00, 0x40, 0x96, 0x00)
+#define RSN_AUTH_KEY_MGMT_OSEN RSN_SELECTOR(0x50, 0x6f, 0x9a, 0x01)
 
 #define RSN_CIPHER_SUITE_NONE RSN_SELECTOR(0x00, 0x0f, 0xac, 0)
 #define RSN_CIPHER_SUITE_WEP40 RSN_SELECTOR(0x00, 0x0f, 0xac, 1)
@@ -70,11 +77,14 @@ WPA_CIPHER_WEP40)
 #endif
 #define RSN_CIPHER_SUITE_CCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 4)
 #define RSN_CIPHER_SUITE_WEP104 RSN_SELECTOR(0x00, 0x0f, 0xac, 5)
-#ifdef CONFIG_IEEE80211W
 #define RSN_CIPHER_SUITE_AES_128_CMAC RSN_SELECTOR(0x00, 0x0f, 0xac, 6)
-#endif /* CONFIG_IEEE80211W */
 #define RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED RSN_SELECTOR(0x00, 0x0f, 0xac, 7)
 #define RSN_CIPHER_SUITE_GCMP RSN_SELECTOR(0x00, 0x0f, 0xac, 8)
+#define RSN_CIPHER_SUITE_GCMP_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 9)
+#define RSN_CIPHER_SUITE_CCMP_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 10)
+#define RSN_CIPHER_SUITE_BIP_GMAC_128 RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
+#define RSN_CIPHER_SUITE_BIP_GMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
+#define RSN_CIPHER_SUITE_BIP_CMAC_256 RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
 
 /* EAPOL-Key Key Data Encapsulation
  * GroupKey and PeerKey require encryption, otherwise, encryption is optional.
@@ -98,6 +108,9 @@ WPA_CIPHER_WEP40)
 #define RSN_KEY_DATA_MULTIBAND_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
 #define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
 
+#define WFA_KEY_DATA_IP_ADDR_REQ RSN_SELECTOR(0x50, 0x6f, 0x9a, 4)
+#define WFA_KEY_DATA_IP_ADDR_ALLOC RSN_SELECTOR(0x50, 0x6f, 0x9a, 5)
+
 #define WPA_OUI_TYPE RSN_SELECTOR(0x00, 0x50, 0xf2, 1)
 
 #define RSN_SELECTOR_PUT(a, val) WPA_PUT_BE32((u8 *) (a), (val))
@@ -115,6 +128,7 @@ WPA_CIPHER_WEP40)
 
 #ifdef CONFIG_IEEE80211W
 #define WPA_IGTK_LEN 16
+#define WPA_IGTK_MAX_LEN 32
 #endif /* CONFIG_IEEE80211W */
 
 
@@ -143,6 +157,7 @@ WPA_CIPHER_WEP40)
 
 /* IEEE 802.11, 8.5.2 EAPOL-Key frames */
 #define WPA_KEY_INFO_TYPE_MASK ((u16) (BIT(0) | BIT(1) | BIT(2)))
+#define WPA_KEY_INFO_TYPE_AKM_DEFINED 0
 #define WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 BIT(0)
 #define WPA_KEY_INFO_TYPE_HMAC_SHA1_AES BIT(1)
 #define WPA_KEY_INFO_TYPE_AES_128_CMAC 3
@@ -176,22 +191,38 @@ struct wpa_eapol_key {
        /* followed by key_data_length bytes of key_data */
 } STRUCT_PACKED;
 
+struct wpa_eapol_key_192 {
+       u8 type;
+       /* Note: key_info, key_length, and key_data_length are unaligned */
+       u8 key_info[2]; /* big endian */
+       u8 key_length[2]; /* big endian */
+       u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
+       u8 key_nonce[WPA_NONCE_LEN];
+       u8 key_iv[16];
+       u8 key_rsc[WPA_KEY_RSC_LEN];
+       u8 key_id[8]; /* Reserved in IEEE 802.11i/RSN */
+       u8 key_mic[24];
+       u8 key_data_length[2]; /* big endian */
+       /* followed by key_data_length bytes of key_data */
+} STRUCT_PACKED;
+
+#define WPA_EAPOL_KEY_MIC_MAX_LEN 24
+#define WPA_KCK_MAX_LEN 24
+#define WPA_KEK_MAX_LEN 32
+#define WPA_TK_MAX_LEN 32
+
 /**
  * struct wpa_ptk - WPA Pairwise Transient Key
  * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
  */
 struct wpa_ptk {
-       u8 kck[16]; /* EAPOL-Key Key Confirmation Key (KCK) */
-       u8 kek[16]; /* EAPOL-Key Key Encryption Key (KEK) */
-       u8 tk1[16]; /* Temporal Key 1 (TK1) */
-       union {
-               u8 tk2[16]; /* Temporal Key 2 (TK2) */
-               struct {
-                       u8 tx_mic_key[8];
-                       u8 rx_mic_key[8];
-               } auth;
-       } u;
-} STRUCT_PACKED;
+       u8 kck[WPA_KCK_MAX_LEN]; /* EAPOL-Key Key Confirmation Key (KCK) */
+       u8 kek[WPA_KEK_MAX_LEN]; /* EAPOL-Key Key Encryption Key (KEK) */
+       u8 tk[WPA_TK_MAX_LEN]; /* Temporal Key (TK) */
+       size_t kck_len;
+       size_t kek_len;
+       size_t tk_len;
+};
 
 
 /* WPA IE version 1
@@ -269,10 +300,11 @@ struct rsn_error_kde {
 } STRUCT_PACKED;
 
 #ifdef CONFIG_IEEE80211W
+#define WPA_IGTK_KDE_PREFIX_LEN (2 + 6)
 struct wpa_igtk_kde {
        u8 keyid[2];
        u8 pn[6];
-       u8 igtk[WPA_IGTK_LEN];
+       u8 igtk[WPA_IGTK_MAX_LEN];
 } STRUCT_PACKED;
 #endif /* CONFIG_IEEE80211W */
 
@@ -311,16 +343,17 @@ struct rsn_rdie {
 #endif /* _MSC_VER */
 
 
-int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len,
-                     u8 *mic);
-void wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
-                   const u8 *addr1, const u8 *addr2,
-                   const u8 *nonce1, const u8 *nonce2,
-                   u8 *ptk, size_t ptk_len, int use_sha256);
+int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
+                     const u8 *buf, size_t len, u8 *mic);
+int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
+                  const u8 *addr1, const u8 *addr2,
+                  const u8 *nonce1, const u8 *nonce2,
+                  struct wpa_ptk *ptk, int akmp, int cipher);
 
 #ifdef CONFIG_IEEE80211R
-int wpa_ft_mic(const u8 *kck, const u8 *sta_addr, const u8 *ap_addr,
-              u8 transaction_seqnum, const u8 *mdie, size_t mdie_len,
+int wpa_ft_mic(const u8 *kck, size_t kck_len, const u8 *sta_addr,
+              const u8 *ap_addr, u8 transaction_seqnum,
+              const u8 *mdie, size_t mdie_len,
               const u8 *ftie, size_t ftie_len,
               const u8 *rsnie, size_t rsnie_len,
               const u8 *ric, size_t ric_len, u8 *mic);
@@ -333,10 +366,10 @@ void wpa_derive_pmk_r1_name(const u8 *pmk_r0_name, const u8 *r1kh_id,
 void wpa_derive_pmk_r1(const u8 *pmk_r0, const u8 *pmk_r0_name,
                       const u8 *r1kh_id, const u8 *s1kh_id,
                       u8 *pmk_r1, u8 *pmk_r1_name);
-void wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
-                      const u8 *sta_addr, const u8 *bssid,
-                      const u8 *pmk_r1_name,
-                      u8 *ptk, size_t ptk_len, u8 *ptk_name);
+int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce,
+                     const u8 *sta_addr, const u8 *bssid,
+                     const u8 *pmk_r1_name,
+                     struct wpa_ptk *ptk, u8 *ptk_name, int akmp, int cipher);
 #endif /* CONFIG_IEEE80211R */
 
 struct wpa_ie_data {
@@ -358,9 +391,30 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
 
 void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
               u8 *pmkid, int use_sha256);
+#ifdef CONFIG_SUITEB
+int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa,
+                      const u8 *spa, u8 *pmkid);
+#else /* CONFIG_SUITEB */
+static inline int rsn_pmkid_suite_b(const u8 *kck, size_t kck_len, const u8 *aa,
+                                   const u8 *spa, u8 *pmkid)
+{
+       return -1;
+}
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SUITEB192
+int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len, const u8 *aa,
+                         const u8 *spa, u8 *pmkid);
+#else /* CONFIG_SUITEB192 */
+static inline int rsn_pmkid_suite_b_192(const u8 *kck, size_t kck_len,
+                                       const u8 *aa, const u8 *spa, u8 *pmkid)
+{
+       return -1;
+}
+#endif /* CONFIG_SUITEB192 */
 
 const char * wpa_cipher_txt(int cipher);
 const char * wpa_key_mgmt_txt(int key_mgmt, int proto);
+u32 wpa_akm_to_suite(int akm);
 int wpa_compare_rsn_ie(int ft_initial_assoc,
                       const u8 *ie1, size_t ie1len,
                       const u8 *ie2, size_t ie2len);
@@ -392,8 +446,8 @@ int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, struct wpa_ft_ies *parse);
 int wpa_cipher_key_len(int cipher);
 int wpa_cipher_rsc_len(int cipher);
 int wpa_cipher_to_alg(int cipher);
-enum wpa_cipher wpa_cipher_to_suite_driver(int cipher);
 int wpa_cipher_valid_pairwise(int cipher);
+int wpa_cipher_valid_mgmt_group(int cipher);
 u32 wpa_cipher_to_suite(int proto, int cipher);
 int rsn_cipher_put_suites(u8 *pos, int ciphers);
 int wpa_cipher_put_suites(u8 *pos, int ciphers);
@@ -402,5 +456,6 @@ int wpa_pick_group_cipher(int ciphers);
 int wpa_parse_cipher(const char *value);
 int wpa_write_ciphers(char *start, char *end, int ciphers, const char *delim);
 int wpa_select_ap_group_cipher(int wpa, int wpa_pairwise, int rsn_pairwise);
+unsigned int wpa_mic_len(int akmp);
 
 #endif /* WPA_COMMON_H */
index d9a7509..ccaaf1b 100644 (file)
 #include "private/android_filesystem_config.h"
 #endif /* ANDROID */
 
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+#include <net/if.h>
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
+
 #include "wpa_ctrl.h"
 #include "common.h"
 
 struct wpa_ctrl {
 #ifdef CONFIG_CTRL_IFACE_UDP
        int s;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+       struct sockaddr_in6 local;
+       struct sockaddr_in6 dest;
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
        struct sockaddr_in local;
        struct sockaddr_in dest;
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
        char *cookie;
        char *remote_ifname;
        char *remote_ip;
@@ -85,10 +94,9 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
        if (ctrl_path == NULL)
                return NULL;
 
-       ctrl = os_malloc(sizeof(*ctrl));
+       ctrl = os_zalloc(sizeof(*ctrl));
        if (ctrl == NULL)
                return NULL;
-       os_memset(ctrl, 0, sizeof(*ctrl));
 
        ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0);
        if (ctrl->s < 0) {
@@ -103,7 +111,7 @@ try_again:
                          CONFIG_CTRL_IFACE_CLIENT_DIR "/"
                          CONFIG_CTRL_IFACE_CLIENT_PREFIX "%d-%d",
                          (int) getpid(), counter);
-       if (ret < 0 || (size_t) ret >= sizeof(ctrl->local.sun_path)) {
+       if (os_snprintf_error(sizeof(ctrl->local.sun_path), ret)) {
                close(ctrl->s);
                os_free(ctrl);
                return NULL;
@@ -229,7 +237,6 @@ void wpa_ctrl_cleanup(void)
        struct dirent entry;
        struct dirent *result;
        size_t dirnamelen;
-       int prefixlen = os_strlen(CONFIG_CTRL_IFACE_CLIENT_PREFIX);
        size_t maxcopy;
        char pathname[PATH_MAX];
        char *namep;
@@ -246,11 +253,8 @@ void wpa_ctrl_cleanup(void)
        namep = pathname + dirnamelen;
        maxcopy = PATH_MAX - dirnamelen;
        while (readdir_r(dir, &entry, &result) == 0 && result != NULL) {
-               if (os_strncmp(entry.d_name, CONFIG_CTRL_IFACE_CLIENT_PREFIX,
-                              prefixlen) == 0) {
-                       if (os_strlcpy(namep, entry.d_name, maxcopy) < maxcopy)
-                               unlink(pathname);
-               }
+               if (os_strlcpy(namep, entry.d_name, maxcopy) < maxcopy)
+                       unlink(pathname);
        }
        closedir(dir);
 }
@@ -278,24 +282,37 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
        struct hostent *h;
 #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 
-       ctrl = os_malloc(sizeof(*ctrl));
+       ctrl = os_zalloc(sizeof(*ctrl));
        if (ctrl == NULL)
                return NULL;
-       os_memset(ctrl, 0, sizeof(*ctrl));
 
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+       ctrl->s = socket(PF_INET6, SOCK_DGRAM, 0);
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
        ctrl->s = socket(PF_INET, SOCK_DGRAM, 0);
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
        if (ctrl->s < 0) {
                perror("socket");
                os_free(ctrl);
                return NULL;
        }
 
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+       ctrl->local.sin6_family = AF_INET6;
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+       ctrl->local.sin6_addr = in6addr_any;
+#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+       inet_pton(AF_INET6, "::1", &ctrl->local.sin6_addr);
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
        ctrl->local.sin_family = AF_INET;
 #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
        ctrl->local.sin_addr.s_addr = INADDR_ANY;
 #else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
        ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1);
 #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
+
        if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
                 sizeof(ctrl->local)) < 0) {
                close(ctrl->s);
@@ -303,14 +320,24 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
                return NULL;
        }
 
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+       ctrl->dest.sin6_family = AF_INET6;
+       inet_pton(AF_INET6, "::1", &ctrl->dest.sin6_addr);
+       ctrl->dest.sin6_port = htons(WPA_CTRL_IFACE_PORT);
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
        ctrl->dest.sin_family = AF_INET;
        ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1);
        ctrl->dest.sin_port = htons(WPA_CTRL_IFACE_PORT);
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 
 #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
        if (ctrl_path) {
                char *port, *name;
                int port_id;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+               char *scope;
+               int scope_id = 0;
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 
                name = os_strdup(ctrl_path);
                if (name == NULL) {
@@ -318,7 +345,11 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
                        os_free(ctrl);
                        return NULL;
                }
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+               port = os_strchr(name, ',');
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
                port = os_strchr(name, ':');
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 
                if (port) {
                        port_id = atoi(&port[1]);
@@ -326,7 +357,16 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
                } else
                        port_id = WPA_CTRL_IFACE_PORT;
 
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+               scope = os_strchr(name, '%');
+               if (scope) {
+                       scope_id = if_nametoindex(&scope[1]);
+                       scope[0] = '\0';
+               }
+               h = gethostbyname2(name, AF_INET6);
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
                h = gethostbyname(name);
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
                ctrl->remote_ip = os_strdup(name);
                os_free(name);
                if (h == NULL) {
@@ -336,16 +376,33 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
                        os_free(ctrl);
                        return NULL;
                }
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+               ctrl->dest.sin6_scope_id = scope_id;
+               ctrl->dest.sin6_port = htons(port_id);
+               os_memcpy(&ctrl->dest.sin6_addr, h->h_addr, h->h_length);
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
                ctrl->dest.sin_port = htons(port_id);
-               os_memcpy(h->h_addr, (char *) &ctrl->dest.sin_addr.s_addr,
-                         h->h_length);
+               os_memcpy(&ctrl->dest.sin_addr.s_addr, h->h_addr, h->h_length);
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
        } else
                ctrl->remote_ip = os_strdup("localhost");
 #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 
        if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest,
                    sizeof(ctrl->dest)) < 0) {
-               perror("connect");
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+               char addr[INET6_ADDRSTRLEN];
+               wpa_printf(MSG_ERROR, "connect(%s:%d) failed: %s",
+                          inet_ntop(AF_INET6, &ctrl->dest.sin6_addr, addr,
+                                    sizeof(ctrl->dest)),
+                          ntohs(ctrl->dest.sin6_port),
+                          strerror(errno));
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
+               wpa_printf(MSG_ERROR, "connect(%s:%d) failed: %s",
+                          inet_ntoa(ctrl->dest.sin_addr),
+                          ntohs(ctrl->dest.sin_port),
+                          strerror(errno));
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
                close(ctrl->s);
                os_free(ctrl->remote_ip);
                os_free(ctrl);
@@ -395,7 +452,7 @@ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len,
                     void (*msg_cb)(char *msg, size_t len))
 {
        struct timeval tv;
-       struct os_time started_at;
+       struct os_reltime started_at;
        int res;
        fd_set rfds;
        const char *_cmd;
@@ -434,12 +491,12 @@ retry_send:
                         * longer before giving up.
                         */
                        if (started_at.sec == 0)
-                               os_get_time(&started_at);
+                               os_get_reltime(&started_at);
                        else {
-                               struct os_time n;
-                               os_get_time(&n);
+                               struct os_reltime n;
+                               os_get_reltime(&n);
                                /* Try for a few seconds. */
-                               if (n.sec > started_at.sec + 5)
+                               if (os_reltime_expired(&n, &started_at, 5))
                                        goto send_err;
                        }
                        os_sleep(1, 0);
@@ -584,7 +641,7 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path)
                ret = os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s",
                                  ctrl_path);
 #endif /* UNICODE */
-       if (ret < 0 || ret >= 256) {
+       if (os_snprintf_error(256, ret)) {
                os_free(ctrl);
                return NULL;
        }
index 0ab4b7f..1d19fc5 100644 (file)
@@ -42,6 +42,8 @@ extern "C" {
 #define WPA_EVENT_EAP_METHOD "CTRL-EVENT-EAP-METHOD "
 /** EAP peer certificate from TLS */
 #define WPA_EVENT_EAP_PEER_CERT "CTRL-EVENT-EAP-PEER-CERT "
+/** EAP peer certificate alternative subject name component from TLS */
+#define WPA_EVENT_EAP_PEER_ALT "CTRL-EVENT-EAP-PEER-ALT "
 /** EAP TLS certificate chain validation error */
 #define WPA_EVENT_EAP_TLS_CERT_ERROR "CTRL-EVENT-EAP-TLS-CERT-ERROR "
 /** EAP status */
@@ -54,15 +56,34 @@ extern "C" {
 #define WPA_EVENT_TEMP_DISABLED "CTRL-EVENT-SSID-TEMP-DISABLED "
 /** Temporarily disabled network block re-enabled */
 #define WPA_EVENT_REENABLED "CTRL-EVENT-SSID-REENABLED "
+/** New scan started */
+#define WPA_EVENT_SCAN_STARTED "CTRL-EVENT-SCAN-STARTED "
 /** New scan results available */
 #define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS "
+/** Scan command failed */
+#define WPA_EVENT_SCAN_FAILED "CTRL-EVENT-SCAN-FAILED "
 /** wpa_supplicant state change */
 #define WPA_EVENT_STATE_CHANGE "CTRL-EVENT-STATE-CHANGE "
 /** A new BSS entry was added (followed by BSS entry id and BSSID) */
 #define WPA_EVENT_BSS_ADDED "CTRL-EVENT-BSS-ADDED "
 /** A BSS entry was removed (followed by BSS entry id and BSSID) */
 #define WPA_EVENT_BSS_REMOVED "CTRL-EVENT-BSS-REMOVED "
+/** Change in the signal level was reported by the driver */
+#define WPA_EVENT_SIGNAL_CHANGE "CTRL-EVENT-SIGNAL-CHANGE "
+/** Regulatory domain channel */
+#define WPA_EVENT_REGDOM_CHANGE "CTRL-EVENT-REGDOM-CHANGE "
 
+/** RSN IBSS 4-way handshakes completed with specified peer */
+#define IBSS_RSN_COMPLETED "IBSS-RSN-COMPLETED "
+
+/** Notification of frequency conflict due to a concurrent operation.
+ *
+ * The indicated network is disabled and needs to be re-enabled before it can
+ * be used again.
+ */
+#define WPA_EVENT_FREQ_CONFLICT "CTRL-EVENT-FREQ-CONFLICT "
+/** Frequency ranges that the driver recommends to avoid */
+#define WPA_EVENT_AVOID_FREQ "CTRL-EVENT-AVOID-FREQ "
 /** WPS overlap detected in PBC mode */
 #define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED "
 /** Available WPS AP with active PBC found in scan results */
@@ -84,6 +105,10 @@ extern "C" {
 #define WPS_EVENT_SUCCESS "WPS-SUCCESS "
 /** WPS enrollment attempt timed out and was terminated */
 #define WPS_EVENT_TIMEOUT "WPS-TIMEOUT "
+/* PBC mode was activated */
+#define WPS_EVENT_ACTIVE "WPS-PBC-ACTIVE "
+/* PBC mode was disabled */
+#define WPS_EVENT_DISABLE "WPS-PBC-DISABLE "
 
 #define WPS_EVENT_ENROLLEE_SEEN "WPS-ENROLLEE-SEEN "
 
@@ -97,6 +122,20 @@ extern "C" {
 #define WPS_EVENT_ER_AP_SETTINGS "WPS-ER-AP-SETTINGS "
 #define WPS_EVENT_ER_SET_SEL_REG "WPS-ER-AP-SET-SEL-REG "
 
+/* MESH events */
+#define MESH_GROUP_STARTED "MESH-GROUP-STARTED "
+#define MESH_GROUP_REMOVED "MESH-GROUP-REMOVED "
+#define MESH_PEER_CONNECTED "MESH-PEER-CONNECTED "
+#define MESH_PEER_DISCONNECTED "MESH-PEER-DISCONNECTED "
+/** Mesh SAE authentication failure. Wrong password suspected. */
+#define MESH_SAE_AUTH_FAILURE "MESH-SAE-AUTH-FAILURE "
+#define MESH_SAE_AUTH_BLOCKED "MESH-SAE-AUTH-BLOCKED "
+
+/* WMM AC events */
+#define WMM_AC_EVENT_TSPEC_ADDED "TSPEC-ADDED "
+#define WMM_AC_EVENT_TSPEC_REMOVED "TSPEC-REMOVED "
+#define WMM_AC_EVENT_TSPEC_REQ_FAILED "TSPEC-REQ-FAILED "
+
 /** P2P device found */
 #define P2P_EVENT_DEVICE_FOUND "P2P-DEVICE-FOUND "
 
@@ -128,17 +167,55 @@ extern "C" {
 #define P2P_EVENT_SERV_DISC_REQ "P2P-SERV-DISC-REQ "
 /* parameters: <src addr> <update indicator> <TLVs> */
 #define P2P_EVENT_SERV_DISC_RESP "P2P-SERV-DISC-RESP "
+#define P2P_EVENT_SERV_ASP_RESP "P2P-SERV-ASP-RESP "
 #define P2P_EVENT_INVITATION_RECEIVED "P2P-INVITATION-RECEIVED "
 #define P2P_EVENT_INVITATION_RESULT "P2P-INVITATION-RESULT "
 #define P2P_EVENT_FIND_STOPPED "P2P-FIND-STOPPED "
+#define P2P_EVENT_PERSISTENT_PSK_FAIL "P2P-PERSISTENT-PSK-FAIL id="
+#define P2P_EVENT_PRESENCE_RESPONSE "P2P-PRESENCE-RESPONSE "
+#define P2P_EVENT_NFC_BOTH_GO "P2P-NFC-BOTH-GO "
+#define P2P_EVENT_NFC_PEER_CLIENT "P2P-NFC-PEER-CLIENT "
+#define P2P_EVENT_NFC_WHILE_CLIENT "P2P-NFC-WHILE-CLIENT "
+#define P2P_EVENT_FALLBACK_TO_GO_NEG "P2P-FALLBACK-TO-GO-NEG "
+#define P2P_EVENT_FALLBACK_TO_GO_NEG_ENABLED "P2P-FALLBACK-TO-GO-NEG-ENABLED "
 
 /* parameters: <PMF enabled> <timeout in ms> <Session Information URL> */
 #define ESS_DISASSOC_IMMINENT "ESS-DISASSOC-IMMINENT "
+#define P2P_EVENT_REMOVE_AND_REFORM_GROUP "P2P-REMOVE-AND-REFORM-GROUP "
+
+#define P2P_EVENT_P2PS_PROVISION_START "P2PS-PROV-START "
+#define P2P_EVENT_P2PS_PROVISION_DONE "P2PS-PROV-DONE "
 
 #define INTERWORKING_AP "INTERWORKING-AP "
+#define INTERWORKING_BLACKLISTED "INTERWORKING-BLACKLISTED "
 #define INTERWORKING_NO_MATCH "INTERWORKING-NO-MATCH "
+#define INTERWORKING_ALREADY_CONNECTED "INTERWORKING-ALREADY-CONNECTED "
+#define INTERWORKING_SELECTED "INTERWORKING-SELECTED "
+
+/* Credential block added; parameters: <id> */
+#define CRED_ADDED "CRED-ADDED "
+/* Credential block modified; parameters: <id> <field> */
+#define CRED_MODIFIED "CRED-MODIFIED "
+/* Credential block removed; parameters: <id> */
+#define CRED_REMOVED "CRED-REMOVED "
 
 #define GAS_RESPONSE_INFO "GAS-RESPONSE-INFO "
+/* parameters: <addr> <dialog_token> <freq> */
+#define GAS_QUERY_START "GAS-QUERY-START "
+/* parameters: <addr> <dialog_token> <freq> <status_code> <result> */
+#define GAS_QUERY_DONE "GAS-QUERY-DONE "
+
+/* parameters: <addr> <result> */
+#define ANQP_QUERY_DONE "ANQP-QUERY-DONE "
+
+#define HS20_SUBSCRIPTION_REMEDIATION "HS20-SUBSCRIPTION-REMEDIATION "
+#define HS20_DEAUTH_IMMINENT_NOTICE "HS20-DEAUTH-IMMINENT-NOTICE "
+
+#define EXT_RADIO_WORK_START "EXT-RADIO-WORK-START "
+#define EXT_RADIO_WORK_TIMEOUT "EXT-RADIO-WORK-TIMEOUT "
+
+#define RRM_EVENT_NEIGHBOR_REP_RXED "RRM-NEIGHBOR-REP-RECEIVED "
+#define RRM_EVENT_NEIGHBOR_REP_FAILED "RRM-NEIGHBOR-REP-REQUEST-FAILED "
 
 /* hostapd control interface - fixed message prefixes */
 #define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED "
@@ -154,6 +231,27 @@ extern "C" {
 #define AP_REJECTED_MAX_STA "AP-REJECTED-MAX-STA "
 #define AP_REJECTED_BLOCKED_STA "AP-REJECTED-BLOCKED-STA "
 
+#define AP_EVENT_ENABLED "AP-ENABLED "
+#define AP_EVENT_DISABLED "AP-DISABLED "
+
+#define INTERFACE_ENABLED "INTERFACE-ENABLED "
+#define INTERFACE_DISABLED "INTERFACE-DISABLED "
+
+#define ACS_EVENT_STARTED "ACS-STARTED "
+#define ACS_EVENT_COMPLETED "ACS-COMPLETED "
+#define ACS_EVENT_FAILED "ACS-FAILED "
+
+#define DFS_EVENT_RADAR_DETECTED "DFS-RADAR-DETECTED "
+#define DFS_EVENT_NEW_CHANNEL "DFS-NEW-CHANNEL "
+#define DFS_EVENT_CAC_START "DFS-CAC-START "
+#define DFS_EVENT_CAC_COMPLETED "DFS-CAC-COMPLETED "
+#define DFS_EVENT_NOP_FINISHED "DFS-NOP-FINISHED "
+
+#define AP_CSA_FINISHED "AP-CSA-FINISHED "
+
+/* BSS Transition Management Response frame received */
+#define BSS_TM_RESP "BSS-TM-RESP "
+
 /* BSS command information masks */
 
 #define WPA_BSS_MASK_ALL               0xFFFDFFFF
@@ -175,6 +273,29 @@ extern "C" {
 #define WPA_BSS_MASK_INTERNETW         BIT(15)
 #define WPA_BSS_MASK_WIFI_DISPLAY      BIT(16)
 #define WPA_BSS_MASK_DELIM             BIT(17)
+#define WPA_BSS_MASK_MESH_SCAN         BIT(18)
+#define WPA_BSS_MASK_SNR               BIT(19)
+#define WPA_BSS_MASK_EST_THROUGHPUT    BIT(20)
+
+
+/* VENDOR_ELEM_* frame id values */
+enum wpa_vendor_elem_frame {
+       VENDOR_ELEM_PROBE_REQ_P2P = 0,
+       VENDOR_ELEM_PROBE_RESP_P2P = 1,
+       VENDOR_ELEM_PROBE_RESP_P2P_GO = 2,
+       VENDOR_ELEM_BEACON_P2P_GO = 3,
+       VENDOR_ELEM_P2P_PD_REQ = 4,
+       VENDOR_ELEM_P2P_PD_RESP = 5,
+       VENDOR_ELEM_P2P_GO_NEG_REQ = 6,
+       VENDOR_ELEM_P2P_GO_NEG_RESP = 7,
+       VENDOR_ELEM_P2P_GO_NEG_CONF = 8,
+       VENDOR_ELEM_P2P_INV_REQ = 9,
+       VENDOR_ELEM_P2P_INV_RESP = 10,
+       VENDOR_ELEM_P2P_ASSOC_REQ = 11,
+       VENDOR_ELEM_P2P_ASSOC_RESP = 12,
+       VENDOR_ELEM_ASSOC_REQ = 13,
+       NUM_VENDOR_ELEM_FRAMES
+};
 
 
 /* wpa_supplicant/hostapd control interface access */
@@ -264,9 +385,10 @@ int wpa_ctrl_detach(struct wpa_ctrl *ctrl);
  * @reply_len: Length of the reply buffer
  * Returns: 0 on success, -1 on failure
  *
- * This function will receive a pending control interface message. This
- * function will block if no messages are available. The received response will
- * be written to reply and reply_len is set to the actual length of the reply.
+ * This function will receive a pending control interface message. The received
+ * response will be written to reply and reply_len is set to the actual length
+ * of the reply.
+
  * wpa_ctrl_recv() is only used for event messages, i.e., wpa_ctrl_attach()
  * must have been used to register the control interface as an event monitor.
  */
@@ -301,8 +423,6 @@ int wpa_ctrl_pending(struct wpa_ctrl *ctrl);
  */
 int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl);
 
-char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl);
-
 #ifdef ANDROID
 /**
  * wpa_ctrl_cleanup() - Delete any local UNIX domain socket files that
@@ -320,6 +440,8 @@ void wpa_ctrl_cleanup(void);
 #define WPA_CTRL_IFACE_PORT_LIMIT 50 /* decremented from start */
 #define WPA_GLOBAL_CTRL_IFACE_PORT 9878
 #define WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT 20 /* incremented from start */
+
+char * wpa_ctrl_get_remote_ifname(struct wpa_ctrl *ctrl);
 #endif /* CONFIG_CTRL_IFACE_UDP */
 
 
diff --git a/src/common/wpa_helpers.c b/src/common/wpa_helpers.c
new file mode 100644 (file)
index 0000000..28913b9
--- /dev/null
@@ -0,0 +1,292 @@
+/*
+ * wpa_supplicant ctrl_iface helpers
+ * Copyright (c) 2010-2011, Atheros Communications, Inc.
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <time.h>
+
+#include "common.h"
+#include "wpa_ctrl.h"
+#include "wpa_helpers.h"
+
+
+char *wpas_ctrl_path = "/var/run/wpa_supplicant/";
+static int default_timeout = 60;
+
+
+static struct wpa_ctrl * wpa_open_ctrl(const char *ifname)
+{
+       char buf[128];
+       struct wpa_ctrl *ctrl;
+
+       os_snprintf(buf, sizeof(buf), "%s%s", wpas_ctrl_path, ifname);
+       ctrl = wpa_ctrl_open(buf);
+       if (ctrl == NULL)
+               printf("wpa_command: wpa_ctrl_open(%s) failed\n", buf);
+       return ctrl;
+}
+
+
+int wpa_command(const char *ifname, const char *cmd)
+{
+       struct wpa_ctrl *ctrl;
+       char buf[128];
+       size_t len;
+
+       printf("wpa_command(ifname='%s', cmd='%s')\n", ifname, cmd);
+       ctrl = wpa_open_ctrl(ifname);
+       if (ctrl == NULL)
+               return -1;
+       len = sizeof(buf);
+       if (wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, NULL) < 0) {
+               printf("wpa_command: wpa_ctrl_request failed\n");
+               wpa_ctrl_close(ctrl);
+               return -1;
+       }
+       wpa_ctrl_close(ctrl);
+       buf[len] = '\0';
+       if (strncmp(buf, "FAIL", 4) == 0) {
+               printf("wpa_command: Command failed (FAIL received)\n");
+               return -1;
+       }
+       return 0;
+}
+
+
+int wpa_command_resp(const char *ifname, const char *cmd,
+                    char *resp, size_t resp_size)
+{
+       struct wpa_ctrl *ctrl;
+       size_t len;
+
+       printf("wpa_command(ifname='%s', cmd='%s')\n", ifname, cmd);
+       ctrl = wpa_open_ctrl(ifname);
+       if (ctrl == NULL)
+               return -1;
+       len = resp_size;
+       if (wpa_ctrl_request(ctrl, cmd, strlen(cmd), resp, &len, NULL) < 0) {
+               printf("wpa_command: wpa_ctrl_request failed\n");
+               wpa_ctrl_close(ctrl);
+               return -1;
+       }
+       wpa_ctrl_close(ctrl);
+       resp[len] = '\0';
+       return 0;
+}
+
+
+struct wpa_ctrl * open_wpa_mon(const char *ifname)
+{
+       struct wpa_ctrl *ctrl;
+
+       ctrl = wpa_open_ctrl(ifname);
+       if (ctrl == NULL)
+               return NULL;
+       if (wpa_ctrl_attach(ctrl) < 0) {
+               wpa_ctrl_close(ctrl);
+               return NULL;
+       }
+
+       return ctrl;
+}
+
+
+int get_wpa_cli_event2(struct wpa_ctrl *mon,
+                      const char *event, const char *event2,
+                      char *buf, size_t buf_size)
+{
+       int fd, ret;
+       fd_set rfd;
+       char *pos;
+       struct timeval tv;
+       time_t start, now;
+
+       printf("Waiting for wpa_cli event %s\n", event);
+       fd = wpa_ctrl_get_fd(mon);
+       if (fd < 0)
+               return -1;
+
+       time(&start);
+       while (1) {
+               size_t len;
+
+               FD_ZERO(&rfd);
+               FD_SET(fd, &rfd);
+               tv.tv_sec = default_timeout;
+               tv.tv_usec = 0;
+               ret = select(fd + 1, &rfd, NULL, NULL, &tv);
+               if (ret == 0) {
+                       printf("Timeout on waiting for event %s\n", event);
+                       return -1;
+               }
+               if (ret < 0) {
+                       printf("select: %s\n", strerror(errno));
+                       return -1;
+               }
+               len = buf_size;
+               if (wpa_ctrl_recv(mon, buf, &len) < 0) {
+                       printf("Failure while waiting for event %s\n", event);
+                       return -1;
+               }
+               if (len == buf_size)
+                       len--;
+               buf[len] = '\0';
+
+               pos = strchr(buf, '>');
+               if (pos &&
+                   (strncmp(pos + 1, event, strlen(event)) == 0 ||
+                    (event2 &&
+                     strncmp(pos + 1, event2, strlen(event2)) == 0)))
+                       return 0; /* Event found */
+
+               time(&now);
+               if ((int) (now - start) > default_timeout) {
+                       printf("Timeout on waiting for event %s\n", event);
+                       return -1;
+               }
+       }
+}
+
+
+int get_wpa_cli_event(struct wpa_ctrl *mon,
+                     const char *event, char *buf, size_t buf_size)
+{
+       return get_wpa_cli_event2(mon, event, NULL, buf, buf_size);
+}
+
+
+int get_wpa_status(const char *ifname, const char *field, char *obuf,
+                  size_t obuf_size)
+{
+       struct wpa_ctrl *ctrl;
+       char buf[4096];
+       char *pos, *end;
+       size_t len, flen;
+
+       ctrl = wpa_open_ctrl(ifname);
+       if (ctrl == NULL)
+               return -1;
+       len = sizeof(buf);
+       if (wpa_ctrl_request(ctrl, "STATUS", 6, buf, &len, NULL) < 0) {
+               wpa_ctrl_close(ctrl);
+               return -1;
+       }
+       wpa_ctrl_close(ctrl);
+       buf[len] = '\0';
+
+       flen = strlen(field);
+       pos = buf;
+       while (pos + flen < buf + len) {
+               if (pos > buf) {
+                       if (*pos != '\n') {
+                               pos++;
+                               continue;
+                       }
+                       pos++;
+               }
+               if (strncmp(pos, field, flen) != 0 || pos[flen] != '=') {
+                       pos++;
+                       continue;
+               }
+               pos += flen + 1;
+               end = strchr(pos, '\n');
+               if (end == NULL)
+                       return -1;
+               *end++ = '\0';
+               if (end - pos > (int) obuf_size)
+                       return -1;
+               memcpy(obuf, pos, end - pos);
+               return 0;
+       }
+
+       return -1;
+}
+
+
+int wait_ip_addr(const char *ifname, int timeout)
+{
+       char ip[30];
+       int count = timeout;
+       struct wpa_ctrl *ctrl;
+
+       while (count > 0) {
+               printf("%s: ifname='%s' - %d seconds remaining\n",
+                      __func__, ifname, count);
+               count--;
+               if (get_wpa_status(ifname, "ip_address", ip, sizeof(ip)) == 0
+                   && strlen(ip) > 0) {
+                       printf("IP address found: '%s'\n", ip);
+                       return 0;
+               }
+               ctrl = wpa_open_ctrl(ifname);
+               if (ctrl == NULL)
+                       return -1;
+               wpa_ctrl_close(ctrl);
+               sleep(1);
+       }
+       printf("%s: Could not get IP address for ifname='%s'", __func__,
+              ifname);
+       return -1;
+}
+
+
+int add_network(const char *ifname)
+{
+       char res[30];
+
+       if (wpa_command_resp(ifname, "ADD_NETWORK", res, sizeof(res)) < 0)
+               return -1;
+       return atoi(res);
+}
+
+
+int set_network(const char *ifname, int id, const char *field,
+               const char *value)
+{
+       char buf[200];
+       snprintf(buf, sizeof(buf), "SET_NETWORK %d %s %s", id, field, value);
+       return wpa_command(ifname, buf);
+}
+
+
+int set_network_quoted(const char *ifname, int id, const char *field,
+                      const char *value)
+{
+       char buf[200];
+       snprintf(buf, sizeof(buf), "SET_NETWORK %d %s \"%s\"",
+                id, field, value);
+       return wpa_command(ifname, buf);
+}
+
+
+int add_cred(const char *ifname)
+{
+       char res[30];
+
+       if (wpa_command_resp(ifname, "ADD_CRED", res, sizeof(res)) < 0)
+               return -1;
+       return atoi(res);
+}
+
+
+int set_cred(const char *ifname, int id, const char *field, const char *value)
+{
+       char buf[200];
+       snprintf(buf, sizeof(buf), "SET_CRED %d %s %s", id, field, value);
+       return wpa_command(ifname, buf);
+}
+
+
+int set_cred_quoted(const char *ifname, int id, const char *field,
+                   const char *value)
+{
+       char buf[200];
+       snprintf(buf, sizeof(buf), "SET_CRED %d %s \"%s\"",
+                id, field, value);
+       return wpa_command(ifname, buf);
+}
diff --git a/src/common/wpa_helpers.h b/src/common/wpa_helpers.h
new file mode 100644 (file)
index 0000000..54c2872
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * wpa_supplicant ctrl_iface helpers
+ * Copyright (c) 2010-2011, Atheros Communications, Inc.
+ * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPA_HELPERS_H
+#define WPA_HELPERS_H
+
+int wpa_command(const char *ifname, const char *cmd);
+int wpa_command_resp(const char *ifname, const char *cmd,
+                    char *resp, size_t resp_size);
+int get_wpa_status(const char *ifname, const char *field, char *obuf,
+                  size_t obuf_size);
+
+struct wpa_ctrl * open_wpa_mon(const char *ifname);
+int wait_ip_addr(const char *ifname, int timeout);
+int get_wpa_cli_event(struct wpa_ctrl *mon,
+                     const char *event, char *buf, size_t buf_size);
+int get_wpa_cli_event2(struct wpa_ctrl *mon,
+                      const char *event, const char *event2,
+                      char *buf, size_t buf_size);
+
+int add_network(const char *ifname);
+int set_network(const char *ifname, int id, const char *field,
+               const char *value);
+int set_network_quoted(const char *ifname, int id, const char *field,
+                      const char *value);
+int add_cred(const char *ifname);
+int set_cred(const char *ifname, int id, const char *field, const char *value);
+int set_cred_quoted(const char *ifname, int id, const char *field,
+                   const char *value);
+
+#endif /* WPA_HELPERS_H */
index a605a65..3e90350 100644 (file)
@@ -1,7 +1,7 @@
 all: libcrypto.a
 
 clean:
-       rm -f *~ *.o *.d libcrypto.a
+       rm -f *~ *.o *.d *.gcno *.gcda *.gcov libcrypto.a
 
 install:
        @echo Nothing to be made.
@@ -9,6 +9,7 @@ install:
 
 include ../lib.rules
 
+CFLAGS += -DCONFIG_CRYPTO_INTERNAL
 CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT
 CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER
 #CFLAGS += -DALL_DH_GROUPS
@@ -25,6 +26,7 @@ LIB_OBJS= \
        aes-internal-dec.o \
        aes-internal-enc.o \
        aes-omac1.o \
+       aes-siv.o \
        aes-unwrap.o \
        aes-wrap.o \
        des-internal.o \
index d14670d..cf22778 100644 (file)
@@ -203,7 +203,7 @@ int aes_ccm_ad(const u8 *key, size_t key_len, const u8 *nonce,
 
        aes_encrypt_deinit(aes);
 
-       if (os_memcmp(x, t, M) != 0) {
+       if (os_memcmp_const(x, t, M) != 0) {
                wpa_printf(MSG_EXCESSIVE, "CCM: Auth mismatch");
                return -1;
        }
index 21941c6..15a09f8 100644 (file)
@@ -71,7 +71,7 @@ int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len,
 
        ret = 0;
 fail:
-       os_free(buf);
+       bin_clear_free(buf, buf_len);
 
        return ret;
 }
index 3d91c71..84294d2 100644 (file)
@@ -310,7 +310,7 @@ int aes_gcm_ad(const u8 *key, size_t key_len, const u8 *iv, size_t iv_len,
 
        aes_encrypt_deinit(aes);
 
-       if (os_memcmp(tag, T, 16) != 0) {
+       if (os_memcmp_const(tag, T, 16) != 0) {
                wpa_printf(MSG_EXCESSIVE, "GCM: Tag mismatch");
                return -1;
        }
index 27895eb..375db57 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * One-key CBC MAC (OMAC1) hash with AES-128
+ * One-key CBC MAC (OMAC1) hash with AES
  *
  * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
  *
@@ -27,8 +27,9 @@ static void gf_mulx(u8 *pad)
 
 
 /**
- * omac1_aes_128_vector - One-Key CBC MAC (OMAC1) hash with AES-128
- * @key: 128-bit key for the hash operation
+ * omac1_aes_vector - One-Key CBC MAC (OMAC1) hash with AES
+ * @key: Key for the hash operation
+ * @key_len: Key length in octets
  * @num_elem: Number of elements in the data vector
  * @addr: Pointers to the data areas
  * @len: Lengths of the data blocks
@@ -39,15 +40,15 @@ static void gf_mulx(u8 *pad)
  * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
  * (SP) 800-38B.
  */
-int omac1_aes_128_vector(const u8 *key, size_t num_elem,
-                        const u8 *addr[], const size_t *len, u8 *mac)
+int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
+                    const u8 *addr[], const size_t *len, u8 *mac)
 {
        void *ctx;
        u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE];
        const u8 *pos, *end;
        size_t i, e, left, total_len;
 
-       ctx = aes_encrypt_init(key, 16);
+       ctx = aes_encrypt_init(key, key_len);
        if (ctx == NULL)
                return -1;
        os_memset(cbc, 0, AES_BLOCK_SIZE);
@@ -65,6 +66,13 @@ int omac1_aes_128_vector(const u8 *key, size_t num_elem,
                for (i = 0; i < AES_BLOCK_SIZE; i++) {
                        cbc[i] ^= *pos++;
                        if (pos >= end) {
+                               /*
+                                * Stop if there are no more bytes to process
+                                * since there are no more entries in the array.
+                                */
+                               if (i + 1 == AES_BLOCK_SIZE &&
+                                   left == AES_BLOCK_SIZE)
+                                       break;
                                e++;
                                pos = addr[e];
                                end = pos + len[e];
@@ -83,6 +91,12 @@ int omac1_aes_128_vector(const u8 *key, size_t num_elem,
                for (i = 0; i < left; i++) {
                        cbc[i] ^= *pos++;
                        if (pos >= end) {
+                               /*
+                                * Stop if there are no more bytes to process
+                                * since there are no more entries in the array.
+                                */
+                               if (i + 1 == left)
+                                       break;
                                e++;
                                pos = addr[e];
                                end = pos + len[e];
@@ -101,6 +115,26 @@ int omac1_aes_128_vector(const u8 *key, size_t num_elem,
 
 
 /**
+ * omac1_aes_128_vector - One-Key CBC MAC (OMAC1) hash with AES-128
+ * @key: 128-bit key for the hash operation
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for MAC (128 bits, i.e., 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is a mode for using block cipher (AES in this case) for authentication.
+ * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
+ * (SP) 800-38B.
+ */
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+                        const u8 *addr[], const size_t *len, u8 *mac)
+{
+       return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
+}
+
+
+/**
  * omac1_aes_128 - One-Key CBC MAC (OMAC1) hash with AES-128 (aka AES-CMAC)
  * @key: 128-bit key for the hash operation
  * @data: Data buffer for which a MAC is determined
@@ -116,3 +150,21 @@ int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
 {
        return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
 }
+
+
+/**
+ * omac1_aes_256 - One-Key CBC MAC (OMAC1) hash with AES-256 (aka AES-CMAC)
+ * @key: 256-bit key for the hash operation
+ * @data: Data buffer for which a MAC is determined
+ * @data_len: Length of data buffer in bytes
+ * @mac: Buffer for MAC (128 bits, i.e., 16 bytes)
+ * Returns: 0 on success, -1 on failure
+ *
+ * This is a mode for using block cipher (AES in this case) for authentication.
+ * OMAC1 was standardized with the name CMAC by NIST in a Special Publication
+ * (SP) 800-38B.
+ */
+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+       return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
+}
diff --git a/src/crypto/aes-siv.c b/src/crypto/aes-siv.c
new file mode 100755 (executable)
index 0000000..5ac82c2
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * AES SIV (RFC 5297)
+ * Copyright (c) 2013 Cozybit, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "aes.h"
+#include "aes_wrap.h"
+#include "aes_siv.h"
+
+
+static const u8 zero[AES_BLOCK_SIZE];
+
+
+static void dbl(u8 *pad)
+{
+       int i, carry;
+
+       carry = pad[0] & 0x80;
+       for (i = 0; i < AES_BLOCK_SIZE - 1; i++)
+               pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7);
+       pad[AES_BLOCK_SIZE - 1] <<= 1;
+       if (carry)
+               pad[AES_BLOCK_SIZE - 1] ^= 0x87;
+}
+
+
+static void xor(u8 *a, const u8 *b)
+{
+       int i;
+
+       for (i = 0; i < AES_BLOCK_SIZE; i++)
+               *a++ ^= *b++;
+}
+
+
+static void xorend(u8 *a, int alen, const u8 *b, int blen)
+{
+       int i;
+
+       if (alen < blen)
+               return;
+
+       for (i = 0; i < blen; i++)
+               a[alen - blen + i] ^= b[i];
+}
+
+
+static void pad_block(u8 *pad, const u8 *addr, size_t len)
+{
+       os_memset(pad, 0, AES_BLOCK_SIZE);
+       os_memcpy(pad, addr, len);
+
+       if (len < AES_BLOCK_SIZE)
+               pad[len] = 0x80;
+}
+
+
+static int aes_s2v(const u8 *key, size_t num_elem, const u8 *addr[],
+                  size_t *len, u8 *mac)
+{
+       u8 tmp[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE];
+       u8 *buf = NULL;
+       int ret;
+       size_t i;
+
+       if (!num_elem) {
+               os_memcpy(tmp, zero, sizeof(zero));
+               tmp[AES_BLOCK_SIZE - 1] = 1;
+               return omac1_aes_128(key, tmp, sizeof(tmp), mac);
+       }
+
+       ret = omac1_aes_128(key, zero, sizeof(zero), tmp);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < num_elem - 1; i++) {
+               ret = omac1_aes_128(key, addr[i], len[i], tmp2);
+               if (ret)
+                       return ret;
+
+               dbl(tmp);
+               xor(tmp, tmp2);
+       }
+       if (len[i] >= AES_BLOCK_SIZE) {
+               buf = os_malloc(len[i]);
+               if (!buf)
+                       return -ENOMEM;
+
+               os_memcpy(buf, addr[i], len[i]);
+               xorend(buf, len[i], tmp, AES_BLOCK_SIZE);
+               ret = omac1_aes_128(key, buf, len[i], mac);
+               bin_clear_free(buf, len[i]);
+               return ret;
+       }
+
+       dbl(tmp);
+       pad_block(tmp2, addr[i], len[i]);
+       xor(tmp, tmp2);
+
+       return omac1_aes_128(key, tmp, sizeof(tmp), mac);
+}
+
+
+int aes_siv_encrypt(const u8 *key, const u8 *pw,
+                   size_t pwlen, size_t num_elem,
+                   const u8 *addr[], const size_t *len, u8 *out)
+{
+       const u8 *_addr[6];
+       size_t _len[6];
+       const u8 *k1 = key, *k2 = key + 16;
+       u8 v[AES_BLOCK_SIZE];
+       size_t i;
+       u8 *iv, *crypt_pw;
+
+       if (num_elem > ARRAY_SIZE(_addr) - 1)
+               return -1;
+
+       for (i = 0; i < num_elem; i++) {
+               _addr[i] = addr[i];
+               _len[i] = len[i];
+       }
+       _addr[num_elem] = pw;
+       _len[num_elem] = pwlen;
+
+       if (aes_s2v(k1, num_elem + 1, _addr, _len, v))
+               return -1;
+
+       iv = out;
+       crypt_pw = out + AES_BLOCK_SIZE;
+
+       os_memcpy(iv, v, AES_BLOCK_SIZE);
+       os_memcpy(crypt_pw, pw, pwlen);
+
+       /* zero out 63rd and 31st bits of ctr (from right) */
+       v[8] &= 0x7f;
+       v[12] &= 0x7f;
+       return aes_128_ctr_encrypt(k2, v, crypt_pw, pwlen);
+}
+
+
+int aes_siv_decrypt(const u8 *key, const u8 *iv_crypt, size_t iv_c_len,
+                   size_t num_elem, const u8 *addr[], const size_t *len,
+                   u8 *out)
+{
+       const u8 *_addr[6];
+       size_t _len[6];
+       const u8 *k1 = key, *k2 = key + 16;
+       size_t crypt_len;
+       size_t i;
+       int ret;
+       u8 iv[AES_BLOCK_SIZE];
+       u8 check[AES_BLOCK_SIZE];
+
+       if (iv_c_len < AES_BLOCK_SIZE || num_elem > ARRAY_SIZE(_addr) - 1)
+               return -1;
+       crypt_len = iv_c_len - AES_BLOCK_SIZE;
+
+       for (i = 0; i < num_elem; i++) {
+               _addr[i] = addr[i];
+               _len[i] = len[i];
+       }
+       _addr[num_elem] = out;
+       _len[num_elem] = crypt_len;
+
+       os_memcpy(iv, iv_crypt, AES_BLOCK_SIZE);
+       os_memcpy(out, iv_crypt + AES_BLOCK_SIZE, crypt_len);
+
+       iv[8] &= 0x7f;
+       iv[12] &= 0x7f;
+
+       ret = aes_128_ctr_encrypt(k2, iv, out, crypt_len);
+       if (ret)
+               return ret;
+
+       ret = aes_s2v(k1, num_elem + 1, _addr, _len, check);
+       if (ret)
+               return ret;
+       if (os_memcmp(check, iv_crypt, AES_BLOCK_SIZE) == 0)
+               return 0;
+
+       return -1;
+}
index 9dd5160..ec793d9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * AES key unwrap (128-bit KEK, RFC3394)
+ * AES key unwrap (RFC3394)
  *
  * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
  *
 #include "aes_wrap.h"
 
 /**
- * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (128-bit KEK) (RFC3394)
+ * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (RFC3394)
  * @kek: Key encryption key (KEK)
+ * @kek_len: Length of KEK in octets
  * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16
  * bytes
  * @cipher: Wrapped key to be unwrapped, (n + 1) * 64 bits
  * @plain: Plaintext key, n * 64 bits
  * Returns: 0 on success, -1 on failure (e.g., integrity verification failed)
  */
-int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain)
+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
+              u8 *plain)
 {
-       u8 a[8], *r, b[16];
+       u8 a[8], *r, b[AES_BLOCK_SIZE];
        int i, j;
        void *ctx;
+       unsigned int t;
 
        /* 1) Initialize variables. */
        os_memcpy(a, cipher, 8);
        r = plain;
        os_memcpy(r, cipher + 8, 8 * n);
 
-       ctx = aes_decrypt_init(kek, 16);
+       ctx = aes_decrypt_init(kek, kek_len);
        if (ctx == NULL)
                return -1;
 
@@ -48,7 +51,11 @@ int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain)
                r = plain + (n - 1) * 8;
                for (i = n; i >= 1; i--) {
                        os_memcpy(b, a, 8);
-                       b[7] ^= n * j + i;
+                       t = n * j + i;
+                       b[7] ^= t;
+                       b[6] ^= t >> 8;
+                       b[5] ^= t >> 16;
+                       b[4] ^= t >> 24;
 
                        os_memcpy(b + 8, r, 8);
                        aes_decrypt(ctx, b, b);
index 89d6f94..7ed34e8 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * AES Key Wrap Algorithm (128-bit KEK) (RFC3394)
+ * AES Key Wrap Algorithm (RFC3394)
  *
  * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
  *
 #include "aes_wrap.h"
 
 /**
- * aes_wrap - Wrap keys with AES Key Wrap Algorithm (128-bit KEK) (RFC3394)
- * @kek: 16-octet Key encryption key (KEK)
+ * aes_wrap - Wrap keys with AES Key Wrap Algorithm (RFC3394)
+ * @kek: Key encryption key (KEK)
+ * @kek_len: Length of KEK in octets
  * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16
  * bytes
  * @plain: Plaintext key to be wrapped, n * 64 bits
  * @cipher: Wrapped key, (n + 1) * 64 bits
  * Returns: 0 on success, -1 on failure
  */
-int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher)
+int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
 {
-       u8 *a, *r, b[16];
+       u8 *a, *r, b[AES_BLOCK_SIZE];
        int i, j;
        void *ctx;
+       unsigned int t;
 
        a = cipher;
        r = cipher + 8;
@@ -35,7 +37,7 @@ int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher)
        os_memset(a, 0xa6, 8);
        os_memcpy(r, plain, 8 * n);
 
-       ctx = aes_encrypt_init(kek, 16);
+       ctx = aes_encrypt_init(kek, kek_len);
        if (ctx == NULL)
                return -1;
 
@@ -53,7 +55,11 @@ int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher)
                        os_memcpy(b + 8, r, 8);
                        aes_encrypt(ctx, b, b);
                        os_memcpy(a, b, 8);
-                       a[7] ^= n * j + i;
+                       t = n * j + i;
+                       a[7] ^= t;
+                       a[6] ^= t >> 8;
+                       a[5] ^= t >> 16;
+                       a[4] ^= t >> 24;
                        os_memcpy(r, b + 8, 8);
                        r += 8;
                }
index 0433c04..4a14209 100644 (file)
@@ -1,8 +1,8 @@
 /*
  * AES-based functions
  *
- * - AES Key Wrap Algorithm (128-bit KEK) (RFC3394)
- * - One-Key CBC MAC (OMAC1) hash with AES-128
+ * - AES Key Wrap Algorithm (RFC3394)
+ * - One-Key CBC MAC (OMAC1) hash with AES-128 and AES-256
  * - AES-128 CTR mode encryption
  * - AES-128 EAX mode encryption/decryption
  * - AES-128 CBC
 #ifndef AES_WRAP_H
 #define AES_WRAP_H
 
-int __must_check aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher);
-int __must_check aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain);
+int __must_check aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain,
+                         u8 *cipher);
+int __must_check aes_unwrap(const u8 *kek, size_t kek_len, int n,
+                           const u8 *cipher, u8 *plain);
+int __must_check omac1_aes_vector(const u8 *key, size_t key_len,
+                                 size_t num_elem, const u8 *addr[],
+                                 const size_t *len, u8 *mac);
 int __must_check omac1_aes_128_vector(const u8 *key, size_t num_elem,
                                      const u8 *addr[], const size_t *len,
                                      u8 *mac);
 int __must_check omac1_aes_128(const u8 *key, const u8 *data, size_t data_len,
                               u8 *mac);
+int __must_check omac1_aes_256(const u8 *key, const u8 *data, size_t data_len,
+                              u8 *mac);
 int __must_check aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out);
 int __must_check aes_128_ctr_encrypt(const u8 *key, const u8 *nonce,
                                     u8 *data, size_t data_len);
index 9bccaaa..f2d5662 100644 (file)
@@ -271,6 +271,10 @@ struct crypto_private_key;
  */
 struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len);
 
+struct crypto_public_key *
+crypto_public_key_import_parts(const u8 *n, size_t n_len,
+                              const u8 *e, size_t e_len);
+
 /**
  * crypto_private_key_import - Import an RSA private key
  * @key: Key buffer (DER encoded RSA private key)
@@ -534,16 +538,6 @@ int crypto_bignum_exptmod(const struct crypto_bignum *a,
                          struct crypto_bignum *d);
 
 /**
- * crypto_bignum_rshift - b = a >> n
- * @a: Bignum
- * @n: Number of bits to shift
- * @b: Bignum; used to store the result of a >> n
- * Returns: 0 on success, -1 on failure
- */
-int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
-                        struct crypto_bignum *b);
-
-/**
  * crypto_bignum_inverse - Inverse a bignum so that a * c = 1 (mod b)
  * @a: Bignum
  * @b: Bignum
index 54209fa..dc7f350 100644 (file)
@@ -26,6 +26,15 @@ struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len)
 }
 
 
+struct crypto_public_key *
+crypto_public_key_import_parts(const u8 *n, size_t n_len,
+                              const u8 *e, size_t e_len)
+{
+       return (struct crypto_public_key *)
+               crypto_rsa_import_public_key_parts(n, n_len, e, e_len);
+}
+
+
 struct crypto_private_key * crypto_private_key_import(const u8 *key,
                                                      size_t len,
                                                      const char *passwd)
diff --git a/src/crypto/crypto_nss.c b/src/crypto/crypto_nss.c
deleted file mode 100644 (file)
index acd0a55..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-/*
- * Crypto wrapper functions for NSS
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-#include <nspr/prtypes.h>
-#include <nspr/plarenas.h>
-#include <nspr/plhash.h>
-#include <nspr/prtime.h>
-#include <nspr/prinrval.h>
-#include <nspr/prclist.h>
-#include <nspr/prlock.h>
-#include <nss/sechash.h>
-#include <nss/pk11pub.h>
-
-#include "common.h"
-#include "crypto.h"
-
-
-static int nss_hash(HASH_HashType type, unsigned int max_res_len,
-                   size_t num_elem, const u8 *addr[], const size_t *len,
-                   u8 *mac)
-{
-       HASHContext *ctx;
-       size_t i;
-       unsigned int reslen;
-
-       ctx = HASH_Create(type);
-       if (ctx == NULL)
-               return -1;
-
-       HASH_Begin(ctx);
-       for (i = 0; i < num_elem; i++)
-               HASH_Update(ctx, addr[i], len[i]);
-       HASH_End(ctx, mac, &reslen, max_res_len);
-       HASH_Destroy(ctx);
-
-       return 0;
-}
-
-
-void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
-{
-       PK11Context *ctx = NULL;
-       PK11SlotInfo *slot;
-       SECItem *param = NULL;
-       PK11SymKey *symkey = NULL;
-       SECItem item;
-       int olen;
-       u8 pkey[8], next, tmp;
-       int i;
-
-       /* Add parity bits to the key */
-       next = 0;
-       for (i = 0; i < 7; i++) {
-               tmp = key[i];
-               pkey[i] = (tmp >> i) | next | 1;
-               next = tmp << (7 - i);
-       }
-       pkey[i] = next | 1;
-
-       slot = PK11_GetBestSlot(CKM_DES_ECB, NULL);
-       if (slot == NULL) {
-               wpa_printf(MSG_ERROR, "NSS: PK11_GetBestSlot failed");
-               goto out;
-       }
-
-       item.type = siBuffer;
-       item.data = pkey;
-       item.len = 8;
-       symkey = PK11_ImportSymKey(slot, CKM_DES_ECB, PK11_OriginDerive,
-                                  CKA_ENCRYPT, &item, NULL);
-       if (symkey == NULL) {
-               wpa_printf(MSG_ERROR, "NSS: PK11_ImportSymKey failed");
-               goto out;
-       }
-
-       param = PK11_GenerateNewParam(CKM_DES_ECB, symkey);
-       if (param == NULL) {
-               wpa_printf(MSG_ERROR, "NSS: PK11_GenerateNewParam failed");
-               goto out;
-       }
-
-       ctx = PK11_CreateContextBySymKey(CKM_DES_ECB, CKA_ENCRYPT,
-                                        symkey, param);
-       if (ctx == NULL) {
-               wpa_printf(MSG_ERROR, "NSS: PK11_CreateContextBySymKey("
-                          "CKM_DES_ECB) failed");
-               goto out;
-       }
-
-       if (PK11_CipherOp(ctx, cypher, &olen, 8, (void *) clear, 8) !=
-           SECSuccess) {
-               wpa_printf(MSG_ERROR, "NSS: PK11_CipherOp failed");
-               goto out;
-       }
-
-out:
-       if (ctx)
-               PK11_DestroyContext(ctx, PR_TRUE);
-       if (symkey)
-               PK11_FreeSymKey(symkey);
-       if (param)
-               SECITEM_FreeItem(param, PR_TRUE);
-}
-
-
-int rc4_skip(const u8 *key, size_t keylen, size_t skip,
-            u8 *data, size_t data_len)
-{
-       return -1;
-}
-
-
-int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-{
-       return nss_hash(HASH_AlgMD5, 16, num_elem, addr, len, mac);
-}
-
-
-int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
-{
-       return nss_hash(HASH_AlgSHA1, 20, num_elem, addr, len, mac);
-}
-
-
-int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len,
-                 u8 *mac)
-{
-       return nss_hash(HASH_AlgSHA256, 32, num_elem, addr, len, mac);
-}
-
-
-void * aes_encrypt_init(const u8 *key, size_t len)
-{
-       return NULL;
-}
-
-
-void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt)
-{
-}
-
-
-void aes_encrypt_deinit(void *ctx)
-{
-}
-
-
-void * aes_decrypt_init(const u8 *key, size_t len)
-{
-       return NULL;
-}
-
-
-void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain)
-{
-}
-
-
-void aes_decrypt_deinit(void *ctx)
-{
-}
-
-
-int crypto_mod_exp(const u8 *base, size_t base_len,
-                  const u8 *power, size_t power_len,
-                  const u8 *modulus, size_t modulus_len,
-                  u8 *result, size_t *result_len)
-{
-       return -1;
-}
-
-
-struct crypto_cipher {
-};
-
-
-struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
-                                         const u8 *iv, const u8 *key,
-                                         size_t key_len)
-{
-       return NULL;
-}
-
-
-int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain,
-                         u8 *crypt, size_t len)
-{
-       return -1;
-}
-
-
-int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt,
-                         u8 *plain, size_t len)
-{
-       return -1;
-}
-
-
-void crypto_cipher_deinit(struct crypto_cipher *ctx)
-{
-}
index 5215c00..f158ef4 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Wrapper functions for OpenSSL libcrypto
- * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 #include "common.h"
 #include "wpabuf.h"
 #include "dh_group5.h"
+#include "sha1.h"
+#include "sha256.h"
+#include "sha384.h"
 #include "crypto.h"
 
-#if OPENSSL_VERSION_NUMBER < 0x00907000
-#define DES_key_schedule des_key_schedule
-#define DES_cblock des_cblock
-#define DES_set_key(key, schedule) des_set_key((key), *(schedule))
-#define DES_ecb_encrypt(input, output, ks, enc) \
-       des_ecb_encrypt((input), (output), *(ks), (enc))
-#endif /* openssl < 0.9.7 */
-
 static BIGNUM * get_group5_prime(void)
 {
-#if OPENSSL_VERSION_NUMBER < 0x00908000
+#ifdef OPENSSL_IS_BORINGSSL
        static const unsigned char RFC3526_PRIME_1536[] = {
                0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2,
                0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1,
@@ -58,20 +53,11 @@ static BIGNUM * get_group5_prime(void)
                0xCA,0x23,0x73,0x27,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
        };
         return BN_bin2bn(RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), NULL);
-#else /* openssl < 0.9.8 */
+#else /* OPENSSL_IS_BORINGSSL */
        return get_rfc3526_prime_1536(NULL);
-#endif /* openssl < 0.9.8 */
+#endif /* OPENSSL_IS_BORINGSSL */
 }
 
-#if OPENSSL_VERSION_NUMBER < 0x00908000
-#ifndef OPENSSL_NO_SHA256
-#ifndef OPENSSL_FIPS
-#define NO_SHA256_WRAPPER
-#endif
-#endif
-
-#endif /* openssl < 0.9.8 */
-
 #ifdef OPENSSL_NO_SHA256
 #define NO_SHA256_WRAPPER
 #endif
@@ -128,7 +114,7 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher)
        }
        pkey[i] = next | 1;
 
-       DES_set_key(&pkey, &ks);
+       DES_set_key((DES_cblock *) &pkey, &ks);
        DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks,
                        DES_ENCRYPT);
 }
@@ -197,8 +183,10 @@ static const EVP_CIPHER * aes_get_evp_cipher(size_t keylen)
        switch (keylen) {
        case 16:
                return EVP_aes_128_ecb();
+#ifndef OPENSSL_IS_BORINGSSL
        case 24:
                return EVP_aes_192_ecb();
+#endif /* OPENSSL_IS_BORINGSSL */
        case 32:
                return EVP_aes_256_ecb();
        }
@@ -254,7 +242,7 @@ void aes_encrypt_deinit(void *ctx)
                           "in AES encrypt", len);
        }
        EVP_CIPHER_CTX_cleanup(c);
-       os_free(c);
+       bin_clear_free(c, sizeof(*c));
 }
 
 
@@ -305,7 +293,34 @@ void aes_decrypt_deinit(void *ctx)
                           "in AES decrypt", len);
        }
        EVP_CIPHER_CTX_cleanup(c);
-       os_free(ctx);
+       bin_clear_free(c, sizeof(*c));
+}
+
+
+int aes_wrap(const u8 *kek, size_t kek_len, int n, const u8 *plain, u8 *cipher)
+{
+       AES_KEY actx;
+       int res;
+
+       if (AES_set_encrypt_key(kek, kek_len << 3, &actx))
+               return -1;
+       res = AES_wrap_key(&actx, NULL, cipher, plain, n * 8);
+       OPENSSL_cleanse(&actx, sizeof(actx));
+       return res <= 0 ? -1 : 0;
+}
+
+
+int aes_unwrap(const u8 *kek, size_t kek_len, int n, const u8 *cipher,
+              u8 *plain)
+{
+       AES_KEY actx;
+       int res;
+
+       if (AES_set_decrypt_key(kek, kek_len << 3, &actx))
+               return -1;
+       res = AES_unwrap_key(&actx, NULL, plain, cipher, (n + 1) * 8);
+       OPENSSL_cleanse(&actx, sizeof(actx));
+       return res <= 0 ? -1 : 0;
 }
 
 
@@ -338,10 +353,10 @@ int crypto_mod_exp(const u8 *base, size_t base_len,
        ret = 0;
 
 error:
-       BN_free(bn_base);
-       BN_free(bn_exp);
-       BN_free(bn_modulus);
-       BN_free(bn_result);
+       BN_clear_free(bn_base);
+       BN_clear_free(bn_exp);
+       BN_clear_free(bn_modulus);
+       BN_clear_free(bn_result);
        BN_CTX_free(ctx);
        return ret;
 }
@@ -376,9 +391,11 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg,
                case 16:
                        cipher = EVP_aes_128_cbc();
                        break;
+#ifndef OPENSSL_IS_BORINGSSL
                case 24:
                        cipher = EVP_aes_192_cbc();
                        break;
+#endif /* OPENSSL_IS_BORINGSSL */
                case 32:
                        cipher = EVP_aes_256_cbc();
                        break;
@@ -501,8 +518,8 @@ void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
        return dh;
 
 err:
-       wpabuf_free(pubkey);
-       wpabuf_free(privkey);
+       wpabuf_clear_free(pubkey);
+       wpabuf_clear_free(privkey);
        DH_free(dh);
        return NULL;
 }
@@ -569,13 +586,13 @@ struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public,
        if (keylen < 0)
                goto err;
        wpabuf_put(res, keylen);
-       BN_free(pub_key);
+       BN_clear_free(pub_key);
 
        return res;
 
 err:
-       BN_free(pub_key);
-       wpabuf_free(res);
+       BN_clear_free(pub_key);
+       wpabuf_clear_free(res);
        return NULL;
 }
 
@@ -632,7 +649,7 @@ struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key,
        HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL);
 #else /* openssl < 0.9.9 */
        if (HMAC_Init_ex(&ctx->ctx, key, key_len, md, NULL) != 1) {
-               os_free(ctx);
+               bin_clear_free(ctx, sizeof(*ctx));
                return NULL;
        }
 #endif /* openssl < 0.9.9 */
@@ -658,7 +675,7 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
                return -2;
 
        if (mac == NULL || len == NULL) {
-               os_free(ctx);
+               bin_clear_free(ctx, sizeof(*ctx));
                return 0;
        }
 
@@ -670,7 +687,7 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
        res = HMAC_Final(&ctx->ctx, mac, &mdlen);
 #endif /* openssl < 0.9.9 */
        HMAC_CTX_cleanup(&ctx->ctx);
-       os_free(ctx);
+       bin_clear_free(ctx, sizeof(*ctx));
 
        if (res == 1) {
                *len = mdlen;
@@ -681,43 +698,26 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
 }
 
 
-int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
-               int iterations, u8 *buf, size_t buflen)
-{
-#if OPENSSL_VERSION_NUMBER < 0x00908000
-       if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase),
-                                  (unsigned char *) ssid,
-                                  ssid_len, 4096, buflen, buf) != 1)
-               return -1;
-#else /* openssl < 0.9.8 */
-       if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid,
-                                  ssid_len, 4096, buflen, buf) != 1)
-               return -1;
-#endif /* openssl < 0.9.8 */
-       return 0;
-}
-
-
-int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
-                    const u8 *addr[], const size_t *len, u8 *mac)
+static int openssl_hmac_vector(const EVP_MD *type, const u8 *key,
+                              size_t key_len, size_t num_elem,
+                              const u8 *addr[], const size_t *len, u8 *mac,
+                              unsigned int mdlen)
 {
        HMAC_CTX ctx;
        size_t i;
-       unsigned int mdlen;
        int res;
 
        HMAC_CTX_init(&ctx);
 #if OPENSSL_VERSION_NUMBER < 0x00909000
-       HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL);
+       HMAC_Init_ex(&ctx, key, key_len, type, NULL);
 #else /* openssl < 0.9.9 */
-       if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha1(), NULL) != 1)
+       if (HMAC_Init_ex(&ctx, key, key_len, type, NULL) != 1)
                return -1;
 #endif /* openssl < 0.9.9 */
 
        for (i = 0; i < num_elem; i++)
                HMAC_Update(&ctx, addr[i], len[i]);
 
-       mdlen = 20;
 #if OPENSSL_VERSION_NUMBER < 0x00909000
        HMAC_Final(&ctx, mac, &mdlen);
        res = 1;
@@ -730,6 +730,43 @@ int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
 }
 
 
+#ifndef CONFIG_FIPS
+
+int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
+                   const u8 *addr[], const size_t *len, u8 *mac)
+{
+       return openssl_hmac_vector(EVP_md5(), key ,key_len, num_elem, addr, len,
+                                  mac, 16);
+}
+
+
+int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
+            u8 *mac)
+{
+       return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac);
+}
+
+#endif /* CONFIG_FIPS */
+
+
+int pbkdf2_sha1(const char *passphrase, const u8 *ssid, size_t ssid_len,
+               int iterations, u8 *buf, size_t buflen)
+{
+       if (PKCS5_PBKDF2_HMAC_SHA1(passphrase, os_strlen(passphrase), ssid,
+                                  ssid_len, iterations, buflen, buf) != 1)
+               return -1;
+       return 0;
+}
+
+
+int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
+                    const u8 *addr[], const size_t *len, u8 *mac)
+{
+       return openssl_hmac_vector(EVP_sha1(), key, key_len, num_elem, addr,
+                                  len, mac, 20);
+}
+
+
 int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
               u8 *mac)
 {
@@ -742,42 +779,37 @@ int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len,
 int hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem,
                       const u8 *addr[], const size_t *len, u8 *mac)
 {
-       HMAC_CTX ctx;
-       size_t i;
-       unsigned int mdlen;
-       int res;
+       return openssl_hmac_vector(EVP_sha256(), key, key_len, num_elem, addr,
+                                  len, mac, 32);
+}
 
-       HMAC_CTX_init(&ctx);
-#if OPENSSL_VERSION_NUMBER < 0x00909000
-       HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL);
-#else /* openssl < 0.9.9 */
-       if (HMAC_Init_ex(&ctx, key, key_len, EVP_sha256(), NULL) != 1)
-               return -1;
-#endif /* openssl < 0.9.9 */
 
-       for (i = 0; i < num_elem; i++)
-               HMAC_Update(&ctx, addr[i], len[i]);
+int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+               size_t data_len, u8 *mac)
+{
+       return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+}
 
-       mdlen = 32;
-#if OPENSSL_VERSION_NUMBER < 0x00909000
-       HMAC_Final(&ctx, mac, &mdlen);
-       res = 1;
-#else /* openssl < 0.9.9 */
-       res = HMAC_Final(&ctx, mac, &mdlen);
-#endif /* openssl < 0.9.9 */
-       HMAC_CTX_cleanup(&ctx);
+#endif /* CONFIG_SHA256 */
 
-       return res == 1 ? 0 : -1;
+
+#ifdef CONFIG_SHA384
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+                      const u8 *addr[], const size_t *len, u8 *mac)
+{
+       return openssl_hmac_vector(EVP_sha384(), key, key_len, num_elem, addr,
+                                  len, mac, 32);
 }
 
 
-int hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
                size_t data_len, u8 *mac)
 {
-       return hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac);
+       return hmac_sha384_vector(key, key_len, 1, &data, &data_len, mac);
 }
 
-#endif /* CONFIG_SHA256 */
+#endif /* CONFIG_SHA384 */
 
 
 int crypto_get_random(void *buf, size_t len)
@@ -789,8 +821,8 @@ int crypto_get_random(void *buf, size_t len)
 
 
 #ifdef CONFIG_OPENSSL_CMAC
-int omac1_aes_128_vector(const u8 *key, size_t num_elem,
-                        const u8 *addr[], const size_t *len, u8 *mac)
+int omac1_aes_vector(const u8 *key, size_t key_len, size_t num_elem,
+                    const u8 *addr[], const size_t *len, u8 *mac)
 {
        CMAC_CTX *ctx;
        int ret = -1;
@@ -800,8 +832,15 @@ int omac1_aes_128_vector(const u8 *key, size_t num_elem,
        if (ctx == NULL)
                return -1;
 
-       if (!CMAC_Init(ctx, key, 16, EVP_aes_128_cbc(), NULL))
+       if (key_len == 32) {
+               if (!CMAC_Init(ctx, key, 32, EVP_aes_256_cbc(), NULL))
+                       goto fail;
+       } else if (key_len == 16) {
+               if (!CMAC_Init(ctx, key, 16, EVP_aes_128_cbc(), NULL))
+                       goto fail;
+       } else {
                goto fail;
+       }
        for (i = 0; i < num_elem; i++) {
                if (!CMAC_Update(ctx, addr[i], len[i]))
                        goto fail;
@@ -816,10 +855,23 @@ fail:
 }
 
 
+int omac1_aes_128_vector(const u8 *key, size_t num_elem,
+                        const u8 *addr[], const size_t *len, u8 *mac)
+{
+       return omac1_aes_vector(key, 16, num_elem, addr, len, mac);
+}
+
+
 int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
 {
        return omac1_aes_128_vector(key, 1, &data, &data_len, mac);
 }
+
+
+int omac1_aes_256(const u8 *key, const u8 *data, size_t data_len, u8 *mac)
+{
+       return omac1_aes_vector(key, 32, 1, &data, &data_len, mac);
+}
 #endif /* CONFIG_OPENSSL_CMAC */
 
 
@@ -914,13 +966,6 @@ int crypto_bignum_exptmod(const struct crypto_bignum *a,
 }
 
 
-int crypto_bignum_rshift(const struct crypto_bignum *a, int n,
-                        struct crypto_bignum *b)
-{
-       return BN_rshift((BIGNUM *) b, (const BIGNUM *) a, n) ? 0 : -1;
-}
-
-
 int crypto_bignum_inverse(const struct crypto_bignum *a,
                          const struct crypto_bignum *b,
                          struct crypto_bignum *c)
@@ -1071,7 +1116,8 @@ void crypto_ec_deinit(struct crypto_ec *e)
 {
        if (e == NULL)
                return;
-       BN_free(e->order);
+       BN_clear_free(e->order);
+       BN_clear_free(e->prime);
        EC_GROUP_free(e->group);
        BN_CTX_free(e->bnctx);
        os_free(e);
@@ -1143,8 +1189,8 @@ int crypto_ec_point_to_bin(struct crypto_ec *e,
                ret = 0;
        }
 
-       BN_free(x_bn);
-       BN_free(y_bn);
+       BN_clear_free(x_bn);
+       BN_clear_free(y_bn);
        return ret;
 }
 
@@ -1160,20 +1206,20 @@ struct crypto_ec_point * crypto_ec_point_from_bin(struct crypto_ec *e,
        y = BN_bin2bn(val + len, len, NULL);
        elem = EC_POINT_new(e->group);
        if (x == NULL || y == NULL || elem == NULL) {
-               BN_free(x);
-               BN_free(y);
-               EC_POINT_free(elem);
+               BN_clear_free(x);
+               BN_clear_free(y);
+               EC_POINT_clear_free(elem);
                return NULL;
        }
 
        if (!EC_POINT_set_affine_coordinates_GFp(e->group, elem, x, y,
                                                 e->bnctx)) {
-               EC_POINT_free(elem);
+               EC_POINT_clear_free(elem);
                elem = NULL;
        }
 
-       BN_free(x);
-       BN_free(y);
+       BN_clear_free(x);
+       BN_clear_free(y);
 
        return (struct crypto_ec_point *) elem;
 }
index 3a675df..d3b2631 100644 (file)
@@ -1169,7 +1169,7 @@ static struct dh_group dh_groups[] = {
 #endif /* ALL_DH_GROUPS */
 };
 
-#define NUM_DH_GROUPS (sizeof(dh_groups) / sizeof(dh_groups[0]))
+#define NUM_DH_GROUPS ARRAY_SIZE(dh_groups)
 
 
 const struct dh_group * dh_groups_get(int id)
@@ -1198,14 +1198,14 @@ struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv)
        if (dh == NULL)
                return NULL;
 
-       wpabuf_free(*priv);
+       wpabuf_clear_free(*priv);
        *priv = wpabuf_alloc(dh->prime_len);
        if (*priv == NULL)
                return NULL;
 
        if (random_get_bytes(wpabuf_put(*priv, dh->prime_len), dh->prime_len))
        {
-               wpabuf_free(*priv);
+               wpabuf_clear_free(*priv);
                *priv = NULL;
                return NULL;
        }
@@ -1224,7 +1224,7 @@ struct wpabuf * dh_init(const struct dh_group *dh, struct wpabuf **priv)
                           wpabuf_head(*priv), wpabuf_len(*priv),
                           dh->prime, dh->prime_len, wpabuf_mhead(pv),
                           &pv_len) < 0) {
-               wpabuf_free(pv);
+               wpabuf_clear_free(pv);
                wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
                return NULL;
        }
@@ -1260,7 +1260,7 @@ struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public,
                           wpabuf_head(own_private), wpabuf_len(own_private),
                           dh->prime, dh->prime_len,
                           wpabuf_mhead(shared), &shared_len) < 0) {
-               wpabuf_free(shared);
+               wpabuf_clear_free(shared);
                wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed");
                return NULL;
        }
index db2b8cc..f64dfd3 100644 (file)
@@ -30,6 +30,7 @@ int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
        u8 tk[16];
        const u8 *_addr[6];
        size_t i, _len[6];
+       int res;
 
        if (num_elem > 5) {
                /*
@@ -85,7 +86,10 @@ int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem,
        _len[0] = 64;
        _addr[1] = mac;
        _len[1] = MD5_MAC_LEN;
-       return md5_vector(2, _addr, _len, mac);
+       res = md5_vector(2, _addr, _len, mac);
+       os_memset(k_pad, 0, sizeof(k_pad));
+       os_memset(tk, 0, sizeof(tk));
+       return res;
 }
 
 
index a7f9c6a..6edea57 100644 (file)
@@ -217,7 +217,7 @@ int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts,
        for (i = 0; i < 6; i++)
                sqn[i] = auts[i] ^ ak[i];
        if (milenage_f1(opc, k, _rand, sqn, amf, NULL, mac_s) ||
-           memcmp(mac_s, auts + 6, 8) != 0)
+           os_memcmp_const(mac_s, auts + 6, 8) != 0)
                return -1;
        return 0;
 }
@@ -312,7 +312,7 @@ int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand,
 
        wpa_hexdump(MSG_DEBUG, "Milenage: MAC_A", mac_a, 8);
 
-       if (os_memcmp(mac_a, autn + 8, 8) != 0) {
+       if (os_memcmp_const(mac_a, autn + 8, 8) != 0) {
                wpa_printf(MSG_DEBUG, "Milenage: MAC mismatch");
                wpa_hexdump(MSG_DEBUG, "Milenage: Received MAC_A",
                            autn + 8, 8);
index b2bbab2..49a5c1c 100644 (file)
@@ -58,6 +58,7 @@ static int utf8_to_ucs2(const u8 *utf8_string, size_t utf8_string_len,
                                WPA_PUT_LE16(ucs2_buffer + j,
                                             ((c & 0xF) << 12) |
                                             ((c2 & 0x3F) << 6) | (c3 & 0x3F));
+                               j += 2;
                        }
                }
        }
index 053740e..bc758aa 100644 (file)
@@ -232,12 +232,8 @@ int random_pool_ready(void)
         */
        fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
        if (fd < 0) {
-#ifndef CONFIG_NO_STDOUT_DEBUG
-               int error = errno;
-               perror("open(/dev/random)");
                wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
-                          strerror(error));
-#endif /* CONFIG_NO_STDOUT_DEBUG */
+                          strerror(errno));
                return -1;
        }
 
@@ -417,12 +413,8 @@ void random_init(const char *entropy_file)
 
        random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
        if (random_fd < 0) {
-#ifndef CONFIG_NO_STDOUT_DEBUG
-               int error = errno;
-               perror("open(/dev/random)");
                wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
-                          strerror(error));
-#endif /* CONFIG_NO_STDOUT_DEBUG */
+                          strerror(errno));
                return;
        }
        wpa_printf(MSG_DEBUG, "random: Trying to read entropy from "
index 10bf153..24bc3ff 100644 (file)
@@ -19,6 +19,7 @@ typedef struct SHA1Context SHA1_CTX;
 void SHA1Transform(u32 state[5], const unsigned char buffer[64]);
 
 
+#ifdef CONFIG_CRYPTO_INTERNAL
 /**
  * sha1_vector - SHA-1 hash for data vector
  * @num_elem: Number of elements in the data vector
@@ -38,6 +39,7 @@ int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac)
        SHA1Final(mac, &ctx);
        return 0;
 }
+#endif /* CONFIG_CRYPTO_INTERNAL */
 
 
 /* ===== start - public domain SHA1 implementation ===== */
index 90b9e74..4b2d137 100644 (file)
@@ -61,6 +61,7 @@ int sha1_prf(const u8 *key, size_t key_len, const char *label,
                }
                counter++;
        }
+       os_memset(hash, 0, sizeof(hash));
 
        return 0;
 }
index d48c77d..8fce139 100644 (file)
@@ -30,6 +30,7 @@ int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
        unsigned char tk[20];
        const u8 *_addr[6];
        size_t _len[6], i;
+       int ret;
 
        if (num_elem > 5) {
                /*
@@ -84,7 +85,9 @@ int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem,
        _len[0] = 64;
        _addr[1] = mac;
        _len[1] = SHA1_MAC_LEN;
-       return sha1_vector(2, _addr, _len, mac);
+       ret = sha1_vector(2, _addr, _len, mac);
+       os_memset(k_pad, 0, sizeof(k_pad));
+       return ret;
 }
 
 
diff --git a/src/crypto/sha256-kdf.c b/src/crypto/sha256-kdf.c
new file mode 100755 (executable)
index 0000000..d8a1beb
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * HMAC-SHA256 KDF (RFC 5295)
+ * 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 "sha256.h"
+
+
+/**
+ * hmac_sha256_kdf - HMAC-SHA256 based KDF (RFC 5295)
+ * @secret: Key for KDF
+ * @secret_len: Length of the key in bytes
+ * @label: A unique label for each purpose of the KDF
+ * @seed: Seed value to bind into the key
+ * @seed_len: Length of the seed
+ * @out: Buffer for the generated pseudo-random key
+ * @outlen: Number of bytes of key to generate
+ * Returns: 0 on success, -1 on failure.
+ *
+ * This function is used to derive new, cryptographically separate keys from a
+ * given key in ERP. This KDF is defined in RFC 5295, Chapter 3.1.2.
+ */
+int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
+                   const char *label, const u8 *seed, size_t seed_len,
+                   u8 *out, size_t outlen)
+{
+       u8 T[SHA256_MAC_LEN];
+       u8 iter = 1;
+       const unsigned char *addr[4];
+       size_t len[4];
+       size_t pos, clen;
+
+       addr[0] = T;
+       len[0] = SHA256_MAC_LEN;
+       addr[1] = (const unsigned char *) label;
+       len[1] = os_strlen(label) + 1;
+       addr[2] = seed;
+       len[2] = seed_len;
+       addr[3] = &iter;
+       len[3] = 1;
+
+       if (hmac_sha256_vector(secret, secret_len, 3, &addr[1], &len[1], T) < 0)
+               return -1;
+
+       pos = 0;
+       for (;;) {
+               clen = outlen - pos;
+               if (clen > SHA256_MAC_LEN)
+                       clen = SHA256_MAC_LEN;
+               os_memcpy(out + pos, T, clen);
+               pos += clen;
+
+               if (pos == outlen)
+                       break;
+
+               if (iter == 255) {
+                       os_memset(out, 0, outlen);
+                       return -1;
+               }
+               iter++;
+
+               if (hmac_sha256_vector(secret, secret_len, 4, addr, len, T) < 0)
+               {
+                       os_memset(out, 0, outlen);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
index 9a11208..79791c0 100644 (file)
@@ -95,4 +95,6 @@ void sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
                u8 mask = 0xff << (8 - buf_len_bits % 8);
                buf[pos - 1] &= mask;
        }
+
+       os_memset(hash, 0, sizeof(hash));
 }
index 7596a52..b15f511 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * SHA256 hash implementation and interface functions
- * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -23,5 +23,8 @@ void sha256_prf_bits(const u8 *key, size_t key_len, const char *label,
 void tls_prf_sha256(const u8 *secret, size_t secret_len,
                    const char *label, const u8 *seed, size_t seed_len,
                    u8 *out, size_t outlen);
+int hmac_sha256_kdf(const u8 *secret, size_t secret_len,
+                   const char *label, const u8 *seed, size_t seed_len,
+                   u8 *out, size_t outlen);
 
 #endif /* SHA256_H */
diff --git a/src/crypto/sha384.h b/src/crypto/sha384.h
new file mode 100755 (executable)
index 0000000..e6a1fe4
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * SHA384 hash implementation and interface functions
+ * 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.
+ */
+
+#ifndef SHA384_H
+#define SHA384_H
+
+#define SHA384_MAC_LEN 48
+
+int hmac_sha384_vector(const u8 *key, size_t key_len, size_t num_elem,
+                      const u8 *addr[], const size_t *len, u8 *mac);
+int hmac_sha384(const u8 *key, size_t key_len, const u8 *data,
+               size_t data_len, u8 *mac);
+
+#endif /* SHA384_H */
index 2fdaa02..9ae95a6 100644 (file)
@@ -40,9 +40,14 @@ enum tls_fail_reason {
        TLS_FAIL_SUBJECT_MISMATCH = 5,
        TLS_FAIL_ALTSUBJECT_MISMATCH = 6,
        TLS_FAIL_BAD_CERTIFICATE = 7,
-       TLS_FAIL_SERVER_CHAIN_PROBE = 8
+       TLS_FAIL_SERVER_CHAIN_PROBE = 8,
+       TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9,
+       TLS_FAIL_DOMAIN_MISMATCH = 10,
 };
 
+
+#define TLS_MAX_ALT_SUBJECT 10
+
 union tls_event_data {
        struct {
                int depth;
@@ -58,6 +63,8 @@ union tls_event_data {
                const struct wpabuf *cert;
                const u8 *hash;
                size_t hash_len;
+               const char *altsubject[TLS_MAX_ALT_SUBJECT];
+               int num_altsubject;
        } peer_cert;
 
        struct {
@@ -73,6 +80,7 @@ struct tls_config {
        const char *pkcs11_module_path;
        int fips_mode;
        int cert_in_cb;
+       const char *openssl_ciphers;
 
        void (*event_cb)(void *ctx, enum tls_event ev,
                         union tls_event_data *data);
@@ -84,6 +92,9 @@ struct tls_config {
 #define TLS_CONN_DISABLE_SESSION_TICKET BIT(2)
 #define TLS_CONN_REQUEST_OCSP BIT(3)
 #define TLS_CONN_REQUIRE_OCSP BIT(4)
+#define TLS_CONN_DISABLE_TLSv1_1 BIT(5)
+#define TLS_CONN_DISABLE_TLSv1_2 BIT(6)
+#define TLS_CONN_EAP_FAST BIT(7)
 
 /**
  * struct tls_connection_params - Parameters for TLS connection
@@ -96,6 +107,12 @@ struct tls_config {
  * %NULL to allow all subjects
  * @altsubject_match: String to match in the alternative subject of the peer
  * certificate or %NULL to allow all alternative subjects
+ * @suffix_match: String to suffix match in the dNSName or CN of the peer
+ * certificate or %NULL to allow all domain names. This may allow subdomains an
+ * wildcard certificates. Each domain name label must have a full match.
+ * @domain_match: String to match in the dNSName or CN of the peer
+ * certificate or %NULL to allow all domain names. This requires a full,
+ * case-insensitive match.
  * @client_cert: File or reference name for client X.509 certificate in PEM or
  * DER format
  * @client_cert_blob: client_cert as inlined data or %NULL if not used
@@ -118,6 +135,7 @@ struct tls_config {
  * specific for now)
  * @cert_id: the certificate's id when using engine
  * @ca_cert_id: the CA certificate's id when using engine
+ * @openssl_ciphers: OpenSSL cipher configuration
  * @flags: Parameter options (TLS_CONN_*)
  * @ocsp_stapling_response: DER encoded file with cached OCSP stapling response
  *     or %NULL if OCSP is not enabled
@@ -137,6 +155,8 @@ struct tls_connection_params {
        const char *ca_path;
        const char *subject_match;
        const char *altsubject_match;
+       const char *suffix_match;
+       const char *domain_match;
        const char *client_cert;
        const u8 *client_cert_blob;
        size_t client_cert_blob_len;
@@ -155,6 +175,7 @@ struct tls_connection_params {
        const char *key_id;
        const char *cert_id;
        const char *ca_cert_id;
+       const char *openssl_ciphers;
 
        unsigned int flags;
        const char *ocsp_stapling_response;
@@ -531,4 +552,21 @@ int __must_check  tls_connection_set_session_ticket_cb(
        void *tls_ctx, struct tls_connection *conn,
        tls_session_ticket_cb cb, void *ctx);
 
+void tls_connection_set_log_cb(struct tls_connection *conn,
+                              void (*log_cb)(void *ctx, const char *msg),
+                              void *ctx);
+
+#define TLS_BREAK_VERIFY_DATA BIT(0)
+#define TLS_BREAK_SRV_KEY_X_HASH BIT(1)
+#define TLS_BREAK_SRV_KEY_X_SIGNATURE BIT(2)
+#define TLS_DHE_PRIME_511B BIT(3)
+#define TLS_DHE_PRIME_767B BIT(4)
+#define TLS_DHE_PRIME_15 BIT(5)
+#define TLS_DHE_PRIME_58B BIT(6)
+#define TLS_DHE_NON_PRIME BIT(7)
+
+void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags);
+
+int tls_get_library_version(char *buf, size_t buf_len);
+
 #endif /* TLS_H */
old mode 100644 (file)
new mode 100755 (executable)
index a5d72f4..65db6fc
 #ifdef PKCS12_FUNCS
 #include <gnutls/pkcs12.h>
 #endif /* PKCS12_FUNCS */
+#if GNUTLS_VERSION_NUMBER >= 0x030103
+#include <gnutls/ocsp.h>
+#endif /* 3.1.3 */
 
 #include "common.h"
+#include "crypto/crypto.h"
 #include "tls.h"
 
 
-#define WPA_TLS_RANDOM_SIZE 32
-#define WPA_TLS_MASTER_SIZE 48
-
-
-#if LIBGNUTLS_VERSION_NUMBER < 0x010302
-/* GnuTLS 1.3.2 added functions for using master secret. Older versions require
- * use of internal structures to get the master_secret and
- * {server,client}_random.
- */
-#define GNUTLS_INTERNAL_STRUCTURE_HACK
-#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */
-
-
-#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
-/*
- * It looks like gnutls does not provide access to client/server_random and
- * master_key. This is somewhat unfortunate since these are needed for key
- * derivation in EAP-{TLS,TTLS,PEAP,FAST}. Workaround for now is a horrible
- * hack that copies the gnutls_session_int definition from gnutls_int.h so that
- * we can get the needed information.
- */
-
-typedef u8 uint8;
-typedef unsigned char opaque;
-typedef struct {
-    uint8 suite[2];
-} cipher_suite_st;
-
-typedef struct {
-       gnutls_connection_end_t entity;
-       gnutls_kx_algorithm_t kx_algorithm;
-       gnutls_cipher_algorithm_t read_bulk_cipher_algorithm;
-       gnutls_mac_algorithm_t read_mac_algorithm;
-       gnutls_compression_method_t read_compression_algorithm;
-       gnutls_cipher_algorithm_t write_bulk_cipher_algorithm;
-       gnutls_mac_algorithm_t write_mac_algorithm;
-       gnutls_compression_method_t write_compression_algorithm;
-       cipher_suite_st current_cipher_suite;
-       opaque master_secret[WPA_TLS_MASTER_SIZE];
-       opaque client_random[WPA_TLS_RANDOM_SIZE];
-       opaque server_random[WPA_TLS_RANDOM_SIZE];
-       /* followed by stuff we are not interested in */
-} security_parameters_st;
-
-struct gnutls_session_int {
-       security_parameters_st security_parameters;
-       /* followed by things we are not interested in */
-};
-#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */
-
 static int tls_gnutls_ref_count = 0;
 
 struct tls_global {
@@ -78,17 +32,23 @@ struct tls_global {
 
        int params_set;
        gnutls_certificate_credentials_t xcred;
+
+       void (*event_cb)(void *ctx, enum tls_event ev,
+                        union tls_event_data *data);
+       void *cb_ctx;
+       int cert_in_cb;
 };
 
 struct tls_connection {
-       gnutls_session session;
-       char *subject_match, *altsubject_match;
+       struct tls_global *global;
+       gnutls_session_t session;
        int read_alerts, write_alerts, failed;
 
        u8 *pre_shared_secret;
        size_t pre_shared_secret_len;
        int established;
        int verify_peer;
+       unsigned int disable_time_checks:1;
 
        struct wpabuf *push_buf;
        struct wpabuf *pull_buf;
@@ -96,9 +56,16 @@ struct tls_connection {
 
        int params_set;
        gnutls_certificate_credentials_t xcred;
+
+       char *suffix_match;
+       char *domain_match;
+       unsigned int flags;
 };
 
 
+static int tls_connection_verify_peer(gnutls_session_t session);
+
+
 static void tls_log_func(int level, const char *msg)
 {
        char *s, *pos;
@@ -125,23 +92,15 @@ static void tls_log_func(int level, const char *msg)
 }
 
 
-extern int wpa_debug_show_keys;
-
 void * tls_init(const struct tls_config *conf)
 {
        struct tls_global *global;
 
-#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
-       /* Because of the horrible hack to get master_secret and client/server
-        * random, we need to make sure that the gnutls version is something
-        * that is expected to have same structure definition for the session
-        * data.. */
-       const char *ver;
-       const char *ok_ver[] = { "1.2.3", "1.2.4", "1.2.5", "1.2.6", "1.2.9",
-                                "1.3.2",
-                                NULL };
-       int i;
-#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
+       if (tls_gnutls_ref_count == 0) {
+               wpa_printf(MSG_DEBUG,
+                          "GnuTLS: Library version %s (runtime) - %s (build)",
+                          gnutls_check_version(NULL), GNUTLS_VERSION);
+       }
 
        global = os_zalloc(sizeof(*global));
        if (global == NULL)
@@ -153,28 +112,16 @@ void * tls_init(const struct tls_config *conf)
        }
        tls_gnutls_ref_count++;
 
-#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
-       ver = gnutls_check_version(NULL);
-       if (ver == NULL) {
-               tls_deinit(global);
-               return NULL;
-       }
-       wpa_printf(MSG_DEBUG, "%s - gnutls version %s", __func__, ver);
-       for (i = 0; ok_ver[i]; i++) {
-               if (strcmp(ok_ver[i], ver) == 0)
-                       break;
-       }
-       if (ok_ver[i] == NULL) {
-               wpa_printf(MSG_INFO, "Untested gnutls version %s - this needs "
-                          "to be tested and enabled in tls_gnutls.c", ver);
-               tls_deinit(global);
-               return NULL;
-       }
-#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
-
        gnutls_global_set_log_function(tls_log_func);
        if (wpa_debug_show_keys)
                gnutls_global_set_log_level(11);
+
+       if (conf) {
+               global->event_cb = conf->event_cb;
+               global->cb_ctx = conf->cb_ctx;
+               global->cert_in_cb = conf->cert_in_cb;
+       }
+
        return global;
 }
 
@@ -201,7 +148,7 @@ int tls_get_errors(void *ssl_ctx)
 }
 
 
-static ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf,
+static ssize_t tls_pull_func(gnutls_transport_ptr_t ptr, void *buf,
                             size_t len)
 {
        struct tls_connection *conn = (struct tls_connection *) ptr;
@@ -230,7 +177,7 @@ static ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf,
 }
 
 
-static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf,
+static ssize_t tls_push_func(gnutls_transport_ptr_t ptr, const void *buf,
                             size_t len)
 {
        struct tls_connection *conn = (struct tls_connection *) ptr;
@@ -248,12 +195,7 @@ static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf,
 static int tls_gnutls_init_session(struct tls_global *global,
                                   struct tls_connection *conn)
 {
-#if LIBGNUTLS_VERSION_NUMBER >= 0x020200
        const char *err;
-#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */
-       const int cert_types[2] = { GNUTLS_CRT_X509, 0 };
-       const int protos[2] = { GNUTLS_TLS1, 0 };
-#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */
        int ret;
 
        ret = gnutls_init(&conn->session,
@@ -268,7 +210,6 @@ static int tls_gnutls_init_session(struct tls_global *global,
        if (ret < 0)
                goto fail;
 
-#if LIBGNUTLS_VERSION_NUMBER >= 0x020200
        ret = gnutls_priority_set_direct(conn->session, "NORMAL:-VERS-SSL3.0",
                                         &err);
        if (ret < 0) {
@@ -276,19 +217,11 @@ static int tls_gnutls_init_session(struct tls_global *global,
                           "'%s'", err);
                goto fail;
        }
-#else /* LIBGNUTLS_VERSION_NUMBER >= 0x020200 */
-       ret = gnutls_certificate_type_set_priority(conn->session, cert_types);
-       if (ret < 0)
-               goto fail;
-
-       ret = gnutls_protocol_set_priority(conn->session, protos);
-       if (ret < 0)
-               goto fail;
-#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020200 */
 
        gnutls_transport_set_pull_function(conn->session, tls_pull_func);
        gnutls_transport_set_push_function(conn->session, tls_push_func);
-       gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr) conn);
+       gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr_t) conn);
+       gnutls_session_set_ptr(conn->session, conn);
 
        return 0;
 
@@ -309,6 +242,7 @@ struct tls_connection * tls_connection_init(void *ssl_ctx)
        conn = os_zalloc(sizeof(*conn));
        if (conn == NULL)
                return NULL;
+       conn->global = global;
 
        if (tls_gnutls_init_session(global, conn)) {
                os_free(conn);
@@ -344,10 +278,10 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
        gnutls_certificate_free_credentials(conn->xcred);
        gnutls_deinit(conn->session);
        os_free(conn->pre_shared_secret);
-       os_free(conn->subject_match);
-       os_free(conn->altsubject_match);
        wpabuf_free(conn->push_buf);
        wpabuf_free(conn->pull_buf);
+       os_free(conn->suffix_match);
+       os_free(conn->domain_match);
        os_free(conn);
 }
 
@@ -405,104 +339,6 @@ int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn)
 }
 
 
-#if 0
-static int tls_match_altsubject(X509 *cert, const char *match)
-{
-       GENERAL_NAME *gen;
-       char *field, *tmp;
-       void *ext;
-       int i, found = 0;
-       size_t len;
-
-       ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
-
-       for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
-               gen = sk_GENERAL_NAME_value(ext, i);
-               switch (gen->type) {
-               case GEN_EMAIL:
-                       field = "EMAIL";
-                       break;
-               case GEN_DNS:
-                       field = "DNS";
-                       break;
-               case GEN_URI:
-                       field = "URI";
-                       break;
-               default:
-                       field = NULL;
-                       wpa_printf(MSG_DEBUG, "TLS: altSubjectName: "
-                                  "unsupported type=%d", gen->type);
-                       break;
-               }
-
-               if (!field)
-                       continue;
-
-               wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s",
-                          field, gen->d.ia5->data);
-               len = os_strlen(field) + 1 +
-                       strlen((char *) gen->d.ia5->data) + 1;
-               tmp = os_malloc(len);
-               if (tmp == NULL)
-                       continue;
-               snprintf(tmp, len, "%s:%s", field, gen->d.ia5->data);
-               if (strstr(tmp, match))
-                       found++;
-               os_free(tmp);
-       }
-
-       return found;
-}
-#endif
-
-
-#if 0
-static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
-{
-       char buf[256];
-       X509 *err_cert;
-       int err, depth;
-       SSL *ssl;
-       struct tls_connection *conn;
-       char *match, *altmatch;
-
-       err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
-       err = X509_STORE_CTX_get_error(x509_ctx);
-       depth = X509_STORE_CTX_get_error_depth(x509_ctx);
-       ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
-                                        SSL_get_ex_data_X509_STORE_CTX_idx());
-       X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf));
-
-       conn = SSL_get_app_data(ssl);
-       match = conn ? conn->subject_match : NULL;
-       altmatch = conn ? conn->altsubject_match : NULL;
-
-       if (!preverify_ok) {
-               wpa_printf(MSG_WARNING, "TLS: Certificate verification failed,"
-                          " error %d (%s) depth %d for '%s'", err,
-                          X509_verify_cert_error_string(err), depth, buf);
-       } else {
-               wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - "
-                          "preverify_ok=%d err=%d (%s) depth=%d buf='%s'",
-                          preverify_ok, err,
-                          X509_verify_cert_error_string(err), depth, buf);
-               if (depth == 0 && match && strstr(buf, match) == NULL) {
-                       wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
-                                  "match with '%s'", buf, match);
-                       preverify_ok = 0;
-               } else if (depth == 0 && altmatch &&
-                          !tls_match_altsubject(err_cert, altmatch)) {
-                       wpa_printf(MSG_WARNING, "TLS: altSubjectName match "
-                                  "'%s' not found", altmatch);
-                       preverify_ok = 0;
-               }
-       }
-
-       return preverify_ok;
-}
-#endif
-
-
 int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
                              const struct tls_connection_params *params)
 {
@@ -511,73 +347,142 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
        if (conn == NULL || params == NULL)
                return -1;
 
-       os_free(conn->subject_match);
-       conn->subject_match = NULL;
        if (params->subject_match) {
-               conn->subject_match = os_strdup(params->subject_match);
-               if (conn->subject_match == NULL)
-                       return -1;
+               wpa_printf(MSG_INFO, "GnuTLS: subject_match not supported");
+               return -1;
        }
 
-       os_free(conn->altsubject_match);
-       conn->altsubject_match = NULL;
        if (params->altsubject_match) {
-               conn->altsubject_match = os_strdup(params->altsubject_match);
-               if (conn->altsubject_match == NULL)
+               wpa_printf(MSG_INFO, "GnuTLS: altsubject_match not supported");
+               return -1;
+       }
+
+       os_free(conn->suffix_match);
+       conn->suffix_match = NULL;
+       if (params->suffix_match) {
+               conn->suffix_match = os_strdup(params->suffix_match);
+               if (conn->suffix_match == NULL)
                        return -1;
        }
 
+#if GNUTLS_VERSION_NUMBER >= 0x030300
+       os_free(conn->domain_match);
+       conn->domain_match = NULL;
+       if (params->domain_match) {
+               conn->domain_match = os_strdup(params->domain_match);
+               if (conn->domain_match == NULL)
+                       return -1;
+       }
+#else /* < 3.3.0 */
+       if (params->domain_match) {
+               wpa_printf(MSG_INFO, "GnuTLS: domain_match not supported");
+               return -1;
+       }
+#endif /* >= 3.3.0 */
+
+       conn->flags = params->flags;
+
+       if (params->openssl_ciphers) {
+               wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported");
+               return -1;
+       }
+
        /* TODO: gnutls_certificate_set_verify_flags(xcred, flags); 
         * to force peer validation(?) */
 
        if (params->ca_cert) {
-               conn->verify_peer = 1;
+               wpa_printf(MSG_DEBUG, "GnuTLS: Try to parse %s in DER format",
+                          params->ca_cert);
                ret = gnutls_certificate_set_x509_trust_file(
-                       conn->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM);
+                       conn->xcred, params->ca_cert, GNUTLS_X509_FMT_DER);
                if (ret < 0) {
-                       wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' "
-                                  "in PEM format: %s", params->ca_cert,
+                       wpa_printf(MSG_DEBUG,
+                                  "GnuTLS: Failed to read CA cert '%s' in DER format (%s) - try in PEM format",
+                                  params->ca_cert,
                                   gnutls_strerror(ret));
                        ret = gnutls_certificate_set_x509_trust_file(
                                conn->xcred, params->ca_cert,
-                               GNUTLS_X509_FMT_DER);
+                               GNUTLS_X509_FMT_PEM);
                        if (ret < 0) {
-                               wpa_printf(MSG_DEBUG, "Failed to read CA cert "
-                                          "'%s' in DER format: %s",
+                               wpa_printf(MSG_DEBUG,
+                                          "Failed to read CA cert '%s' in PEM format: %s",
                                           params->ca_cert,
                                           gnutls_strerror(ret));
                                return -1;
                        }
                }
+       } else if (params->ca_cert_blob) {
+               gnutls_datum_t ca;
+
+               ca.data = (unsigned char *) params->ca_cert_blob;
+               ca.size = params->ca_cert_blob_len;
+
+               ret = gnutls_certificate_set_x509_trust_mem(
+                       conn->xcred, &ca, GNUTLS_X509_FMT_DER);
+               if (ret < 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "Failed to parse CA cert in DER format: %s",
+                                  gnutls_strerror(ret));
+                       ret = gnutls_certificate_set_x509_trust_mem(
+                               conn->xcred, &ca, GNUTLS_X509_FMT_PEM);
+                       if (ret < 0) {
+                               wpa_printf(MSG_DEBUG,
+                                          "Failed to parse CA cert in PEM format: %s",
+                                          gnutls_strerror(ret));
+                               return -1;
+                       }
+               }
+       } else if (params->ca_path) {
+               wpa_printf(MSG_INFO, "GnuTLS: ca_path not supported");
+               return -1;
+       }
+
+       conn->disable_time_checks = 0;
+       if (params->ca_cert || params->ca_cert_blob) {
+               conn->verify_peer = 1;
+               gnutls_certificate_set_verify_function(
+                       conn->xcred, tls_connection_verify_peer);
 
                if (params->flags & TLS_CONN_ALLOW_SIGN_RSA_MD5) {
                        gnutls_certificate_set_verify_flags(
                                conn->xcred, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
                }
 
-#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
                if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
+                       conn->disable_time_checks = 1;
                        gnutls_certificate_set_verify_flags(
                                conn->xcred,
                                GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
                }
-#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
        }
 
        if (params->client_cert && params->private_key) {
-               /* TODO: private_key_passwd? */
+#if GNUTLS_VERSION_NUMBER >= 0x03010b
+               ret = gnutls_certificate_set_x509_key_file2(
+                       conn->xcred, params->client_cert, params->private_key,
+                       GNUTLS_X509_FMT_DER, params->private_key_passwd, 0);
+#else
+               /* private_key_passwd not (easily) supported here */
                ret = gnutls_certificate_set_x509_key_file(
                        conn->xcred, params->client_cert, params->private_key,
-                       GNUTLS_X509_FMT_PEM);
+                       GNUTLS_X509_FMT_DER);
+#endif
                if (ret < 0) {
                        wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
-                                  "in PEM format: %s", gnutls_strerror(ret));
+                                  "in DER format: %s", gnutls_strerror(ret));
+#if GNUTLS_VERSION_NUMBER >= 0x03010b
+                       ret = gnutls_certificate_set_x509_key_file2(
+                               conn->xcred, params->client_cert,
+                               params->private_key, GNUTLS_X509_FMT_PEM,
+                               params->private_key_passwd, 0);
+#else
                        ret = gnutls_certificate_set_x509_key_file(
                                conn->xcred, params->client_cert,
-                               params->private_key, GNUTLS_X509_FMT_DER);
+                               params->private_key, GNUTLS_X509_FMT_PEM);
+#endif
                        if (ret < 0) {
                                wpa_printf(MSG_DEBUG, "Failed to read client "
-                                          "cert/key in DER format: %s",
+                                          "cert/key in PEM format: %s",
                                           gnutls_strerror(ret));
                                return ret;
                        }
@@ -586,7 +491,6 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
                int pkcs12_ok = 0;
 #ifdef PKCS12_FUNCS
                /* Try to load in PKCS#12 format */
-#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
                ret = gnutls_certificate_set_x509_simple_pkcs12_file(
                        conn->xcred, params->private_key, GNUTLS_X509_FMT_DER,
                        params->private_key_passwd);
@@ -596,7 +500,6 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
                        return -1;
                } else
                        pkcs12_ok = 1;
-#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
 #endif /* PKCS12_FUNCS */
 
                if (!pkcs12_ok) {
@@ -604,8 +507,82 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
                                   "included");
                        return -1;
                }
+       } else if (params->client_cert_blob && params->private_key_blob) {
+               gnutls_datum_t cert, key;
+
+               cert.data = (unsigned char *) params->client_cert_blob;
+               cert.size = params->client_cert_blob_len;
+               key.data = (unsigned char *) params->private_key_blob;
+               key.size = params->private_key_blob_len;
+
+#if GNUTLS_VERSION_NUMBER >= 0x03010b
+               ret = gnutls_certificate_set_x509_key_mem2(
+                       conn->xcred, &cert, &key, GNUTLS_X509_FMT_DER,
+                       params->private_key_passwd, 0);
+#else
+               /* private_key_passwd not (easily) supported here */
+               ret = gnutls_certificate_set_x509_key_mem(
+                       conn->xcred, &cert, &key, GNUTLS_X509_FMT_DER);
+#endif
+               if (ret < 0) {
+                       wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
+                                  "in DER format: %s", gnutls_strerror(ret));
+#if GNUTLS_VERSION_NUMBER >= 0x03010b
+                       ret = gnutls_certificate_set_x509_key_mem2(
+                               conn->xcred, &cert, &key, GNUTLS_X509_FMT_PEM,
+                               params->private_key_passwd, 0);
+#else
+                       /* private_key_passwd not (easily) supported here */
+                       ret = gnutls_certificate_set_x509_key_mem(
+                               conn->xcred, &cert, &key, GNUTLS_X509_FMT_PEM);
+#endif
+                       if (ret < 0) {
+                               wpa_printf(MSG_DEBUG, "Failed to read client "
+                                          "cert/key in PEM format: %s",
+                                          gnutls_strerror(ret));
+                               return ret;
+                       }
+               }
+       } else if (params->private_key_blob) {
+#ifdef PKCS12_FUNCS
+               gnutls_datum_t key;
+
+               key.data = (unsigned char *) params->private_key_blob;
+               key.size = params->private_key_blob_len;
+
+               /* Try to load in PKCS#12 format */
+               ret = gnutls_certificate_set_x509_simple_pkcs12_mem(
+                       conn->xcred, &key, GNUTLS_X509_FMT_DER,
+                       params->private_key_passwd);
+               if (ret != 0) {
+                       wpa_printf(MSG_DEBUG, "Failed to load private_key in "
+                                  "PKCS#12 format: %s", gnutls_strerror(ret));
+                       return -1;
+               }
+#else /* PKCS12_FUNCS */
+               wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not included");
+               return -1;
+#endif /* PKCS12_FUNCS */
        }
 
+#if GNUTLS_VERSION_NUMBER >= 0x030103
+       if (params->flags & (TLS_CONN_REQUEST_OCSP | TLS_CONN_REQUIRE_OCSP)) {
+               ret = gnutls_ocsp_status_request_enable_client(conn->session,
+                                                              NULL, 0, NULL);
+               if (ret != GNUTLS_E_SUCCESS) {
+                       wpa_printf(MSG_INFO,
+                                  "GnuTLS: Failed to enable OCSP client");
+                       return -1;
+               }
+       }
+#else /* 3.1.3 */
+       if (params->flags & TLS_CONN_REQUIRE_OCSP) {
+               wpa_printf(MSG_INFO,
+                          "GnuTLS: OCSP not supported by this version of GnuTLS");
+               return -1;
+       }
+#endif /* 3.1.3 */
+
        conn->params_set = 1;
 
        ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE,
@@ -643,17 +620,17 @@ int tls_global_set_params(void *tls_ctx,
 
        if (params->ca_cert) {
                ret = gnutls_certificate_set_x509_trust_file(
-                       global->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM);
+                       global->xcred, params->ca_cert, GNUTLS_X509_FMT_DER);
                if (ret < 0) {
                        wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' "
-                                  "in PEM format: %s", params->ca_cert,
+                                  "in DER format: %s", params->ca_cert,
                                   gnutls_strerror(ret));
                        ret = gnutls_certificate_set_x509_trust_file(
                                global->xcred, params->ca_cert,
-                               GNUTLS_X509_FMT_DER);
+                               GNUTLS_X509_FMT_PEM);
                        if (ret < 0) {
                                wpa_printf(MSG_DEBUG, "Failed to read CA cert "
-                                          "'%s' in DER format: %s",
+                                          "'%s' in PEM format: %s",
                                           params->ca_cert,
                                           gnutls_strerror(ret));
                                goto fail;
@@ -666,29 +643,27 @@ int tls_global_set_params(void *tls_ctx,
                                GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5);
                }
 
-#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
                if (params->flags & TLS_CONN_DISABLE_TIME_CHECKS) {
                        gnutls_certificate_set_verify_flags(
                                global->xcred,
                                GNUTLS_VERIFY_DISABLE_TIME_CHECKS);
                }
-#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
        }
 
        if (params->client_cert && params->private_key) {
                /* TODO: private_key_passwd? */
                ret = gnutls_certificate_set_x509_key_file(
                        global->xcred, params->client_cert,
-                       params->private_key, GNUTLS_X509_FMT_PEM);
+                       params->private_key, GNUTLS_X509_FMT_DER);
                if (ret < 0) {
                        wpa_printf(MSG_DEBUG, "Failed to read client cert/key "
-                                  "in PEM format: %s", gnutls_strerror(ret));
+                                  "in DER format: %s", gnutls_strerror(ret));
                        ret = gnutls_certificate_set_x509_key_file(
                                global->xcred, params->client_cert,
-                               params->private_key, GNUTLS_X509_FMT_DER);
+                               params->private_key, GNUTLS_X509_FMT_PEM);
                        if (ret < 0) {
                                wpa_printf(MSG_DEBUG, "Failed to read client "
-                                          "cert/key in DER format: %s",
+                                          "cert/key in PEM format: %s",
                                           gnutls_strerror(ret));
                                goto fail;
                        }
@@ -697,7 +672,6 @@ int tls_global_set_params(void *tls_ctx,
                int pkcs12_ok = 0;
 #ifdef PKCS12_FUNCS
                /* Try to load in PKCS#12 format */
-#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
                ret = gnutls_certificate_set_x509_simple_pkcs12_file(
                        global->xcred, params->private_key,
                        GNUTLS_X509_FMT_DER, params->private_key_passwd);
@@ -707,7 +681,6 @@ int tls_global_set_params(void *tls_ctx,
                        goto fail;
                } else
                        pkcs12_ok = 1;
-#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
 #endif /* PKCS12_FUNCS */
 
                if (!pkcs12_ok) {
@@ -752,37 +725,23 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn,
 int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn,
                            struct tls_keys *keys)
 {
-#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
-       security_parameters_st *sec;
-#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
+#if GNUTLS_VERSION_NUMBER >= 0x030012
+       gnutls_datum_t client, server;
 
        if (conn == NULL || conn->session == NULL || keys == NULL)
                return -1;
 
        os_memset(keys, 0, sizeof(*keys));
-
-#if LIBGNUTLS_VERSION_NUMBER < 0x020c00
-#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK
-       sec = &conn->session->security_parameters;
-       keys->master_key = sec->master_secret;
-       keys->master_key_len = WPA_TLS_MASTER_SIZE;
-       keys->client_random = sec->client_random;
-       keys->server_random = sec->server_random;
-#else /* GNUTLS_INTERNAL_STRUCTURE_HACK */
-       keys->client_random =
-               (u8 *) gnutls_session_get_client_random(conn->session);
-       keys->server_random =
-               (u8 *) gnutls_session_get_server_random(conn->session);
-       /* No access to master_secret */
-#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */
-#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */
-
-#if LIBGNUTLS_VERSION_NUMBER < 0x020c00
-       keys->client_random_len = WPA_TLS_RANDOM_SIZE;
-       keys->server_random_len = WPA_TLS_RANDOM_SIZE;
-#endif /* LIBGNUTLS_VERSION_NUMBER < 0x020c00 */
+       gnutls_session_get_random(conn->session, &client, &server);
+       keys->client_random = client.data;
+       keys->server_random = server.data;
+       keys->client_random_len = client.size;
+       keys->server_random_len = client.size;
 
        return 0;
+#else /* 3.0.18 */
+       return -1;
+#endif /* 3.0.18 */
 }
 
 
@@ -790,86 +749,317 @@ int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
                       const char *label, int server_random_first,
                       u8 *out, size_t out_len)
 {
-#if LIBGNUTLS_VERSION_NUMBER >= 0x010302
        if (conn == NULL || conn->session == NULL)
                return -1;
 
        return gnutls_prf(conn->session, os_strlen(label), label,
                          server_random_first, 0, NULL, out_len, (char *) out);
-#else /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
+}
+
+
+static void gnutls_tls_fail_event(struct tls_connection *conn,
+                                 const gnutls_datum_t *cert, int depth,
+                                 const char *subject, const char *err_str,
+                                 enum tls_fail_reason reason)
+{
+       union tls_event_data ev;
+       struct tls_global *global = conn->global;
+       struct wpabuf *cert_buf = NULL;
+
+       if (global->event_cb == NULL)
+               return;
+
+       os_memset(&ev, 0, sizeof(ev));
+       ev.cert_fail.depth = depth;
+       ev.cert_fail.subject = subject ? subject : "";
+       ev.cert_fail.reason = reason;
+       ev.cert_fail.reason_txt = err_str;
+       if (cert) {
+               cert_buf = wpabuf_alloc_copy(cert->data, cert->size);
+               ev.cert_fail.cert = cert_buf;
+       }
+       global->event_cb(global->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev);
+       wpabuf_free(cert_buf);
+}
+
+
+#if GNUTLS_VERSION_NUMBER < 0x030300
+static int server_eku_purpose(gnutls_x509_crt_t cert)
+{
+       unsigned int i;
+
+       for (i = 0; ; i++) {
+               char oid[128];
+               size_t oid_size = sizeof(oid);
+               int res;
+
+               res = gnutls_x509_crt_get_key_purpose_oid(cert, i, oid,
+                                                         &oid_size, NULL);
+               if (res == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+                       if (i == 0) {
+                               /* No EKU - assume any use allowed */
+                               return 1;
+                       }
+                       break;
+               }
+
+               if (res < 0) {
+                       wpa_printf(MSG_INFO, "GnuTLS: Failed to get EKU");
+                       return 0;
+               }
+
+               wpa_printf(MSG_DEBUG, "GnuTLS: Certificate purpose: %s", oid);
+               if (os_strcmp(oid, GNUTLS_KP_TLS_WWW_SERVER) == 0 ||
+                   os_strcmp(oid, GNUTLS_KP_ANY) == 0)
+                       return 1;
+       }
+
+       return 0;
+}
+#endif /* < 3.3.0 */
+
+
+static int check_ocsp(struct tls_connection *conn, gnutls_session_t session,
+                     gnutls_alert_description_t *err)
+{
+#if GNUTLS_VERSION_NUMBER >= 0x030103
+       gnutls_datum_t response, buf;
+       gnutls_ocsp_resp_t resp;
+       unsigned int cert_status;
+       int res;
+
+       if (!(conn->flags & (TLS_CONN_REQUEST_OCSP | TLS_CONN_REQUIRE_OCSP)))
+               return 0;
+
+       if (!gnutls_ocsp_status_request_is_checked(session, 0)) {
+               if (conn->flags & TLS_CONN_REQUIRE_OCSP) {
+                       wpa_printf(MSG_INFO,
+                                  "GnuTLS: No valid OCSP response received");
+                       goto ocsp_error;
+               }
+
+               wpa_printf(MSG_DEBUG,
+                          "GnuTLS: Valid OCSP response was not received - continue since OCSP was not required");
+               return 0;
+       }
+
+       /*
+        * GnuTLS has already verified the OCSP response in
+        * check_ocsp_response() and rejected handshake if the certificate was
+        * found to be revoked. However, if the response indicates that the
+        * status is unknown, handshake continues and reaches here. We need to
+        * re-import the OCSP response to check for unknown certificate status,
+        * but we do not need to repeat gnutls_ocsp_resp_check_crt() and
+        * gnutls_ocsp_resp_verify_direct() calls.
+        */
+
+       res = gnutls_ocsp_status_request_get(session, &response);
+       if (res != GNUTLS_E_SUCCESS) {
+               wpa_printf(MSG_INFO,
+                          "GnuTLS: OCSP response was received, but it was not valid");
+               goto ocsp_error;
+       }
+
+       if (gnutls_ocsp_resp_init(&resp) != GNUTLS_E_SUCCESS)
+               goto ocsp_error;
+
+       res = gnutls_ocsp_resp_import(resp, &response);
+       if (res != GNUTLS_E_SUCCESS) {
+               wpa_printf(MSG_INFO,
+                          "GnuTLS: Could not parse received OCSP response: %s",
+                          gnutls_strerror(res));
+               gnutls_ocsp_resp_deinit(resp);
+               goto ocsp_error;
+       }
+
+       res = gnutls_ocsp_resp_print(resp, GNUTLS_OCSP_PRINT_FULL, &buf);
+       if (res == GNUTLS_E_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "GnuTLS: %s", buf.data);
+               gnutls_free(buf.data);
+       }
+
+       res = gnutls_ocsp_resp_get_single(resp, 0, NULL, NULL, NULL,
+                                         NULL, &cert_status, NULL,
+                                         NULL, NULL, NULL);
+       gnutls_ocsp_resp_deinit(resp);
+       if (res != GNUTLS_E_SUCCESS) {
+               wpa_printf(MSG_INFO,
+                          "GnuTLS: Failed to extract OCSP information: %s",
+                          gnutls_strerror(res));
+               goto ocsp_error;
+       }
+
+       if (cert_status == GNUTLS_OCSP_CERT_GOOD) {
+               wpa_printf(MSG_DEBUG, "GnuTLS: OCSP cert status: good");
+       } else if (cert_status == GNUTLS_OCSP_CERT_REVOKED) {
+               wpa_printf(MSG_DEBUG,
+                          "GnuTLS: OCSP cert status: revoked");
+               goto ocsp_error;
+       } else {
+               wpa_printf(MSG_DEBUG,
+                          "GnuTLS: OCSP cert status: unknown");
+               if (conn->flags & TLS_CONN_REQUIRE_OCSP)
+                       goto ocsp_error;
+               wpa_printf(MSG_DEBUG,
+                          "GnuTLS: OCSP was not required, so allow connection to continue");
+       }
+
+       return 0;
+
+ocsp_error:
+       gnutls_tls_fail_event(conn, NULL, 0, NULL,
+                             "bad certificate status response",
+                             TLS_FAIL_REVOKED);
+       *err = GNUTLS_A_CERTIFICATE_REVOKED;
        return -1;
-#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */
+#else /* GnuTLS 3.1.3 or newer */
+       return 0;
+#endif /* GnuTLS 3.1.3 or newer */
 }
 
 
-static int tls_connection_verify_peer(struct tls_connection *conn,
-                                     gnutls_alert_description_t *err)
+static int tls_connection_verify_peer(gnutls_session_t session)
 {
+       struct tls_connection *conn;
        unsigned int status, num_certs, i;
        struct os_time now;
        const gnutls_datum_t *certs;
        gnutls_x509_crt_t cert;
+       gnutls_alert_description_t err;
+       int res;
+
+       conn = gnutls_session_get_ptr(session);
+       if (!conn->verify_peer) {
+               wpa_printf(MSG_DEBUG,
+                          "GnuTLS: No peer certificate verification enabled");
+               return 0;
+       }
+
+       wpa_printf(MSG_DEBUG, "GnuTSL: Verifying peer certificate");
+
+#if GNUTLS_VERSION_NUMBER >= 0x030300
+       {
+               gnutls_typed_vdata_st data[1];
+               unsigned int elements = 0;
 
-       if (gnutls_certificate_verify_peers2(conn->session, &status) < 0) {
+               os_memset(data, 0, sizeof(data));
+               if (!conn->global->server) {
+                       data[elements].type = GNUTLS_DT_KEY_PURPOSE_OID;
+                       data[elements].data = (void *) GNUTLS_KP_TLS_WWW_SERVER;
+                       elements++;
+               }
+               res = gnutls_certificate_verify_peers(session, data, 1,
+                                                     &status);
+       }
+#else /* < 3.3.0 */
+       res = gnutls_certificate_verify_peers2(session, &status);
+#endif
+       if (res < 0) {
                wpa_printf(MSG_INFO, "TLS: Failed to verify peer "
                           "certificate chain");
-               *err = GNUTLS_A_INTERNAL_ERROR;
-               return -1;
+               err = GNUTLS_A_INTERNAL_ERROR;
+               goto out;
+       }
+
+#if GNUTLS_VERSION_NUMBER >= 0x030104
+       {
+               gnutls_datum_t info;
+               int ret, type;
+
+               type = gnutls_certificate_type_get(session);
+               ret = gnutls_certificate_verification_status_print(status, type,
+                                                                  &info, 0);
+               if (ret < 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "GnuTLS: Failed to print verification status");
+                       err = GNUTLS_A_INTERNAL_ERROR;
+                       goto out;
+               }
+               wpa_printf(MSG_DEBUG, "GnuTLS: %s", info.data);
+               gnutls_free(info.data);
+       }
+#endif /* GnuTLS 3.1.4 or newer */
+
+       certs = gnutls_certificate_get_peers(session, &num_certs);
+       if (certs == NULL || num_certs == 0) {
+               wpa_printf(MSG_INFO, "TLS: No peer certificate chain received");
+               err = GNUTLS_A_UNKNOWN_CA;
+               goto out;
        }
 
        if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) {
                wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted");
-               *err = GNUTLS_A_INTERNAL_ERROR;
                if (status & GNUTLS_CERT_INSECURE_ALGORITHM) {
                        wpa_printf(MSG_INFO, "TLS: Certificate uses insecure "
                                   "algorithm");
-                       *err = GNUTLS_A_INSUFFICIENT_SECURITY;
+                       gnutls_tls_fail_event(conn, NULL, 0, NULL,
+                                             "certificate uses insecure algorithm",
+                                             TLS_FAIL_BAD_CERTIFICATE);
+                       err = GNUTLS_A_INSUFFICIENT_SECURITY;
+                       goto out;
                }
-#if LIBGNUTLS_VERSION_NUMBER >= 0x020800
                if (status & GNUTLS_CERT_NOT_ACTIVATED) {
                        wpa_printf(MSG_INFO, "TLS: Certificate not yet "
                                   "activated");
-                       *err = GNUTLS_A_CERTIFICATE_EXPIRED;
+                       gnutls_tls_fail_event(conn, NULL, 0, NULL,
+                                             "certificate not yet valid",
+                                             TLS_FAIL_NOT_YET_VALID);
+                       err = GNUTLS_A_CERTIFICATE_EXPIRED;
+                       goto out;
                }
                if (status & GNUTLS_CERT_EXPIRED) {
                        wpa_printf(MSG_INFO, "TLS: Certificate expired");
-                       *err = GNUTLS_A_CERTIFICATE_EXPIRED;
+                       gnutls_tls_fail_event(conn, NULL, 0, NULL,
+                                             "certificate has expired",
+                                             TLS_FAIL_EXPIRED);
+                       err = GNUTLS_A_CERTIFICATE_EXPIRED;
+                       goto out;
                }
-#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x020800 */
-               return -1;
+               gnutls_tls_fail_event(conn, NULL, 0, NULL,
+                                     "untrusted certificate",
+                                     TLS_FAIL_UNTRUSTED);
+               err = GNUTLS_A_INTERNAL_ERROR;
+               goto out;
        }
 
        if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) {
                wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a "
                           "known issuer");
-               *err = GNUTLS_A_UNKNOWN_CA;
-               return -1;
+               gnutls_tls_fail_event(conn, NULL, 0, NULL, "signed not found",
+                                     TLS_FAIL_UNTRUSTED);
+               err = GNUTLS_A_UNKNOWN_CA;
+               goto out;
        }
 
        if (status & GNUTLS_CERT_REVOKED) {
                wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked");
-               *err = GNUTLS_A_CERTIFICATE_REVOKED;
-               return -1;
+               gnutls_tls_fail_event(conn, NULL, 0, NULL,
+                                     "certificate revoked",
+                                     TLS_FAIL_REVOKED);
+               err = GNUTLS_A_CERTIFICATE_REVOKED;
+               goto out;
        }
 
-       os_get_time(&now);
-
-       certs = gnutls_certificate_get_peers(conn->session, &num_certs);
-       if (certs == NULL) {
-               wpa_printf(MSG_INFO, "TLS: No peer certificate chain "
-                          "received");
-               *err = GNUTLS_A_UNKNOWN_CA;
-               return -1;
+       if (status != 0) {
+               wpa_printf(MSG_INFO, "TLS: Unknown verification status: %d",
+                          status);
+               err = GNUTLS_A_INTERNAL_ERROR;
+               goto out;
        }
 
+       if (check_ocsp(conn, session, &err))
+               goto out;
+
+       os_get_time(&now);
+
        for (i = 0; i < num_certs; i++) {
                char *buf;
                size_t len;
                if (gnutls_x509_crt_init(&cert) < 0) {
                        wpa_printf(MSG_INFO, "TLS: Certificate initialization "
                                   "failed");
-                       *err = GNUTLS_A_BAD_CERTIFICATE;
-                       return -1;
+                       err = GNUTLS_A_BAD_CERTIFICATE;
+                       goto out;
                }
 
                if (gnutls_x509_crt_import(cert, &certs[i],
@@ -877,8 +1067,8 @@ static int tls_connection_verify_peer(struct tls_connection *conn,
                        wpa_printf(MSG_INFO, "TLS: Could not parse peer "
                                   "certificate %d/%d", i + 1, num_certs);
                        gnutls_x509_crt_deinit(cert);
-                       *err = GNUTLS_A_BAD_CERTIFICATE;
-                       return -1;
+                       err = GNUTLS_A_BAD_CERTIFICATE;
+                       goto out;
                }
 
                gnutls_x509_crt_get_dn(cert, NULL, &len);
@@ -891,26 +1081,128 @@ static int tls_connection_verify_peer(struct tls_connection *conn,
                wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s",
                           i + 1, num_certs, buf);
 
-               if (i == 0) {
-                       /* TODO: validate subject_match and altsubject_match */
+               if (conn->global->event_cb) {
+                       struct wpabuf *cert_buf = NULL;
+                       union tls_event_data ev;
+#ifdef CONFIG_SHA256
+                       u8 hash[32];
+                       const u8 *_addr[1];
+                       size_t _len[1];
+#endif /* CONFIG_SHA256 */
+
+                       os_memset(&ev, 0, sizeof(ev));
+                       if (conn->global->cert_in_cb) {
+                               cert_buf = wpabuf_alloc_copy(certs[i].data,
+                                                            certs[i].size);
+                               ev.peer_cert.cert = cert_buf;
+                       }
+#ifdef CONFIG_SHA256
+                       _addr[0] = certs[i].data;
+                       _len[0] = certs[i].size;
+                       if (sha256_vector(1, _addr, _len, hash) == 0) {
+                               ev.peer_cert.hash = hash;
+                               ev.peer_cert.hash_len = sizeof(hash);
+                       }
+#endif /* CONFIG_SHA256 */
+                       ev.peer_cert.depth = i;
+                       ev.peer_cert.subject = buf;
+                       conn->global->event_cb(conn->global->cb_ctx,
+                                              TLS_PEER_CERTIFICATE, &ev);
+                       wpabuf_free(cert_buf);
                }
 
-               os_free(buf);
+               if (i == 0) {
+                       if (conn->suffix_match &&
+                           !gnutls_x509_crt_check_hostname(
+                                   cert, conn->suffix_match)) {
+                               wpa_printf(MSG_WARNING,
+                                          "TLS: Domain suffix match '%s' not found",
+                                          conn->suffix_match);
+                               gnutls_tls_fail_event(
+                                       conn, &certs[i], i, buf,
+                                       "Domain suffix mismatch",
+                                       TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
+                               err = GNUTLS_A_BAD_CERTIFICATE;
+                               gnutls_x509_crt_deinit(cert);
+                               os_free(buf);
+                               goto out;
+                       }
 
-               if (gnutls_x509_crt_get_expiration_time(cert) < now.sec ||
-                   gnutls_x509_crt_get_activation_time(cert) > now.sec) {
+#if GNUTLS_VERSION_NUMBER >= 0x030300
+                       if (conn->domain_match &&
+                           !gnutls_x509_crt_check_hostname2(
+                                   cert, conn->domain_match,
+                                   GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS)) {
+                               wpa_printf(MSG_WARNING,
+                                          "TLS: Domain match '%s' not found",
+                                          conn->domain_match);
+                               gnutls_tls_fail_event(
+                                       conn, &certs[i], i, buf,
+                                       "Domain mismatch",
+                                       TLS_FAIL_DOMAIN_MISMATCH);
+                               err = GNUTLS_A_BAD_CERTIFICATE;
+                               gnutls_x509_crt_deinit(cert);
+                               os_free(buf);
+                               goto out;
+                       }
+#endif /* >= 3.3.0 */
+
+                       /* TODO: validate altsubject_match.
+                        * For now, any such configuration is rejected in
+                        * tls_connection_set_params() */
+
+#if GNUTLS_VERSION_NUMBER < 0x030300
+                       /*
+                        * gnutls_certificate_verify_peers() not available, so
+                        * need to check EKU separately.
+                        */
+                       if (!conn->global->server &&
+                           !server_eku_purpose(cert)) {
+                               wpa_printf(MSG_WARNING,
+                                          "GnuTLS: No server EKU");
+                               gnutls_tls_fail_event(
+                                       conn, &certs[i], i, buf,
+                                       "No server EKU",
+                                       TLS_FAIL_BAD_CERTIFICATE);
+                               err = GNUTLS_A_BAD_CERTIFICATE;
+                               gnutls_x509_crt_deinit(cert);
+                               os_free(buf);
+                               goto out;
+                       }
+#endif /* < 3.3.0 */
+               }
+
+               if (!conn->disable_time_checks &&
+                   (gnutls_x509_crt_get_expiration_time(cert) < now.sec ||
+                    gnutls_x509_crt_get_activation_time(cert) > now.sec)) {
                        wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is "
                                   "not valid at this time",
                                   i + 1, num_certs);
+                       gnutls_tls_fail_event(
+                               conn, &certs[i], i, buf,
+                               "Certificate is not valid at this time",
+                               TLS_FAIL_EXPIRED);
                        gnutls_x509_crt_deinit(cert);
-                       *err = GNUTLS_A_CERTIFICATE_EXPIRED;
-                       return -1;
+                       os_free(buf);
+                       err = GNUTLS_A_CERTIFICATE_EXPIRED;
+                       goto out;
                }
 
+               os_free(buf);
+
                gnutls_x509_crt_deinit(cert);
        }
 
+       if (conn->global->event_cb != NULL)
+               conn->global->event_cb(conn->global->cb_ctx,
+                                      TLS_CERT_CHAIN_SUCCESS, NULL);
+
        return 0;
+
+out:
+       conn->failed++;
+       gnutls_alert_send(session, GNUTLS_AL_FATAL, err);
+       return GNUTLS_E_CERTIFICATE_ERROR;
 }
 
 
@@ -968,6 +1260,8 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx,
 
        ret = gnutls_handshake(conn->session);
        if (ret < 0) {
+               gnutls_alert_description_t alert;
+
                switch (ret) {
                case GNUTLS_E_AGAIN:
                        if (global->server && conn->established &&
@@ -978,10 +1272,20 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx,
                        }
                        break;
                case GNUTLS_E_FATAL_ALERT_RECEIVED:
+                       alert = gnutls_alert_get(conn->session);
                        wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert",
-                                  __func__, gnutls_alert_get_name(
-                                          gnutls_alert_get(conn->session)));
+                                  __func__, gnutls_alert_get_name(alert));
                        conn->read_alerts++;
+                       if (conn->global->event_cb != NULL) {
+                               union tls_event_data ev;
+
+                               os_memset(&ev, 0, sizeof(ev));
+                               ev.alert.is_local = 0;
+                               ev.alert.type = gnutls_alert_get_name(alert);
+                               ev.alert.description = ev.alert.type;
+                               conn->global->event_cb(conn->global->cb_ctx,
+                                                      TLS_ALERT, &ev);
+                       }
                        /* continue */
                default:
                        wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed "
@@ -990,18 +1294,21 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx,
                }
        } else {
                size_t size;
-               gnutls_alert_description_t err;
 
-               if (conn->verify_peer &&
-                   tls_connection_verify_peer(conn, &err)) {
-                       wpa_printf(MSG_INFO, "TLS: Peer certificate chain "
-                                  "failed validation");
-                       conn->failed++;
-                       gnutls_alert_send(conn->session, GNUTLS_AL_FATAL, err);
-                       goto out;
+               wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully");
+
+#if GNUTLS_VERSION_NUMBER >= 0x03010a
+               {
+                       char *desc;
+
+                       desc = gnutls_session_get_desc(conn->session);
+                       if (desc) {
+                               wpa_printf(MSG_DEBUG, "GnuTLS: %s", desc);
+                               gnutls_free(desc);
+                       }
                }
+#endif /* GnuTLS 3.1.10 or newer */
 
-               wpa_printf(MSG_DEBUG, "TLS: Handshake completed successfully");
                conn->established = 1;
                if (conn->push_buf == NULL) {
                        /* Need to return something to get final TLS ACK. */
@@ -1025,7 +1332,6 @@ struct wpabuf * tls_connection_handshake(void *tls_ctx,
                        *appl_data = gnutls_get_appl_data(conn);
        }
 
-out:
        out_data = conn->push_buf;
        conn->push_buf = NULL;
        return out_data;
@@ -1190,3 +1496,10 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx,
 {
        return -1;
 }
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+       return os_snprintf(buf, buf_len, "GnuTLS build=%s run=%s",
+                          GNUTLS_VERSION, gnutls_check_version(NULL));
+}
index 91f0690..0c955da 100644 (file)
@@ -28,6 +28,7 @@ struct tls_global {
 struct tls_connection {
        struct tlsv1_client *client;
        struct tlsv1_server *server;
+       struct tls_global *global;
 };
 
 
@@ -85,6 +86,7 @@ struct tls_connection * tls_connection_init(void *tls_ctx)
        conn = os_zalloc(sizeof(*conn));
        if (conn == NULL)
                return NULL;
+       conn->global = global;
 
 #ifdef CONFIG_TLS_INTERNAL_CLIENT
        if (!global->server) {
@@ -109,6 +111,28 @@ struct tls_connection * tls_connection_init(void *tls_ctx)
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+void tls_connection_set_test_flags(struct tls_connection *conn, u32 flags)
+{
+       if (conn->server)
+               tlsv1_server_set_test_flags(conn->server, flags);
+}
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+void tls_connection_set_log_cb(struct tls_connection *conn,
+                              void (*log_cb)(void *ctx, const char *msg),
+                              void *ctx)
+{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+       if (conn->server)
+               tlsv1_server_set_log_cb(conn->server, log_cb, ctx);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
+}
+
+
 void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
 {
        if (conn == NULL)
@@ -166,6 +190,31 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
        if (cred == NULL)
                return -1;
 
+       if (params->subject_match) {
+               wpa_printf(MSG_INFO, "TLS: subject_match not supported");
+               return -1;
+       }
+
+       if (params->altsubject_match) {
+               wpa_printf(MSG_INFO, "TLS: altsubject_match not supported");
+               return -1;
+       }
+
+       if (params->suffix_match) {
+               wpa_printf(MSG_INFO, "TLS: suffix_match not supported");
+               return -1;
+       }
+
+       if (params->domain_match) {
+               wpa_printf(MSG_INFO, "TLS: domain_match not supported");
+               return -1;
+       }
+
+       if (params->openssl_ciphers) {
+               wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported");
+               return -1;
+       }
+
        if (tlsv1_set_ca_cert(cred, params->ca_cert,
                              params->ca_cert_blob, params->ca_cert_blob_len,
                              params->ca_path)) {
@@ -628,3 +677,9 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx,
 #endif /* CONFIG_TLS_INTERNAL_SERVER */
        return -1;
 }
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+       return os_snprintf(buf, buf_len, "internal");
+}
index 1a1092a..a6d210a 100644 (file)
@@ -192,3 +192,9 @@ unsigned int tls_capabilities(void *tls_ctx)
 {
        return 0;
 }
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+       return os_snprintf(buf, buf_len, "none");
+}
diff --git a/src/crypto/tls_nss.c b/src/crypto/tls_nss.c
deleted file mode 100644 (file)
index c53c192..0000000
+++ /dev/null
@@ -1,645 +0,0 @@
-/*
- * SSL/TLS interface functions for NSS
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
- *
- * This software may be distributed under the terms of the BSD license.
- * See README for more details.
- */
-
-#include "includes.h"
-#include <nspr/prtypes.h>
-#include <nspr/plarenas.h>
-#include <nspr/plhash.h>
-#include <nspr/prio.h>
-#include <nspr/prclist.h>
-#include <nspr/prlock.h>
-#include <nspr/prinit.h>
-#include <nspr/prerror.h>
-#include <nspr/prmem.h>
-#include <nss/nss.h>
-#include <nss/nssilckt.h>
-#include <nss/ssl.h>
-#include <nss/pk11func.h>
-#include <nss/secerr.h>
-
-#include "common.h"
-#include "tls.h"
-
-static int tls_nss_ref_count = 0;
-
-static PRDescIdentity nss_layer_id;
-
-
-struct tls_connection {
-       PRFileDesc *fd;
-
-       int established;
-       int verify_peer;
-       u8 *push_buf, *pull_buf, *pull_buf_offset;
-       size_t push_buf_len, pull_buf_len;
-};
-
-
-static PRStatus nss_io_close(PRFileDesc *fd)
-{
-       wpa_printf(MSG_DEBUG, "NSS: I/O close");
-       return PR_SUCCESS;
-}
-
-
-static PRInt32 nss_io_read(PRFileDesc *fd, void *buf, PRInt32 amount)
-{
-       wpa_printf(MSG_DEBUG, "NSS: I/O read(%d)", amount);
-       return PR_FAILURE;
-}
-
-
-static PRInt32 nss_io_write(PRFileDesc *fd, const void *buf, PRInt32 amount)
-{
-       wpa_printf(MSG_DEBUG, "NSS: I/O write(%d)", amount);
-       return PR_FAILURE;
-}
-
-
-static PRInt32 nss_io_writev(PRFileDesc *fd, const PRIOVec *iov,
-                            PRInt32 iov_size, PRIntervalTime timeout)
-{
-       wpa_printf(MSG_DEBUG, "NSS: I/O writev(%d)", iov_size);
-       return PR_FAILURE;
-}
-
-
-static PRInt32 nss_io_recv(PRFileDesc *fd, void *buf, PRInt32 amount,
-                          PRIntn flags, PRIntervalTime timeout)
-{
-       struct tls_connection *conn = (struct tls_connection *) fd->secret;
-       u8 *end;
-
-       wpa_printf(MSG_DEBUG, "NSS: I/O recv(%d)", amount);
-
-       if (conn->pull_buf == NULL) {
-               wpa_printf(MSG_DEBUG, "NSS: No data available to be read yet");
-               return PR_FAILURE;
-       }
-
-       end = conn->pull_buf + conn->pull_buf_len;
-       if (end - conn->pull_buf_offset < amount)
-               amount = end - conn->pull_buf_offset;
-       os_memcpy(buf, conn->pull_buf_offset, amount);
-       conn->pull_buf_offset += amount;
-       if (conn->pull_buf_offset == end) {
-               wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__);
-               os_free(conn->pull_buf);
-               conn->pull_buf = conn->pull_buf_offset = NULL;
-               conn->pull_buf_len = 0;
-       } else {
-               wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in pull_buf",
-                          __func__,
-                          (unsigned long) (end - conn->pull_buf_offset));
-       }
-       return amount;
-}
-
-
-static PRInt32 nss_io_send(PRFileDesc *fd, const void *buf, PRInt32 amount,
-                          PRIntn flags, PRIntervalTime timeout)
-{
-       struct tls_connection *conn = (struct tls_connection *) fd->secret;
-       u8 *nbuf;
-
-       wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__);
-       wpa_hexdump(MSG_MSGDUMP, "NSS: I/O send data", buf, amount);
-
-       nbuf = os_realloc(conn->push_buf, conn->push_buf_len + amount);
-       if (nbuf == NULL) {
-               wpa_printf(MSG_ERROR, "NSS: Failed to allocate memory for the "
-                          "data to be sent");
-               return PR_FAILURE;
-       }
-       os_memcpy(nbuf + conn->push_buf_len, buf, amount);
-       conn->push_buf = nbuf;
-       conn->push_buf_len += amount;
-
-       return amount;
-}
-
-
-static PRInt32 nss_io_recvfrom(PRFileDesc *fd, void *buf, PRInt32 amount,
-                              PRIntn flags, PRNetAddr *addr,
-                              PRIntervalTime timeout)
-{
-       wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__);
-       return PR_FAILURE;
-}
-
-
-static PRInt32 nss_io_sendto(PRFileDesc *fd, const void *buf, PRInt32 amount,
-                            PRIntn flags, const PRNetAddr *addr,
-                            PRIntervalTime timeout)
-{
-       wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__);
-       return PR_FAILURE;
-}
-
-
-static PRStatus nss_io_getpeername(PRFileDesc *fd, PRNetAddr *addr)
-{
-       wpa_printf(MSG_DEBUG, "NSS: I/O getpeername");
-
-       /*
-        * It Looks like NSS only supports IPv4 and IPv6 TCP sockets. Provide a
-        * fake IPv4 address to work around this even though we are not really
-        * using TCP.
-        */
-       os_memset(addr, 0, sizeof(*addr));
-       addr->inet.family = PR_AF_INET;
-
-       return PR_SUCCESS;
-}
-
-
-static PRStatus nss_io_getsocketoption(PRFileDesc *fd,
-                                      PRSocketOptionData *data)
-{
-       switch (data->option) {
-       case PR_SockOpt_Nonblocking:
-               wpa_printf(MSG_DEBUG, "NSS: I/O getsocketoption(Nonblocking)");
-               data->value.non_blocking = PR_TRUE;
-               return PR_SUCCESS;
-       default:
-               wpa_printf(MSG_DEBUG, "NSS: I/O getsocketoption(%d)",
-                          data->option);
-               return PR_FAILURE;
-       }
-}
-
-
-static const PRIOMethods nss_io = {
-       PR_DESC_LAYERED,
-       nss_io_close,
-       nss_io_read,
-       nss_io_write,
-       NULL /* available */,
-       NULL /* available64 */,
-       NULL /* fsync */,
-       NULL /* fseek */,
-       NULL /* fseek64 */,
-       NULL /* fileinfo */,
-       NULL /* fileinfo64 */,
-       nss_io_writev,
-       NULL /* connect */,
-       NULL /* accept */,
-       NULL /* bind */,
-       NULL /* listen */,
-       NULL /* shutdown */,
-       nss_io_recv,
-       nss_io_send,
-       nss_io_recvfrom,
-       nss_io_sendto,
-       NULL /* poll */,
-       NULL /* acceptread */,
-       NULL /* transmitfile */,
-       NULL /* getsockname */,
-       nss_io_getpeername,
-       NULL /* reserved_fn_6 */,
-       NULL /* reserved_fn_5 */,
-       nss_io_getsocketoption,
-       NULL /* setsocketoption */,
-       NULL /* sendfile */,
-       NULL /* connectcontinue */,
-       NULL /* reserved_fn_3 */,
-       NULL /* reserved_fn_2 */,
-       NULL /* reserved_fn_1 */,
-       NULL /* reserved_fn_0 */
-};
-
-
-static char * nss_password_cb(PK11SlotInfo *slot, PRBool retry, void *arg)
-{
-       wpa_printf(MSG_ERROR, "NSS: TODO - %s", __func__);
-       return NULL;
-}
-
-
-void * tls_init(const struct tls_config *conf)
-{
-       char *dir;
-
-       tls_nss_ref_count++;
-       if (tls_nss_ref_count > 1)
-               return (void *) 1;
-
-       PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
-
-       nss_layer_id = PR_GetUniqueIdentity("wpa_supplicant");
-
-       PK11_SetPasswordFunc(nss_password_cb);
-
-       dir = getenv("SSL_DIR");
-       if (dir) {
-               if (NSS_Init(dir) != SECSuccess) {
-                       wpa_printf(MSG_ERROR, "NSS: NSS_Init(cert_dir=%s) "
-                                  "failed", dir);
-                       return NULL;
-               }
-       } else {
-               if (NSS_NoDB_Init(NULL) != SECSuccess) {
-                       wpa_printf(MSG_ERROR, "NSS: NSS_NoDB_Init(NULL) "
-                                  "failed");
-                       return NULL;
-               }
-       }
-
-       if (SSL_OptionSetDefault(SSL_V2_COMPATIBLE_HELLO, PR_FALSE) !=
-           SECSuccess ||
-           SSL_OptionSetDefault(SSL_ENABLE_SSL3, PR_FALSE) != SECSuccess ||
-           SSL_OptionSetDefault(SSL_ENABLE_SSL2, PR_FALSE) != SECSuccess ||
-           SSL_OptionSetDefault(SSL_ENABLE_TLS, PR_TRUE) != SECSuccess) {
-               wpa_printf(MSG_ERROR, "NSS: SSL_OptionSetDefault failed");
-               return NULL;
-       }
-
-       if (NSS_SetDomesticPolicy() != SECSuccess) {
-               wpa_printf(MSG_ERROR, "NSS: NSS_SetDomesticPolicy() failed");
-               return NULL;
-       }
-
-       return (void *) 1;
-}
-
-void tls_deinit(void *ssl_ctx)
-{
-       tls_nss_ref_count--;
-       if (tls_nss_ref_count == 0) {
-               if (NSS_Shutdown() != SECSuccess)
-                       wpa_printf(MSG_ERROR, "NSS: NSS_Shutdown() failed");
-       }
-}
-
-
-int tls_get_errors(void *tls_ctx)
-{
-       return 0;
-}
-
-
-static SECStatus nss_bad_cert_cb(void *arg, PRFileDesc *fd)
-{
-       struct tls_connection *conn = arg;
-       SECStatus res = SECSuccess;
-       PRErrorCode err;
-       CERTCertificate *cert;
-       char *subject, *issuer;
-
-       err = PR_GetError();
-       if (IS_SEC_ERROR(err))
-               wpa_printf(MSG_DEBUG, "NSS: Bad Server Certificate (sec err "
-                          "%d)", err - SEC_ERROR_BASE);
-       else
-               wpa_printf(MSG_DEBUG, "NSS: Bad Server Certificate (err %d)",
-                          err);
-       cert = SSL_PeerCertificate(fd);
-       subject = CERT_NameToAscii(&cert->subject);
-       issuer = CERT_NameToAscii(&cert->issuer);
-       wpa_printf(MSG_DEBUG, "NSS: Peer certificate subject='%s' issuer='%s'",
-                  subject, issuer);
-       CERT_DestroyCertificate(cert);
-       PR_Free(subject);
-       PR_Free(issuer);
-       if (conn->verify_peer)
-               res = SECFailure;
-
-       return res;
-}
-
-
-static void nss_handshake_cb(PRFileDesc *fd, void *client_data)
-{
-       struct tls_connection *conn = client_data;
-       wpa_printf(MSG_DEBUG, "NSS: Handshake completed");
-       conn->established = 1;
-}
-
-
-struct tls_connection * tls_connection_init(void *tls_ctx)
-{
-       struct tls_connection *conn;
-
-       conn = os_zalloc(sizeof(*conn));
-       if (conn == NULL)
-               return NULL;
-
-       conn->fd = PR_CreateIOLayerStub(nss_layer_id, &nss_io);
-       if (conn->fd == NULL) {
-               os_free(conn);
-               return NULL;
-       }
-       conn->fd->secret = (void *) conn;
-
-       conn->fd = SSL_ImportFD(NULL, conn->fd);
-       if (conn->fd == NULL) {
-               os_free(conn);
-               return NULL;
-       }
-
-       if (SSL_OptionSet(conn->fd, SSL_SECURITY, PR_TRUE) != SECSuccess ||
-           SSL_OptionSet(conn->fd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) !=
-           SECSuccess ||
-           SSL_OptionSet(conn->fd, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) !=
-           SECSuccess ||
-           SSL_OptionSet(conn->fd, SSL_ENABLE_TLS, PR_TRUE) != SECSuccess ||
-           SSL_BadCertHook(conn->fd, nss_bad_cert_cb, conn) != SECSuccess ||
-           SSL_HandshakeCallback(conn->fd, nss_handshake_cb, conn) !=
-           SECSuccess) {
-               wpa_printf(MSG_ERROR, "NSS: Failed to set options");
-               PR_Close(conn->fd);
-               os_free(conn);
-               return NULL;
-       }
-
-       SSL_ResetHandshake(conn->fd, PR_FALSE);
-
-       return conn;
-}
-
-
-void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn)
-{
-       PR_Close(conn->fd);
-       os_free(conn->push_buf);
-       os_free(conn->pull_buf);
-       os_free(conn);
-}
-
-
-int tls_connection_established(void *tls_ctx, struct tls_connection *conn)
-{
-       return conn->established;
-}
-
-
-int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn)
-{
-       return -1;
-}
-
-
-int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
-                             const struct tls_connection_params *params)
-{
-       wpa_printf(MSG_ERROR, "NSS: TODO - %s", __func__);
-       return 0;
-}
-
-
-int tls_global_set_params(void *tls_ctx,
-                         const struct tls_connection_params *params)
-{
-       return -1;
-}
-
-
-int tls_global_set_verify(void *tls_ctx, int check_crl)
-{
-       return -1;
-}
-
-
-int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn,
-                             int verify_peer)
-{
-       conn->verify_peer = verify_peer;
-       return 0;
-}
-
-
-int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn,
-                           struct tls_keys *keys)
-{
-       /* NSS does not export master secret or client/server random. */
-       return -1;
-}
-
-
-int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
-                      const char *label, int server_random_first,
-                      u8 *out, size_t out_len)
-{
-       if (conn == NULL || server_random_first) {
-               wpa_printf(MSG_INFO, "NSS: Unsupported PRF request "
-                          "(server_random_first=%d)",
-                          server_random_first);
-               return -1;
-       }
-
-       if (SSL_ExportKeyingMaterial(conn->fd, label, NULL, 0, out, out_len) !=
-           SECSuccess) {
-               wpa_printf(MSG_INFO, "NSS: Failed to use TLS extractor "
-                          "(label='%s' out_len=%d", label, (int) out_len);
-               return -1;
-       }
-
-       return 0;
-}
-
-
-struct wpabuf * tls_connection_handshake(void *tls_ctx,
-                                        struct tls_connection *conn,
-                                        const struct wpabuf *in_data,
-                                        struct wpabuf **appl_data)
-{
-       struct wpabuf *out_data;
-
-       wpa_printf(MSG_DEBUG, "NSS: handshake: in_len=%u",
-                  in_data ? (unsigned int) wpabuf_len(in_data) : 0);
-
-       if (appl_data)
-               *appl_data = NULL;
-
-       if (in_data && wpabuf_len(in_data) > 0) {
-               if (conn->pull_buf) {
-                       wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
-                                  "pull_buf", __func__,
-                                  (unsigned long) conn->pull_buf_len);
-                       os_free(conn->pull_buf);
-               }
-               conn->pull_buf = os_malloc(wpabuf_len(in_data));
-               if (conn->pull_buf == NULL)
-                       return NULL;
-               os_memcpy(conn->pull_buf, wpabuf_head(in_data),
-                         wpabuf_len(in_data));
-               conn->pull_buf_offset = conn->pull_buf;
-               conn->pull_buf_len = wpabuf_len(in_data);
-       }
-
-       SSL_ForceHandshake(conn->fd);
-
-       if (conn->established && conn->push_buf == NULL) {
-               /* Need to return something to get final TLS ACK. */
-               conn->push_buf = os_malloc(1);
-       }
-
-       if (conn->push_buf == NULL)
-               return NULL;
-       out_data = wpabuf_alloc_ext_data(conn->push_buf, conn->push_buf_len);
-       if (out_data == NULL)
-               os_free(conn->push_buf);
-       conn->push_buf = NULL;
-       conn->push_buf_len = 0;
-       return out_data;
-}
-
-
-struct wpabuf * tls_connection_server_handshake(void *tls_ctx,
-                                               struct tls_connection *conn,
-                                               const struct wpabuf *in_data,
-                                               struct wpabuf **appl_data)
-{
-       return NULL;
-}
-
-
-struct wpabuf * tls_connection_encrypt(void *tls_ctx,
-                                      struct tls_connection *conn,
-                                      const struct wpabuf *in_data)
-{
-       PRInt32 res;
-       struct wpabuf *buf;
-
-       wpa_printf(MSG_DEBUG, "NSS: encrypt %d bytes",
-                  (int) wpabuf_len(in_data));
-       res = PR_Send(conn->fd, wpabuf_head(in_data), wpabuf_len(in_data), 0,
-                     0);
-       if (res < 0) {
-               wpa_printf(MSG_ERROR, "NSS: Encryption failed");
-               return NULL;
-       }
-       if (conn->push_buf == NULL)
-               return NULL;
-       buf = wpabuf_alloc_ext_data(conn->push_buf, conn->push_buf_len);
-       if (buf == NULL)
-               os_free(conn->push_buf);
-       conn->push_buf = NULL;
-       conn->push_buf_len = 0;
-       return buf;
-}
-
-
-struct wpabuf * tls_connection_decrypt(void *tls_ctx,
-                                      struct tls_connection *conn,
-                                      const struct wpabuf *in_data)
-{
-       PRInt32 res;
-       struct wpabuf *out;
-
-       wpa_printf(MSG_DEBUG, "NSS: decrypt %d bytes",
-                  (int) wpabuf_len(in_data));
-       if (conn->pull_buf) {
-               wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in "
-                          "pull_buf", __func__,
-                          (unsigned long) conn->pull_buf_len);
-               os_free(conn->pull_buf);
-       }
-       conn->pull_buf = os_malloc(wpabuf_len(in_data));
-       if (conn->pull_buf == NULL)
-               return NULL;
-       os_memcpy(conn->pull_buf, wpabuf_head(in_data), wpabuf_len(in_data));
-       conn->pull_buf_offset = conn->pull_buf;
-       conn->pull_buf_len = wpabuf_len(in_data);
-
-       /*
-        * Even though we try to disable TLS compression, it is possible that
-        * this cannot be done with all TLS libraries. Add extra buffer space
-        * to handle the possibility of the decrypted data being longer than
-        * input data.
-        */
-       out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3);
-       if (out == NULL)
-               return NULL;
-
-       res = PR_Recv(conn->fd, wpabuf_mhead(out), wpabuf_size(out), 0, 0);
-       wpa_printf(MSG_DEBUG, "NSS: PR_Recv: %d", res);
-       if (res < 0) {
-               wpabuf_free(out);
-               return NULL;
-       }
-       wpabuf_put(out, res);
-
-       return out;
-}
-
-
-int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn)
-{
-       return 0;
-}
-
-
-int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
-                                  u8 *ciphers)
-{
-       return -1;
-}
-
-
-int tls_get_cipher(void *tls_ctx, struct tls_connection *conn,
-                  char *buf, size_t buflen)
-{
-       return -1;
-}
-
-
-int tls_connection_enable_workaround(void *tls_ctx,
-                                    struct tls_connection *conn)
-{
-       return -1;
-}
-
-
-int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
-                                   int ext_type, const u8 *data,
-                                   size_t data_len)
-{
-       return -1;
-}
-
-
-int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
-{
-       return 0;
-}
-
-
-int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
-{
-       return 0;
-}
-
-
-int tls_connection_get_write_alerts(void *tls_ctx,
-                                   struct tls_connection *conn)
-{
-       return 0;
-}
-
-
-int tls_connection_get_keyblock_size(void *tls_ctx,
-                                    struct tls_connection *conn)
-{
-       return -1;
-}
-
-
-unsigned int tls_capabilities(void *tls_ctx)
-{
-       return 0;
-}
-
-
-int tls_connection_set_session_ticket_cb(void *tls_ctx,
-                                        struct tls_connection *conn,
-                                        tls_session_ticket_cb cb,
-                                        void *ctx)
-{
-       return -1;
-}
old mode 100644 (file)
new mode 100755 (executable)
index 28b1313..52db8fc
 
 #ifndef CONFIG_SMARTCARD
 #ifndef OPENSSL_NO_ENGINE
+#ifndef ANDROID
 #define OPENSSL_NO_ENGINE
 #endif
 #endif
+#endif
 
 #include <openssl/ssl.h>
 #include <openssl/err.h>
 #include <openssl/engine.h>
 #endif /* OPENSSL_NO_ENGINE */
 
-#ifdef ANDROID
-#include <openssl/pem.h>
-#include "keystore_get.h"
-#endif /* ANDROID */
-
 #include "common.h"
 #include "crypto.h"
 #include "tls.h"
 
-#if OPENSSL_VERSION_NUMBER >= 0x0090800fL
-#define OPENSSL_d2i_TYPE const unsigned char **
-#else
-#define OPENSSL_d2i_TYPE unsigned char **
-#endif
-
 #if defined(SSL_CTX_get_app_data) && defined(SSL_CTX_set_app_data)
 #define OPENSSL_SUPPORTS_CTX_APP_DATA
 #endif
 
-#ifdef SSL_F_SSL_SET_SESSION_TICKET_EXT
-#ifdef SSL_OP_NO_TICKET
-/*
- * Session ticket override patch was merged into OpenSSL 0.9.9 tree on
- * 2008-11-15. This version uses a bit different API compared to the old patch.
- */
-#define CONFIG_OPENSSL_TICKET_OVERRIDE
+#if OPENSSL_VERSION_NUMBER < 0x10000000L
+/* ERR_remove_thread_state replaces ERR_remove_state and the latter is
+ * deprecated. However, OpenSSL 0.9.8 doesn't include
+ * ERR_remove_thread_state. */
+#define ERR_remove_thread_state(tid) ERR_remove_state(0)
 #endif
+
+#if defined(OPENSSL_IS_BORINGSSL)
+/* stack_index_t is the return type of OpenSSL's sk_XXX_num() functions. */
+typedef size_t stack_index_t;
+#else
+typedef int stack_index_t;
 #endif
 
 #ifdef SSL_set_tlsext_status_type
 #endif /* OPENSSL_NO_TLSEXT */
 #endif /* SSL_set_tlsext_status_type */
 
+#ifdef ANDROID
+#include <openssl/pem.h>
+#include <keystore/keystore_get.h>
+
+static BIO * BIO_from_keystore(const char *key)
+{
+       BIO *bio = NULL;
+       uint8_t *value = NULL;
+       int length = keystore_get(key, strlen(key), &value);
+       if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL)
+               BIO_write(bio, value, length);
+       free(value);
+       return bio;
+}
+#endif /* ANDROID */
+
 static int tls_openssl_ref_count = 0;
 
 struct tls_context {
@@ -73,13 +84,14 @@ static struct tls_context *tls_global = NULL;
 
 struct tls_connection {
        struct tls_context *context;
+       SSL_CTX *ssl_ctx;
        SSL *ssl;
        BIO *ssl_in, *ssl_out;
 #ifndef OPENSSL_NO_ENGINE
        ENGINE *engine;        /* functional reference to the engine */
        EVP_PKEY *private_key; /* the private key if using engine */
 #endif /* OPENSSL_NO_ENGINE */
-       char *subject_match, *altsubject_match;
+       char *subject_match, *altsubject_match, *suffix_match, *domain_match;
        int read_alerts, write_alerts, failed;
 
        tls_session_ticket_cb session_ticket_cb;
@@ -92,6 +104,7 @@ struct tls_connection {
        unsigned int ca_cert_verify:1;
        unsigned int cert_probe:1;
        unsigned int server_cert_only:1;
+       unsigned int invalid_hb_used:1;
 
        u8 srv_cert_hash[32];
 
@@ -99,6 +112,7 @@ struct tls_connection {
 
        X509 *peer_cert;
        X509 *peer_issuer;
+       X509 *peer_issuer_issuer;
 };
 
 
@@ -381,7 +395,8 @@ static int tls_cryptoapi_cert(SSL *ssl, const char *name)
                goto err;
        }
 
-       cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &priv->cert->pbCertEncoded,
+       cert = d2i_X509(NULL,
+                       (const unsigned char **) &priv->cert->pbCertEncoded,
                        priv->cert->cbCertEncoded);
        if (cert == NULL) {
                wpa_printf(MSG_INFO, "CryptoAPI: Could not process X509 DER "
@@ -481,7 +496,8 @@ static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name)
        }
 
        while ((ctx = CertEnumCertificatesInStore(cs, ctx))) {
-               cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ctx->pbCertEncoded,
+               cert = d2i_X509(NULL,
+                               (const unsigned char **) &ctx->pbCertEncoded,
                                ctx->cbCertEncoded);
                if (cert == NULL) {
                        wpa_printf(MSG_INFO, "CryptoAPI: Could not process "
@@ -673,12 +689,15 @@ static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path,
                NULL, NULL
        };
 
-       if (!pkcs11_so_path || !pkcs11_module_path)
+       if (!pkcs11_so_path)
                return 0;
 
        pre_cmd[1] = pkcs11_so_path;
        pre_cmd[3] = engine_id;
-       post_cmd[1] = pkcs11_module_path;
+       if (pkcs11_module_path)
+               post_cmd[1] = pkcs11_module_path;
+       else
+               post_cmd[0] = NULL;
 
        wpa_printf(MSG_DEBUG, "ENGINE: Loading pkcs11 Engine from %s",
                   pkcs11_so_path);
@@ -720,6 +739,7 @@ void * tls_init(const struct tls_config *conf)
 {
        SSL_CTX *ssl;
        struct tls_context *context;
+       const char *ciphers;
 
        if (tls_openssl_ref_count == 0) {
                tls_global = context = tls_context_new(conf);
@@ -751,7 +771,7 @@ void * tls_init(const struct tls_config *conf)
 #endif /* CONFIG_FIPS */
                SSL_load_error_strings();
                SSL_library_init();
-#if (OPENSSL_VERSION_NUMBER >= 0x0090800fL) && !defined(OPENSSL_NO_SHA256)
+#ifndef OPENSSL_NO_SHA256
                EVP_add_digest(EVP_sha256());
 #endif /* OPENSSL_NO_SHA256 */
                /* TODO: if /dev/urandom is available, PRNG is seeded
@@ -771,41 +791,47 @@ void * tls_init(const struct tls_config *conf)
                PKCS12_PBE_add();
 #endif  /* PKCS12_FUNCS */
        } else {
-               context = tls_global;
 #ifdef OPENSSL_SUPPORTS_CTX_APP_DATA
                /* Newer OpenSSL can store app-data per-SSL */
                context = tls_context_new(conf);
                if (context == NULL)
                        return NULL;
+#else /* OPENSSL_SUPPORTS_CTX_APP_DATA */
+               context = tls_global;
 #endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */
        }
        tls_openssl_ref_count++;
 
-       ssl = SSL_CTX_new(TLSv1_method());
+       ssl = SSL_CTX_new(SSLv23_method());
        if (ssl == NULL) {
                tls_openssl_ref_count--;
+#ifdef OPENSSL_SUPPORTS_CTX_APP_DATA
+               if (context != tls_global)
+                       os_free(context);
+#endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */
                if (tls_openssl_ref_count == 0) {
                        os_free(tls_global);
                        tls_global = NULL;
-               } else if (context != tls_global) {
-                       os_free(context);
                }
                return NULL;
        }
 
+       SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2);
+       SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3);
+
        SSL_CTX_set_info_callback(ssl, ssl_info_cb);
 #ifdef OPENSSL_SUPPORTS_CTX_APP_DATA
        SSL_CTX_set_app_data(ssl, context);
 #endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */
 
 #ifndef OPENSSL_NO_ENGINE
+       wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine");
+       ERR_load_ENGINE_strings();
+       ENGINE_load_dynamic();
+
        if (conf &&
            (conf->opensc_engine_path || conf->pkcs11_engine_path ||
             conf->pkcs11_module_path)) {
-               wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine");
-               ERR_load_ENGINE_strings();
-               ENGINE_load_dynamic();
-
                if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) ||
                    tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path,
                                                   conf->pkcs11_module_path)) {
@@ -815,6 +841,18 @@ void * tls_init(const struct tls_config *conf)
        }
 #endif /* OPENSSL_NO_ENGINE */
 
+       if (conf && conf->openssl_ciphers)
+               ciphers = conf->openssl_ciphers;
+       else
+               ciphers = "DEFAULT:!EXP:!LOW";
+       if (SSL_CTX_set_cipher_list(ssl, ciphers) != 1) {
+               wpa_printf(MSG_ERROR,
+                          "OpenSSL: Failed to set cipher string '%s'",
+                          ciphers);
+               tls_deinit(ssl);
+               return NULL;
+       }
+
        return ssl;
 }
 
@@ -835,7 +873,7 @@ void tls_deinit(void *ssl_ctx)
                ENGINE_cleanup();
 #endif /* OPENSSL_NO_ENGINE */
                CRYPTO_cleanup_all_ex_data();
-               ERR_remove_state(0);
+               ERR_remove_thread_state(NULL);
                ERR_free_strings();
                EVP_cleanup();
                os_free(tls_global->ocsp_stapling_response);
@@ -856,16 +894,11 @@ static int tls_engine_init(struct tls_connection *conn, const char *engine_id,
                wpa_printf(MSG_ERROR, "ENGINE: Engine ID not set");
                return -1;
        }
-       if (pin == NULL) {
-               wpa_printf(MSG_ERROR, "ENGINE: Smartcard PIN not set");
-               return -1;
-       }
-       if (key_id == NULL) {
-               wpa_printf(MSG_ERROR, "ENGINE: Key Id not set");
-               return -1;
-       }
 
        ERR_clear_error();
+#ifdef ANDROID
+       ENGINE_load_dynamic();
+#endif
        conn->engine = ENGINE_by_id(engine_id);
        if (!conn->engine) {
                wpa_printf(MSG_ERROR, "ENGINE: engine %s not available [%s]",
@@ -880,20 +913,35 @@ static int tls_engine_init(struct tls_connection *conn, const char *engine_id,
        }
        wpa_printf(MSG_DEBUG, "ENGINE: engine initialized");
 
-       if (ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) {
+#ifndef ANDROID
+       if (pin && ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) {
                wpa_printf(MSG_ERROR, "ENGINE: cannot set pin [%s]",
                           ERR_error_string(ERR_get_error(), NULL));
                goto err;
        }
-       /* load private key first in-case PIN is required for cert */
-       conn->private_key = ENGINE_load_private_key(conn->engine,
-                                                   key_id, NULL, NULL);
-       if (!conn->private_key) {
-               wpa_printf(MSG_ERROR, "ENGINE: cannot load private key with id"
-                               " '%s' [%s]", key_id,
-                          ERR_error_string(ERR_get_error(), NULL));
-               ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
-               goto err;
+#endif
+       if (key_id) {
+               /*
+                * Ensure that the ENGINE does not attempt to use the OpenSSL
+                * UI system to obtain a PIN, if we didn't provide one.
+                */
+               struct {
+                       const void *password;
+                       const char *prompt_info;
+               } key_cb = { "", NULL };
+
+               /* load private key first in-case PIN is required for cert */
+               conn->private_key = ENGINE_load_private_key(conn->engine,
+                                                           key_id, NULL,
+                                                           &key_cb);
+               if (!conn->private_key) {
+                       wpa_printf(MSG_ERROR,
+                                  "ENGINE: cannot load private key with id '%s' [%s]",
+                                  key_id,
+                                  ERR_error_string(ERR_get_error(), NULL));
+                       ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED;
+                       goto err;
+               }
        }
 
        /* handle a certificate and/or CA certificate */
@@ -960,19 +1008,41 @@ int tls_get_errors(void *ssl_ctx)
        return count;
 }
 
+
+static void tls_msg_cb(int write_p, int version, int content_type,
+                      const void *buf, size_t len, SSL *ssl, void *arg)
+{
+       struct tls_connection *conn = arg;
+       const u8 *pos = buf;
+
+       wpa_printf(MSG_DEBUG, "OpenSSL: %s ver=0x%x content_type=%d",
+                  write_p ? "TX" : "RX", version, content_type);
+       wpa_hexdump_key(MSG_MSGDUMP, "OpenSSL: Message", buf, len);
+       if (content_type == 24 && len >= 3 && pos[0] == 1) {
+               size_t payload_len = WPA_GET_BE16(pos + 1);
+               if (payload_len + 3 > len) {
+                       wpa_printf(MSG_ERROR, "OpenSSL: Heartbeat attack detected");
+                       conn->invalid_hb_used = 1;
+               }
+       }
+}
+
+
 struct tls_connection * tls_connection_init(void *ssl_ctx)
 {
        SSL_CTX *ssl = ssl_ctx;
        struct tls_connection *conn;
        long options;
-       struct tls_context *context = tls_global;
 #ifdef OPENSSL_SUPPORTS_CTX_APP_DATA
-       context = SSL_CTX_get_app_data(ssl);
+       struct tls_context *context = SSL_CTX_get_app_data(ssl);
+#else /* OPENSSL_SUPPORTS_CTX_APP_DATA */
+       struct tls_context *context = tls_global;
 #endif /* OPENSSL_SUPPORTS_CTX_APP_DATA */
 
        conn = os_zalloc(sizeof(*conn));
        if (conn == NULL)
                return NULL;
+       conn->ssl_ctx = ssl_ctx;
        conn->ssl = SSL_new(ssl);
        if (conn->ssl == NULL) {
                tls_show_errors(MSG_INFO, __func__,
@@ -983,6 +1053,8 @@ struct tls_connection * tls_connection_init(void *ssl_ctx)
 
        conn->context = context;
        SSL_set_app_data(conn->ssl, conn);
+       SSL_set_msg_callback(conn->ssl, tls_msg_cb);
+       SSL_set_msg_callback_arg(conn->ssl, conn);
        options = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
                SSL_OP_SINGLE_DH_USE;
 #ifdef SSL_OP_NO_COMPRESSION
@@ -1023,6 +1095,8 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
        tls_engine_deinit(conn);
        os_free(conn->subject_match);
        os_free(conn->altsubject_match);
+       os_free(conn->suffix_match);
+       os_free(conn->domain_match);
        os_free(conn->session_ticket);
        os_free(conn);
 }
@@ -1053,7 +1127,8 @@ static int tls_match_altsubject_component(X509 *cert, int type,
 {
        GENERAL_NAME *gen;
        void *ext;
-       int i, found = 0;
+       int found = 0;
+       stack_index_t i;
 
        ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
 
@@ -1113,6 +1188,112 @@ static int tls_match_altsubject(X509 *cert, const char *match)
 }
 
 
+#ifndef CONFIG_NATIVE_WINDOWS
+static int domain_suffix_match(const u8 *val, size_t len, const char *match,
+                              int full)
+{
+       size_t i, match_len;
+
+       /* Check for embedded nuls that could mess up suffix matching */
+       for (i = 0; i < len; i++) {
+               if (val[i] == '\0') {
+                       wpa_printf(MSG_DEBUG, "TLS: Embedded null in a string - reject");
+                       return 0;
+               }
+       }
+
+       match_len = os_strlen(match);
+       if (match_len > len || (full && match_len != len))
+               return 0;
+
+       if (os_strncasecmp((const char *) val + len - match_len, match,
+                          match_len) != 0)
+               return 0; /* no match */
+
+       if (match_len == len)
+               return 1; /* exact match */
+
+       if (val[len - match_len - 1] == '.')
+               return 1; /* full label match completes suffix match */
+
+       wpa_printf(MSG_DEBUG, "TLS: Reject due to incomplete label match");
+       return 0;
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+static int tls_match_suffix(X509 *cert, const char *match, int full)
+{
+#ifdef CONFIG_NATIVE_WINDOWS
+       /* wincrypt.h has conflicting X509_NAME definition */
+       return -1;
+#else /* CONFIG_NATIVE_WINDOWS */
+       GENERAL_NAME *gen;
+       void *ext;
+       int i;
+       stack_index_t j;
+       int dns_name = 0;
+       X509_NAME *name;
+
+       wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s",
+                  full ? "": "suffix ", match);
+
+       ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+
+       for (j = 0; ext && j < sk_GENERAL_NAME_num(ext); j++) {
+               gen = sk_GENERAL_NAME_value(ext, j);
+               if (gen->type != GEN_DNS)
+                       continue;
+               dns_name++;
+               wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName",
+                                 gen->d.dNSName->data,
+                                 gen->d.dNSName->length);
+               if (domain_suffix_match(gen->d.dNSName->data,
+                                       gen->d.dNSName->length, match, full) ==
+                   1) {
+                       wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
+                                  full ? "Match" : "Suffix match");
+                       return 1;
+               }
+       }
+
+       if (dns_name) {
+               wpa_printf(MSG_DEBUG, "TLS: None of the dNSName(s) matched");
+               return 0;
+       }
+
+       name = X509_get_subject_name(cert);
+       i = -1;
+       for (;;) {
+               X509_NAME_ENTRY *e;
+               ASN1_STRING *cn;
+
+               i = X509_NAME_get_index_by_NID(name, NID_commonName, i);
+               if (i == -1)
+                       break;
+               e = X509_NAME_get_entry(name, i);
+               if (e == NULL)
+                       continue;
+               cn = X509_NAME_ENTRY_get_data(e);
+               if (cn == NULL)
+                       continue;
+               wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName",
+                                 cn->data, cn->length);
+               if (domain_suffix_match(cn->data, cn->length, match, full) == 1)
+               {
+                       wpa_printf(MSG_DEBUG, "TLS: %s in commonName found",
+                                  full ? "Match" : "Suffix match");
+                       return 1;
+               }
+       }
+
+       wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found",
+                  full ? "": "suffix ");
+       return 0;
+#endif /* CONFIG_NATIVE_WINDOWS */
+}
+
+
 static enum tls_fail_reason openssl_tls_fail_reason(int err)
 {
        switch (err) {
@@ -1202,6 +1383,11 @@ static void openssl_tls_cert_event(struct tls_connection *conn,
        struct wpabuf *cert = NULL;
        union tls_event_data ev;
        struct tls_context *context = conn->context;
+       char *altsubject[TLS_MAX_ALT_SUBJECT];
+       int alt, num_altsubject = 0;
+       GENERAL_NAME *gen;
+       void *ext;
+       stack_index_t i;
 #ifdef CONFIG_SHA256
        u8 hash[32];
 #endif /* CONFIG_SHA256 */
@@ -1228,8 +1414,52 @@ static void openssl_tls_cert_event(struct tls_connection *conn,
 #endif /* CONFIG_SHA256 */
        ev.peer_cert.depth = depth;
        ev.peer_cert.subject = subject;
+
+       ext = X509_get_ext_d2i(err_cert, NID_subject_alt_name, NULL, NULL);
+       for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) {
+               char *pos;
+
+               if (num_altsubject == TLS_MAX_ALT_SUBJECT)
+                       break;
+               gen = sk_GENERAL_NAME_value(ext, i);
+               if (gen->type != GEN_EMAIL &&
+                   gen->type != GEN_DNS &&
+                   gen->type != GEN_URI)
+                       continue;
+
+               pos = os_malloc(10 + gen->d.ia5->length + 1);
+               if (pos == NULL)
+                       break;
+               altsubject[num_altsubject++] = pos;
+
+               switch (gen->type) {
+               case GEN_EMAIL:
+                       os_memcpy(pos, "EMAIL:", 6);
+                       pos += 6;
+                       break;
+               case GEN_DNS:
+                       os_memcpy(pos, "DNS:", 4);
+                       pos += 4;
+                       break;
+               case GEN_URI:
+                       os_memcpy(pos, "URI:", 4);
+                       pos += 4;
+                       break;
+               }
+
+               os_memcpy(pos, gen->d.ia5->data, gen->d.ia5->length);
+               pos += gen->d.ia5->length;
+               *pos = '\0';
+       }
+
+       for (alt = 0; alt < num_altsubject; alt++)
+               ev.peer_cert.altsubject[alt] = altsubject[alt];
+       ev.peer_cert.num_altsubject = num_altsubject;
+
        context->event_cb(context->cb_ctx, TLS_PEER_CERTIFICATE, &ev);
        wpabuf_free(cert);
+       for (alt = 0; alt < num_altsubject; alt++)
+               os_free(altsubject[alt]);
 }
 
 
@@ -1241,10 +1471,13 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
        SSL *ssl;
        struct tls_connection *conn;
        struct tls_context *context;
-       char *match, *altmatch;
+       char *match, *altmatch, *suffix_match, *domain_match;
        const char *err_str;
 
        err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
+       if (!err_cert)
+               return 0;
+
        err = X509_STORE_CTX_get_error(x509_ctx);
        depth = X509_STORE_CTX_get_error_depth(x509_ctx);
        ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
@@ -1259,10 +1492,14 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
                conn->peer_cert = err_cert;
        else if (depth == 1)
                conn->peer_issuer = err_cert;
+       else if (depth == 2)
+               conn->peer_issuer_issuer = err_cert;
 
        context = conn->context;
        match = conn->subject_match;
        altmatch = conn->altsubject_match;
+       suffix_match = conn->suffix_match;
+       domain_match = conn->domain_match;
 
        if (!preverify_ok && !conn->ca_cert_verify)
                preverify_ok = 1;
@@ -1279,7 +1516,11 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
        err_str = X509_verify_cert_error_string(err);
 
 #ifdef CONFIG_SHA256
-       if (preverify_ok && depth == 0 && conn->server_cert_only) {
+       /*
+        * Do not require preverify_ok so we can explicity allow otherwise
+        * invalid pinned server certificates.
+        */
+       if (depth == 0 && conn->server_cert_only) {
                struct wpabuf *cert;
                cert = get_x509_cert(err_cert);
                if (!cert) {
@@ -1297,6 +1538,14 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
                                err_str = "Server certificate mismatch";
                                err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN;
                                preverify_ok = 0;
+                       } else if (!preverify_ok) {
+                               /*
+                                * Certificate matches pinned certificate, allow
+                                * regardless of other problems.
+                                */
+                               wpa_printf(MSG_DEBUG,
+                                          "OpenSSL: Ignore validation issues for a pinned server certificate");
+                               preverify_ok = 1;
                        }
                        wpabuf_free(cert);
                }
@@ -1331,6 +1580,22 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
                openssl_tls_fail_event(conn, err_cert, err, depth, buf,
                                       "AltSubject mismatch",
                                       TLS_FAIL_ALTSUBJECT_MISMATCH);
+       } else if (depth == 0 && suffix_match &&
+                  !tls_match_suffix(err_cert, suffix_match, 0)) {
+               wpa_printf(MSG_WARNING, "TLS: Domain suffix match '%s' not found",
+                          suffix_match);
+               preverify_ok = 0;
+               openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+                                      "Domain suffix mismatch",
+                                      TLS_FAIL_DOMAIN_SUFFIX_MISMATCH);
+       } else if (depth == 0 && domain_match &&
+                  !tls_match_suffix(err_cert, domain_match, 1)) {
+               wpa_printf(MSG_WARNING, "TLS: Domain match '%s' not found",
+                          domain_match);
+               preverify_ok = 0;
+               openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+                                      "Domain mismatch",
+                                      TLS_FAIL_DOMAIN_MISMATCH);
        } else
                openssl_tls_cert_event(conn, err_cert, depth, buf);
 
@@ -1358,7 +1623,7 @@ static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert)
        X509_LOOKUP *lookup;
        int ret = 0;
 
-       lookup = X509_STORE_add_lookup(ssl_ctx->cert_store,
+       lookup = X509_STORE_add_lookup(SSL_CTX_get_cert_store(ssl_ctx),
                                       X509_LOOKUP_file());
        if (lookup == NULL) {
                tls_show_errors(MSG_WARNING, __func__,
@@ -1384,36 +1649,24 @@ static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert)
 #endif /* OPENSSL_NO_STDIO */
 
 
-#ifdef ANDROID
-static BIO * BIO_from_keystore(const char *key)
-{
-       BIO *bio = NULL;
-       char value[KEYSTORE_MESSAGE_SIZE];
-       int length = keystore_get(key, strlen(key), value);
-       if (length != -1 && (bio = BIO_new(BIO_s_mem())) != NULL)
-               BIO_write(bio, value, length);
-       return bio;
-}
-#endif /* ANDROID */
-
-
 static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn,
                                  const char *ca_cert, const u8 *ca_cert_blob,
                                  size_t ca_cert_blob_len, const char *ca_path)
 {
        SSL_CTX *ssl_ctx = _ssl_ctx;
+       X509_STORE *store;
 
        /*
         * Remove previously configured trusted CA certificates before adding
         * new ones.
         */
-       X509_STORE_free(ssl_ctx->cert_store);
-       ssl_ctx->cert_store = X509_STORE_new();
-       if (ssl_ctx->cert_store == NULL) {
+       store = X509_STORE_new();
+       if (store == NULL) {
                wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new "
                           "certificate store", __func__);
                return -1;
        }
+       SSL_CTX_set_cert_store(ssl_ctx, store);
 
        SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb);
        conn->ca_cert_verify = 1;
@@ -1457,7 +1710,8 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn,
        }
 
        if (ca_cert_blob) {
-               X509 *cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ca_cert_blob,
+               X509 *cert = d2i_X509(NULL,
+                                     (const unsigned char **) &ca_cert_blob,
                                      ca_cert_blob_len);
                if (cert == NULL) {
                        tls_show_errors(MSG_WARNING, __func__,
@@ -1465,7 +1719,8 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn,
                        return -1;
                }
 
-               if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) {
+               if (!X509_STORE_add_cert(SSL_CTX_get_cert_store(ssl_ctx),
+                                        cert)) {
                        unsigned long err = ERR_peek_error();
                        tls_show_errors(MSG_WARNING, __func__,
                                        "Failed to add ca_cert_blob to "
@@ -1491,7 +1746,7 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn,
        if (ca_cert && os_strncmp("keystore://", ca_cert, 11) == 0) {
                BIO *bio = BIO_from_keystore(&ca_cert[11]);
                STACK_OF(X509_INFO) *stack = NULL;
-               int i;
+               stack_index_t i;
 
                if (bio) {
                        stack = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
@@ -1606,7 +1861,9 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl)
 
 static int tls_connection_set_subject_match(struct tls_connection *conn,
                                            const char *subject_match,
-                                           const char *altsubject_match)
+                                           const char *altsubject_match,
+                                           const char *suffix_match,
+                                           const char *domain_match)
 {
        os_free(conn->subject_match);
        conn->subject_match = NULL;
@@ -1624,6 +1881,22 @@ static int tls_connection_set_subject_match(struct tls_connection *conn,
                        return -1;
        }
 
+       os_free(conn->suffix_match);
+       conn->suffix_match = NULL;
+       if (suffix_match) {
+               conn->suffix_match = os_strdup(suffix_match);
+               if (conn->suffix_match == NULL)
+                       return -1;
+       }
+
+       os_free(conn->domain_match);
+       conn->domain_match = NULL;
+       if (domain_match) {
+               conn->domain_match = os_strdup(domain_match);
+               if (conn->domain_match == NULL)
+                       return -1;
+       }
+
        return 0;
 }
 
@@ -1878,7 +2151,7 @@ static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl,
 #ifdef PKCS12_FUNCS
        PKCS12 *p12;
 
-       p12 = d2i_PKCS12(NULL, (OPENSSL_d2i_TYPE) &blob, len);
+       p12 = d2i_PKCS12(NULL, (const unsigned char **) &blob, len);
        if (p12 == NULL) {
                tls_show_errors(MSG_INFO, __func__,
                                "Failed to use PKCS#12 blob");
@@ -1959,20 +2232,21 @@ static int tls_connection_engine_ca_cert(void *_ssl_ctx,
 #ifndef OPENSSL_NO_ENGINE
        X509 *cert;
        SSL_CTX *ssl_ctx = _ssl_ctx;
+       X509_STORE *store;
 
        if (tls_engine_get_cert(conn, ca_cert_id, &cert))
                return -1;
 
        /* start off the same as tls_connection_ca_cert */
-       X509_STORE_free(ssl_ctx->cert_store);
-       ssl_ctx->cert_store = X509_STORE_new();
-       if (ssl_ctx->cert_store == NULL) {
+       store = X509_STORE_new();
+       if (store == NULL) {
                wpa_printf(MSG_DEBUG, "OpenSSL: %s - failed to allocate new "
                           "certificate store", __func__);
                X509_free(cert);
                return -1;
        }
-       if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) {
+       SSL_CTX_set_cert_store(ssl_ctx, store);
+       if (!X509_STORE_add_cert(store, cert)) {
                unsigned long err = ERR_peek_error();
                tls_show_errors(MSG_WARNING, __func__,
                                "Failed to add CA certificate from engine "
@@ -2087,26 +2361,6 @@ static int tls_connection_private_key(void *_ssl_ctx,
                break;
        }
 
-#ifdef ANDROID
-       if (!ok && private_key &&
-           os_strncmp("keystore://", private_key, 11) == 0) {
-               BIO *bio = BIO_from_keystore(&private_key[11]);
-               EVP_PKEY *pkey = NULL;
-               if (bio) {
-                       pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
-                       BIO_free(bio);
-               }
-               if (pkey) {
-                       if (SSL_use_PrivateKey(conn->ssl, pkey) == 1) {
-                               wpa_printf(MSG_DEBUG, "OpenSSL: Private key "
-                                          "from keystore");
-                               ok = 1;
-                       }
-                       EVP_PKEY_free(pkey);
-               }
-       }
-#endif /* ANDROID */
-
        while (!ok && private_key) {
 #ifndef OPENSSL_NO_STDIO
                if (SSL_use_PrivateKey_file(conn->ssl, private_key,
@@ -2528,10 +2782,25 @@ openssl_connection_handshake(struct tls_connection *conn,
        out_data = openssl_handshake(conn, in_data, server);
        if (out_data == NULL)
                return NULL;
+       if (conn->invalid_hb_used) {
+               wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response");
+               wpabuf_free(out_data);
+               return NULL;
+       }
 
        if (SSL_is_init_finished(conn->ssl) && appl_data && in_data)
                *appl_data = openssl_get_appl_data(conn, wpabuf_len(in_data));
 
+       if (conn->invalid_hb_used) {
+               wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response");
+               if (appl_data) {
+                       wpabuf_free(*appl_data);
+                       *appl_data = NULL;
+               }
+               wpabuf_free(out_data);
+               return NULL;
+       }
+
        return out_data;
 }
 
@@ -2633,13 +2902,23 @@ struct wpabuf * tls_connection_decrypt(void *tls_ctx,
        }
        wpabuf_put(buf, res);
 
+       if (conn->invalid_hb_used) {
+               wpa_printf(MSG_INFO, "TLS: Heartbeat attack detected - do not send response");
+               wpabuf_free(buf);
+               return NULL;
+       }
+
        return buf;
 }
 
 
 int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn)
 {
+#if OPENSSL_VERSION_NUMBER >= 0x10001000L
+       return conn ? SSL_cache_hit(conn->ssl) : 0;
+#else
        return conn ? conn->ssl->hit : 0;
+#endif
 }
 
 
@@ -2680,7 +2959,7 @@ int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn,
                        return -1;
                }
                ret = os_snprintf(pos, end - pos, ":%s", suite);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        break;
                pos += ret;
 
@@ -2735,15 +3014,9 @@ int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn,
        if (conn == NULL || conn->ssl == NULL || ext_type != 35)
                return -1;
 
-#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE
        if (SSL_set_session_ticket_ext(conn->ssl, (void *) data,
                                       data_len) != 1)
                return -1;
-#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */
-       if (SSL_set_hello_extension(conn->ssl, ext_type, (void *) data,
-                                   data_len) != 1)
-               return -1;
-#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */
 
        return 0;
 }
@@ -2779,7 +3052,6 @@ int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn)
 static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp)
 {
 #ifndef CONFIG_NO_STDOUT_DEBUG
-       extern int wpa_debug_level;
        BIO *out;
        size_t rlen;
        char *txt;
@@ -2811,6 +3083,41 @@ static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp)
 }
 
 
+static void debug_print_cert(X509 *cert, const char *title)
+{
+#ifndef CONFIG_NO_STDOUT_DEBUG
+       BIO *out;
+       size_t rlen;
+       char *txt;
+       int res;
+
+       if (wpa_debug_level > MSG_DEBUG)
+               return;
+
+       out = BIO_new(BIO_s_mem());
+       if (!out)
+               return;
+
+       X509_print(out, cert);
+       rlen = BIO_ctrl_pending(out);
+       txt = os_malloc(rlen + 1);
+       if (!txt) {
+               BIO_free(out);
+               return;
+       }
+
+       res = BIO_read(out, txt, rlen);
+       if (res > 0) {
+               txt[res] = '\0';
+               wpa_printf(MSG_DEBUG, "OpenSSL: %s\n%s", title, txt);
+       }
+       os_free(txt);
+
+       BIO_free(out);
+#endif /* CONFIG_NO_STDOUT_DEBUG */
+}
+
+
 static int ocsp_resp_cb(SSL *s, void *arg)
 {
        struct tls_connection *conn = arg;
@@ -2820,6 +3127,8 @@ static int ocsp_resp_cb(SSL *s, void *arg)
        OCSP_BASICRESP *basic;
        OCSP_CERTID *id;
        ASN1_GENERALIZEDTIME *produced_at, *this_update, *next_update;
+       X509_STORE *store;
+       STACK_OF(X509) *certs = NULL;
 
        len = SSL_get_tlsext_status_ocsp_resp(s, &p);
        if (!p) {
@@ -2850,8 +3159,40 @@ static int ocsp_resp_cb(SSL *s, void *arg)
                return 0;
        }
 
-       status = OCSP_basic_verify(basic, NULL, SSL_CTX_get_cert_store(s->ctx),
-                                  0);
+       store = SSL_CTX_get_cert_store(conn->ssl_ctx);
+       if (conn->peer_issuer) {
+               debug_print_cert(conn->peer_issuer, "Add OCSP issuer");
+
+               if (X509_STORE_add_cert(store, conn->peer_issuer) != 1) {
+                       tls_show_errors(MSG_INFO, __func__,
+                                       "OpenSSL: Could not add issuer to certificate store");
+               }
+               certs = sk_X509_new_null();
+               if (certs) {
+                       X509 *cert;
+                       cert = X509_dup(conn->peer_issuer);
+                       if (cert && !sk_X509_push(certs, cert)) {
+                               tls_show_errors(
+                                       MSG_INFO, __func__,
+                                       "OpenSSL: Could not add issuer to OCSP responder trust store");
+                               X509_free(cert);
+                               sk_X509_free(certs);
+                               certs = NULL;
+                       }
+                       if (certs && conn->peer_issuer_issuer) {
+                               cert = X509_dup(conn->peer_issuer_issuer);
+                               if (cert && !sk_X509_push(certs, cert)) {
+                                       tls_show_errors(
+                                               MSG_INFO, __func__,
+                                               "OpenSSL: Could not add issuer's issuer to OCSP responder trust store");
+                                       X509_free(cert);
+                               }
+                       }
+               }
+       }
+
+       status = OCSP_basic_verify(basic, certs, store, OCSP_TRUSTOTHER);
+       sk_X509_pop_free(certs, X509_free);
        if (status <= 0) {
                tls_show_errors(MSG_INFO, __func__,
                                "OpenSSL: OCSP response failed verification");
@@ -2862,8 +3203,15 @@ static int ocsp_resp_cb(SSL *s, void *arg)
 
        wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response verification succeeded");
 
-       if (!conn->peer_cert || !conn->peer_issuer) {
-               wpa_printf(MSG_DEBUG, "OpenSSL: Peer certificate or issue certificate not available for OCSP status check");
+       if (!conn->peer_cert) {
+               wpa_printf(MSG_DEBUG, "OpenSSL: Peer certificate not available for OCSP status check");
+               OCSP_BASICRESP_free(basic);
+               OCSP_RESPONSE_free(rsp);
+               return 0;
+       }
+
+       if (!conn->peer_issuer) {
+               wpa_printf(MSG_DEBUG, "OpenSSL: Peer issuer certificate not available for OCSP status check");
                OCSP_BASICRESP_free(basic);
                OCSP_RESPONSE_free(rsp);
                return 0;
@@ -2909,7 +3257,7 @@ static int ocsp_resp_cb(SSL *s, void *arg)
                wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP required");
                return 0;
        }
-               wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue");
+       wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue");
        return 1;
 }
 
@@ -2954,32 +3302,77 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
 {
        int ret;
        unsigned long err;
-       SSL_CTX *ssl_ctx = tls_ctx;
+       int can_pkcs11 = 0;
+       const char *key_id = params->key_id;
+       const char *cert_id = params->cert_id;
+       const char *ca_cert_id = params->ca_cert_id;
+       const char *engine_id = params->engine ? params->engine_id : NULL;
 
        if (conn == NULL)
                return -1;
 
+       /*
+        * If the engine isn't explicitly configured, and any of the
+        * cert/key fields are actually PKCS#11 URIs, then automatically
+        * use the PKCS#11 ENGINE.
+        */
+       if (!engine_id || os_strcmp(engine_id, "pkcs11") == 0)
+               can_pkcs11 = 1;
+
+       if (!key_id && params->private_key && can_pkcs11 &&
+           os_strncmp(params->private_key, "pkcs11:", 7) == 0) {
+               can_pkcs11 = 2;
+               key_id = params->private_key;
+       }
+
+       if (!cert_id && params->client_cert && can_pkcs11 &&
+           os_strncmp(params->client_cert, "pkcs11:", 7) == 0) {
+               can_pkcs11 = 2;
+               cert_id = params->client_cert;
+       }
+
+       if (!ca_cert_id && params->ca_cert && can_pkcs11 &&
+           os_strncmp(params->ca_cert, "pkcs11:", 7) == 0) {
+               can_pkcs11 = 2;
+               ca_cert_id = params->ca_cert;
+       }
+
+       /* If we need to automatically enable the PKCS#11 ENGINE, do so. */
+       if (can_pkcs11 == 2 && !engine_id)
+               engine_id = "pkcs11";
+
+       if (params->flags & TLS_CONN_EAP_FAST) {
+               wpa_printf(MSG_DEBUG,
+                          "OpenSSL: Use TLSv1_method() for EAP-FAST");
+               if (SSL_set_ssl_method(conn->ssl, TLSv1_method()) != 1) {
+                       tls_show_errors(MSG_INFO, __func__,
+                                       "Failed to set TLSv1_method() for EAP-FAST");
+                       return -1;
+               }
+       }
+
        while ((err = ERR_get_error())) {
                wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s",
                           __func__, ERR_error_string(err, NULL));
        }
 
-       if (params->engine) {
+       if (engine_id) {
                wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine");
-               ret = tls_engine_init(conn, params->engine_id, params->pin,
-                                     params->key_id, params->cert_id,
-                                     params->ca_cert_id);
+               ret = tls_engine_init(conn, engine_id, params->pin,
+                                     key_id, cert_id, ca_cert_id);
                if (ret)
                        return ret;
        }
        if (tls_connection_set_subject_match(conn,
                                             params->subject_match,
-                                            params->altsubject_match))
+                                            params->altsubject_match,
+                                            params->suffix_match,
+                                            params->domain_match))
                return -1;
 
-       if (params->engine && params->ca_cert_id) {
+       if (engine_id && ca_cert_id) {
                if (tls_connection_engine_ca_cert(tls_ctx, conn,
-                                                 params->ca_cert_id))
+                                                 ca_cert_id))
                        return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
        } else if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert,
                                          params->ca_cert_blob,
@@ -2987,15 +3380,15 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
                                          params->ca_path))
                return -1;
 
-       if (params->engine && params->cert_id) {
-               if (tls_connection_engine_client_cert(conn, params->cert_id))
+       if (engine_id && cert_id) {
+               if (tls_connection_engine_client_cert(conn, cert_id))
                        return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
        } else if (tls_connection_client_cert(conn, params->client_cert,
                                              params->client_cert_blob,
                                              params->client_cert_blob_len))
                return -1;
 
-       if (params->engine && params->key_id) {
+       if (engine_id && key_id) {
                wpa_printf(MSG_DEBUG, "TLS: Using private key from engine");
                if (tls_connection_engine_private_key(conn))
                        return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED;
@@ -3015,6 +3408,14 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
                return -1;
        }
 
+       if (params->openssl_ciphers &&
+           SSL_set_cipher_list(conn->ssl, params->openssl_ciphers) != 1) {
+               wpa_printf(MSG_INFO,
+                          "OpenSSL: Failed to set cipher string '%s'",
+                          params->openssl_ciphers);
+               return -1;
+       }
+
 #ifdef SSL_OP_NO_TICKET
        if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET)
                SSL_set_options(conn->ssl, SSL_OP_NO_TICKET);
@@ -3024,8 +3425,22 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
 #endif /* SSL_clear_options */
 #endif /*  SSL_OP_NO_TICKET */
 
+#ifdef SSL_OP_NO_TLSv1_1
+       if (params->flags & TLS_CONN_DISABLE_TLSv1_1)
+               SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_1);
+       else
+               SSL_clear_options(conn->ssl, SSL_OP_NO_TLSv1_1);
+#endif /* SSL_OP_NO_TLSv1_1 */
+#ifdef SSL_OP_NO_TLSv1_2
+       if (params->flags & TLS_CONN_DISABLE_TLSv1_2)
+               SSL_set_options(conn->ssl, SSL_OP_NO_TLSv1_2);
+       else
+               SSL_clear_options(conn->ssl, SSL_OP_NO_TLSv1_2);
+#endif /* SSL_OP_NO_TLSv1_2 */
+
 #ifdef HAVE_OCSP
        if (params->flags & TLS_CONN_REQUEST_OCSP) {
+               SSL_CTX *ssl_ctx = tls_ctx;
                SSL_set_tlsext_status_type(conn->ssl, TLSEXT_STATUSTYPE_ocsp);
                SSL_CTX_set_tlsext_status_cb(ssl_ctx, ocsp_resp_cb);
                SSL_CTX_set_tlsext_status_arg(ssl_ctx, conn);
@@ -3067,6 +3482,14 @@ int tls_global_set_params(void *tls_ctx,
                return -1;
        }
 
+       if (params->openssl_ciphers &&
+           SSL_CTX_set_cipher_list(ssl_ctx, params->openssl_ciphers) != 1) {
+               wpa_printf(MSG_INFO,
+                          "OpenSSL: Failed to set cipher string '%s'",
+                          params->openssl_ciphers);
+               return -1;
+       }
+
 #ifdef SSL_OP_NO_TICKET
        if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET)
                SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET);
@@ -3139,9 +3562,15 @@ unsigned int tls_capabilities(void *tls_ctx)
  * commented out unless explicitly needed for EAP-FAST in order to be able to
  * build this file with unmodified openssl. */
 
+#ifdef OPENSSL_IS_BORINGSSL
+static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len,
+                          STACK_OF(SSL_CIPHER) *peer_ciphers,
+                          const SSL_CIPHER **cipher, void *arg)
+#else /* OPENSSL_IS_BORINGSSL */
 static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len,
                           STACK_OF(SSL_CIPHER) *peer_ciphers,
                           SSL_CIPHER **cipher, void *arg)
+#endif /* OPENSSL_IS_BORINGSSL */
 {
        struct tls_connection *conn = arg;
        int ret;
@@ -3165,7 +3594,6 @@ static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len,
 }
 
 
-#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE
 static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data,
                                     int len, void *arg)
 {
@@ -3191,62 +3619,6 @@ static int tls_session_ticket_ext_cb(SSL *s, const unsigned char *data,
 
        return 1;
 }
-#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */
-#ifdef SSL_OP_NO_TICKET
-static void tls_hello_ext_cb(SSL *s, int client_server, int type,
-                            unsigned char *data, int len, void *arg)
-{
-       struct tls_connection *conn = arg;
-
-       if (conn == NULL || conn->session_ticket_cb == NULL)
-               return;
-
-       wpa_printf(MSG_DEBUG, "OpenSSL: %s: type=%d length=%d", __func__,
-                  type, len);
-
-       if (type == TLSEXT_TYPE_session_ticket && !client_server) {
-               os_free(conn->session_ticket);
-               conn->session_ticket = NULL;
-
-               wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket "
-                           "extension", data, len);
-               conn->session_ticket = os_malloc(len);
-               if (conn->session_ticket == NULL)
-                       return;
-
-               os_memcpy(conn->session_ticket, data, len);
-               conn->session_ticket_len = len;
-       }
-}
-#else /* SSL_OP_NO_TICKET */
-static int tls_hello_ext_cb(SSL *s, TLS_EXTENSION *ext, void *arg)
-{
-       struct tls_connection *conn = arg;
-
-       if (conn == NULL || conn->session_ticket_cb == NULL)
-               return 0;
-
-       wpa_printf(MSG_DEBUG, "OpenSSL: %s: type=%d length=%d", __func__,
-                  ext->type, ext->length);
-
-       os_free(conn->session_ticket);
-       conn->session_ticket = NULL;
-
-       if (ext->type == 35) {
-               wpa_hexdump(MSG_DEBUG, "OpenSSL: ClientHello SessionTicket "
-                           "extension", ext->data, ext->length);
-               conn->session_ticket = os_malloc(ext->length);
-               if (conn->session_ticket == NULL)
-                       return SSL_AD_INTERNAL_ERROR;
-
-               os_memcpy(conn->session_ticket, ext->data, ext->length);
-               conn->session_ticket_len = ext->length;
-       }
-
-       return 0;
-}
-#endif /* SSL_OP_NO_TICKET */
-#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */
 #endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
 
 
@@ -3263,33 +3635,12 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx,
                if (SSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb,
                                              conn) != 1)
                        return -1;
-#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE
                SSL_set_session_ticket_ext_cb(conn->ssl,
                                              tls_session_ticket_ext_cb, conn);
-#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */
-#ifdef SSL_OP_NO_TICKET
-               SSL_set_tlsext_debug_callback(conn->ssl, tls_hello_ext_cb);
-               SSL_set_tlsext_debug_arg(conn->ssl, conn);
-#else /* SSL_OP_NO_TICKET */
-               if (SSL_set_hello_extension_cb(conn->ssl, tls_hello_ext_cb,
-                                              conn) != 1)
-                       return -1;
-#endif /* SSL_OP_NO_TICKET */
-#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */
        } else {
                if (SSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1)
                        return -1;
-#ifdef CONFIG_OPENSSL_TICKET_OVERRIDE
                SSL_set_session_ticket_ext_cb(conn->ssl, NULL, NULL);
-#else /* CONFIG_OPENSSL_TICKET_OVERRIDE */
-#ifdef SSL_OP_NO_TICKET
-               SSL_set_tlsext_debug_callback(conn->ssl, NULL);
-               SSL_set_tlsext_debug_arg(conn->ssl, conn);
-#else /* SSL_OP_NO_TICKET */
-               if (SSL_set_hello_extension_cb(conn->ssl, NULL, NULL) != 1)
-                       return -1;
-#endif /* SSL_OP_NO_TICKET */
-#endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */
        }
 
        return 0;
@@ -3297,3 +3648,11 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx,
        return -1;
 #endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */
 }
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+       return os_snprintf(buf, buf_len, "OpenSSL build=%s run=%s",
+                          OPENSSL_VERSION_TEXT,
+                          SSLeay_version(SSLEAY_VERSION));
+}
index 2c2daa8..31a2c94 100644 (file)
@@ -692,6 +692,31 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
        if (conn == NULL)
                return -1;
 
+       if (params->subject_match) {
+               wpa_printf(MSG_INFO, "TLS: subject_match not supported");
+               return -1;
+       }
+
+       if (params->altsubject_match) {
+               wpa_printf(MSG_INFO, "TLS: altsubject_match not supported");
+               return -1;
+       }
+
+       if (params->suffix_match) {
+               wpa_printf(MSG_INFO, "TLS: suffix_match not supported");
+               return -1;
+       }
+
+       if (params->domain_match) {
+               wpa_printf(MSG_INFO, "TLS: domain_match not supported");
+               return -1;
+       }
+
+       if (params->openssl_ciphers) {
+               wpa_printf(MSG_INFO, "GnuTLS: openssl_ciphers not supported");
+               return -1;
+       }
+
        if (global->my_cert_store == NULL &&
            (global->my_cert_store = CertOpenSystemStore(0, TEXT("MY"))) ==
            NULL) {
@@ -730,3 +755,9 @@ unsigned int tls_capabilities(void *tls_ctx)
 {
        return 0;
 }
+
+
+int tls_get_library_version(char *buf, size_t buf_len)
+{
+       return os_snprintf(buf, buf_len, "schannel");
+}
index 07600e5..5721154 100644 (file)
@@ -2,7 +2,7 @@ all:
        @echo Nothing to be made.
 
 clean:
-       rm -f *~ *.o *.d
+       rm -f *~ *.o *.d *.gcno *.gcda *.gcov
        rm -f build.wpa_supplicant build.hostapd
 
 install:
index 5906527..31d9440 100644 (file)
@@ -1,12 +1,8 @@
 /*
  * Android driver interface
  *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
  */
 
 #ifndef ANDROID_DRV_H
old mode 100644 (file)
new mode 100755 (executable)
index d5b508d..5cab576
@@ -1,6 +1,6 @@
 /*
  * Driver interface definition
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 #define WPA_SUPPLICANT_DRIVER_VERSION 4
 
 #include "common/defs.h"
+#include "utils/list.h"
 
 #define HOSTAPD_CHAN_DISABLED 0x00000001
-#define HOSTAPD_CHAN_PASSIVE_SCAN 0x00000002
-#define HOSTAPD_CHAN_NO_IBSS 0x00000004
+#define HOSTAPD_CHAN_NO_IR 0x00000002
 #define HOSTAPD_CHAN_RADAR 0x00000008
 #define HOSTAPD_CHAN_HT40PLUS 0x00000010
 #define HOSTAPD_CHAN_HT40MINUS 0x00000020
 #define HOSTAPD_CHAN_HT40 0x00000040
+#define HOSTAPD_CHAN_SURVEY_LIST_INITIALIZED 0x00000080
 
 #define HOSTAPD_CHAN_DFS_UNKNOWN 0x00000000
 #define HOSTAPD_CHAN_DFS_USABLE 0x00000100
 #define HOSTAPD_CHAN_DFS_AVAILABLE 0x00000300
 #define HOSTAPD_CHAN_DFS_MASK 0x00000300
 
+#define HOSTAPD_CHAN_VHT_10_70 0x00000800
+#define HOSTAPD_CHAN_VHT_30_50 0x00001000
+#define HOSTAPD_CHAN_VHT_50_30 0x00002000
+#define HOSTAPD_CHAN_VHT_70_10 0x00004000
+
+#define HOSTAPD_CHAN_INDOOR_ONLY 0x00010000
+#define HOSTAPD_CHAN_GO_CONCURRENT 0x00020000
+
+/**
+ * enum reg_change_initiator - Regulatory change initiator
+ */
+enum reg_change_initiator {
+       REGDOM_SET_BY_CORE,
+       REGDOM_SET_BY_USER,
+       REGDOM_SET_BY_DRIVER,
+       REGDOM_SET_BY_COUNTRY_IE,
+       REGDOM_BEACON_HINT,
+};
+
+/**
+ * enum reg_type - Regulatory change types
+ */
+enum reg_type {
+       REGDOM_TYPE_UNKNOWN,
+       REGDOM_TYPE_COUNTRY,
+       REGDOM_TYPE_WORLD,
+       REGDOM_TYPE_CUSTOM_WORLD,
+       REGDOM_TYPE_INTERSECTION,
+};
+
 /**
  * struct hostapd_channel_data - Channel information
  */
@@ -55,12 +86,38 @@ struct hostapd_channel_data {
        int flag;
 
        /**
-        * max_tx_power - maximum transmit power in dBm
+        * max_tx_power - Regulatory transmit power limit in dBm
         */
        u8 max_tx_power;
+
+       /**
+        * survey_list - Linked list of surveys (struct freq_survey)
+        */
+       struct dl_list survey_list;
+
+       /**
+        * min_nf - Minimum observed noise floor, in dBm, based on all
+        * surveyed channel data
+        */
+       s8 min_nf;
+
+#ifdef CONFIG_ACS
+       /**
+        * interference_factor - Computed interference factor on this
+        * channel (used internally in src/ap/acs.c; driver wrappers do not
+        * need to set this)
+        */
+       long double interference_factor;
+#endif /* CONFIG_ACS */
+
+       /**
+        * dfs_cac_ms - DFS CAC time in milliseconds
+        */
+       unsigned int dfs_cac_ms;
 };
 
 #define HOSTAPD_MODE_FLAG_HT_INFO_KNOWN BIT(0)
+#define HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN BIT(1)
 
 /**
  * struct hostapd_hw_modes - Supported hardware mode information
@@ -123,10 +180,12 @@ struct hostapd_hw_modes {
 #define IEEE80211_MODE_INFRA   0
 #define IEEE80211_MODE_IBSS    1
 #define IEEE80211_MODE_AP      2
+#define IEEE80211_MODE_MESH    5
 
 #define IEEE80211_CAP_ESS      0x0001
 #define IEEE80211_CAP_IBSS     0x0002
 #define IEEE80211_CAP_PRIVACY  0x0010
+#define IEEE80211_CAP_RRM      0x1000
 
 /* DMG (60 GHz) IEEE 802.11ad */
 /* type - bits 0..1 */
@@ -139,7 +198,6 @@ struct hostapd_hw_modes {
 #define WPA_SCAN_NOISE_INVALID         BIT(1)
 #define WPA_SCAN_LEVEL_INVALID         BIT(2)
 #define WPA_SCAN_LEVEL_DBM             BIT(3)
-#define WPA_SCAN_AUTHENTICATED         BIT(4)
 #define WPA_SCAN_ASSOCIATED            BIT(5)
 
 /**
@@ -155,6 +213,9 @@ struct hostapd_hw_modes {
  * @tsf: Timestamp
  * @age: Age of the information in milliseconds (i.e., how many milliseconds
  * ago the last Beacon or Probe Response frame was received)
+ * @est_throughput: Estimated throughput in kbps (this is calculated during
+ * scan result processing if left zero by the driver wrapper)
+ * @snr: Signal-to-noise ratio in dB (calculated during scan result processing)
  * @ie_len: length of the following IE field in octets
  * @beacon_ie_len: length of the following Beacon IE field in octets
  *
@@ -166,6 +227,11 @@ struct hostapd_hw_modes {
  * constructed of the IEs that are available. This field will also need to
  * include SSID in IE format. All drivers are encouraged to be extended to
  * report all IEs to make it easier to support future additions.
+ *
+ * This structure data is followed by ie_len octets of IEs from Probe Response
+ * frame (or if the driver does not indicate source of IEs, these may also be
+ * from Beacon frame). After the first set of IEs, another set of IEs may follow
+ * (with beacon_ie_len octets of data) if the driver provides both IE sets.
  */
 struct wpa_scan_res {
        unsigned int flags;
@@ -178,15 +244,11 @@ struct wpa_scan_res {
        int level;
        u64 tsf;
        unsigned int age;
+       unsigned int est_throughput;
+       int snr;
        size_t ie_len;
        size_t beacon_ie_len;
-       /*
-        * Followed by ie_len octets of IEs from Probe Response frame (or if
-        * the driver does not indicate source of IEs, these may also be from
-        * Beacon frame). After the first set of IEs, another set of IEs may
-        * follow (with beacon_ie_len octets of data) if the driver provides
-        * both IE sets.
-        */
+       /* Followed by ie_len + beacon_ie_len octets of IE data */
 };
 
 /**
@@ -198,7 +260,7 @@ struct wpa_scan_res {
 struct wpa_scan_results {
        struct wpa_scan_res **res;
        size_t num;
-       struct os_time fetch_time;
+       struct os_reltime fetch_time;
 };
 
 /**
@@ -304,7 +366,51 @@ struct wpa_driver_scan_params {
         * Mbps from the support rates element(s) in the Probe Request frames
         * and not to transmit the frames at any of those rates.
         */
-       u8 p2p_probe;
+       unsigned int p2p_probe:1;
+
+       /**
+        * only_new_results - Request driver to report only new results
+        *
+        * This is used to request the driver to report only BSSes that have
+        * been detected after this scan request has been started, i.e., to
+        * flush old cached BSS entries.
+        */
+       unsigned int only_new_results:1;
+
+       /**
+        * low_priority - Requests driver to use a lower scan priority
+        *
+        * This is used to request the driver to use a lower scan priority
+        * if it supports such a thing.
+        */
+       unsigned int low_priority:1;
+
+       /**
+        * mac_addr_rand - Requests driver to randomize MAC address
+        */
+       unsigned int mac_addr_rand:1;
+
+       /**
+        * mac_addr - MAC address used with randomization. The address cannot be
+        * a multicast one, i.e., bit 0 of byte 0 should not be set.
+        */
+       const u8 *mac_addr;
+
+       /**
+        * mac_addr_mask - MAC address mask used with randomization.
+        *
+        * Bits that are 0 in the mask should be randomized. Bits that are 1 in
+        * the mask should be taken as is from mac_addr. The mask should not
+        * allow the generation of a multicast address, i.e., bit 0 of byte 0
+        * must be set.
+        */
+       const u8 *mac_addr_mask;
+
+       /*
+        * NOTE: Whenever adding new parameters here, please make sure
+        * wpa_scan_clone_params() and wpa_scan_free_params() get updated with
+        * matching changes.
+        */
 };
 
 /**
@@ -329,16 +435,96 @@ struct wpa_driver_auth_params {
         */
        int p2p;
 
+       /**
+        * sae_data - SAE elements for Authentication frame
+        *
+        * This buffer starts with the Authentication transaction sequence
+        * number field. If SAE is not used, this pointer is %NULL.
+        */
        const u8 *sae_data;
-       size_t sae_data_len;
 
+       /**
+        * sae_data_len - Length of sae_data buffer in octets
+        */
+       size_t sae_data_len;
 };
 
+/**
+ * enum wps_mode - WPS mode
+ */
 enum wps_mode {
-       WPS_MODE_NONE /* no WPS provisioning being used */,
-       WPS_MODE_OPEN /* WPS provisioning with AP that is in open mode */,
-       WPS_MODE_PRIVACY /* WPS provisioning with AP that is using protection
-                         */
+       /**
+        * WPS_MODE_NONE - No WPS provisioning being used
+        */
+       WPS_MODE_NONE,
+
+       /**
+        * WPS_MODE_OPEN - WPS provisioning with AP that is in open mode
+        */
+       WPS_MODE_OPEN,
+
+       /**
+        * WPS_MODE_PRIVACY - WPS provisioning with AP that is using protection
+        */
+       WPS_MODE_PRIVACY
+};
+
+/**
+ * struct hostapd_freq_params - Channel parameters
+ */
+struct hostapd_freq_params {
+       /**
+        * mode - Mode/band (HOSTAPD_MODE_IEEE80211A, ..)
+        */
+       enum hostapd_hw_mode mode;
+
+       /**
+        * freq - Primary channel center frequency in MHz
+        */
+       int freq;
+
+       /**
+        * channel - Channel number
+        */
+       int channel;
+
+       /**
+        * ht_enabled - Whether HT is enabled
+        */
+       int ht_enabled;
+
+       /**
+        * sec_channel_offset - Secondary channel offset for HT40
+        *
+        * 0 = HT40 disabled,
+        * -1 = HT40 enabled, secondary channel below primary,
+        * 1 = HT40 enabled, secondary channel above primary
+        */
+       int sec_channel_offset;
+
+       /**
+        * vht_enabled - Whether VHT is enabled
+        */
+       int vht_enabled;
+
+       /**
+        * center_freq1 - Segment 0 center frequency in MHz
+        *
+        * Valid for both HT and VHT.
+        */
+       int center_freq1;
+
+       /**
+        * center_freq2 - Segment 1 center frequency in MHz
+        *
+        * Non-zero only for bandwidth 80 and an 80+80 channel
+        */
+       int center_freq2;
+
+       /**
+        * bandwidth - Channel bandwidth in MHz (20, 40, 80, 160)
+        */
+       int bandwidth;
 };
 
 /**
@@ -353,6 +539,16 @@ struct wpa_driver_associate_params {
        const u8 *bssid;
 
        /**
+        * bssid_hint - BSSID of a proposed AP
+        *
+        * This indicates which BSS has been found a suitable candidate for
+        * initial association for drivers that use driver/firmwate-based BSS
+        * selection. Unlike the @bssid parameter, @bssid_hint does not limit
+        * the driver from selecting other BSSes in the ESS.
+        */
+       const u8 *bssid_hint;
+
+       /**
         * ssid - The selected SSID
         */
        const u8 *ssid;
@@ -363,11 +559,19 @@ struct wpa_driver_associate_params {
        size_t ssid_len;
 
        /**
-        * freq - Frequency of the channel the selected AP is using
-        * Frequency that the selected AP is using (in MHz as
-        * reported in the scan results)
+        * freq - channel parameters
         */
-       int freq;
+       struct hostapd_freq_params freq;
+
+       /**
+        * freq_hint - Frequency of the channel the proposed AP is using
+        *
+        * This provides a channel on which a suitable BSS has been found as a
+        * hint for the driver. Unlike the @freq parameter, @freq_hint does not
+        * limit the driver from selecting other channels for
+        * driver/firmware-based BSS selection.
+        */
+       int freq_hint;
 
        /**
         * bg_scan_period - Background scan period in seconds, 0 to disable
@@ -377,6 +581,11 @@ struct wpa_driver_associate_params {
        int bg_scan_period;
 
        /**
+        * beacon_int - Beacon interval for IBSS or 0 to use driver default
+        */
+       int beacon_int;
+
+       /**
         * wpa_ie - WPA information element for (Re)Association Request
         * WPA information element to be included in (Re)Association
         * Request (including information element id and length). Use
@@ -407,25 +616,25 @@ struct wpa_driver_associate_params {
        unsigned int wpa_proto;
 
        /**
-        * pairwise_suite - Selected pairwise cipher suite
+        * pairwise_suite - Selected pairwise cipher suite (WPA_CIPHER_*)
         *
         * This is usually ignored if @wpa_ie is used.
         */
-       enum wpa_cipher pairwise_suite;
+       unsigned int pairwise_suite;
 
        /**
-        * group_suite - Selected group cipher suite
+        * group_suite - Selected group cipher suite (WPA_CIPHER_*)
         *
         * This is usually ignored if @wpa_ie is used.
         */
-       enum wpa_cipher group_suite;
+       unsigned int group_suite;
 
        /**
-        * key_mgmt_suite - Selected key management suite
+        * key_mgmt_suite - Selected key management suite (WPA_KEY_MGMT_*)
         *
         * This is usually ignored if @wpa_ie is used.
         */
-       enum wpa_key_mgmt key_mgmt_suite;
+       unsigned int key_mgmt_suite;
 
        /**
         * auth_alg - Allowed authentication algorithms
@@ -563,17 +772,34 @@ struct wpa_driver_associate_params {
        int fixed_bssid;
 
        /**
+        * fixed_freq - Fix control channel in IBSS mode
+        * 0 = don't fix control channel (default)
+        * 1 = fix control channel; this prevents IBSS merging with another
+        *      channel
+        */
+       int fixed_freq;
+
+       /**
         * disable_ht - Disable HT (IEEE 802.11n) for this connection
         */
        int disable_ht;
 
        /**
-        * HT Capabilities over-rides. Only bits set in the mask will be used,
-        * and not all values are used by the kernel anyway. Currently, MCS,
-        * MPDU and MSDU fields are used.
+        * htcaps - HT Capabilities over-rides
+        *
+        * Only bits set in the mask will be used, and not all values are used
+        * by the kernel anyway. Currently, MCS, MPDU and MSDU fields are used.
+        *
+        * Pointer to struct ieee80211_ht_capabilities.
+        */
+       const u8 *htcaps;
+
+       /**
+        * htcaps_mask - HT Capabilities over-rides mask
+        *
+        * Pointer to struct ieee80211_ht_capabilities.
         */
-       const u8 *htcaps;       /* struct ieee80211_ht_capabilities * */
-       const u8 *htcaps_mask;  /* struct ieee80211_ht_capabilities * */
+       const u8 *htcaps_mask;
 
 #ifdef CONFIG_VHT_OVERRIDES
        /**
@@ -587,6 +813,20 @@ struct wpa_driver_associate_params {
        const struct ieee80211_vht_capabilities *vhtcaps;
        const struct ieee80211_vht_capabilities *vhtcaps_mask;
 #endif /* CONFIG_VHT_OVERRIDES */
+
+       /**
+        * req_key_mgmt_offload - Request key management offload for connection
+        *
+        * Request key management offload for this connection if the device
+        * supports it.
+        */
+       int req_key_mgmt_offload;
+
+       /**
+        * Flag for indicating whether this association includes support for
+        * RRM (Radio Resource Measurements)
+        */
+       int rrm_used;
 };
 
 enum hide_ssid {
@@ -595,11 +835,21 @@ enum hide_ssid {
        HIDDEN_SSID_ZERO_CONTENTS
 };
 
+struct wowlan_triggers {
+       u8 any;
+       u8 disconnect;
+       u8 magic_pkt;
+       u8 gtk_rekey_failure;
+       u8 eap_identity_req;
+       u8 four_way_handshake;
+       u8 rfkill_release;
+};
+
 struct wpa_driver_ap_params {
        /**
         * head - Beacon head from IEEE 802.11 header to IEs before TIM IE
         */
-       const u8 *head;
+       u8 *head;
 
        /**
         * head_len - Length of the head buffer in octets
@@ -609,7 +859,7 @@ struct wpa_driver_ap_params {
        /**
         * tail - Beacon tail following TIM IE
         */
-       const u8 *tail;
+       u8 *tail;
 
        /**
         * tail_len - Length of the tail buffer in octets
@@ -640,7 +890,7 @@ struct wpa_driver_ap_params {
         * This is used by drivers that reply to Probe Requests internally in
         * AP mode and require the full Probe Response template.
         */
-       const u8 *proberesp;
+       u8 *proberesp;
 
        /**
         * proberesp_len - Length of the proberesp buffer in octets
@@ -773,9 +1023,64 @@ struct wpa_driver_ap_params {
        int ap_max_inactivity;
 
        /**
+        * ctwindow - Client Traffic Window (in TUs)
+        */
+       u8 p2p_go_ctwindow;
+
+       /**
+        * smps_mode - SMPS mode
+        *
+        * SMPS mode to be used by the AP, specified as the relevant bits of
+        * ht_capab (i.e. HT_CAP_INFO_SMPS_*).
+        */
+       unsigned int smps_mode;
+
+       /**
         * disable_dgaf - Whether group-addressed frames are disabled
         */
        int disable_dgaf;
+
+       /**
+        * osen - Whether OSEN security is enabled
+        */
+       int osen;
+
+       /**
+        * freq - Channel parameters for dynamic bandwidth changes
+        */
+       struct hostapd_freq_params *freq;
+
+       /**
+        * reenable - Whether this is to re-enable beaconing
+        */
+       int reenable;
+};
+
+struct wpa_driver_mesh_bss_params {
+#define WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS  0x00000001
+       /*
+        * TODO: Other mesh configuration parameters would go here.
+        * See NL80211_MESHCONF_* for all the mesh config parameters.
+        */
+       unsigned int flags;
+       int peer_link_timeout;
+};
+
+struct wpa_driver_mesh_join_params {
+       const u8 *meshid;
+       int meshid_len;
+       const int *basic_rates;
+       const u8 *ies;
+       int ie_len;
+       struct hostapd_freq_params freq;
+       int beacon_int;
+       int max_peer_links;
+       struct wpa_driver_mesh_bss_params conf;
+#define WPA_DRIVER_MESH_FLAG_USER_MPM  0x00000001
+#define WPA_DRIVER_MESH_FLAG_DRIVER_MPM        0x00000002
+#define WPA_DRIVER_MESH_FLAG_SAE_AUTH  0x00000004
+#define WPA_DRIVER_MESH_FLAG_AMPE      0x00000008
+       unsigned int flags;
 };
 
 /**
@@ -790,6 +1095,9 @@ struct wpa_driver_capa {
 #define WPA_DRIVER_CAPA_KEY_MGMT_FT            0x00000020
 #define WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK                0x00000040
 #define WPA_DRIVER_CAPA_KEY_MGMT_WAPI_PSK      0x00000080
+#define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B       0x00000100
+#define WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192   0x00000200
+       /** Bitfield of supported key management suites */
        unsigned int key_mgmt;
 
 #define WPA_DRIVER_CAPA_ENC_WEP40      0x00000001
@@ -798,89 +1106,132 @@ struct wpa_driver_capa {
 #define WPA_DRIVER_CAPA_ENC_CCMP       0x00000008
 #define WPA_DRIVER_CAPA_ENC_WEP128     0x00000010
 #define WPA_DRIVER_CAPA_ENC_GCMP       0x00000020
+#define WPA_DRIVER_CAPA_ENC_GCMP_256   0x00000040
+#define WPA_DRIVER_CAPA_ENC_CCMP_256   0x00000080
+#define WPA_DRIVER_CAPA_ENC_BIP                0x00000100
+#define WPA_DRIVER_CAPA_ENC_BIP_GMAC_128       0x00000200
+#define WPA_DRIVER_CAPA_ENC_BIP_GMAC_256       0x00000400
+#define WPA_DRIVER_CAPA_ENC_BIP_CMAC_256       0x00000800
+#define WPA_DRIVER_CAPA_ENC_GTK_NOT_USED       0x00001000
+       /** Bitfield of supported cipher suites */
        unsigned int enc;
 
 #define WPA_DRIVER_AUTH_OPEN           0x00000001
 #define WPA_DRIVER_AUTH_SHARED         0x00000002
 #define WPA_DRIVER_AUTH_LEAP           0x00000004
+       /** Bitfield of supported IEEE 802.11 authentication algorithms */
        unsigned int auth;
 
-/* Driver generated WPA/RSN IE */
+/** Driver generated WPA/RSN IE */
 #define WPA_DRIVER_FLAGS_DRIVER_IE     0x00000001
-/* Driver needs static WEP key setup after association command */
+/** Driver needs static WEP key setup after association command */
 #define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002
-/* unused: 0x00000004 */
-/* Driver takes care of RSN 4-way handshake internally; PMK is configured with
+/** Driver takes care of all DFS operations */
+#define WPA_DRIVER_FLAGS_DFS_OFFLOAD                   0x00000004
+/** Driver takes care of RSN 4-way handshake internally; PMK is configured with
  * struct wpa_driver_ops::set_key using alg = WPA_ALG_PMK */
 #define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE 0x00000008
+/** Driver is for a wired Ethernet interface */
 #define WPA_DRIVER_FLAGS_WIRED         0x00000010
-/* Driver provides separate commands for authentication and association (SME in
+/** Driver provides separate commands for authentication and association (SME in
  * wpa_supplicant). */
 #define WPA_DRIVER_FLAGS_SME           0x00000020
-/* Driver supports AP mode */
+/** Driver supports AP mode */
 #define WPA_DRIVER_FLAGS_AP            0x00000040
-/* Driver needs static WEP key setup after association has been completed */
+/** Driver needs static WEP key setup after association has been completed */
 #define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE     0x00000080
-/* Driver takes care of P2P management operations */
-#define WPA_DRIVER_FLAGS_P2P_MGMT      0x00000100
-/* Driver supports concurrent P2P operations */
+/** Driver supports dynamic HT 20/40 MHz channel changes during BSS lifetime */
+#define WPA_DRIVER_FLAGS_HT_2040_COEX                  0x00000100
+/** Driver supports concurrent P2P operations */
 #define WPA_DRIVER_FLAGS_P2P_CONCURRENT        0x00000200
-/*
+/**
  * Driver uses the initial interface as a dedicated management interface, i.e.,
  * it cannot be used for P2P group operations or non-P2P purposes.
  */
 #define WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE       0x00000400
-/* This interface is P2P capable (P2P GO or P2P Client) */
+/** This interface is P2P capable (P2P GO or P2P Client) */
 #define WPA_DRIVER_FLAGS_P2P_CAPABLE   0x00000800
-/* Driver supports concurrent operations on multiple channels */
-#define WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT      0x00001000
-/*
+/** Driver supports station and key removal when stopping an AP */
+#define WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT           0x00001000
+/**
  * Driver uses the initial interface for P2P management interface and non-P2P
  * purposes (e.g., connect to infra AP), but this interface cannot be used for
  * P2P group operations.
  */
 #define WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P          0x00002000
-/*
+/**
  * Driver is known to use sane error codes, i.e., when it indicates that
  * something (e.g., association) fails, there was indeed a failure and the
  * operation does not end up getting completed successfully later.
  */
 #define WPA_DRIVER_FLAGS_SANE_ERROR_CODES              0x00004000
-/* Driver supports off-channel TX */
+/** Driver supports off-channel TX */
 #define WPA_DRIVER_FLAGS_OFFCHANNEL_TX                 0x00008000
-/* Driver indicates TX status events for EAPOL Data frames */
+/** Driver indicates TX status events for EAPOL Data frames */
 #define WPA_DRIVER_FLAGS_EAPOL_TX_STATUS               0x00010000
-/* Driver indicates TX status events for Deauth/Disassoc frames */
+/** Driver indicates TX status events for Deauth/Disassoc frames */
 #define WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS              0x00020000
-/* Driver supports roaming (BSS selection) in firmware */
+/** Driver supports roaming (BSS selection) in firmware */
 #define WPA_DRIVER_FLAGS_BSS_SELECTION                 0x00040000
-/* Driver supports operating as a TDLS peer */
+/** Driver supports operating as a TDLS peer */
 #define WPA_DRIVER_FLAGS_TDLS_SUPPORT                  0x00080000
-/* Driver requires external TDLS setup/teardown/discovery */
+/** Driver requires external TDLS setup/teardown/discovery */
 #define WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP           0x00100000
-/* Driver indicates support for Probe Response offloading in AP mode */
+/** Driver indicates support for Probe Response offloading in AP mode */
 #define WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD            0x00200000
-/* Driver supports U-APSD in AP mode */
+/** Driver supports U-APSD in AP mode */
 #define WPA_DRIVER_FLAGS_AP_UAPSD                      0x00400000
-/* Driver supports inactivity timer in AP mode */
+/** Driver supports inactivity timer in AP mode */
 #define WPA_DRIVER_FLAGS_INACTIVITY_TIMER              0x00800000
-/* Driver expects user space implementation of MLME in AP mode */
+/** Driver expects user space implementation of MLME in AP mode */
 #define WPA_DRIVER_FLAGS_AP_MLME                       0x01000000
-/* Driver supports SAE with user space SME */
+/** Driver supports SAE with user space SME */
 #define WPA_DRIVER_FLAGS_SAE                           0x02000000
-/* Driver makes use of OBSS scan mechanism in wpa_supplicant */
+/** Driver makes use of OBSS scan mechanism in wpa_supplicant */
 #define WPA_DRIVER_FLAGS_OBSS_SCAN                     0x04000000
-/* Driver supports IBSS (Ad-hoc) mode */
+/** Driver supports IBSS (Ad-hoc) mode */
 #define WPA_DRIVER_FLAGS_IBSS                          0x08000000
-/* Driver supports radar detection */
+/** Driver supports radar detection */
 #define WPA_DRIVER_FLAGS_RADAR                         0x10000000
-/* Driver supports a dedicated interface for P2P Device */
+/** Driver supports a dedicated interface for P2P Device */
 #define WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE          0x20000000
-       unsigned int flags;
-
+/** Driver supports QoS Mapping */
+#define WPA_DRIVER_FLAGS_QOS_MAPPING                   0x40000000
+/** Driver supports CSA in AP mode */
+#define WPA_DRIVER_FLAGS_AP_CSA                                0x80000000
+/** Driver supports mesh */
+#define WPA_DRIVER_FLAGS_MESH                  0x0000000100000000ULL
+/** Driver support ACS offload */
+#define WPA_DRIVER_FLAGS_ACS_OFFLOAD           0x0000000200000000ULL
+/** Driver supports key management offload */
+#define WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD      0x0000000400000000ULL
+/** Driver supports TDLS channel switching */
+#define WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH   0x0000000800000000ULL
+/** Driver supports IBSS with HT datarates */
+#define WPA_DRIVER_FLAGS_HT_IBSS               0x0000001000000000ULL
+/** Driver supports IBSS with VHT datarates */
+#define WPA_DRIVER_FLAGS_VHT_IBSS              0x0000002000000000ULL
+       u64 flags;
+
+#define WPA_DRIVER_SMPS_MODE_STATIC                    0x00000001
+#define WPA_DRIVER_SMPS_MODE_DYNAMIC                   0x00000002
+       unsigned int smps_modes;
+
+       unsigned int wmm_ac_supported:1;
+
+       unsigned int mac_addr_rand_scan_supported:1;
+       unsigned int mac_addr_rand_sched_scan_supported:1;
+
+       /** Maximum number of supported active probe SSIDs */
        int max_scan_ssids;
+
+       /** Maximum number of supported active probe SSIDs for sched_scan */
        int max_sched_scan_ssids;
+
+       /** Whether sched_scan (offloaded scanning) is supported */
        int sched_scan_supported;
+
+       /** Maximum number of supported match sets for sched_scan */
        int max_match_sets;
 
        /**
@@ -898,19 +1249,24 @@ struct wpa_driver_capa {
         * probe_resp_offloads - Bitmap of supported protocols by the driver
         * for Probe Response offloading.
         */
-/* Driver Probe Response offloading support for WPS ver. 1 */
+/** Driver Probe Response offloading support for WPS ver. 1 */
 #define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS              0x00000001
-/* Driver Probe Response offloading support for WPS ver. 2 */
+/** Driver Probe Response offloading support for WPS ver. 2 */
 #define WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2             0x00000002
-/* Driver Probe Response offloading support for P2P */
+/** Driver Probe Response offloading support for P2P */
 #define WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P              0x00000004
-/* Driver Probe Response offloading support for IEEE 802.11u (Interworking) */
+/** Driver Probe Response offloading support for IEEE 802.11u (Interworking) */
 #define WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING     0x00000008
        unsigned int probe_resp_offloads;
 
        unsigned int max_acl_mac_addrs;
 
        /**
+        * Number of supported concurrent channels
+        */
+       unsigned int num_multichan_concurrent;
+
+       /**
         * extended_capa - extended capabilities in driver/device
         *
         * Must be allocated and freed by driver and the pointers must be
@@ -918,6 +1274,26 @@ struct wpa_driver_capa {
         */
        const u8 *extended_capa, *extended_capa_mask;
        unsigned int extended_capa_len;
+
+       struct wowlan_triggers wowlan_triggers;
+
+/** Driver adds the DS Params Set IE in Probe Request frames */
+#define WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES     0x00000001
+/** Driver adds the WFA TPC IE in Probe Request frames */
+#define WPA_DRIVER_FLAGS_WFA_TPC_IE_IN_PROBES          0x00000002
+/** Driver handles quiet period requests */
+#define WPA_DRIVER_FLAGS_QUIET                         0x00000004
+/**
+ * Driver is capable of inserting the current TX power value into the body of
+ * transmitted frames.
+ * Background: Some Action frames include a TPC Report IE. This IE contains a
+ * TX power field, which has to be updated by lower layers. One such Action
+ * frame is Link Measurement Report (part of RRM). Another is TPC Report (part
+ * of spectrum management). Note that this insertion takes place at a fixed
+ * offset, namely the 6th byte in the Action frame body.
+ */
+#define WPA_DRIVER_FLAGS_TX_POWER_INSERTION            0x00000008
+       u32 rrm_flags;
 };
 
 
@@ -944,30 +1320,21 @@ struct hostapd_sta_add_params {
        u16 listen_interval;
        const struct ieee80211_ht_capabilities *ht_capabilities;
        const struct ieee80211_vht_capabilities *vht_capabilities;
+       int vht_opmode_enabled;
+       u8 vht_opmode;
        u32 flags; /* bitmask of WPA_STA_* flags */
+       u32 flags_mask; /* unset bits in flags */
+#ifdef CONFIG_MESH
+       enum mesh_plink_state plink_state;
+#endif /* CONFIG_MESH */
        int set; /* Set STA parameters instead of add */
        u8 qosinfo;
        const u8 *ext_capab;
        size_t ext_capab_len;
-};
-
-struct hostapd_freq_params {
-       int mode;
-       int freq;
-       int channel;
-       /* for HT */
-       int ht_enabled;
-       int sec_channel_offset; /* 0 = HT40 disabled, -1 = HT40 enabled,
-                                * secondary channel below primary, 1 = HT40
-                                * enabled, secondary channel above primary */
-
-       /* for VHT */
-       int vht_enabled;
-
-       /* valid for both HT and VHT, center_freq2 is non-zero
-        * only for bandwidth 80 and an 80+80 channel */
-       int center_freq1, center_freq2;
-       int bandwidth;
+       const u8 *supp_channels;
+       size_t supp_channels_len;
+       const u8 *supp_oper_classes;
+       size_t supp_oper_classes_len;
 };
 
 struct mac_address {
@@ -1021,16 +1388,19 @@ enum wpa_driver_if_type {
         * WPA_IF_P2P_DEVICE - P2P Device interface is used to indentify the
         * abstracted P2P Device function in the driver
         */
-       WPA_IF_P2P_DEVICE
+       WPA_IF_P2P_DEVICE,
+
+       /*
+        * WPA_IF_MESH - Mesh interface
+        */
+       WPA_IF_MESH,
 };
 
 struct wpa_init_params {
        void *global_priv;
        const u8 *bssid;
        const char *ifname;
-       const u8 *ssid;
-       size_t ssid_len;
-       const char *test_socket;
+       const char *driver_params;
        int use_pae_group_addr;
        char **bridge;
        size_t num_bridge;
@@ -1059,17 +1429,7 @@ struct wpa_bss_params {
 #define WPA_STA_SHORT_PREAMBLE BIT(2)
 #define WPA_STA_MFP BIT(3)
 #define WPA_STA_TDLS_PEER BIT(4)
-
-/**
- * struct p2p_params - P2P parameters for driver-based P2P management
- */
-struct p2p_params {
-       const char *dev_name;
-       u8 pri_dev_type[8];
-#define DRV_MAX_SEC_DEV_TYPES 5
-       u8 sec_dev_type[DRV_MAX_SEC_DEV_TYPES][8];
-       size_t num_sec_dev_types;
-};
+#define WPA_STA_AUTHENTICATED BIT(5)
 
 enum tdls_oper {
        TDLS_DISCOVERY_REQ,
@@ -1125,6 +1485,112 @@ struct wpa_signal_info {
 };
 
 /**
+ * struct beacon_data - Beacon data
+ * @head: Head portion of Beacon frame (before TIM IE)
+ * @tail: Tail portion of Beacon frame (after TIM IE)
+ * @beacon_ies: Extra information element(s) to add into Beacon frames or %NULL
+ * @proberesp_ies: Extra information element(s) to add into Probe Response
+ *     frames or %NULL
+ * @assocresp_ies: Extra information element(s) to add into (Re)Association
+ *     Response frames or %NULL
+ * @probe_resp: Probe Response frame template
+ * @head_len: Length of @head
+ * @tail_len: Length of @tail
+ * @beacon_ies_len: Length of beacon_ies in octets
+ * @proberesp_ies_len: Length of proberesp_ies in octets
+ * @proberesp_ies_len: Length of proberesp_ies in octets
+ * @probe_resp_len: Length of probe response template (@probe_resp)
+ */
+struct beacon_data {
+       u8 *head, *tail;
+       u8 *beacon_ies;
+       u8 *proberesp_ies;
+       u8 *assocresp_ies;
+       u8 *probe_resp;
+
+       size_t head_len, tail_len;
+       size_t beacon_ies_len;
+       size_t proberesp_ies_len;
+       size_t assocresp_ies_len;
+       size_t probe_resp_len;
+};
+
+/**
+ * struct csa_settings - Settings for channel switch command
+ * @cs_count: Count in Beacon frames (TBTT) to perform the switch
+ * @block_tx: 1 - block transmission for CSA period
+ * @freq_params: Next channel frequency parameter
+ * @beacon_csa: Beacon/probe resp/asooc resp info for CSA period
+ * @beacon_after: Next beacon/probe resp/asooc resp info
+ * @counter_offset_beacon: Offset to the count field in beacon's tail
+ * @counter_offset_presp: Offset to the count field in probe resp.
+ */
+struct csa_settings {
+       u8 cs_count;
+       u8 block_tx;
+
+       struct hostapd_freq_params freq_params;
+       struct beacon_data beacon_csa;
+       struct beacon_data beacon_after;
+
+       u16 counter_offset_beacon;
+       u16 counter_offset_presp;
+};
+
+/* TDLS peer capabilities for send_tdls_mgmt() */
+enum tdls_peer_capability {
+       TDLS_PEER_HT = BIT(0),
+       TDLS_PEER_VHT = BIT(1),
+       TDLS_PEER_WMM = BIT(2),
+};
+
+/* valid info in the wmm_params struct */
+enum wmm_params_valid_info {
+       WMM_PARAMS_UAPSD_QUEUES_INFO = BIT(0),
+};
+
+/**
+ * struct wmm_params - WMM parameterss configured for this association
+ * @info_bitmap: Bitmap of valid wmm_params info; indicates what fields
+ *     of the struct contain valid information.
+ * @uapsd_queues: Bitmap of ACs configured for uapsd (valid only if
+ *     %WMM_PARAMS_UAPSD_QUEUES_INFO is set)
+ */
+struct wmm_params {
+       u8 info_bitmap;
+       u8 uapsd_queues;
+};
+
+#ifdef CONFIG_MACSEC
+struct macsec_init_params {
+       Boolean always_include_sci;
+       Boolean use_es;
+       Boolean use_scb;
+};
+#endif /* CONFIG_MACSEC */
+
+enum drv_br_port_attr {
+       DRV_BR_PORT_ATTR_PROXYARP,
+       DRV_BR_PORT_ATTR_HAIRPIN_MODE,
+};
+
+enum drv_br_net_param {
+       DRV_BR_NET_PARAM_GARP_ACCEPT,
+};
+
+struct drv_acs_params {
+       /* Selected mode (HOSTAPD_MODE_*) */
+       enum hostapd_hw_mode hw_mode;
+
+       /* Indicates whether HT is enabled */
+       int ht_enabled;
+
+       /* Indicates whether HT40 is enabled */
+       int ht40_enabled;
+};
+
+
+/**
  * struct wpa_driver_ops - Driver interface API definition
  *
  * This structure defines the API that each driver interface needs to implement
@@ -1173,7 +1639,9 @@ struct wpa_driver_ops {
         * @priv: private driver interface data
         * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP,
         *      %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK,
-        *      %WPA_ALG_GCMP);
+        *      %WPA_ALG_GCMP, %WPA_ALG_GCMP_256, %WPA_ALG_CCMP_256,
+        *      %WPA_ALG_BIP_GMAC_128, %WPA_ALG_BIP_GMAC_256,
+        *      %WPA_ALG_BIP_CMAC_256);
         *      %WPA_ALG_NONE clears the key.
         * @addr: Address of the peer STA (BSSID of the current AP when setting
         *      pairwise key in station mode), ff:ff:ff:ff:ff:ff for
@@ -1291,17 +1759,6 @@ struct wpa_driver_ops {
        int (*deauthenticate)(void *priv, const u8 *addr, int reason_code);
 
        /**
-        * disassociate - Request driver to disassociate
-        * @priv: private driver interface data
-        * @addr: peer address (BSSID of the AP)
-        * @reason_code: 16-bit reason code to be sent in the disassociation
-        *      frame
-        *
-        * Returns: 0 on success, -1 on failure
-        */
-       int (*disassociate)(void *priv, const u8 *addr, int reason_code);
-
-       /**
         * associate - Request driver to associate
         * @priv: private driver interface data
         * @params: association parameters
@@ -1418,27 +1875,6 @@ struct wpa_driver_ops {
        const u8 * (*get_mac_addr)(void *priv);
 
        /**
-        * send_eapol - Optional function for sending EAPOL packets
-        * @priv: private driver interface data
-        * @dest: Destination MAC address
-        * @proto: Ethertype
-        * @data: EAPOL packet starting with IEEE 802.1X header
-        * @data_len: Size of the EAPOL packet
-        *
-        * Returns: 0 on success, -1 on failure
-        *
-        * This optional function can be used to override l2_packet operations
-        * with driver specific functionality. If this function pointer is set,
-        * l2_packet module is not used at all and the driver interface code is
-        * responsible for receiving and sending all EAPOL packets. The
-        * received EAPOL packets are sent to core code with EVENT_EAPOL_RX
-        * event. The driver interface is required to implement get_mac_addr()
-        * handler if send_eapol() is used.
-        */
-       int (*send_eapol)(void *priv, const u8 *dest, u16 proto,
-                         const u8 *data, size_t data_len);
-
-       /**
         * set_operstate - Sets device operating state to DORMANT or UP
         * @priv: private driver interface data
         * @state: 0 = dormant, 1 = up
@@ -1513,22 +1949,6 @@ struct wpa_driver_ops {
                             size_t ies_len);
 
        /**
-        * send_ft_action - Send FT Action frame (IEEE 802.11r)
-        * @priv: Private driver interface data
-        * @action: Action field value
-        * @target_ap: Target AP address
-        * @ies: FT IEs (MDIE, FTIE, ...) (FT Request action frame body)
-        * @ies_len: Length of FT IEs in bytes
-        * Returns: 0 on success, -1 on failure
-        *
-        * The supplicant uses this callback to request the driver to transmit
-        * an FT Action frame (action category 6) for over-the-DS fast BSS
-        * transition.
-        */
-       int (*send_ft_action)(void *priv, u8 action, const u8 *target_ap,
-                             const u8 *ies, size_t ies_len);
-
-       /**
         * get_scan_results2 - Fetch the latest scan results
         * @priv: private driver interface data
         *
@@ -1549,10 +1969,18 @@ struct wpa_driver_ops {
        int (*set_country)(void *priv, const char *alpha2);
 
        /**
-        * global_init - Global driver initialization
-        * Returns: Pointer to private data (global), %NULL on failure
-        *
-        * This optional function is called to initialize the driver wrapper
+        * get_country - Get country
+        * @priv: Private driver interface data
+        * @alpha2: Buffer for returning country code (at least 3 octets)
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*get_country)(void *priv, char *alpha2);
+
+       /**
+        * global_init - Global driver initialization
+        * Returns: Pointer to private data (global), %NULL on failure
+        *
+        * This optional function is called to initialize the driver wrapper
         * for global data, i.e., data that applies to all interfaces. If this
         * function is implemented, global_deinit() will also need to be
         * implemented to free the private data. The driver will also likely
@@ -1934,12 +2362,13 @@ struct wpa_driver_ops {
         *      (this may differ from the requested addr if the driver cannot
         *      change interface address)
         * @bridge: Bridge interface to use or %NULL if no bridge configured
+        * @use_existing: Whether to allow existing interface to be used
         * Returns: 0 on success, -1 on failure
         */
        int (*if_add)(void *priv, enum wpa_driver_if_type type,
                      const char *ifname, const u8 *addr, void *bss_ctx,
                      void **drv_priv, char *force_ifname, u8 *if_addr,
-                     const char *bridge);
+                     const char *bridge, int use_existing);
 
        /**
         * if_remove - Remove a virtual interface
@@ -2060,10 +2489,12 @@ struct wpa_driver_ops {
         * @val: 1 = bind to 4-address WDS; 0 = unbind
         * @bridge_ifname: Bridge interface to use for the WDS station or %NULL
         *      to indicate that bridge is not to be used
+        * @ifname_wds: Buffer to return the interface name for the new WDS
+        *      station or %NULL to indicate name is not returned.
         * Returns: 0 on success, -1 on failure
         */
        int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val,
-                          const char *bridge_ifname);
+                          const char *bridge_ifname, char *ifname_wds);
 
        /**
         * send_action - Transmit an Action frame
@@ -2114,7 +2545,7 @@ struct wpa_driver_ops {
         *
         * This command is used to request the driver to remain awake on the
         * specified channel for the specified duration and report received
-        * Action frames with EVENT_RX_ACTION events. Optionally, received
+        * Action frames with EVENT_RX_MGMT events. Optionally, received
         * Probe Request frames may also be requested to be reported by calling
         * probe_req_report(). These will be reported with EVENT_RX_PROBE_REQ.
         *
@@ -2165,8 +2596,9 @@ struct wpa_driver_ops {
         * Returns: 0 on success, -1 on failure (or if not supported)
         *
         * This optional function can be used to disable AP mode related
-        * configuration and change the driver mode to station mode to allow
-        * normal station operations like scanning to be completed.
+        * configuration. If the interface was not dynamically added,
+        * change the driver mode to station mode to allow normal station
+        * operations like scanning to be completed.
         */
        int (*deinit_ap)(void *priv);
 
@@ -2175,8 +2607,9 @@ struct wpa_driver_ops {
         * @priv: Private driver interface data
         * Returns: 0 on success, -1 on failure (or if not supported)
         *
-        * This optional function can be used to disable P2P client mode. It
-        * can be used to change the interface type back to station mode.
+        * This optional function can be used to disable P2P client mode. If the
+        * interface was not dynamically added, change the interface type back
+        * to station mode.
         */
        int (*deinit_p2p_cli)(void *priv);
 
@@ -2294,228 +2727,14 @@ struct wpa_driver_ops {
        const char * (*get_radio_name)(void *priv);
 
        /**
-        * p2p_find - Start P2P Device Discovery
-        * @priv: Private driver interface data
-        * @timeout: Timeout for find operation in seconds or 0 for no timeout
-        * @type: Device Discovery type (enum p2p_discovery_type)
-        * Returns: 0 on success, -1 on failure
-        *
-        * This function is only used if the driver implements P2P management,
-        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-        * struct wpa_driver_capa.
-        */
-       int (*p2p_find)(void *priv, unsigned int timeout, int type);
-
-       /**
-        * p2p_stop_find - Stop P2P Device Discovery
-        * @priv: Private driver interface data
-        * Returns: 0 on success, -1 on failure
-        *
-        * This function is only used if the driver implements P2P management,
-        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-        * struct wpa_driver_capa.
-        */
-       int (*p2p_stop_find)(void *priv);
-
-       /**
-        * p2p_listen - Start P2P Listen state for specified duration
-        * @priv: Private driver interface data
-        * @timeout: Listen state duration in milliseconds
-        * Returns: 0 on success, -1 on failure
-        *
-        * This function can be used to request the P2P module to keep the
-        * device discoverable on the listen channel for an extended set of
-        * time. At least in its current form, this is mainly used for testing
-        * purposes and may not be of much use for normal P2P operations.
-        *
-        * This function is only used if the driver implements P2P management,
-        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-        * struct wpa_driver_capa.
-        */
-       int (*p2p_listen)(void *priv, unsigned int timeout);
-
-       /**
-        * p2p_connect - Start P2P group formation (GO negotiation)
-        * @priv: Private driver interface data
-        * @peer_addr: MAC address of the peer P2P client
-        * @wps_method: enum p2p_wps_method value indicating config method
-        * @go_intent: Local GO intent value (1..15)
-        * @own_interface_addr: Intended interface address to use with the
-        *      group
-        * @force_freq: The only allowed channel frequency in MHz or 0
-        * @persistent_group: Whether to create persistent group
-        * Returns: 0 on success, -1 on failure
-        *
-        * This function is only used if the driver implements P2P management,
-        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-        * struct wpa_driver_capa.
-        */
-       int (*p2p_connect)(void *priv, const u8 *peer_addr, int wps_method,
-                          int go_intent, const u8 *own_interface_addr,
-                          unsigned int force_freq, int persistent_group);
-
-       /**
-        * wps_success_cb - Report successfully completed WPS provisioning
-        * @priv: Private driver interface data
-        * @peer_addr: Peer address
-        * Returns: 0 on success, -1 on failure
-        *
-        * This function is used to report successfully completed WPS
-        * provisioning during group formation in both GO/Registrar and
-        * client/Enrollee roles.
-        *
-        * This function is only used if the driver implements P2P management,
-        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-        * struct wpa_driver_capa.
-        */
-       int (*wps_success_cb)(void *priv, const u8 *peer_addr);
-
-       /**
-        * p2p_group_formation_failed - Report failed WPS provisioning
-        * @priv: Private driver interface data
-        * Returns: 0 on success, -1 on failure
-        *
-        * This function is used to report failed group formation. This can
-        * happen either due to failed WPS provisioning or due to 15 second
-        * timeout during the provisioning phase.
-        *
-        * This function is only used if the driver implements P2P management,
-        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-        * struct wpa_driver_capa.
-        */
-       int (*p2p_group_formation_failed)(void *priv);
-
-       /**
-        * p2p_set_params - Set P2P parameters
-        * @priv: Private driver interface data
-        * @params: P2P parameters
-        * Returns: 0 on success, -1 on failure
-        *
-        * This function is only used if the driver implements P2P management,
-        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-        * struct wpa_driver_capa.
-        */
-       int (*p2p_set_params)(void *priv, const struct p2p_params *params);
-
-       /**
-        * p2p_prov_disc_req - Send Provision Discovery Request
-        * @priv: Private driver interface data
-        * @peer_addr: MAC address of the peer P2P client
-        * @config_methods: WPS Config Methods value (only one bit set)
-        * Returns: 0 on success, -1 on failure
-        *
-        * This function can be used to request a discovered P2P peer to
-        * display a PIN (config_methods = WPS_CONFIG_DISPLAY) or be prepared
-        * to enter a PIN from us (config_methods = WPS_CONFIG_KEYPAD). The
-        * Provision Discovery Request frame is transmitted once immediately
-        * and if no response is received, the frame will be sent again
-        * whenever the target device is discovered during device dsicovery
-        * (start with a p2p_find() call). Response from the peer is indicated
-        * with the EVENT_P2P_PROV_DISC_RESPONSE event.
-        *
-        * This function is only used if the driver implements P2P management,
-        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-        * struct wpa_driver_capa.
-        */
-       int (*p2p_prov_disc_req)(void *priv, const u8 *peer_addr,
-                                u16 config_methods, int join);
-
-       /**
-        * p2p_sd_request - Schedule a service discovery query
-        * @priv: Private driver interface data
-        * @dst: Destination peer or %NULL to apply for all peers
-        * @tlvs: P2P Service Query TLV(s)
-        * Returns: Reference to the query or 0 on failure
-        *
-        * Response to the query is indicated with the
-        * EVENT_P2P_SD_RESPONSE driver event.
-        *
-        * This function is only used if the driver implements P2P management,
-        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-        * struct wpa_driver_capa.
-        */
-       u64 (*p2p_sd_request)(void *priv, const u8 *dst,
-                             const struct wpabuf *tlvs);
-
-       /**
-        * p2p_sd_cancel_request - Cancel a pending service discovery query
-        * @priv: Private driver interface data
-        * @req: Query reference from p2p_sd_request()
-        * Returns: 0 on success, -1 on failure
-        *
-        * This function is only used if the driver implements P2P management,
-        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-        * struct wpa_driver_capa.
-        */
-       int (*p2p_sd_cancel_request)(void *priv, u64 req);
-
-       /**
-        * p2p_sd_response - Send response to a service discovery query
-        * @priv: Private driver interface data
-        * @freq: Frequency from EVENT_P2P_SD_REQUEST event
-        * @dst: Destination address from EVENT_P2P_SD_REQUEST event
-        * @dialog_token: Dialog token from EVENT_P2P_SD_REQUEST event
-        * @resp_tlvs: P2P Service Response TLV(s)
-        * Returns: 0 on success, -1 on failure
-        *
-        * This function is called as a response to the request indicated with
-        * the EVENT_P2P_SD_REQUEST driver event.
-        *
-        * This function is only used if the driver implements P2P management,
-        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-        * struct wpa_driver_capa.
-        */
-       int (*p2p_sd_response)(void *priv, int freq, const u8 *dst,
-                              u8 dialog_token,
-                              const struct wpabuf *resp_tlvs);
-
-       /**
-        * p2p_service_update - Indicate a change in local services
-        * @priv: Private driver interface data
-        * Returns: 0 on success, -1 on failure
-        *
-        * This function needs to be called whenever there is a change in
-        * availability of the local services. This will increment the
-        * Service Update Indicator value which will be used in SD Request and
-        * Response frames.
-        *
-        * This function is only used if the driver implements P2P management,
-        * i.e., if it sets WPA_DRIVER_FLAGS_P2P_MGMT in
-        * struct wpa_driver_capa.
-        */
-       int (*p2p_service_update)(void *priv);
-
-       /**
-        * p2p_reject - Reject peer device (explicitly block connections)
-        * @priv: Private driver interface data
-        * @addr: MAC address of the peer
-        * Returns: 0 on success, -1 on failure
-        */
-       int (*p2p_reject)(void *priv, const u8 *addr);
-
-       /**
-        * p2p_invite - Invite a P2P Device into a group
-        * @priv: Private driver interface data
-        * @peer: Device Address of the peer P2P Device
-        * @role: Local role in the group
-        * @bssid: Group BSSID or %NULL if not known
-        * @ssid: Group SSID
-        * @ssid_len: Length of ssid in octets
-        * @go_dev_addr: Forced GO Device Address or %NULL if none
-        * @persistent_group: Whether this is to reinvoke a persistent group
-        * Returns: 0 on success, -1 on failure
-        */
-       int (*p2p_invite)(void *priv, const u8 *peer, int role,
-                         const u8 *bssid, const u8 *ssid, size_t ssid_len,
-                         const u8 *go_dev_addr, int persistent_group);
-
-       /**
         * send_tdls_mgmt - for sending TDLS management packets
         * @priv: private driver interface data
         * @dst: Destination (peer) MAC address
         * @action_code: TDLS action code for the mssage
         * @dialog_token: Dialog Token to use in the message (if needed)
         * @status_code: Status Code or Reason Code to use (if needed)
+        * @peer_capab: TDLS peer capability (TDLS_PEER_* bitfield)
+        * @initiator: Is the current end the TDLS link initiator
         * @buf: TDLS IEs to add to the message
         * @len: Length of buf in octets
         * Returns: 0 on success, negative (<0) on failure
@@ -2524,8 +2743,8 @@ struct wpa_driver_ops {
         * responsible for receiving and sending all TDLS packets.
         */
        int (*send_tdls_mgmt)(void *priv, const u8 *dst, u8 action_code,
-                             u8 dialog_token, u16 status_code,
-                             const u8 *buf, size_t len);
+                             u8 dialog_token, u16 status_code, u32 peer_capab,
+                             int initiator, const u8 *buf, size_t len);
 
        /**
         * tdls_oper - Ask the driver to perform high-level TDLS operations
@@ -2552,10 +2771,65 @@ struct wpa_driver_ops {
                        u8 *buf, u16 *buf_len);
 
        /**
+        * set_qos_map - Set QoS Map
+        * @priv: Private driver interface data
+        * @qos_map_set: QoS Map
+        * @qos_map_set_len: Length of QoS Map
+        */
+       int (*set_qos_map)(void *priv, const u8 *qos_map_set,
+                          u8 qos_map_set_len);
+
+       /**
+        * br_add_ip_neigh - Add a neigh to the bridge ip neigh table
+        * @priv: Private driver interface data
+        * @version: IP version of the IP address, 4 or 6
+        * @ipaddr: IP address for the neigh entry
+        * @prefixlen: IP address prefix length
+        * @addr: Corresponding MAC address
+        * Returns: 0 on success, negative (<0) on failure
+        */
+       int (*br_add_ip_neigh)(void *priv, u8 version, const u8 *ipaddr,
+                              int prefixlen, const u8 *addr);
+
+       /**
+        * br_delete_ip_neigh - Remove a neigh from the bridge ip neigh table
+        * @priv: Private driver interface data
+        * @version: IP version of the IP address, 4 or 6
+        * @ipaddr: IP address for the neigh entry
+        * Returns: 0 on success, negative (<0) on failure
+        */
+       int (*br_delete_ip_neigh)(void *priv, u8 version, const u8 *ipaddr);
+
+       /**
+        * br_port_set_attr - Set a bridge port attribute
+        * @attr: Bridge port attribute to set
+        * @val: Value to be set
+        * Returns: 0 on success, negative (<0) on failure
+        */
+       int (*br_port_set_attr)(void *priv, enum drv_br_port_attr attr,
+                               unsigned int val);
+
+       /**
+        * br_port_set_attr - Set a bridge network parameter
+        * @param: Bridge parameter to set
+        * @val: Value to be set
+        * Returns: 0 on success, negative (<0) on failure
+        */
+       int (*br_set_net_param)(void *priv, enum drv_br_net_param param,
+                               unsigned int val);
+
+       /**
+        * set_wowlan - Set wake-on-wireless triggers
+        * @priv: Private driver interface data
+        * @triggers: wowlan triggers
+        */
+       int (*set_wowlan)(void *priv, const struct wowlan_triggers *triggers);
+
+       /**
         * signal_poll - Get current connection information
         * @priv: Private driver interface data
         * @signal_info: Connection info structure
-         */
+        */
        int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info);
 
        /**
@@ -2572,18 +2846,57 @@ struct wpa_driver_ops {
         */
        int (*set_authmode)(void *priv, int authmode);
 
+#ifdef ANDROID
+       /**
+        * driver_cmd - Execute driver-specific command
+        * @priv: Private driver interface data
+        * @cmd: Command to execute
+        * @buf: Return buffer
+        * @buf_len: Buffer length
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*driver_cmd)(void *priv, char *cmd, char *buf, size_t buf_len);
+#endif /* ANDROID */
+
+       /**
+        * vendor_cmd - Execute vendor specific command
+        * @priv: Private driver interface data
+        * @vendor_id: Vendor id
+        * @subcmd: Vendor command id
+        * @data: Vendor command parameters (%NULL if no parameters)
+        * @data_len: Data length
+        * @buf: Return buffer (%NULL to ignore reply)
+        * Returns: 0 on success, negative (<0) on failure
+        *
+        * This function handles vendor specific commands that are passed to
+        * the driver/device. The command is identified by vendor id and
+        * command id. Parameters can be passed as argument to the command
+        * in the data buffer. Reply (if any) will be filled in the supplied
+        * return buffer.
+        *
+        * The exact driver behavior is driver interface and vendor specific. As
+        * an example, this will be converted to a vendor specific cfg80211
+        * command in case of the nl80211 driver interface.
+        */
+       int (*vendor_cmd)(void *priv, unsigned int vendor_id,
+                         unsigned int subcmd, const u8 *data, size_t data_len,
+                         struct wpabuf *buf);
+
        /**
         * set_rekey_info - Set rekey information
         * @priv: Private driver interface data
         * @kek: Current KEK
+        * @kek_len: KEK length in octets
         * @kck: Current KCK
+        * @kck_len: KCK length in octets
         * @replay_ctr: Current EAPOL-Key Replay Counter
         *
         * This optional function can be used to provide information for the
         * driver/firmware to process EAPOL-Key frames in Group Key Handshake
         * while the host (including wpa_supplicant) is sleeping.
         */
-       void (*set_rekey_info)(void *priv, const u8 *kek, const u8 *kck,
+       void (*set_rekey_info)(void *priv, const u8 *kek, size_t kek_len,
+                              const u8 *kck, size_t kck_len,
                               const u8 *replay_ctr);
 
        /**
@@ -2704,21 +3017,70 @@ struct wpa_driver_ops {
         * switch_channel - Announce channel switch and migrate the GO to the
         * given frequency
         * @priv: Private driver interface data
-        * @freq: Frequency in MHz
+        * @settings: Settings for CSA period and new channel
         * Returns: 0 on success, -1 on failure
         *
         * This function is used to move the GO to the legacy STA channel to
         * avoid frequency conflict in single channel concurrency.
         */
-       int (*switch_channel)(void *priv, unsigned int freq);
+       int (*switch_channel)(void *priv, struct csa_settings *settings);
+
+       /**
+        * add_tx_ts - Add traffic stream
+        * @priv: Private driver interface data
+        * @tsid: Traffic stream ID
+        * @addr: Receiver address
+        * @user_prio: User priority of the traffic stream
+        * @admitted_time: Admitted time for this TS in units of
+        *      32 microsecond periods (per second).
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*add_tx_ts)(void *priv, u8 tsid, const u8 *addr, u8 user_prio,
+                        u16 admitted_time);
+
+       /**
+        * del_tx_ts - Delete traffic stream
+        * @priv: Private driver interface data
+        * @tsid: Traffic stream ID
+        * @addr: Receiver address
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*del_tx_ts)(void *priv, u8 tsid, const u8 *addr);
+
+       /**
+        * Enable channel-switching with TDLS peer
+        * @priv: Private driver interface data
+        * @addr: MAC address of the TDLS peer
+        * @oper_class: Operating class of the switch channel
+        * @params: Channel specification
+        * Returns: 0 on success, -1 on failure
+        *
+        * The function indicates to driver that it can start switching to a
+        * different channel with a specified TDLS peer. The switching is
+        * assumed on until canceled with tdls_disable_channel_switch().
+        */
+       int (*tdls_enable_channel_switch)(
+               void *priv, const u8 *addr, u8 oper_class,
+               const struct hostapd_freq_params *params);
+
+       /**
+        * Disable channel switching with TDLS peer
+        * @priv: Private driver interface data
+        * @addr: MAC address of the TDLS peer
+        * Returns: 0 on success, -1 on failure
+        *
+        * This function indicates to the driver that it should stop switching
+        * with a given TDLS peer.
+        */
+       int (*tdls_disable_channel_switch)(void *priv, const u8 *addr);
 
        /**
         * start_dfs_cac - Listen for radar interference on the channel
         * @priv: Private driver interface data
-        * @freq: Frequency (in MHz) of the channel
+        * @freq: Channel parameters
         * Returns: 0 on success, -1 on failure
         */
-       int (*start_dfs_cac)(void *priv, int freq);
+       int (*start_dfs_cac)(void *priv, struct hostapd_freq_params *freq);
 
        /**
         * stop_ap - Removes beacon from AP
@@ -2731,7 +3093,7 @@ struct wpa_driver_ops {
         */
        int (*stop_ap)(void *priv);
 
-#ifdef TIZEN_EXT
+#ifdef BCM_DRIVER_V115
        /**
         * priv_cmd - execute driver-specific command
         * @priv: private driver interface data
@@ -2742,7 +3104,296 @@ struct wpa_driver_ops {
         * Returns: 0 on success, -1 on failure
         */
        int (*priv_cmd)(void *priv, char *cmd, char *buf, size_t buf_len);
-#endif /* TIZEN_EXT */
+#endif /* BCM_DRIVER_V115 */
+
+       /**
+        * get_survey - Retrieve survey data
+        * @priv: Private driver interface data
+        * @freq: If set, survey data for the specified frequency is only
+        *      being requested. If not set, all survey data is requested.
+        * Returns: 0 on success, -1 on failure
+        *
+        * Use this to retrieve:
+        *
+        * - the observed channel noise floor
+        * - the amount of time we have spent on the channel
+        * - the amount of time during which we have spent on the channel that
+        *   the radio has determined the medium is busy and we cannot
+        *   transmit
+        * - the amount of time we have spent receiving data
+        * - the amount of time we have spent transmitting data
+        *
+        * This data can be used for spectrum heuristics. One example is
+        * Automatic Channel Selection (ACS). The channel survey data is
+        * kept on a linked list on the channel data, one entry is added
+        * for each survey. The min_nf of the channel is updated for each
+        * survey.
+        */
+       int (*get_survey)(void *priv, unsigned int freq);
+
+       /**
+        * status - Get driver interface status information
+        * @priv: Private driver interface data
+        * @buf: Buffer for printing tou the status information
+        * @buflen: Maximum length of the buffer
+        * Returns: Length of written status information or -1 on failure
+        */
+       int (*status)(void *priv, char *buf, size_t buflen);
+
+       /**
+        * roaming - Set roaming policy for driver-based BSS selection
+        * @priv: Private driver interface data
+        * @allowed: Whether roaming within ESS is allowed
+        * @bssid: Forced BSSID if roaming is disabled or %NULL if not set
+        * Returns: Length of written status information or -1 on failure
+        *
+        * This optional callback can be used to update roaming policy from the
+        * associate() command (bssid being set there indicates that the driver
+        * should not roam before getting this roaming() call to allow roaming.
+        * If the driver does not indicate WPA_DRIVER_FLAGS_BSS_SELECTION
+        * capability, roaming policy is handled within wpa_supplicant and there
+        * is no need to implement or react to this callback.
+        */
+       int (*roaming)(void *priv, int allowed, const u8 *bssid);
+
+       /**
+        * set_mac_addr - Set MAC address
+        * @priv: Private driver interface data
+        * @addr: MAC address to use or %NULL for setting back to permanent
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*set_mac_addr)(void *priv, const u8 *addr);
+
+#ifdef CONFIG_MACSEC
+       int (*macsec_init)(void *priv, struct macsec_init_params *params);
+
+       int (*macsec_deinit)(void *priv);
+
+       /**
+        * enable_protect_frames - Set protect frames status
+        * @priv: Private driver interface data
+        * @enabled: TRUE = protect frames enabled
+        *           FALSE = protect frames disabled
+        * Returns: 0 on success, -1 on failure (or if not supported)
+        */
+       int (*enable_protect_frames)(void *priv, Boolean enabled);
+
+       /**
+        * set_replay_protect - Set replay protect status and window size
+        * @priv: Private driver interface data
+        * @enabled: TRUE = replay protect enabled
+        *           FALSE = replay protect disabled
+        * @window: replay window size, valid only when replay protect enabled
+        * Returns: 0 on success, -1 on failure (or if not supported)
+        */
+       int (*set_replay_protect)(void *priv, Boolean enabled, u32 window);
+
+       /**
+        * set_current_cipher_suite - Set current cipher suite
+        * @priv: Private driver interface data
+        * @cs: EUI64 identifier
+        * @cs_len: Length of the cs buffer in octets
+        * Returns: 0 on success, -1 on failure (or if not supported)
+        */
+       int (*set_current_cipher_suite)(void *priv, const u8 *cs,
+                                       size_t cs_len);
+
+       /**
+        * enable_controlled_port - Set controlled port status
+        * @priv: Private driver interface data
+        * @enabled: TRUE = controlled port enabled
+        *           FALSE = controlled port disabled
+        * Returns: 0 on success, -1 on failure (or if not supported)
+        */
+       int (*enable_controlled_port)(void *priv, Boolean enabled);
+
+       /**
+        * get_receive_lowest_pn - Get receive lowest pn
+        * @priv: Private driver interface data
+        * @channel: secure channel
+        * @an: association number
+        * @lowest_pn: lowest accept pn
+        * Returns: 0 on success, -1 on failure (or if not supported)
+        */
+       int (*get_receive_lowest_pn)(void *priv, u32 channel, u8 an,
+                                    u32 *lowest_pn);
+
+       /**
+        * get_transmit_next_pn - Get transmit next pn
+        * @priv: Private driver interface data
+        * @channel: secure channel
+        * @an: association number
+        * @next_pn: next pn
+        * Returns: 0 on success, -1 on failure (or if not supported)
+        */
+       int (*get_transmit_next_pn)(void *priv, u32 channel, u8 an,
+                                   u32 *next_pn);
+
+       /**
+        * set_transmit_next_pn - Set transmit next pn
+        * @priv: Private driver interface data
+        * @channel: secure channel
+        * @an: association number
+        * @next_pn: next pn
+        * Returns: 0 on success, -1 on failure (or if not supported)
+        */
+       int (*set_transmit_next_pn)(void *priv, u32 channel, u8 an,
+                                   u32 next_pn);
+
+       /**
+        * get_available_receive_sc - get available receive channel
+        * @priv: Private driver interface data
+        * @channel: secure channel
+        * Returns: 0 on success, -1 on failure (or if not supported)
+        */
+       int (*get_available_receive_sc)(void *priv, u32 *channel);
+
+       /**
+        * create_receive_sc - create secure channel for receiving
+        * @priv: Private driver interface data
+        * @channel: secure channel
+        * @sci_addr: secure channel identifier - address
+        * @sci_port: secure channel identifier - port
+        * @conf_offset: confidentiality offset (0, 30, or 50)
+        * @validation: frame validation policy (0 = Disabled, 1 = Checked,
+        *      2 = Strict)
+        * Returns: 0 on success, -1 on failure (or if not supported)
+        */
+       int (*create_receive_sc)(void *priv, u32 channel, const u8 *sci_addr,
+                                u16 sci_port, unsigned int conf_offset,
+                                int validation);
+
+       /**
+        * delete_receive_sc - delete secure connection for receiving
+        * @priv: private driver interface data from init()
+        * @channel: secure channel
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*delete_receive_sc)(void *priv, u32 channel);
+
+       /**
+        * create_receive_sa - create secure association for receive
+        * @priv: private driver interface data from init()
+        * @channel: secure channel
+        * @an: association number
+        * @lowest_pn: the lowest packet number can be received
+        * @sak: the secure association key
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*create_receive_sa)(void *priv, u32 channel, u8 an,
+                                u32 lowest_pn, const u8 *sak);
+
+       /**
+        * enable_receive_sa - enable the SA for receive
+        * @priv: private driver interface data from init()
+        * @channel: secure channel
+        * @an: association number
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*enable_receive_sa)(void *priv, u32 channel, u8 an);
+
+       /**
+        * disable_receive_sa - disable SA for receive
+        * @priv: private driver interface data from init()
+        * @channel: secure channel index
+        * @an: association number
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*disable_receive_sa)(void *priv, u32 channel, u8 an);
+
+       /**
+        * get_available_transmit_sc - get available transmit channel
+        * @priv: Private driver interface data
+        * @channel: secure channel
+        * Returns: 0 on success, -1 on failure (or if not supported)
+        */
+       int (*get_available_transmit_sc)(void *priv, u32 *channel);
+
+       /**
+        * create_transmit_sc - create secure connection for transmit
+        * @priv: private driver interface data from init()
+        * @channel: secure channel
+        * @sci_addr: secure channel identifier - address
+        * @sci_port: secure channel identifier - port
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*create_transmit_sc)(void *priv, u32 channel, const u8 *sci_addr,
+                                 u16 sci_port, unsigned int conf_offset);
+
+       /**
+        * delete_transmit_sc - delete secure connection for transmit
+        * @priv: private driver interface data from init()
+        * @channel: secure channel
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*delete_transmit_sc)(void *priv, u32 channel);
+
+       /**
+        * create_transmit_sa - create secure association for transmit
+        * @priv: private driver interface data from init()
+        * @channel: secure channel index
+        * @an: association number
+        * @next_pn: the packet number used as next transmit packet
+        * @confidentiality: True if the SA is to provide confidentiality
+        *                   as well as integrity
+        * @sak: the secure association key
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*create_transmit_sa)(void *priv, u32 channel, u8 an, u32 next_pn,
+                                 Boolean confidentiality, const u8 *sak);
+
+       /**
+        * enable_transmit_sa - enable SA for transmit
+        * @priv: private driver interface data from init()
+        * @channel: secure channel
+        * @an: association number
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*enable_transmit_sa)(void *priv, u32 channel, u8 an);
+
+       /**
+        * disable_transmit_sa - disable SA for transmit
+        * @priv: private driver interface data from init()
+        * @channel: secure channel
+        * @an: association number
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*disable_transmit_sa)(void *priv, u32 channel, u8 an);
+#endif /* CONFIG_MACSEC */
+
+       /**
+        * init_mesh - Driver specific initialization for mesh
+        * @priv: Private driver interface data
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*init_mesh)(void *priv);
+
+       /**
+        * join_mesh - Join a mesh network
+        * @priv: Private driver interface data
+        * @params: Mesh configuration parameters
+        * Returns: 0 on success, -1 on failure
+        */
+       int (*join_mesh)(void *priv,
+                        struct wpa_driver_mesh_join_params *params);
+
+       /**
+        * leave_mesh - Leave a mesh network
+        * @priv: Private driver interface data
+        * Returns 0 on success, -1 on failure
+        */
+       int (*leave_mesh)(void *priv);
+
+       /**
+        * do_acs - Automatically select channel
+        * @priv: Private driver interface data
+        * @params: Parameters for ACS
+        * Returns 0 on success, -1 on failure
+        *
+        * This command can be used to offload ACS to the driver if the driver
+        * indicates support for such offloading (WPA_DRIVER_FLAGS_ACS_OFFLOAD).
+        */
+       int (*do_acs)(void *priv, struct drv_acs_params *params);
 };
 
 
@@ -2931,11 +3582,6 @@ enum wpa_event_type {
        EVENT_ASSOC_TIMED_OUT,
 
        /**
-        * EVENT_FT_RRB_RX - FT (IEEE 802.11r) RRB frame received
-        */
-       EVENT_FT_RRB_RX,
-
-       /**
         * EVENT_WPS_BUTTON_PUSHED - Report hardware push button press for WPS
         */
        EVENT_WPS_BUTTON_PUSHED,
@@ -2956,15 +3602,6 @@ enum wpa_event_type {
        EVENT_RX_MGMT,
 
        /**
-        * EVENT_RX_ACTION - Action frame received
-        *
-        * This event is used to indicate when an Action frame has been
-        * received. Information about the received frame is included in
-        * union wpa_event_data::rx_action.
-        */
-       EVENT_RX_ACTION,
-
-       /**
         * EVENT_REMAIN_ON_CHANNEL - Remain-on-channel duration started
         *
         * This event is used to indicate when the driver has started the
@@ -2984,13 +3621,6 @@ enum wpa_event_type {
        EVENT_CANCEL_REMAIN_ON_CHANNEL,
 
        /**
-        * EVENT_MLME_RX - Report reception of frame for MLME (test use only)
-        *
-        * This event is used only by driver_test.c and userspace MLME.
-        */
-       EVENT_MLME_RX,
-
-       /**
         * EVENT_RX_PROBE_REQ - Indicate received Probe Request frame
         *
         * This event is used to indicate when a Probe Request frame has been
@@ -3018,9 +3648,7 @@ enum wpa_event_type {
         * EVENT_EAPOL_RX - Report received EAPOL frame
         *
         * When in AP mode with hostapd, this event is required to be used to
-        * deliver the receive EAPOL frames from the driver. With
-        * %wpa_supplicant, this event is used only if the send_eapol() handler
-        * is used to override the use of l2_packet for EAPOL frame TX.
+        * deliver the receive EAPOL frames from the driver.
         */
        EVENT_EAPOL_RX,
 
@@ -3068,7 +3696,8 @@ enum wpa_event_type {
         * the driver does not support radar detection and another virtual
         * interfaces caused the operating channel to change. Other similar
         * resource conflicts could also trigger this for station mode
-        * interfaces.
+        * interfaces. This event can be propagated when channel switching
+        * fails.
         */
        EVENT_INTERFACE_UNAVAILABLE,
 
@@ -3111,38 +3740,6 @@ enum wpa_event_type {
        EVENT_STATION_LOW_ACK,
 
        /**
-        * EVENT_P2P_DEV_FOUND - Report a discovered P2P device
-        *
-        * This event is used only if the driver implements P2P management
-        * internally. Event data is stored in
-        * union wpa_event_data::p2p_dev_found.
-        */
-       EVENT_P2P_DEV_FOUND,
-
-       /**
-        * EVENT_P2P_GO_NEG_REQ_RX - Report reception of GO Negotiation Request
-        *
-        * This event is used only if the driver implements P2P management
-        * internally. Event data is stored in
-        * union wpa_event_data::p2p_go_neg_req_rx.
-        */
-       EVENT_P2P_GO_NEG_REQ_RX,
-
-       /**
-        * EVENT_P2P_GO_NEG_COMPLETED - Report completion of GO Negotiation
-        *
-        * This event is used only if the driver implements P2P management
-        * internally. Event data is stored in
-        * union wpa_event_data::p2p_go_neg_completed.
-        */
-       EVENT_P2P_GO_NEG_COMPLETED,
-
-       EVENT_P2P_PROV_DISC_REQUEST,
-       EVENT_P2P_PROV_DISC_RESPONSE,
-       EVENT_P2P_SD_REQUEST,
-       EVENT_P2P_SD_RESPONSE,
-
-       /**
         * EVENT_IBSS_PEER_LOST - IBSS peer not reachable anymore
         */
        EVENT_IBSS_PEER_LOST,
@@ -3199,7 +3796,7 @@ enum wpa_event_type {
        EVENT_CONNECT_FAILED_REASON,
 
        /**
-        * EVENT_RADAR_DETECTED - Notify of radar detection
+        * EVENT_DFS_RADAR_DETECTED - Notify of radar detection
         *
         * A radar has been detected on the supplied frequency, hostapd should
         * react accordingly (e.g., change channel).
@@ -3207,14 +3804,14 @@ enum wpa_event_type {
        EVENT_DFS_RADAR_DETECTED,
 
        /**
-        * EVENT_CAC_FINISHED - Notify that channel availability check has been completed
+        * EVENT_DFS_CAC_FINISHED - Notify that channel availability check has been completed
         *
         * After a successful CAC, the channel can be marked clear and used.
         */
        EVENT_DFS_CAC_FINISHED,
 
        /**
-        * EVENT_CAC_ABORTED - Notify that channel availability check has been aborted
+        * EVENT_DFS_CAC_ABORTED - Notify that channel availability check has been aborted
         *
         * The CAC was not successful, and the channel remains in the previous
         * state. This may happen due to a radar beeing detected or other
@@ -3223,15 +3820,102 @@ enum wpa_event_type {
        EVENT_DFS_CAC_ABORTED,
 
        /**
-        * EVENT_DFS_CAC_NOP_FINISHED - Notify that non-occupancy period is over
+        * EVENT_DFS_NOP_FINISHED - Notify that non-occupancy period is over
         *
         * The channel which was previously unavailable is now available again.
         */
-       EVENT_DFS_NOP_FINISHED
+       EVENT_DFS_NOP_FINISHED,
+
+       /**
+        * EVENT_SURVEY - Received survey data
+        *
+        * This event gets triggered when a driver query is issued for survey
+        * data and the requested data becomes available. The returned data is
+        * stored in struct survey_results. The results provide at most one
+        * survey entry for each frequency and at minimum will provide one
+        * survey entry for one frequency. The survey data can be os_malloc()'d
+        * and then os_free()'d, so the event callback must only copy data.
+        */
+       EVENT_SURVEY,
+
+       /**
+        * EVENT_SCAN_STARTED - Scan started
+        *
+        * This indicates that driver has started a scan operation either based
+        * on a request from wpa_supplicant/hostapd or from another application.
+        * EVENT_SCAN_RESULTS is used to indicate when the scan has been
+        * completed (either successfully or by getting cancelled).
+        */
+       EVENT_SCAN_STARTED,
+
+       /**
+        * EVENT_AVOID_FREQUENCIES - Received avoid frequency range
+        *
+        * This event indicates a set of frequency ranges that should be avoided
+        * to reduce issues due to interference or internal co-existence
+        * information in the driver.
+        */
+       EVENT_AVOID_FREQUENCIES,
+
+       /**
+        * EVENT_NEW_PEER_CANDIDATE - new (unknown) mesh peer notification
+        */
+       EVENT_NEW_PEER_CANDIDATE,
+
+       /**
+        * EVENT_ACS_CHANNEL_SELECTED - Received selected channels by ACS
+        *
+        * Indicates a pair of primary and secondary channels chosen by ACS
+        * in device.
+        */
+       EVENT_ACS_CHANNEL_SELECTED,
+
+       /**
+        * EVENT_DFS_CAC_STARTED - Notify that channel availability check has
+        * been started.
+        *
+        * This event indicates that channel availability check has been started
+        * on a DFS frequency by a driver that supports DFS Offload.
+        */
+       EVENT_DFS_CAC_STARTED,
 };
 
 
 /**
+ * struct freq_survey - Channel survey info
+ *
+ * @ifidx: Interface index in which this survey was observed
+ * @freq: Center of frequency of the surveyed channel
+ * @nf: Channel noise floor in dBm
+ * @channel_time: Amount of time in ms the radio spent on the channel
+ * @channel_time_busy: Amount of time in ms the radio detected some signal
+ *     that indicated to the radio the channel was not clear
+ * @channel_time_rx: Amount of time the radio spent receiving data
+ * @channel_time_tx: Amount of time the radio spent transmitting data
+ * @filled: bitmask indicating which fields have been reported, see
+ *     SURVEY_HAS_* defines.
+ * @list: Internal list pointers
+ */
+struct freq_survey {
+       u32 ifidx;
+       unsigned int freq;
+       s8 nf;
+       u64 channel_time;
+       u64 channel_time_busy;
+       u64 channel_time_rx;
+       u64 channel_time_tx;
+       unsigned int filled;
+       struct dl_list list;
+};
+
+#define SURVEY_HAS_NF BIT(0)
+#define SURVEY_HAS_CHAN_TIME BIT(1)
+#define SURVEY_HAS_CHAN_TIME_BUSY BIT(2)
+#define SURVEY_HAS_CHAN_TIME_RX BIT(3)
+#define SURVEY_HAS_CHAN_TIME_TX BIT(4)
+
+
+/**
  * union wpa_event_data - Additional data for wpa_supplicant_event() calls
  */
 union wpa_event_data {
@@ -3313,9 +3997,62 @@ union wpa_event_data {
                unsigned int freq;
 
                /**
+                * wmm_params - WMM parameters used in this association.
+                */
+               struct wmm_params wmm_params;
+
+               /**
                 * addr - Station address (for AP mode)
                 */
                const u8 *addr;
+
+               /**
+                * The following is the key management offload information
+                * @authorized
+                * @key_replay_ctr
+                * @key_replay_ctr_len
+                * @ptk_kck
+                * @ptk_kek_len
+                * @ptk_kek
+                * @ptk_kek_len
+                */
+
+               /**
+                * authorized - Status of key management offload,
+                * 1 = successful
+                */
+               int authorized;
+
+               /**
+                * key_replay_ctr - Key replay counter value last used
+                * in a valid EAPOL-Key frame
+                */
+               const u8 *key_replay_ctr;
+
+               /**
+                * key_replay_ctr_len - The length of key_replay_ctr
+                */
+               size_t key_replay_ctr_len;
+
+               /**
+                * ptk_kck - The derived PTK KCK
+                */
+               const u8 *ptk_kck;
+
+               /**
+                * ptk_kek_len - The length of ptk_kck
+                */
+               size_t ptk_kck_len;
+
+               /**
+                * ptk_kek - The derived PTK KEK
+                */
+               const u8 *ptk_kek;
+
+               /**
+                * ptk_kek_len - The length of ptk_kek
+                */
+               size_t ptk_kek_len;
        } assoc_info;
 
        /**
@@ -3424,7 +4161,8 @@ union wpa_event_data {
                u8 peer[ETH_ALEN];
                enum {
                        TDLS_REQUEST_SETUP,
-                       TDLS_REQUEST_TEARDOWN
+                       TDLS_REQUEST_TEARDOWN,
+                       TDLS_REQUEST_DISCOVER,
                } oper;
                u16 reason_code; /* for teardown */
        } tdls;
@@ -3525,15 +4263,6 @@ union wpa_event_data {
        } timeout_event;
 
        /**
-        * struct ft_rrb_rx - Data for EVENT_FT_RRB_RX events
-        */
-       struct ft_rrb_rx {
-               const u8 *src;
-               const u8 *data;
-               size_t data_len;
-       } ft_rrb_rx;
-
-       /**
         * struct tx_status - Data for EVENT_TX_STATUS events
         */
        struct tx_status {
@@ -3561,48 +4290,26 @@ union wpa_event_data {
                const u8 *frame;
                size_t frame_len;
                u32 datarate;
-               int ssi_signal; /* dBm */
-       } rx_mgmt;
-
-       /**
-        * struct rx_action - Data for EVENT_RX_ACTION events
-        */
-       struct rx_action {
-               /**
-                * da - Destination address of the received Action frame
-                */
-               const u8 *da;
-
-               /**
-                * sa - Source address of the received Action frame
-                */
-               const u8 *sa;
-
-               /**
-                * bssid - Address 3 of the received Action frame
-                */
-               const u8 *bssid;
-
-               /**
-                * category - Action frame category
-                */
-               u8 category;
 
                /**
-                * data - Action frame body after category field
+                * drv_priv - Pointer to store driver private BSS information
+                *
+                * If not set to NULL, this is used for comparison with
+                * hostapd_data->drv_priv to determine which BSS should process
+                * the frame.
                 */
-               const u8 *data;
+               void *drv_priv;
 
                /**
-                * len - Length of data in octets
+                * freq - Frequency (in MHz) on which the frame was received
                 */
-               size_t len;
+               int freq;
 
                /**
-                * freq - Frequency (in MHz) on which the frame was received
+                * ssi_signal - Signal strength in dBm (or 0 if not available)
                 */
-               int freq;
-       } rx_action;
+               int ssi_signal;
+       } rx_mgmt;
 
        /**
         * struct remain_on_channel - Data for EVENT_REMAIN_ON_CHANNEL events
@@ -3639,17 +4346,6 @@ union wpa_event_data {
        } scan_info;
 
        /**
-        * struct mlme_rx - Data for EVENT_MLME_RX events
-        */
-       struct mlme_rx {
-               const u8 *buf;
-               size_t len;
-               int freq;
-               int channel;
-               int ssi;
-       } mlme_rx;
-
-       /**
         * struct rx_probe_req - Data for EVENT_RX_PROBE_REQ events
         */
        struct rx_probe_req {
@@ -3742,66 +4438,6 @@ union wpa_event_data {
        } low_ack;
 
        /**
-        * struct p2p_dev_found - Data for EVENT_P2P_DEV_FOUND
-        */
-       struct p2p_dev_found {
-               const u8 *addr;
-               const u8 *dev_addr;
-               const u8 *pri_dev_type;
-               const char *dev_name;
-               u16 config_methods;
-               u8 dev_capab;
-               u8 group_capab;
-       } p2p_dev_found;
-
-       /**
-        * struct p2p_go_neg_req_rx - Data for EVENT_P2P_GO_NEG_REQ_RX
-        */
-       struct p2p_go_neg_req_rx {
-               const u8 *src;
-               u16 dev_passwd_id;
-       } p2p_go_neg_req_rx;
-
-       /**
-        * struct p2p_go_neg_completed - Data for EVENT_P2P_GO_NEG_COMPLETED
-        */
-       struct p2p_go_neg_completed {
-               struct p2p_go_neg_results *res;
-       } p2p_go_neg_completed;
-
-       struct p2p_prov_disc_req {
-               const u8 *peer;
-               u16 config_methods;
-               const u8 *dev_addr;
-               const u8 *pri_dev_type;
-               const char *dev_name;
-               u16 supp_config_methods;
-               u8 dev_capab;
-               u8 group_capab;
-       } p2p_prov_disc_req;
-
-       struct p2p_prov_disc_resp {
-               const u8 *peer;
-               u16 config_methods;
-       } p2p_prov_disc_resp;
-
-       struct p2p_sd_req {
-               int freq;
-               const u8 *sa;
-               u8 dialog_token;
-               u16 update_indic;
-               const u8 *tlvs;
-               size_t tlvs_len;
-       } p2p_sd_req;
-
-       struct p2p_sd_resp {
-               const u8 *sa;
-               u16 update_indic;
-               const u8 *tlvs;
-               size_t tlvs_len;
-       } p2p_sd_resp;
-
-       /**
         * struct ibss_peer_lost - Data for EVENT_IBSS_PEER_LOST
         */
        struct ibss_peer_lost {
@@ -3846,11 +4482,17 @@ union wpa_event_data {
         * @freq: Frequency of new channel in MHz
         * @ht_enabled: Whether this is an HT channel
         * @ch_offset: Secondary channel offset
+        * @ch_width: Channel width
+        * @cf1: Center frequency 1
+        * @cf2: Center frequency 2
         */
        struct ch_switch {
                int freq;
                int ht_enabled;
                int ch_offset;
+               enum chan_width ch_width;
+               int cf1;
+               int cf2;
        } ch_switch;
 
        /**
@@ -3872,7 +4514,67 @@ union wpa_event_data {
         */
        struct dfs_event {
                int freq;
+               int ht_enabled;
+               int chan_offset;
+               enum chan_width chan_width;
+               int cf1;
+               int cf2;
        } dfs_event;
+
+       /**
+        * survey_results - Survey result data for EVENT_SURVEY
+        * @freq_filter: Requested frequency survey filter, 0 if request
+        *      was for all survey data
+        * @survey_list: Linked list of survey data (struct freq_survey)
+        */
+       struct survey_results {
+               unsigned int freq_filter;
+               struct dl_list survey_list; /* struct freq_survey */
+       } survey_results;
+
+       /**
+        * channel_list_changed - Data for EVENT_CHANNEL_LIST_CHANGED
+        * @initiator: Initiator of the regulatory change
+        * @type: Regulatory change type
+        * @alpha2: Country code (or "" if not available)
+        */
+       struct channel_list_changed {
+               enum reg_change_initiator initiator;
+               enum reg_type type;
+               char alpha2[3];
+       } channel_list_changed;
+
+       /**
+        * freq_range - List of frequency ranges
+        *
+        * This is used as the data with EVENT_AVOID_FREQUENCIES.
+        */
+       struct wpa_freq_range_list freq_range;
+
+       /**
+        * struct mesh_peer
+        *
+        * @peer: Peer address
+        * @ies: Beacon IEs
+        * @ie_len: Length of @ies
+        *
+        * Notification of new candidate mesh peer.
+        */
+       struct mesh_peer {
+               const u8 *peer;
+               const u8 *ies;
+               size_t ie_len;
+       } mesh_peer;
+
+       /**
+        * struct acs_selected_channels - Data for EVENT_ACS_CHANNEL_SELECTED
+        * @pri_channel: Selected primary channel
+        * @sec_channel: Selected secondary channel
+        */
+       struct acs_selected_channels {
+               u8 pri_channel;
+               u8 sec_channel;
+       } acs_selected_channels;
 };
 
 /**
@@ -3931,4 +4633,17 @@ void wpa_scan_results_free(struct wpa_scan_results *res);
 /* Convert wpa_event_type to a string for logging */
 const char * event_to_string(enum wpa_event_type event);
 
+/* Convert chan_width to a string for logging and control interfaces */
+const char * channel_width_to_string(enum chan_width width);
+
+int ht_supported(const struct hostapd_hw_modes *mode);
+int vht_supported(const struct hostapd_hw_modes *mode);
+
+struct wowlan_triggers *
+wpa_get_wowlan_triggers(const char *wowlan_triggers,
+                       const struct wpa_driver_capa *capa);
+
+/* NULL terminated array of linked in driver wrappers */
+extern struct wpa_driver_ops *wpa_drivers[];
+
 #endif /* DRIVER_H */
old mode 100644 (file)
new mode 100755 (executable)
index c2f5934..f464421
@@ -224,10 +224,10 @@ set80211param(struct atheros_driver_data *drv, int op, int arg)
        memcpy(iwr.u.name+sizeof(__u32), &arg, sizeof(arg));
 
        if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) {
-               perror("ioctl[IEEE80211_IOCTL_SETPARAM]");
-               wpa_printf(MSG_DEBUG, "%s: %s: Failed to set parameter (op %d "
-                          "(%s) arg %d)", __func__, drv->iface, op,
-                          athr_get_param_name(op), arg);
+               wpa_printf(MSG_INFO,
+                          "%s: %s: Failed to set parameter (op %d (%s) arg %d): ioctl[IEEE80211_IOCTL_SETPARAM]: %s",
+                          __func__, drv->iface, op, athr_get_param_name(op),
+                          arg, strerror(errno));
                return -1;
        }
        return 0;
@@ -260,6 +260,17 @@ atheros_configure_wpa(struct atheros_driver_data *drv,
        case WPA_CIPHER_CCMP:
                v = IEEE80211_CIPHER_AES_CCM;
                break;
+#ifdef ATH_GCM_SUPPORT
+       case WPA_CIPHER_CCMP_256:
+               v = IEEE80211_CIPHER_AES_CCM_256;
+               break;
+       case WPA_CIPHER_GCMP:
+               v = IEEE80211_CIPHER_AES_GCM;
+               break;
+       case WPA_CIPHER_GCMP_256:
+               v = IEEE80211_CIPHER_AES_GCM_256;
+               break;
+#endif /* ATH_GCM_SUPPORT */
        case WPA_CIPHER_TKIP:
                v = IEEE80211_CIPHER_TKIP;
                break;
@@ -279,14 +290,15 @@ atheros_configure_wpa(struct atheros_driver_data *drv,
        }
        wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v);
        if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) {
-               printf("Unable to set group key cipher to %u\n", v);
+               wpa_printf(MSG_INFO, "Unable to set group key cipher to %u", v);
                return -1;
        }
        if (v == IEEE80211_CIPHER_WEP) {
                /* key length is done only for specific ciphers */
                v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5);
                if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) {
-                       printf("Unable to set group key length to %u\n", v);
+                       wpa_printf(MSG_INFO,
+                                  "Unable to set group key length to %u", v);
                        return -1;
                }
        }
@@ -294,13 +306,22 @@ atheros_configure_wpa(struct atheros_driver_data *drv,
        v = 0;
        if (params->wpa_pairwise & WPA_CIPHER_CCMP)
                v |= 1<<IEEE80211_CIPHER_AES_CCM;
+#ifdef ATH_GCM_SUPPORT
+       if (params->wpa_pairwise & WPA_CIPHER_CCMP_256)
+               v |= 1<<IEEE80211_CIPHER_AES_CCM_256;
+       if (params->wpa_pairwise & WPA_CIPHER_GCMP)
+               v |= 1<<IEEE80211_CIPHER_AES_GCM;
+       if (params->wpa_pairwise & WPA_CIPHER_GCMP_256)
+               v |= 1<<IEEE80211_CIPHER_AES_GCM_256;
+#endif /* ATH_GCM_SUPPORT */
        if (params->wpa_pairwise & WPA_CIPHER_TKIP)
                v |= 1<<IEEE80211_CIPHER_TKIP;
        if (params->wpa_pairwise & WPA_CIPHER_NONE)
                v |= 1<<IEEE80211_CIPHER_NONE;
        wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v);
        if (set80211param(drv, IEEE80211_PARAM_UCASTCIPHERS, v)) {
-               printf("Unable to set pairwise key ciphers to 0x%x\n", v);
+               wpa_printf(MSG_INFO,
+                          "Unable to set pairwise key ciphers to 0x%x", v);
                return -1;
        }
 
@@ -308,8 +329,9 @@ atheros_configure_wpa(struct atheros_driver_data *drv,
                   __func__, params->wpa_key_mgmt);
        if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS,
                          params->wpa_key_mgmt)) {
-               printf("Unable to set key management algorithms to 0x%x\n",
-                       params->wpa_key_mgmt);
+               wpa_printf(MSG_INFO,
+                          "Unable to set key management algorithms to 0x%x",
+                          params->wpa_key_mgmt);
                return -1;
        }
 
@@ -326,13 +348,14 @@ atheros_configure_wpa(struct atheros_driver_data *drv,
 
        wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", __func__, v);
        if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) {
-               printf("Unable to set RSN capabilities to 0x%x\n", v);
+               wpa_printf(MSG_INFO, "Unable to set RSN capabilities to 0x%x",
+                          v);
                return -1;
        }
 
        wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, params->wpa);
        if (set80211param(drv, IEEE80211_PARAM_WPA, params->wpa)) {
-               printf("Unable to set WPA to %u\n", params->wpa);
+               wpa_printf(MSG_INFO, "Unable to set WPA to %u", params->wpa);
                return -1;
        }
        return 0;
@@ -354,19 +377,16 @@ atheros_set_ieee8021x(void *priv, struct wpa_bss_params *params)
                return atheros_set_privacy(drv, 0);
        }
        if (!params->wpa && !params->ieee802_1x) {
-               hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
-                       HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!");
+               wpa_printf(MSG_WARNING, "No 802.1X or WPA enabled!");
                return -1;
        }
        if (params->wpa && atheros_configure_wpa(drv, params) != 0) {
-               hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
-                       HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!");
+               wpa_printf(MSG_WARNING, "Error configuring WPA state!");
                return -1;
        }
        if (set80211param(priv, IEEE80211_PARAM_AUTHMODE,
                (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
-               hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
-                       HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!");
+               wpa_printf(MSG_WARNING, "Error enabling WPA/802.1X!");
                return -1;
        }
 
@@ -474,20 +494,42 @@ atheros_set_key(const char *ifname, void *priv, enum wpa_alg alg,
        case WPA_ALG_CCMP:
                cipher = IEEE80211_CIPHER_AES_CCM;
                break;
+#ifdef ATH_GCM_SUPPORT
+       case WPA_ALG_CCMP_256:
+               cipher = IEEE80211_CIPHER_AES_CCM_256;
+               break;
+       case WPA_ALG_GCMP:
+               cipher = IEEE80211_CIPHER_AES_GCM;
+               break;
+       case WPA_ALG_GCMP_256:
+               cipher = IEEE80211_CIPHER_AES_GCM_256;
+               break;
+#endif /* ATH_GCM_SUPPORT */
 #ifdef CONFIG_IEEE80211W
        case WPA_ALG_IGTK:
                cipher = IEEE80211_CIPHER_AES_CMAC;
                break;
+#ifdef ATH_GCM_SUPPORT
+       case WPA_ALG_BIP_CMAC_256:
+               cipher = IEEE80211_CIPHER_AES_CMAC_256;
+               break;
+       case WPA_ALG_BIP_GMAC_128:
+               cipher = IEEE80211_CIPHER_AES_GMAC;
+               break;
+       case WPA_ALG_BIP_GMAC_256:
+               cipher = IEEE80211_CIPHER_AES_GMAC_256;
+               break;
+#endif /* ATH_GCM_SUPPORT */
 #endif /* CONFIG_IEEE80211W */
        default:
-               printf("%s: unknown/unsupported algorithm %d\n",
-                       __func__, alg);
+               wpa_printf(MSG_INFO, "%s: unknown/unsupported algorithm %d",
+                          __func__, alg);
                return -1;
        }
 
        if (key_len > sizeof(wk.ik_keydata)) {
-               printf("%s: key length %lu too big\n", __func__,
-                      (unsigned long) key_len);
+               wpa_printf(MSG_INFO, "%s: key length %lu too big", __func__,
+                          (unsigned long) key_len);
                return -3;
        }
 
@@ -598,7 +640,8 @@ atheros_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data,
                        return 0;
                }
 
-               printf("Failed to get station stats information element.\n");
+               wpa_printf(MSG_INFO,
+                          "Failed to get station stats information element");
                return -1;
        }
 
@@ -731,97 +774,125 @@ atheros_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
        return ret;
 }
 
-#ifdef CONFIG_WPS
-static void atheros_raw_recv_wps(void *ctx, const u8 *src_addr, const u8 *buf,
-                                size_t len)
+static int atheros_set_qos_map(void *ctx, const u8 *qos_map_set,
+                              u8 qos_map_set_len)
 {
+#ifdef CONFIG_ATHEROS_QOS_MAP
        struct atheros_driver_data *drv = ctx;
-       const struct ieee80211_mgmt *mgmt;
-       u16 fc;
-       union wpa_event_data event;
+       struct ieee80211req_athdbg req;
+       struct ieee80211_qos_map *qos_map = &req.data.qos_map;
+       struct iwreq iwr;
+       int i, up_start;
 
-       /* Send Probe Request information to WPS processing */
+       if (qos_map_set_len < 16 || qos_map_set_len > 58 ||
+           qos_map_set_len & 1) {
+               wpa_printf(MSG_ERROR, "Invalid QoS Map");
+               return -1;
+       } else {
+               memset(&req, 0, sizeof(struct ieee80211req_athdbg));
+               req.cmd = IEEE80211_DBGREQ_SETQOSMAPCONF;
+               os_memset(&iwr, 0, sizeof(iwr));
+               os_strlcpy(iwr.ifr_name, drv->iface, sizeof(iwr.ifr_name));
+               iwr.u.data.pointer = (void *) &req;
+               iwr.u.data.length = sizeof(struct ieee80211req_athdbg);
+       }
 
-       if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
-               return;
-       mgmt = (const struct ieee80211_mgmt *) buf;
+       qos_map->valid = 1;
+       qos_map->num_dscp_except = (qos_map_set_len - 16) / 2;
+       if (qos_map->num_dscp_except) {
+               for (i = 0; i < qos_map->num_dscp_except; i++) {
+                       qos_map->dscp_exception[i].dscp = qos_map_set[i * 2];
+                       qos_map->dscp_exception[i].up = qos_map_set[i * 2 + 1];
+               }
+       }
 
-       fc = le_to_host16(mgmt->frame_control);
-       if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ||
-           WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_PROBE_REQ)
-               return;
+       up_start = qos_map_set_len - 16;
+       for (i = 0; i < IEEE80211_MAX_QOS_UP_RANGE; i++) {
+               qos_map->up[i].low = qos_map_set[up_start + (i * 2)];
+               qos_map->up[i].high = qos_map_set[up_start + (i * 2) + 1];
+       }
 
-       os_memset(&event, 0, sizeof(event));
-       event.rx_probe_req.sa = mgmt->sa;
-       event.rx_probe_req.da = mgmt->da;
-       event.rx_probe_req.bssid = mgmt->bssid;
-       event.rx_probe_req.ie = mgmt->u.probe_req.variable;
-       event.rx_probe_req.ie_len =
-               len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
-       wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event);
+       if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_DBGREQ, &iwr) < 0) {
+               wpa_printf(MSG_ERROR,
+                          "%s: %s: Failed to set QoS Map: ioctl[IEEE80211_IOCTL_DBGREQ]: %s",
+                          __func__, drv->iface, strerror(errno));
+               return -1;
+       }
+#endif /* CONFIG_ATHEROS_QOS_MAP */
+
+       return 0;
 }
-#endif /* CONFIG_WPS */
 
-#ifdef CONFIG_IEEE80211R
-static void atheros_raw_recv_11r(void *ctx, const u8 *src_addr, const u8 *buf,
-                                size_t len)
+#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_WNM) || defined(CONFIG_HS20)
+static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
+                               size_t len)
 {
        struct atheros_driver_data *drv = ctx;
-       union wpa_event_data event;
        const struct ieee80211_mgmt *mgmt;
-       u16 fc;
-       u16 stype;
+       union wpa_event_data event;
+       u16 fc, stype;
        int ielen;
        const u8 *iebuf;
 
-       /* Do 11R processing for ASSOC/AUTH/FT ACTION frames */
        if (len < IEEE80211_HDRLEN)
                return;
+
        mgmt = (const struct ieee80211_mgmt *) buf;
 
        fc = le_to_host16(mgmt->frame_control);
 
        if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT)
                return;
+
        stype = WLAN_FC_GET_STYPE(fc);
 
        wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype,
                   (int) len);
 
+       if (stype == WLAN_FC_STYPE_PROBE_REQ) {
+               if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req))
+                       return;
+
+               os_memset(&event, 0, sizeof(event));
+               event.rx_probe_req.sa = mgmt->sa;
+               event.rx_probe_req.da = mgmt->da;
+               event.rx_probe_req.bssid = mgmt->bssid;
+               event.rx_probe_req.ie = mgmt->u.probe_req.variable;
+               event.rx_probe_req.ie_len =
+                       len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req));
+               wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event);
+               return;
+       }
+
        if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) {
                wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore",
                           __func__);
                return;
        }
+
        switch (stype) {
        case WLAN_FC_STYPE_ASSOC_REQ:
-               if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.assoc_req))
+               if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req))
                        break;
                ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req));
                iebuf = mgmt->u.assoc_req.variable;
                drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 0);
                break;
        case WLAN_FC_STYPE_REASSOC_REQ:
-               if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.reassoc_req))
+               if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req))
                        break;
                ielen = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req));
                iebuf = mgmt->u.reassoc_req.variable;
                drv_event_assoc(drv->hapd, mgmt->sa, iebuf, ielen, 1);
                break;
        case WLAN_FC_STYPE_ACTION:
-               if (&mgmt->u.action.category > buf + len)
-                       break;
                os_memset(&event, 0, sizeof(event));
-               event.rx_action.da = mgmt->da;
-               event.rx_action.sa = mgmt->sa;
-               event.rx_action.bssid = mgmt->bssid;
-               event.rx_action.category = mgmt->u.action.category;
-               event.rx_action.data = &mgmt->u.action.category;
-               event.rx_action.len = buf + len - event.rx_action.data;
-               wpa_supplicant_event(drv->hapd, EVENT_RX_ACTION, &event);
+               event.rx_mgmt.frame = buf;
+               event.rx_mgmt.frame_len = len;
+               wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
                break;
        case WLAN_FC_STYPE_AUTH:
-               if (len - IEEE80211_HDRLEN < sizeof(mgmt->u.auth))
+               if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth))
                        break;
                os_memset(&event, 0, sizeof(event));
                os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN);
@@ -840,107 +911,7 @@ static void atheros_raw_recv_11r(void *ctx, const u8 *src_addr, const u8 *buf,
                break;
        }
 }
-#endif /* CONFIG_IEEE80211R */
-
-#ifdef CONFIG_HS20
-static void atheros_raw_recv_hs20(void *ctx, const u8 *src_addr, const u8 *buf,
-                                size_t len)
-{
-       struct atheros_driver_data *drv = ctx;
-       const struct ieee80211_mgmt *mgmt;
-       u16 fc;
-       union wpa_event_data event;
-
-       /* Send the Action frame for HS20 processing */
-
-       if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.action.category) +
-           sizeof(mgmt->u.action.u.public_action))
-               return;
-
-       mgmt = (const struct ieee80211_mgmt *) buf;
-
-       fc = le_to_host16(mgmt->frame_control);
-       if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT ||
-           WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_ACTION ||
-           mgmt->u.action.category != WLAN_ACTION_PUBLIC)
-               return;
-
-       wpa_printf(MSG_DEBUG, "%s:Received Public Action frame", __func__);
-
-       os_memset(&event, 0, sizeof(event));
-       event.rx_mgmt.frame = (const u8 *) mgmt;
-       event.rx_mgmt.frame_len = len;
-       wpa_supplicant_event(drv->hapd, EVENT_RX_MGMT, &event);
-}
-#endif /* CONFIG_HS20 */
-
-#if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R)
-static void atheros_raw_recv_11v(void *ctx, const u8 *src_addr, const u8 *buf,
-                                size_t len)
-{
-       struct atheros_driver_data *drv = ctx;
-       union wpa_event_data event;
-       const struct ieee80211_mgmt *mgmt;
-       u16 fc;
-       u16 stype;
-
-       /* Do 11R processing for WNM ACTION frames */
-       if (len < IEEE80211_HDRLEN)
-               return;
-       mgmt = (const struct ieee80211_mgmt *) buf;
-
-       fc = le_to_host16(mgmt->frame_control);
-
-       if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT)
-               return;
-       stype = WLAN_FC_GET_STYPE(fc);
-
-       wpa_printf(MSG_DEBUG, "%s: subtype 0x%x len %d", __func__, stype,
-                  (int) len);
-
-       if (os_memcmp(drv->own_addr, mgmt->bssid, ETH_ALEN) != 0) {
-               wpa_printf(MSG_DEBUG, "%s: BSSID does not match - ignore",
-                          __func__);
-               return;
-       }
-
-       switch (stype) {
-       case WLAN_FC_STYPE_ACTION:
-               if (&mgmt->u.action.category > buf + len)
-                       break;
-               os_memset(&event, 0, sizeof(event));
-               event.rx_action.da = mgmt->da;
-               event.rx_action.sa = mgmt->sa;
-               event.rx_action.bssid = mgmt->bssid;
-               event.rx_action.category = mgmt->u.action.category;
-               event.rx_action.data = &mgmt->u.action.category;
-               event.rx_action.len = buf + len - event.rx_action.data;
-               wpa_supplicant_event(drv->hapd, EVENT_RX_ACTION, &event);
-               break;
-       default:
-               break;
-       }
-}
-#endif /* CONFIG_WNM */
-
-#if defined(CONFIG_WPS) || defined(CONFIG_IEEE80211R) || defined(CONFIG_WNM)
-static void atheros_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf,
-                               size_t len)
-{
-#ifdef CONFIG_WPS
-       atheros_raw_recv_wps(ctx, src_addr, buf, len);
-#endif /* CONFIG_WPS */
-#ifdef CONFIG_IEEE80211R
-       atheros_raw_recv_11r(ctx, src_addr, buf, len);
-#endif /* CONFIG_IEEE80211R */
-#if defined(CONFIG_WNM) && !defined(CONFIG_IEEE80211R)
-       atheros_raw_recv_11v(ctx, src_addr, buf, len);
-#endif /* CONFIG_WNM */
-#ifdef CONFIG_HS20
-       atheros_raw_recv_hs20(ctx, src_addr, buf, len);
-#endif /* CONFIG_HS20 */
-}
-#endif /* CONFIG_WPS || CONFIG_IEEE80211R */
+#endif
 
 static int atheros_receive_pkt(struct atheros_driver_data *drv)
 {
@@ -1334,7 +1305,7 @@ static void fetch_pending_big_events(struct atheros_driver_data *drv)
 
        while (1) {
                os_memset(&iwr, 0, sizeof(iwr));
-               os_strncpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
+               os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ);
 
                iwr.u.data.pointer = (void *) tbuf;
                iwr.u.data.length = sizeof(tbuf);
@@ -1529,8 +1500,9 @@ atheros_get_we_version(struct atheros_driver_data *drv)
                sizeof(range->enc_capa);
 
        if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) {
-               perror("ioctl[SIOCGIWRANGE]");
-               free(range);
+               wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s",
+                          strerror(errno));
+               os_free(range);
                return -1;
        } else if (iwr.u.data.length >= minlen &&
                   range->we_version_compiled >= 18) {
@@ -1590,8 +1562,9 @@ atheros_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len,
        if (len > sizeof(buf)) {
                bp = malloc(len);
                if (bp == NULL) {
-                       printf("EAPOL frame discarded, cannot malloc temp "
-                              "buffer of size %lu!\n", (unsigned long) len);
+                       wpa_printf(MSG_INFO,
+                                  "EAPOL frame discarded, cannot malloc temp buffer of size %lu!",
+                                  (unsigned long) len);
                        return -1;
                }
        }
@@ -1628,14 +1601,16 @@ atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params)
 
        drv = os_zalloc(sizeof(struct atheros_driver_data));
        if (drv == NULL) {
-               printf("Could not allocate memory for atheros driver data\n");
+               wpa_printf(MSG_INFO,
+                          "Could not allocate memory for atheros driver data");
                return NULL;
        }
 
        drv->hapd = hapd;
        drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
        if (drv->ioctl_sock < 0) {
-               perror("socket[PF_INET,SOCK_DGRAM]");
+               wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
+                          strerror(errno));
                goto bad;
        }
        memcpy(drv->iface, params->ifname, sizeof(drv->iface));
@@ -1643,7 +1618,8 @@ atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params)
        memset(&ifr, 0, sizeof(ifr));
        os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name));
        if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) {
-               perror("ioctl(SIOCGIFINDEX)");
+               wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
+                          strerror(errno));
                goto bad;
        }
        drv->ifindex = ifr.ifr_ifindex;
@@ -1679,8 +1655,9 @@ atheros_init(struct hostapd_data *hapd, struct wpa_init_params *params)
        iwr.u.mode = IW_MODE_MASTER;
 
        if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) {
-               perror("ioctl[SIOCSIWMODE]");
-               printf("Could not set interface to master mode!\n");
+               wpa_printf(MSG_ERROR,
+                          "Could not set interface to master mode! ioctl[SIOCSIWMODE]: %s",
+                          strerror(errno));
                goto bad;
        }
 
@@ -1746,8 +1723,8 @@ atheros_set_ssid(void *priv, const u8 *buf, int len)
        iwr.u.essid.length = len + 1;
 
        if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
-               perror("ioctl[SIOCSIWESSID]");
-               printf("len=%d\n", len);
+               wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s",
+                          len, strerror(errno));
                return -1;
        }
        return 0;
@@ -1767,7 +1744,8 @@ atheros_get_ssid(void *priv, u8 *buf, int len)
                IW_ESSID_MAX_SIZE : len;
 
        if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
-               perror("ioctl[SIOCGIWESSID]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCGIWESSID]: %s",
+                          strerror(errno));
                ret = -1;
        } else
                ret = iwr.u.essid.length;
@@ -1832,6 +1810,25 @@ static int atheros_set_ap(void *priv, struct wpa_driver_ap_params *params)
        wpa_hexdump_buf(MSG_DEBUG, "atheros: assocresp_ies",
                        params->assocresp_ies);
 
+#if defined(CONFIG_HS20) && (defined(IEEE80211_PARAM_OSEN) || defined(CONFIG_ATHEROS_OSEN))
+       if (params->osen) {
+               struct wpa_bss_params bss_params;
+
+               os_memset(&bss_params, 0, sizeof(struct wpa_bss_params));
+               bss_params.enabled = 1;
+               bss_params.wpa = 2;
+               bss_params.wpa_pairwise = WPA_CIPHER_CCMP;
+               bss_params.wpa_group = WPA_CIPHER_CCMP;
+               bss_params.ieee802_1x = 1;
+
+               if (atheros_set_privacy(priv, 1) ||
+                   set80211param(priv, IEEE80211_PARAM_OSEN, 1))
+                       return -1;
+
+               return atheros_set_ieee8021x(priv, &bss_params);
+       }
+#endif /* CONFIG_HS20 && IEEE80211_PARAM_OSEN */
+
        return 0;
 }
 
@@ -1963,7 +1960,7 @@ static int atheros_send_action(void *priv, unsigned int freq,
 }
 
 
-#ifdef CONFIG_WNM
+#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM)
 static int athr_wnm_tfs(struct atheros_driver_data *drv, const u8* peer,
                        u8 *ie, u16 *len, enum wnm_oper oper)
 {
@@ -2116,7 +2113,7 @@ static int atheros_wnm_oper(void *priv, enum wnm_oper oper, const u8 *peer,
                return -1;
        }
 }
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */
 
 
 const struct wpa_driver_ops wpa_driver_atheros_ops = {
@@ -2150,7 +2147,8 @@ const struct wpa_driver_ops wpa_driver_atheros_ops = {
        .add_sta_node           = atheros_add_sta_node,
 #endif /* CONFIG_IEEE80211R */
        .send_action            = atheros_send_action,
-#ifdef CONFIG_WNM
+#if defined(CONFIG_WNM) && defined(IEEE80211_APPIE_FRAME_WNM)
        .wnm_oper               = atheros_wnm_oper,
-#endif /* CONFIG_WNM */
+#endif /* CONFIG_WNM && IEEE80211_APPIE_FRAME_WNM */
+       .set_qos_map            = atheros_set_qos_map,
 };
index 9d869b1..0f1a0f6 100644 (file)
@@ -61,6 +61,9 @@ struct bsd_driver_data {
        int     prev_roaming;   /* roaming state to restore on deinit */
        int     prev_privacy;   /* privacy state to restore on deinit */
        int     prev_wpa;       /* wpa state to restore on deinit */
+       enum ieee80211_opmode opmode;   /* operation mode */
+       char    *event_buf;
+       size_t  event_buf_len;
 };
 
 /* Generic functions for hostapd and wpa_supplicant */
@@ -261,17 +264,24 @@ bsd_ctrl_iface(void *priv, int enable)
        os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
 
        if (ioctl(drv->sock, SIOCGIFFLAGS, &ifr) < 0) {
-               perror("ioctl[SIOCGIFFLAGS]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
+                          strerror(errno));
                return -1;
        }
 
-       if (enable)
+       if (enable) {
+               if (ifr.ifr_flags & IFF_UP)
+                       return 0;
                ifr.ifr_flags |= IFF_UP;
-       else
+       } else {
+               if (!(ifr.ifr_flags & IFF_UP))
+                       return 0;
                ifr.ifr_flags &= ~IFF_UP;
+       }
 
        if (ioctl(drv->sock, SIOCSIFFLAGS, &ifr) < 0) {
-               perror("ioctl[SIOCSIFFLAGS]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
+                          strerror(errno));
                return -1;
        }
 
@@ -284,6 +294,9 @@ bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg,
            size_t seq_len, const u8 *key, size_t key_len)
 {
        struct ieee80211req_key wk;
+#ifdef IEEE80211_KEY_NOREPLAY
+       struct bsd_driver_data *drv = priv;
+#endif /* IEEE80211_KEY_NOREPLAY */
 
        wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%p key_idx=%d set_tx=%d "
                   "seq_len=%zu key_len=%zu", __func__, alg, addr, key_idx,
@@ -338,6 +351,16 @@ bsd_set_key(const char *ifname, void *priv, enum wpa_alg alg,
        }
        if (wk.ik_keyix != IEEE80211_KEYIX_NONE && set_tx)
                wk.ik_flags |= IEEE80211_KEY_DEFAULT;
+#ifndef HOSTAPD
+#ifdef IEEE80211_KEY_NOREPLAY
+       /*
+        * Ignore replay failures in IBSS and AHDEMO mode.
+        */
+       if (drv->opmode == IEEE80211_M_IBSS ||
+           drv->opmode == IEEE80211_M_AHDEMO)
+               wk.ik_flags |= IEEE80211_KEY_NOREPLAY;
+#endif /* IEEE80211_KEY_NOREPLAY */
+#endif /* HOSTAPD */
        wk.ik_keylen = key_len;
        if (seq) {
 #ifdef WORDS_BIGENDIAN
@@ -383,22 +406,24 @@ bsd_configure_wpa(void *priv, struct wpa_bss_params *params)
                v = IEEE80211_CIPHER_NONE;
                break;
        default:
-               printf("Unknown group key cipher %u\n",
-                       params->wpa_group);
+               wpa_printf(MSG_INFO, "Unknown group key cipher %u",
+                          params->wpa_group);
                return -1;
        }
        wpa_printf(MSG_DEBUG, "%s: group key cipher=%s (%u)",
                   __func__, ciphernames[v], v);
        if (set80211param(priv, IEEE80211_IOC_MCASTCIPHER, v)) {
-               printf("Unable to set group key cipher to %u (%s)\n",
-                       v, ciphernames[v]);
+               wpa_printf(MSG_INFO,
+                          "Unable to set group key cipher to %u (%s)",
+                          v, ciphernames[v]);
                return -1;
        }
        if (v == IEEE80211_CIPHER_WEP) {
                /* key length is done only for specific ciphers */
                v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5);
                if (set80211param(priv, IEEE80211_IOC_MCASTKEYLEN, v)) {
-                       printf("Unable to set group key length to %u\n", v);
+                       wpa_printf(MSG_INFO,
+                                  "Unable to set group key length to %u", v);
                        return -1;
                }
        }
@@ -412,7 +437,8 @@ bsd_configure_wpa(void *priv, struct wpa_bss_params *params)
                v |= 1<<IEEE80211_CIPHER_NONE;
        wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v);
        if (set80211param(priv, IEEE80211_IOC_UCASTCIPHERS, v)) {
-               printf("Unable to set pairwise key ciphers to 0x%x\n", v);
+               wpa_printf(MSG_INFO,
+                          "Unable to set pairwise key ciphers to 0x%x", v);
                return -1;
        }
 
@@ -420,8 +446,9 @@ bsd_configure_wpa(void *priv, struct wpa_bss_params *params)
                   __func__, params->wpa_key_mgmt);
        if (set80211param(priv, IEEE80211_IOC_KEYMGTALGS,
                          params->wpa_key_mgmt)) {
-               printf("Unable to set key management algorithms to 0x%x\n",
-                       params->wpa_key_mgmt);
+               wpa_printf(MSG_INFO,
+                          "Unable to set key management algorithms to 0x%x",
+                          params->wpa_key_mgmt);
                return -1;
        }
 
@@ -431,14 +458,15 @@ bsd_configure_wpa(void *priv, struct wpa_bss_params *params)
        wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x",
                   __func__, params->rsn_preauth);
        if (set80211param(priv, IEEE80211_IOC_RSNCAPS, v)) {
-               printf("Unable to set RSN capabilities to 0x%x\n", v);
+               wpa_printf(MSG_INFO, "Unable to set RSN capabilities to 0x%x",
+                          v);
                return -1;
        }
 #endif /* IEEE80211_IOC_APPIE */
 
        wpa_printf(MSG_DEBUG, "%s: enable WPA= 0x%x", __func__, params->wpa);
        if (set80211param(priv, IEEE80211_IOC_WPA, params->wpa)) {
-               printf("Unable to set WPA to %u\n", params->wpa);
+               wpa_printf(MSG_INFO, "Unable to set WPA to %u", params->wpa);
                return -1;
        }
        return 0;
@@ -473,26 +501,6 @@ bsd_set_ieee8021x(void *priv, struct wpa_bss_params *params)
        return bsd_ctrl_iface(priv, 1);
 }
 
-static int
-bsd_set_sta_authorized(void *priv, const u8 *addr,
-                      int total_flags, int flags_or, int flags_and)
-{
-       int authorized = -1;
-
-       /* For now, only support setting Authorized flag */
-       if (flags_or & WPA_STA_AUTHORIZED)
-               authorized = 1;
-       if (!(flags_and & WPA_STA_AUTHORIZED))
-               authorized = 0;
-
-       if (authorized < 0)
-               return 0;
-
-       return bsd_send_mlme_param(priv, authorized ?
-                                  IEEE80211_MLME_AUTHORIZE :
-                                  IEEE80211_MLME_UNAUTHORIZE, 0, addr);
-}
-
 static void
 bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN])
 {
@@ -506,7 +514,8 @@ bsd_new_sta(void *priv, void *ctx, u8 addr[IEEE80211_ADDR_LEN])
        memset(&ie, 0, sizeof(ie));
        memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN);
        if (get80211var(priv, IEEE80211_IOC_WPAIE, &ie, sizeof(ie)) < 0) {
-               printf("Failed to get WPA/RSN information element.\n");
+               wpa_printf(MSG_INFO,
+                          "Failed to get WPA/RSN information element");
                goto no_ie;
        }
        iebuf = ie.wpa_ie;
@@ -585,7 +594,7 @@ bsd_set_opt_ie(void *priv, const u8 *ie, size_t ie_len)
        return 0;
 }
 
-static int
+static size_t
 rtbuf_len(void)
 {
        size_t len;
@@ -593,7 +602,7 @@ rtbuf_len(void)
        int mib[6] = {CTL_NET, AF_ROUTE, 0, AF_INET, NET_RT_DUMP, 0};
 
        if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
-               wpa_printf(MSG_WARNING, "%s failed: %s\n", __func__,
+               wpa_printf(MSG_WARNING, "%s failed: %s", __func__,
                           strerror(errno));
                len = 2048;
        }
@@ -651,7 +660,7 @@ bsd_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx,
        wk.ik_keyix = idx;
 
        if (get80211var(priv, IEEE80211_IOC_WPAKEY, &wk, sizeof(wk)) < 0) {
-               printf("Failed to get encryption.\n");
+               wpa_printf(MSG_INFO, "Failed to get encryption");
                return -1;
        }
 
@@ -722,37 +731,26 @@ static void
 bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx)
 {
        struct bsd_driver_data *drv = ctx;
-       char *buf;
        struct if_announcemsghdr *ifan;
        struct rt_msghdr *rtm;
        struct ieee80211_michael_event *mic;
        struct ieee80211_join_event *join;
        struct ieee80211_leave_event *leave;
-       int n, len;
+       int n;
        union wpa_event_data data;
 
-       len = rtbuf_len();
-
-       buf = os_malloc(len);
-       if (buf == NULL) {
-               wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__);
-               return;
-       }
-
-       n = read(sock, buf, len);
+       n = read(sock, drv->event_buf, drv->event_buf_len);
        if (n < 0) {
                if (errno != EINTR && errno != EAGAIN)
-                       wpa_printf(MSG_ERROR, "%s read() failed: %s\n",
+                       wpa_printf(MSG_ERROR, "%s read() failed: %s",
                                   __func__, strerror(errno));
-               os_free(buf);
                return;
        }
 
-       rtm = (struct rt_msghdr *) buf;
+       rtm = (struct rt_msghdr *) drv->event_buf;
        if (rtm->rtm_version != RTM_VERSION) {
                wpa_printf(MSG_DEBUG, "Invalid routing message version=%d",
                           rtm->rtm_version);
-               os_free(buf);
                return;
        }
        ifan = (struct if_announcemsghdr *) rtm;
@@ -793,7 +791,6 @@ bsd_wireless_event_receive(int sock, void *ctx, void *sock_ctx)
                }
                break;
        }
-       os_free(buf);
 }
 
 static void
@@ -810,14 +807,23 @@ bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params)
 
        drv = os_zalloc(sizeof(struct bsd_driver_data));
        if (drv == NULL) {
-               printf("Could not allocate memory for bsd driver data\n");
+               wpa_printf(MSG_ERROR, "Could not allocate memory for bsd driver data");
+               return NULL;
+       }
+
+       drv->event_buf_len = rtbuf_len();
+
+       drv->event_buf = os_malloc(drv->event_buf_len);
+       if (drv->event_buf == NULL) {
+               wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__);
                goto bad;
        }
 
        drv->hapd = hapd;
        drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
        if (drv->sock < 0) {
-               perror("socket[PF_INET,SOCK_DGRAM]");
+               wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
+                          strerror(errno));
                goto bad;
        }
        os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname));
@@ -835,7 +841,8 @@ bsd_init(struct hostapd_data *hapd, struct wpa_init_params *params)
 
        drv->route = socket(PF_ROUTE, SOCK_RAW, 0);
        if (drv->route < 0) {
-               perror("socket(PF_ROUTE,SOCK_RAW)");
+               wpa_printf(MSG_ERROR, "socket(PF_ROUTE,SOCK_RAW): %s",
+                          strerror(errno));
                goto bad;
        }
        eloop_register_read_sock(drv->route, bsd_wireless_event_receive, drv,
@@ -853,6 +860,7 @@ bad:
                l2_packet_deinit(drv->sock_xmit);
        if (drv->sock >= 0)
                close(drv->sock);
+       os_free(drv->event_buf);
        if (drv != NULL)
                os_free(drv);
        return NULL;
@@ -873,9 +881,37 @@ bsd_deinit(void *priv)
                close(drv->sock);
        if (drv->sock_xmit != NULL)
                l2_packet_deinit(drv->sock_xmit);
+       os_free(drv->event_buf);
        os_free(drv);
 }
 
+
+static int
+bsd_commit(void *priv)
+{
+       return bsd_ctrl_iface(priv, 1);
+}
+
+
+static int
+bsd_set_sta_authorized(void *priv, const u8 *addr,
+                      int total_flags, int flags_or, int flags_and)
+{
+       int authorized = -1;
+
+       /* For now, only support setting Authorized flag */
+       if (flags_or & WPA_STA_AUTHORIZED)
+               authorized = 1;
+       if (!(flags_and & WPA_STA_AUTHORIZED))
+               authorized = 0;
+
+       if (authorized < 0)
+               return 0;
+
+       return bsd_send_mlme_param(priv, authorized ?
+                                  IEEE80211_MLME_AUTHORIZE :
+                                  IEEE80211_MLME_UNAUTHORIZE, 0, addr);
+}
 #else /* HOSTAPD */
 
 static int
@@ -1053,9 +1089,9 @@ wpa_driver_bsd_associate(void *priv, struct wpa_driver_associate_params *params)
        if (wpa_driver_bsd_set_wpa_ie(drv, params->wpa_ie, params->wpa_ie_len) < 0)
                return -1;
 
-       privacy = !(params->pairwise_suite == CIPHER_NONE &&
-           params->group_suite == CIPHER_NONE &&
-           params->key_mgmt_suite == KEY_MGMT_NONE &&
+       privacy = !(params->pairwise_suite == WPA_CIPHER_NONE &&
+           params->group_suite == WPA_CIPHER_NONE &&
+           params->key_mgmt_suite == WPA_KEY_MGMT_NONE &&
            params->wpa_ie_len == 0);
        wpa_printf(MSG_DEBUG, "%s: set PRIVACY %u", __func__, privacy);
 
@@ -1151,7 +1187,6 @@ static void
 wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx)
 {
        struct bsd_driver_data *drv = sock_ctx;
-       char *buf;
        struct if_announcemsghdr *ifan;
        struct if_msghdr *ifm;
        struct rt_msghdr *rtm;
@@ -1159,30 +1194,20 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx)
        struct ieee80211_michael_event *mic;
        struct ieee80211_leave_event *leave;
        struct ieee80211_join_event *join;
-       int n, len;
-
-       len = rtbuf_len();
-
-       buf = os_malloc(len);
-       if (buf == NULL) {
-               wpa_printf(MSG_ERROR, "%s os_malloc() failed\n", __func__);
-               return;
-       }
+       int n;
 
-       n = read(sock, buf, len);
+       n = read(sock, drv->event_buf, drv->event_buf_len);
        if (n < 0) {
                if (errno != EINTR && errno != EAGAIN)
-                       wpa_printf(MSG_ERROR, "%s read() failed: %s\n",
+                       wpa_printf(MSG_ERROR, "%s read() failed: %s",
                                   __func__, strerror(errno));
-               os_free(buf);
                return;
        }
 
-       rtm = (struct rt_msghdr *) buf;
+       rtm = (struct rt_msghdr *) drv->event_buf;
        if (rtm->rtm_version != RTM_VERSION) {
                wpa_printf(MSG_DEBUG, "Invalid routing message version=%d",
                           rtm->rtm_version);
-               os_free(buf);
                return;
        }
        os_memset(&event, 0, sizeof(event));
@@ -1197,7 +1222,6 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx)
                case IFAN_DEPARTURE:
                        event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
                default:
-                       os_free(buf);
                        return;
                }
                wpa_printf(MSG_DEBUG, "RTM_IFANNOUNCE: Interface '%s' %s",
@@ -1270,7 +1294,6 @@ wpa_driver_bsd_event_receive(int sock, void *ctx, void *sock_ctx)
                }
                break;
        }
-       os_free(buf);
 }
 
 static void
@@ -1295,6 +1318,11 @@ wpa_driver_bsd_add_scan_entry(struct wpa_scan_results *res,
        result->caps = sr->isr_capinfo;
        result->qual = sr->isr_rssi;
        result->noise = sr->isr_noise;
+       /*
+        * the rssi value reported by the kernel is in 0.5dB steps relative to
+        * the reported noise floor. see ieee80211_node.h for details.
+        */
+       result->level = sr->isr_rssi / 2 + sr->isr_noise;
 
        pos = (u8 *)(result + 1);
 
@@ -1316,7 +1344,12 @@ wpa_driver_bsd_add_scan_entry(struct wpa_scan_results *res,
        *pos++ = 1;
        *pos++ = sr->isr_erp;
 
+#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
+       os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len + sr->isr_meshid_len,
+                 sr->isr_ie_len);
+#else
        os_memcpy(pos, (u8 *)(sr + 1) + sr->isr_ssid_len, sr->isr_ie_len);
+#endif
        pos += sr->isr_ie_len;
 
        result->ie_len = pos - (u8 *)(result + 1);
@@ -1436,6 +1469,33 @@ static int wpa_driver_bsd_capa(struct bsd_driver_data *drv)
        return 0;
 }
 
+static enum ieee80211_opmode
+get80211opmode(struct bsd_driver_data *drv)
+{
+       struct ifmediareq ifmr;
+
+       (void) memset(&ifmr, 0, sizeof(ifmr));
+       (void) os_strlcpy(ifmr.ifm_name, drv->ifname, sizeof(ifmr.ifm_name));
+
+       if (ioctl(drv->sock, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0) {
+               if (ifmr.ifm_current & IFM_IEEE80211_ADHOC) {
+                       if (ifmr.ifm_current & IFM_FLAG0)
+                               return IEEE80211_M_AHDEMO;
+                       else
+                               return IEEE80211_M_IBSS;
+               }
+               if (ifmr.ifm_current & IFM_IEEE80211_HOSTAP)
+                       return IEEE80211_M_HOSTAP;
+               if (ifmr.ifm_current & IFM_IEEE80211_MONITOR)
+                       return IEEE80211_M_MONITOR;
+#ifdef IEEE80211_M_MBSS
+               if (ifmr.ifm_current & IFM_IEEE80211_MBSS)
+                       return IEEE80211_M_MBSS;
+#endif /* IEEE80211_M_MBSS */
+       }
+       return IEEE80211_M_STA;
+}
+
 static void *
 wpa_driver_bsd_init(void *ctx, const char *ifname)
 {
@@ -1446,6 +1506,15 @@ wpa_driver_bsd_init(void *ctx, const char *ifname)
        drv = os_zalloc(sizeof(*drv));
        if (drv == NULL)
                return NULL;
+
+       drv->event_buf_len = rtbuf_len();
+
+       drv->event_buf = os_malloc(drv->event_buf_len);
+       if (drv->event_buf == NULL) {
+               wpa_printf(MSG_ERROR, "%s: os_malloc() failed", __func__);
+               goto fail1;
+       }
+
        /*
         * NB: We require the interface name be mappable to an index.
         *     This implies we do not support having wpa_supplicant
@@ -1461,6 +1530,12 @@ wpa_driver_bsd_init(void *ctx, const char *ifname)
        drv->sock = socket(PF_INET, SOCK_DGRAM, 0);
        if (drv->sock < 0)
                goto fail1;
+
+       os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+       /* Down interface during setup. */
+       if (bsd_ctrl_iface(drv, 0) < 0)
+               goto fail;
+
        drv->route = socket(PF_ROUTE, SOCK_RAW, 0);
        if (drv->route < 0)
                goto fail;
@@ -1468,11 +1543,6 @@ wpa_driver_bsd_init(void *ctx, const char *ifname)
                wpa_driver_bsd_event_receive, ctx, drv);
 
        drv->ctx = ctx;
-       os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
-
-       /* Down interface during setup. */
-       if (bsd_ctrl_iface(drv, 0) < 0)
-               goto fail;
 
        if (!GETPARAM(drv, IEEE80211_IOC_ROAMING, drv->prev_roaming)) {
                wpa_printf(MSG_DEBUG, "%s: failed to get roaming state: %s",
@@ -1493,10 +1563,13 @@ wpa_driver_bsd_init(void *ctx, const char *ifname)
        if (wpa_driver_bsd_capa(drv))
                goto fail;
 
+       drv->opmode = get80211opmode(drv);
+
        return drv;
 fail:
        close(drv->sock);
 fail1:
+       os_free(drv->event_buf);
        os_free(drv);
        return NULL;
 #undef GETPARAM
@@ -1522,6 +1595,7 @@ wpa_driver_bsd_deinit(void *priv)
                l2_packet_deinit(drv->sock_xmit);
        (void) close(drv->route);               /* ioctl socket */
        (void) close(drv->sock);                /* event socket */
+       os_free(drv->event_buf);
        os_free(drv);
 }
 
@@ -1548,6 +1622,8 @@ const struct wpa_driver_ops wpa_driver_bsd_ops = {
        .read_sta_data          = bsd_read_sta_driver_data,
        .sta_disassoc           = bsd_sta_disassoc,
        .sta_deauth             = bsd_sta_deauth,
+       .sta_set_flags          = bsd_set_sta_authorized,
+       .commit                 = bsd_commit,
 #else /* HOSTAPD */
        .init                   = wpa_driver_bsd_init,
        .deinit                 = wpa_driver_bsd_deinit,
@@ -1566,6 +1642,5 @@ const struct wpa_driver_ops wpa_driver_bsd_ops = {
        .hapd_set_ssid          = bsd_set_ssid,
        .hapd_get_ssid          = bsd_get_ssid,
        .hapd_send_eapol        = bsd_send_eapol,
-       .sta_set_flags          = bsd_set_sta_authorized,
        .set_generic_elem       = bsd_set_opt_ie,
 };
index 12ccc14..aebea8c 100644 (file)
@@ -44,15 +44,12 @@ const char * event_to_string(enum wpa_event_type event)
        E2S(ASSOC_REJECT);
        E2S(AUTH_TIMED_OUT);
        E2S(ASSOC_TIMED_OUT);
-       E2S(FT_RRB_RX);
        E2S(WPS_BUTTON_PUSHED);
        E2S(TX_STATUS);
        E2S(RX_FROM_UNKNOWN);
        E2S(RX_MGMT);
-       E2S(RX_ACTION);
        E2S(REMAIN_ON_CHANNEL);
        E2S(CANCEL_REMAIN_ON_CHANNEL);
-       E2S(MLME_RX);
        E2S(RX_PROBE_REQ);
        E2S(NEW_STA);
        E2S(EAPOL_RX);
@@ -65,13 +62,6 @@ const char * event_to_string(enum wpa_event_type event)
        E2S(UNPROT_DEAUTH);
        E2S(UNPROT_DISASSOC);
        E2S(STATION_LOW_ACK);
-       E2S(P2P_DEV_FOUND);
-       E2S(P2P_GO_NEG_REQ_RX);
-       E2S(P2P_GO_NEG_COMPLETED);
-       E2S(P2P_PROV_DISC_REQUEST);
-       E2S(P2P_PROV_DISC_RESPONSE);
-       E2S(P2P_SD_REQUEST);
-       E2S(P2P_SD_RESPONSE);
        E2S(IBSS_PEER_LOST);
        E2S(DRIVER_GTK_REKEY);
        E2S(SCHED_SCAN_STOPPED);
@@ -84,8 +74,147 @@ const char * event_to_string(enum wpa_event_type event)
        E2S(DFS_CAC_FINISHED);
        E2S(DFS_CAC_ABORTED);
        E2S(DFS_NOP_FINISHED);
+       E2S(SURVEY);
+       E2S(SCAN_STARTED);
+       E2S(AVOID_FREQUENCIES);
+       E2S(NEW_PEER_CANDIDATE);
+       E2S(ACS_CHANNEL_SELECTED);
+       E2S(DFS_CAC_STARTED);
        }
 
        return "UNKNOWN";
 #undef E2S
 }
+
+
+const char * channel_width_to_string(enum chan_width width)
+{
+       switch (width) {
+       case CHAN_WIDTH_20_NOHT:
+               return "20 MHz (no HT)";
+       case CHAN_WIDTH_20:
+               return "20 MHz";
+       case CHAN_WIDTH_40:
+               return "40 MHz";
+       case CHAN_WIDTH_80:
+               return "80 MHz";
+       case CHAN_WIDTH_80P80:
+               return "80+80 MHz";
+       case CHAN_WIDTH_160:
+               return "160 MHz";
+       default:
+               return "unknown";
+       }
+}
+
+
+int ht_supported(const struct hostapd_hw_modes *mode)
+{
+       if (!(mode->flags & HOSTAPD_MODE_FLAG_HT_INFO_KNOWN)) {
+               /*
+                * The driver did not indicate whether it supports HT. Assume
+                * it does to avoid connection issues.
+                */
+               return 1;
+       }
+
+       /*
+        * IEEE Std 802.11n-2009 20.1.1:
+        * An HT non-AP STA shall support all EQM rates for one spatial stream.
+        */
+       return mode->mcs_set[0] == 0xff;
+}
+
+
+int vht_supported(const struct hostapd_hw_modes *mode)
+{
+       if (!(mode->flags & HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN)) {
+               /*
+                * The driver did not indicate whether it supports VHT. Assume
+                * it does to avoid connection issues.
+                */
+               return 1;
+       }
+
+       /*
+        * A VHT non-AP STA shall support MCS 0-7 for one spatial stream.
+        * TODO: Verify if this complies with the standard
+        */
+       return (mode->vht_mcs_set[0] & 0x3) != 3;
+}
+
+
+static int wpa_check_wowlan_trigger(const char *start, const char *trigger,
+                                   int capa_trigger, u8 *param_trigger)
+{
+       if (os_strcmp(start, trigger) != 0)
+               return 0;
+       if (!capa_trigger)
+               return 0;
+
+       *param_trigger = 1;
+       return 1;
+}
+
+
+struct wowlan_triggers *
+wpa_get_wowlan_triggers(const char *wowlan_triggers,
+                       const struct wpa_driver_capa *capa)
+{
+       struct wowlan_triggers *triggers;
+       char *start, *end, *buf;
+       int last;
+
+       if (!wowlan_triggers)
+               return NULL;
+
+       buf = os_strdup(wowlan_triggers);
+       if (buf == NULL)
+               return NULL;
+
+       triggers = os_zalloc(sizeof(*triggers));
+       if (triggers == NULL)
+               goto out;
+
+#define CHECK_TRIGGER(trigger) \
+       wpa_check_wowlan_trigger(start, #trigger,                       \
+                                 capa->wowlan_triggers.trigger,        \
+                                 &triggers->trigger)
+
+       start = buf;
+       while (*start != '\0') {
+               while (isblank(*start))
+                       start++;
+               if (*start == '\0')
+                       break;
+               end = start;
+               while (!isblank(*end) && *end != '\0')
+                       end++;
+               last = *end == '\0';
+               *end = '\0';
+
+               if (!CHECK_TRIGGER(any) &&
+                   !CHECK_TRIGGER(disconnect) &&
+                   !CHECK_TRIGGER(magic_pkt) &&
+                   !CHECK_TRIGGER(gtk_rekey_failure) &&
+                   !CHECK_TRIGGER(eap_identity_req) &&
+                   !CHECK_TRIGGER(four_way_handshake) &&
+                   !CHECK_TRIGGER(rfkill_release)) {
+                       wpa_printf(MSG_DEBUG,
+                                  "Unknown/unsupported wowlan trigger '%s'",
+                                  start);
+                       os_free(triggers);
+                       triggers = NULL;
+                       goto out;
+               }
+
+               if (last)
+                       break;
+               start = end + 1;
+       }
+#undef CHECK_TRIGGER
+
+out:
+       os_free(buf);
+       return triggers;
+}
index 16f5563..84b98fb 100644 (file)
@@ -214,7 +214,7 @@ static void handle_read(int sock, void *eloop_ctx, void *sock_ctx)
 
        len = recv(sock, buf, sizeof(buf), 0);
        if (len < 0) {
-               perror("recv");
+               wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
                return;
        }
 
@@ -229,19 +229,21 @@ static int hostap_init_sockets(struct hostap_driver_data *drv, u8 *own_addr)
 
        drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
        if (drv->sock < 0) {
-               perror("socket[PF_PACKET,SOCK_RAW]");
+               wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
+                          strerror(errno));
                return -1;
        }
 
        if (eloop_register_read_sock(drv->sock, handle_read, drv, NULL)) {
-               printf("Could not register read socket\n");
+               wpa_printf(MSG_ERROR, "Could not register read socket");
                return -1;
        }
 
         memset(&ifr, 0, sizeof(ifr));
         snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap", drv->iface);
         if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) {
-               perror("ioctl(SIOCGIFINDEX)");
+               wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
+                          strerror(errno));
                return -1;
         }
 
@@ -256,7 +258,7 @@ static int hostap_init_sockets(struct hostap_driver_data *drv, u8 *own_addr)
                   addr.sll_ifindex);
 
        if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-               perror("bind");
+               wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
                return -1;
        }
 
@@ -361,9 +363,9 @@ static int hostap_set_iface_flags(void *priv, int dev_up)
                os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
                ifr.ifr_mtu = HOSTAPD_MTU;
                if (ioctl(drv->ioctl_sock, SIOCSIFMTU, &ifr) != 0) {
-                       perror("ioctl[SIOCSIFMTU]");
-                       printf("Setting MTU failed - trying to survive with "
-                              "current value\n");
+                       wpa_printf(MSG_INFO,
+                                  "Setting MTU failed - trying to survive with current value: ioctl[SIOCSIFMTU]: %s",
+                                  strerror(errno));
                }
        }
 
@@ -383,7 +385,8 @@ static int hostapd_ioctl(void *priv, struct prism2_hostapd_param *param,
        iwr.u.data.length = len;
 
        if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_HOSTAPD, &iwr) < 0) {
-               perror("ioctl[PRISM2_IOCTL_HOSTAPD]");
+               wpa_printf(MSG_ERROR, "ioctl[PRISM2_IOCTL_HOSTAPD]: %s",
+                          strerror(errno));
                return -1;
        }
 
@@ -497,7 +500,8 @@ static int hostap_ioctl_prism2param(void *priv, int param, int value)
        *i++ = value;
 
        if (ioctl(drv->ioctl_sock, PRISM2_IOCTL_PRISM2_PARAM, &iwr) < 0) {
-               perror("ioctl[PRISM2_IOCTL_PRISM2_PARAM]");
+               wpa_printf(MSG_ERROR, "ioctl[PRISM2_IOCTL_PRISM2_PARAM]: %s",
+                          strerror(errno));
                return -1;
        }
 
@@ -554,8 +558,8 @@ static int hostap_set_ssid(void *priv, const u8 *buf, int len)
        iwr.u.essid.length = len + 1;
 
        if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
-               perror("ioctl[SIOCSIWESSID]");
-               printf("len=%d\n", len);
+               wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID,len=%d]: %s",
+                          len, strerror(errno));
                return -1;
        }
 
@@ -919,8 +923,9 @@ static int hostap_get_we_version(struct hostap_driver_data *drv)
                sizeof(range->enc_capa);
 
        if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) {
-               perror("ioctl[SIOCGIWRANGE]");
-               free(range);
+               wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s",
+                          strerror(errno));
+               os_free(range);
                return -1;
        } else if (iwr.u.data.length >= minlen &&
                   range->we_version_compiled >= 18) {
@@ -975,23 +980,25 @@ static void * hostap_init(struct hostapd_data *hapd,
 
        drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
        if (drv->ioctl_sock < 0) {
-               perror("socket[PF_INET,SOCK_DGRAM]");
-               free(drv);
+               wpa_printf(MSG_ERROR, "socket[PF_INET,SOCK_DGRAM]: %s",
+                          strerror(errno));
+               os_free(drv);
                return NULL;
        }
 
        if (hostap_ioctl_prism2param(drv, PRISM2_PARAM_HOSTAPD, 1)) {
-               printf("Could not enable hostapd mode for interface %s\n",
-                      drv->iface);
+               wpa_printf(MSG_ERROR,
+                          "Could not enable hostapd mode for interface %s",
+                          drv->iface);
                close(drv->ioctl_sock);
-               free(drv);
+               os_free(drv);
                return NULL;
        }
 
        if (hostap_init_sockets(drv, params->own_addr) ||
            hostap_wireless_event_init(drv)) {
                close(drv->ioctl_sock);
-               free(drv);
+               os_free(drv);
                return NULL;
        }
 
@@ -1060,7 +1067,8 @@ static int hostap_set_freq(void *priv, struct hostapd_freq_params *freq)
        iwr.u.freq.e = 0;
 
        if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) {
-               perror("ioctl[SIOCSIWFREQ]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCSIWFREQ]: %s",
+                          strerror(errno));
                return -1;
        }
 
diff --git a/src/drivers/driver_macsec_qca.c b/src/drivers/driver_macsec_qca.c
new file mode 100644 (file)
index 0000000..3eae2f8
--- /dev/null
@@ -0,0 +1,891 @@
+/*
+ * Wired Ethernet driver interface for QCA MACsec driver
+ * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004, Gunter Burchardt <tira@isx.de>
+ * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <net/if.h>
+#ifdef __linux__
+#include <netpacket/packet.h>
+#include <net/if_arp.h>
+#include <net/if.h>
+#endif /* __linux__ */
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+#include <net/if_dl.h>
+#include <net/if_media.h>
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
+#ifdef __sun__
+#include <sys/sockio.h>
+#endif /* __sun__ */
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/defs.h"
+#include "common/ieee802_1x_defs.h"
+#include "driver.h"
+
+#include "nss_macsec_secy.h"
+#include "nss_macsec_secy_rx.h"
+#include "nss_macsec_secy_tx.h"
+
+#define MAXSC 16
+
+/* TCI field definition */
+#define TCI_ES                0x40
+#define TCI_SC                0x20
+#define TCI_SCB               0x10
+#define TCI_E                 0x08
+#define TCI_C                 0x04
+
+#ifdef _MSC_VER
+#pragma pack(push, 1)
+#endif /* _MSC_VER */
+
+#ifdef _MSC_VER
+#pragma pack(pop)
+#endif /* _MSC_VER */
+
+static const u8 pae_group_addr[ETH_ALEN] =
+{ 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 };
+
+struct macsec_qca_data {
+       char ifname[IFNAMSIZ + 1];
+       u32 secy_id;
+       void *ctx;
+
+       int sock; /* raw packet socket for driver access */
+       int pf_sock;
+       int membership, multi, iff_allmulti, iff_up;
+
+       /* shadow */
+       Boolean always_include_sci;
+       Boolean use_es;
+       Boolean use_scb;
+       Boolean protect_frames;
+       Boolean replay_protect;
+       u32 replay_window;
+};
+
+
+static int macsec_qca_multicast_membership(int sock, int ifindex,
+                                          const u8 *addr, int add)
+{
+#ifdef __linux__
+       struct packet_mreq mreq;
+
+       if (sock < 0)
+               return -1;
+
+       os_memset(&mreq, 0, sizeof(mreq));
+       mreq.mr_ifindex = ifindex;
+       mreq.mr_type = PACKET_MR_MULTICAST;
+       mreq.mr_alen = ETH_ALEN;
+       os_memcpy(mreq.mr_address, addr, ETH_ALEN);
+
+       if (setsockopt(sock, SOL_PACKET,
+                      add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP,
+                      &mreq, sizeof(mreq)) < 0) {
+               wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno));
+               return -1;
+       }
+       return 0;
+#else /* __linux__ */
+       return -1;
+#endif /* __linux__ */
+}
+
+
+static int macsec_qca_get_ssid(void *priv, u8 *ssid)
+{
+       ssid[0] = 0;
+       return 0;
+}
+
+
+static int macsec_qca_get_bssid(void *priv, u8 *bssid)
+{
+       /* Report PAE group address as the "BSSID" for macsec connection. */
+       os_memcpy(bssid, pae_group_addr, ETH_ALEN);
+       return 0;
+}
+
+
+static int macsec_qca_get_capa(void *priv, struct wpa_driver_capa *capa)
+{
+       os_memset(capa, 0, sizeof(*capa));
+       capa->flags = WPA_DRIVER_FLAGS_WIRED;
+       return 0;
+}
+
+
+static int macsec_qca_get_ifflags(const char *ifname, int *flags)
+{
+       struct ifreq ifr;
+       int s;
+
+       s = socket(PF_INET, SOCK_DGRAM, 0);
+       if (s < 0) {
+               wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+               return -1;
+       }
+
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+       if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
+               wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
+                          strerror(errno));
+               close(s);
+               return -1;
+       }
+       close(s);
+       *flags = ifr.ifr_flags & 0xffff;
+       return 0;
+}
+
+
+static int macsec_qca_set_ifflags(const char *ifname, int flags)
+{
+       struct ifreq ifr;
+       int s;
+
+       s = socket(PF_INET, SOCK_DGRAM, 0);
+       if (s < 0) {
+               wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+               return -1;
+       }
+
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+       ifr.ifr_flags = flags & 0xffff;
+       if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
+               wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
+                          strerror(errno));
+               close(s);
+               return -1;
+       }
+       close(s);
+       return 0;
+}
+
+
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+static int macsec_qca_get_ifstatus(const char *ifname, int *status)
+{
+       struct ifmediareq ifmr;
+       int s;
+
+       s = socket(PF_INET, SOCK_DGRAM, 0);
+       if (s < 0) {
+               wpa_print(MSG_ERROR, "socket: %s", strerror(errno));
+               return -1;
+       }
+
+       os_memset(&ifmr, 0, sizeof(ifmr));
+       os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ);
+       if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) {
+               wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s",
+                          strerror(errno));
+               close(s);
+               return -1;
+       }
+       close(s);
+       *status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) ==
+               (IFM_ACTIVE | IFM_AVALID);
+
+       return 0;
+}
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+
+
+static int macsec_qca_multi(const char *ifname, const u8 *addr, int add)
+{
+       struct ifreq ifr;
+       int s;
+
+#ifdef __sun__
+       return -1;
+#endif /* __sun__ */
+
+       s = socket(PF_INET, SOCK_DGRAM, 0);
+       if (s < 0) {
+               wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+               return -1;
+       }
+
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
+#ifdef __linux__
+       ifr.ifr_hwaddr.sa_family = AF_UNSPEC;
+       os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN);
+#endif /* __linux__ */
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+       {
+               struct sockaddr_dl *dlp;
+               dlp = (struct sockaddr_dl *) &ifr.ifr_addr;
+               dlp->sdl_len = sizeof(struct sockaddr_dl);
+               dlp->sdl_family = AF_LINK;
+               dlp->sdl_index = 0;
+               dlp->sdl_nlen = 0;
+               dlp->sdl_alen = ETH_ALEN;
+               dlp->sdl_slen = 0;
+               os_memcpy(LLADDR(dlp), addr, ETH_ALEN);
+       }
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+#if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__)
+       {
+               struct sockaddr *sap;
+               sap = (struct sockaddr *) &ifr.ifr_addr;
+               sap->sa_len = sizeof(struct sockaddr);
+               sap->sa_family = AF_UNSPEC;
+               os_memcpy(sap->sa_data, addr, ETH_ALEN);
+       }
+#endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */
+
+       if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) {
+               wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s",
+                          strerror(errno));
+               close(s);
+               return -1;
+       }
+       close(s);
+       return 0;
+}
+
+
+static void __macsec_drv_init(struct macsec_qca_data *drv)
+{
+       int ret = 0;
+       fal_rx_ctl_filt_t rx_ctl_filt;
+       fal_tx_ctl_filt_t tx_ctl_filt;
+
+       wpa_printf(MSG_INFO, "%s: secy_id=%d", __func__, drv->secy_id);
+
+       /* Enable Secy and Let EAPoL bypass */
+       ret = nss_macsec_secy_en_set(drv->secy_id, TRUE);
+       if (ret)
+               wpa_printf(MSG_ERROR, "nss_macsec_secy_en_set: FAIL");
+
+       ret = nss_macsec_secy_sc_sa_mapping_mode_set(drv->secy_id,
+                                                    FAL_SC_SA_MAP_1_4);
+       if (ret)
+               wpa_printf(MSG_ERROR,
+                          "nss_macsec_secy_sc_sa_mapping_mode_set: FAIL");
+
+       os_memset(&rx_ctl_filt, 0, sizeof(rx_ctl_filt));
+       rx_ctl_filt.bypass = 1;
+       rx_ctl_filt.match_type = IG_CTL_COMPARE_ETHER_TYPE;
+       rx_ctl_filt.match_mask = 0xffff;
+       rx_ctl_filt.ether_type_da_range = 0x888e;
+       ret = nss_macsec_secy_rx_ctl_filt_set(drv->secy_id, 0, &rx_ctl_filt);
+       if (ret)
+               wpa_printf(MSG_ERROR, "nss_macsec_secy_rx_ctl_filt_set: FAIL");
+
+       os_memset(&tx_ctl_filt, 0, sizeof(tx_ctl_filt));
+       tx_ctl_filt.bypass = 1;
+       tx_ctl_filt.match_type = EG_CTL_COMPARE_ETHER_TYPE;
+       tx_ctl_filt.match_mask = 0xffff;
+       tx_ctl_filt.ether_type_da_range = 0x888e;
+       ret = nss_macsec_secy_tx_ctl_filt_set(drv->secy_id, 0, &tx_ctl_filt);
+       if (ret)
+               wpa_printf(MSG_ERROR, "nss_macsec_secy_tx_ctl_filt_set: FAIL");
+}
+
+
+static void __macsec_drv_deinit(struct macsec_qca_data *drv)
+{
+       nss_macsec_secy_en_set(drv->secy_id, FALSE);
+       nss_macsec_secy_rx_sc_del_all(drv->secy_id);
+       nss_macsec_secy_tx_sc_del_all(drv->secy_id);
+}
+
+
+static void * macsec_qca_init(void *ctx, const char *ifname)
+{
+       struct macsec_qca_data *drv;
+       int flags;
+
+       drv = os_zalloc(sizeof(*drv));
+       if (drv == NULL)
+               return NULL;
+       os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
+       drv->ctx = ctx;
+
+       /* Board specific settings */
+       if (os_memcmp("eth2", drv->ifname, 4) == 0)
+               drv->secy_id = 1;
+       else if (os_memcmp("eth3", drv->ifname, 4) == 0)
+               drv->secy_id = 2;
+       else
+               drv->secy_id = -1;
+
+#ifdef __linux__
+       drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
+       if (drv->pf_sock < 0)
+               wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno));
+#else /* __linux__ */
+       drv->pf_sock = -1;
+#endif /* __linux__ */
+
+       if (macsec_qca_get_ifflags(ifname, &flags) == 0 &&
+           !(flags & IFF_UP) &&
+           macsec_qca_set_ifflags(ifname, flags | IFF_UP) == 0) {
+               drv->iff_up = 1;
+       }
+
+       if (macsec_qca_multicast_membership(drv->pf_sock,
+                                           if_nametoindex(drv->ifname),
+                                           pae_group_addr, 1) == 0) {
+               wpa_printf(MSG_DEBUG,
+                          "%s: Added multicast membership with packet socket",
+                          __func__);
+               drv->membership = 1;
+       } else if (macsec_qca_multi(ifname, pae_group_addr, 1) == 0) {
+               wpa_printf(MSG_DEBUG,
+                          "%s: Added multicast membership with SIOCADDMULTI",
+                          __func__);
+               drv->multi = 1;
+       } else if (macsec_qca_get_ifflags(ifname, &flags) < 0) {
+               wpa_printf(MSG_INFO, "%s: Could not get interface flags",
+                          __func__);
+               os_free(drv);
+               return NULL;
+       } else if (flags & IFF_ALLMULTI) {
+               wpa_printf(MSG_DEBUG,
+                          "%s: Interface is already configured for multicast",
+                          __func__);
+       } else if (macsec_qca_set_ifflags(ifname, flags | IFF_ALLMULTI) < 0) {
+               wpa_printf(MSG_INFO, "%s: Failed to enable allmulti",
+                          __func__);
+               os_free(drv);
+               return NULL;
+       } else {
+               wpa_printf(MSG_DEBUG, "%s: Enabled allmulti mode", __func__);
+               drv->iff_allmulti = 1;
+       }
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+       {
+               int status;
+               wpa_printf(MSG_DEBUG, "%s: waiting for link to become active",
+                          __func__);
+               while (macsec_qca_get_ifstatus(ifname, &status) == 0 &&
+                      status == 0)
+                       sleep(1);
+       }
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+
+       return drv;
+}
+
+
+static void macsec_qca_deinit(void *priv)
+{
+       struct macsec_qca_data *drv = priv;
+       int flags;
+
+       if (drv->membership &&
+           macsec_qca_multicast_membership(drv->pf_sock,
+                                           if_nametoindex(drv->ifname),
+                                           pae_group_addr, 0) < 0) {
+               wpa_printf(MSG_DEBUG,
+                          "%s: Failed to remove PAE multicast group (PACKET)",
+                          __func__);
+       }
+
+       if (drv->multi &&
+           macsec_qca_multi(drv->ifname, pae_group_addr, 0) < 0) {
+               wpa_printf(MSG_DEBUG,
+                          "%s: Failed to remove PAE multicast group (SIOCDELMULTI)",
+                          __func__);
+       }
+
+       if (drv->iff_allmulti &&
+           (macsec_qca_get_ifflags(drv->ifname, &flags) < 0 ||
+            macsec_qca_set_ifflags(drv->ifname, flags & ~IFF_ALLMULTI) < 0)) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to disable allmulti mode",
+                          __func__);
+       }
+
+       if (drv->iff_up &&
+           macsec_qca_get_ifflags(drv->ifname, &flags) == 0 &&
+           (flags & IFF_UP) &&
+           macsec_qca_set_ifflags(drv->ifname, flags & ~IFF_UP) < 0) {
+               wpa_printf(MSG_DEBUG, "%s: Failed to set the interface down",
+                          __func__);
+       }
+
+       if (drv->pf_sock != -1)
+               close(drv->pf_sock);
+
+       os_free(drv);
+}
+
+
+static int macsec_qca_macsec_init(void *priv, struct macsec_init_params *params)
+{
+       struct macsec_qca_data *drv = priv;
+
+       drv->always_include_sci = params->always_include_sci;
+       drv->use_es = params->use_es;
+       drv->use_scb = params->use_scb;
+
+       wpa_printf(MSG_DEBUG, "%s: es=%d, scb=%d, sci=%d",
+                  __func__, drv->use_es, drv->use_scb,
+                  drv->always_include_sci);
+
+       __macsec_drv_init(drv);
+
+       return 0;
+}
+
+
+static int macsec_qca_macsec_deinit(void *priv)
+{
+       struct macsec_qca_data *drv = priv;
+
+       wpa_printf(MSG_DEBUG, "%s", __func__);
+
+       __macsec_drv_deinit(drv);
+
+       return 0;
+}
+
+
+static int macsec_qca_enable_protect_frames(void *priv, Boolean enabled)
+{
+       struct macsec_qca_data *drv = priv;
+       int ret = 0;
+
+       wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled);
+
+       drv->protect_frames = enabled;
+
+       return ret;
+}
+
+
+static int macsec_qca_set_replay_protect(void *priv, Boolean enabled,
+                                        unsigned int window)
+{
+       struct macsec_qca_data *drv = priv;
+       int ret = 0;
+
+       wpa_printf(MSG_DEBUG, "%s: enabled=%d, win=%u",
+                  __func__, enabled, window);
+
+       drv->replay_protect = enabled;
+       drv->replay_window = window;
+
+       return ret;
+}
+
+
+static int macsec_qca_set_current_cipher_suite(void *priv, const u8 *cs,
+                                              size_t cs_len)
+{
+       u8 default_cs_id[] = CS_ID_GCM_AES_128;
+
+       if (cs_len != CS_ID_LEN ||
+           os_memcmp(cs, default_cs_id, cs_len) != 0) {
+               wpa_hexdump(MSG_ERROR, "macsec: NOT supported CipherSuite",
+                           cs, cs_len);
+               return -1;
+       }
+
+       /* Support default Cipher Suite 0080020001000001 (GCM-AES-128) */
+       wpa_printf(MSG_DEBUG, "%s: default support aes-gcm-128", __func__);
+
+       return 0;
+}
+
+
+static int macsec_qca_enable_controlled_port(void *priv, Boolean enabled)
+{
+       struct macsec_qca_data *drv = priv;
+       int ret = 0;
+
+       wpa_printf(MSG_DEBUG, "%s: enable=%d", __func__, enabled);
+
+       ret += nss_macsec_secy_controlled_port_en_set(drv->secy_id, enabled);
+
+       return ret;
+}
+
+
+static int macsec_qca_get_receive_lowest_pn(void *priv, u32 channel, u8 an,
+                                           u32 *lowest_pn)
+{
+       struct macsec_qca_data *drv = priv;
+       int ret = 0;
+       u32 next_pn = 0;
+       bool enabled = FALSE;
+       u32 win;
+
+       ret += nss_macsec_secy_rx_sa_next_pn_get(drv->secy_id, channel, an,
+                                                &next_pn);
+       ret += nss_macsec_secy_rx_sc_replay_protect_get(drv->secy_id, channel,
+                                                       &enabled);
+       ret += nss_macsec_secy_rx_sc_anti_replay_window_get(drv->secy_id,
+                                                           channel, &win);
+
+       if (enabled)
+               *lowest_pn = (next_pn > win) ? (next_pn - win) : 1;
+       else
+               *lowest_pn = next_pn;
+
+       wpa_printf(MSG_DEBUG, "%s: lpn=0x%x", __func__, *lowest_pn);
+
+       return ret;
+}
+
+
+static int macsec_qca_get_transmit_next_pn(void *priv, u32 channel, u8 an,
+                                          u32 *next_pn)
+{
+       struct macsec_qca_data *drv = priv;
+       int ret = 0;
+
+       ret += nss_macsec_secy_tx_sa_next_pn_get(drv->secy_id, channel, an,
+                                                next_pn);
+
+       wpa_printf(MSG_DEBUG, "%s: npn=0x%x", __func__, *next_pn);
+
+       return ret;
+}
+
+
+int macsec_qca_set_transmit_next_pn(void *priv, u32 channel, u8 an, u32 next_pn)
+{
+       struct macsec_qca_data *drv = priv;
+       int ret = 0;
+
+       ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, an,
+                                                next_pn);
+
+       wpa_printf(MSG_INFO, "%s: npn=0x%x", __func__, next_pn);
+
+       return ret;
+}
+
+
+static int macsec_qca_get_available_receive_sc(void *priv, u32 *channel)
+{
+       struct macsec_qca_data *drv = priv;
+       int ret = 0;
+       u32 sc_ch = 0;
+       bool in_use = FALSE;
+
+       for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) {
+               ret = nss_macsec_secy_rx_sc_in_used_get(drv->secy_id, sc_ch,
+                                                       &in_use);
+               if (ret)
+                       continue;
+
+               if (!in_use) {
+                       *channel = sc_ch;
+                       wpa_printf(MSG_DEBUG, "%s: channel=%d",
+                                  __func__, *channel);
+                       return 0;
+               }
+       }
+
+       wpa_printf(MSG_DEBUG, "%s: no available channel", __func__);
+
+       return -1;
+}
+
+
+static int macsec_qca_create_receive_sc(void *priv, u32 channel,
+                                       const u8 *sci_addr, u16 sci_port,
+                                       unsigned int conf_offset,
+                                       int validation)
+{
+       struct macsec_qca_data *drv = priv;
+       int ret = 0;
+       fal_rx_prc_lut_t entry;
+       fal_rx_sc_validate_frame_e vf;
+       enum validate_frames validate_frames = validation;
+
+       wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
+
+       /* rx prc lut */
+       os_memset(&entry, 0, sizeof(entry));
+
+       os_memcpy(entry.sci, sci_addr, ETH_ALEN);
+       entry.sci[6] = (sci_port >> 8) & 0xf;
+       entry.sci[7] = sci_port & 0xf;
+       entry.sci_mask = 0xf;
+
+       entry.valid = 1;
+       entry.channel = channel;
+       entry.action = FAL_RX_PRC_ACTION_PROCESS;
+       entry.offset = conf_offset;
+
+       /* rx validate frame  */
+       if (validate_frames == Strict)
+               vf = FAL_RX_SC_VALIDATE_FRAME_STRICT;
+       else if (validate_frames == Checked)
+               vf = FAL_RX_SC_VALIDATE_FRAME_CHECK;
+       else
+               vf = FAL_RX_SC_VALIDATE_FRAME_DISABLED;
+
+       ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry);
+       ret += nss_macsec_secy_rx_sc_create(drv->secy_id, channel);
+       ret += nss_macsec_secy_rx_sc_validate_frame_set(drv->secy_id, channel,
+                                                       vf);
+       ret += nss_macsec_secy_rx_sc_replay_protect_set(drv->secy_id, channel,
+                                                       drv->replay_protect);
+       ret += nss_macsec_secy_rx_sc_anti_replay_window_set(drv->secy_id,
+                                                           channel,
+                                                           drv->replay_window);
+
+       return ret;
+}
+
+
+static int macsec_qca_delete_receive_sc(void *priv, u32 channel)
+{
+       struct macsec_qca_data *drv = priv;
+       int ret = 0;
+       fal_rx_prc_lut_t entry;
+
+       wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
+
+       /* rx prc lut */
+       os_memset(&entry, 0, sizeof(entry));
+
+       ret += nss_macsec_secy_rx_sc_del(drv->secy_id, channel);
+       ret += nss_macsec_secy_rx_prc_lut_set(drv->secy_id, channel, &entry);
+
+       return ret;
+}
+
+
+static int macsec_qca_create_receive_sa(void *priv, u32 channel, u8 an,
+                                       u32 lowest_pn, const u8 *sak)
+{
+       struct macsec_qca_data *drv = priv;
+       int ret = 0;
+       fal_rx_sak_t rx_sak;
+       int i = 0;
+
+       wpa_printf(MSG_DEBUG, "%s, channel=%d, an=%d, lpn=0x%x",
+                  __func__, channel, an, lowest_pn);
+
+       os_memset(&rx_sak, 0, sizeof(rx_sak));
+       for (i = 0; i < 16; i++)
+               rx_sak.sak[i] = sak[15 - i];
+
+       ret += nss_macsec_secy_rx_sa_create(drv->secy_id, channel, an);
+       ret += nss_macsec_secy_rx_sak_set(drv->secy_id, channel, an, &rx_sak);
+
+       return ret;
+}
+
+
+static int macsec_qca_enable_receive_sa(void *priv, u32 channel, u8 an)
+{
+       struct macsec_qca_data *drv = priv;
+       int ret = 0;
+
+       wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+
+       ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, an, TRUE);
+
+       return ret;
+}
+
+
+static int macsec_qca_disable_receive_sa(void *priv, u32 channel, u8 an)
+{
+       struct macsec_qca_data *drv = priv;
+       int ret = 0;
+
+       wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+
+       ret += nss_macsec_secy_rx_sa_en_set(drv->secy_id, channel, an, FALSE);
+
+       return ret;
+}
+
+
+static int macsec_qca_get_available_transmit_sc(void *priv, u32 *channel)
+{
+       struct macsec_qca_data *drv = priv;
+       int ret = 0;
+       u32 sc_ch = 0;
+       bool in_use = FALSE;
+
+       for (sc_ch = 0; sc_ch < MAXSC; sc_ch++) {
+               ret = nss_macsec_secy_tx_sc_in_used_get(drv->secy_id, sc_ch,
+                                                       &in_use);
+               if (ret)
+                       continue;
+
+               if (!in_use) {
+                       *channel = sc_ch;
+                       wpa_printf(MSG_DEBUG, "%s: channel=%d",
+                                  __func__, *channel);
+                       return 0;
+               }
+       }
+
+       wpa_printf(MSG_DEBUG, "%s: no avaiable channel", __func__);
+
+       return -1;
+}
+
+
+static int macsec_qca_create_transmit_sc(void *priv, u32 channel,
+                                        const u8 *sci_addr, u16 sci_port,
+                                        unsigned int conf_offset)
+{
+       struct macsec_qca_data *drv = priv;
+       int ret = 0;
+       fal_tx_class_lut_t entry;
+       u8 psci[ETH_ALEN + 2];
+
+       wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
+
+       /* class lut */
+       os_memset(&entry, 0, sizeof(entry));
+
+       entry.valid = 1;
+       entry.action = FAL_TX_CLASS_ACTION_FORWARD;
+       entry.channel = channel;
+
+       os_memcpy(psci, sci_addr, ETH_ALEN);
+       psci[6] = (sci_port >> 8) & 0xf;
+       psci[7] = sci_port & 0xf;
+
+       ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry);
+       ret += nss_macsec_secy_tx_sc_create(drv->secy_id, channel, psci, 8);
+       ret += nss_macsec_secy_tx_sc_protect_set(drv->secy_id, channel,
+                                                drv->protect_frames);
+       ret += nss_macsec_secy_tx_sc_confidentiality_offset_set(drv->secy_id,
+                                                               channel,
+                                                               conf_offset);
+
+       return ret;
+}
+
+
+static int macsec_qca_delete_transmit_sc(void *priv, u32 channel)
+{
+       struct macsec_qca_data *drv = priv;
+       int ret = 0;
+       fal_tx_class_lut_t entry;
+
+       wpa_printf(MSG_DEBUG, "%s: channel=%d", __func__, channel);
+
+       /* class lut */
+       os_memset(&entry, 0, sizeof(entry));
+
+       ret += nss_macsec_secy_tx_class_lut_set(drv->secy_id, channel, &entry);
+       ret += nss_macsec_secy_tx_sc_del(drv->secy_id, channel);
+
+       return ret;
+}
+
+
+static int macsec_qca_create_transmit_sa(void *priv, u32 channel, u8 an,
+                                        u32 next_pn, Boolean confidentiality,
+                                        const u8 *sak)
+{
+       struct macsec_qca_data *drv = priv;
+       int ret = 0;
+       u8 tci = 0;
+       fal_tx_sak_t tx_sak;
+       int i;
+
+       wpa_printf(MSG_DEBUG,
+                  "%s: channel=%d, an=%d, next_pn=0x%x, confidentiality=%d",
+                  __func__, channel, an, next_pn, confidentiality);
+
+       if (drv->always_include_sci)
+               tci |= TCI_SC;
+       else if (drv->use_es)
+               tci |= TCI_ES;
+       else if (drv->use_scb)
+               tci |= TCI_SCB;
+
+       if (confidentiality)
+               tci |= TCI_E | TCI_C;
+
+       os_memset(&tx_sak, 0, sizeof(tx_sak));
+       for (i = 0; i < 16; i++)
+               tx_sak.sak[i] = sak[15 - i];
+
+       ret += nss_macsec_secy_tx_sa_next_pn_set(drv->secy_id, channel, an,
+                                                next_pn);
+       ret += nss_macsec_secy_tx_sak_set(drv->secy_id, channel, an, &tx_sak);
+       ret += nss_macsec_secy_tx_sc_tci_7_2_set(drv->secy_id, channel,
+                                                (tci >> 2));
+       ret += nss_macsec_secy_tx_sc_an_set(drv->secy_id, channel, an);
+
+       return ret;
+}
+
+
+static int macsec_qca_enable_transmit_sa(void *priv, u32 channel, u8 an)
+{
+       struct macsec_qca_data *drv = priv;
+       int ret = 0;
+
+       wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+
+       ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, an, TRUE);
+
+       return ret;
+}
+
+
+static int macsec_qca_disable_transmit_sa(void *priv, u32 channel, u8 an)
+{
+       struct macsec_qca_data *drv = priv;
+       int ret = 0;
+
+       wpa_printf(MSG_DEBUG, "%s: channel=%d, an=%d", __func__, channel, an);
+
+       ret += nss_macsec_secy_tx_sa_en_set(drv->secy_id, channel, an, FALSE);
+
+       return ret;
+}
+
+
+const struct wpa_driver_ops wpa_driver_macsec_qca_ops = {
+       .name = "macsec_qca",
+       .desc = "QCA MACsec Ethernet driver",
+       .get_ssid = macsec_qca_get_ssid,
+       .get_bssid = macsec_qca_get_bssid,
+       .get_capa = macsec_qca_get_capa,
+       .init = macsec_qca_init,
+       .deinit = macsec_qca_deinit,
+
+       .macsec_init = macsec_qca_macsec_init,
+       .macsec_deinit = macsec_qca_macsec_deinit,
+       .enable_protect_frames = macsec_qca_enable_protect_frames,
+       .set_replay_protect = macsec_qca_set_replay_protect,
+       .set_current_cipher_suite = macsec_qca_set_current_cipher_suite,
+       .enable_controlled_port = macsec_qca_enable_controlled_port,
+       .get_receive_lowest_pn = macsec_qca_get_receive_lowest_pn,
+       .get_transmit_next_pn = macsec_qca_get_transmit_next_pn,
+       .set_transmit_next_pn = macsec_qca_set_transmit_next_pn,
+       .get_available_receive_sc = macsec_qca_get_available_receive_sc,
+       .create_receive_sc = macsec_qca_create_receive_sc,
+       .delete_receive_sc = macsec_qca_delete_receive_sc,
+       .create_receive_sa = macsec_qca_create_receive_sa,
+       .enable_receive_sa = macsec_qca_enable_receive_sa,
+       .disable_receive_sa = macsec_qca_disable_receive_sa,
+       .get_available_transmit_sc = macsec_qca_get_available_transmit_sc,
+       .create_transmit_sc = macsec_qca_create_transmit_sc,
+       .delete_transmit_sc = macsec_qca_delete_transmit_sc,
+       .create_transmit_sa = macsec_qca_create_transmit_sa,
+       .enable_transmit_sa = macsec_qca_enable_transmit_sa,
+       .disable_transmit_sa = macsec_qca_disable_transmit_sa,
+};
index bb48011..1635c1f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * WPA Supplicant - driver interaction with MADWIFI 802.11 driver
+ * hostapd - driver interaction with MADWIFI 802.11 driver
  * Copyright (c) 2004, Sam Leffler <sam@errno.com>
  * Copyright (c) 2004, Video54 Technologies
  * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
@@ -7,10 +7,9 @@
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
  *
- * While this driver wrapper supports both AP (hostapd) and station
- * (wpa_supplicant) operations, the station side is deprecated and
- * driver_wext.c should be used instead. This driver wrapper should only be
- * used with hostapd for AP mode functionality.
+ * This driver wrapper is only for hostapd AP mode functionality. Station
+ * (wpa_supplicant) operations with madwifi are supported by the driver_wext.c
+ * wrapper.
  */
 
 #include "includes.h"
@@ -182,7 +181,7 @@ set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len)
 #endif /* MADWIFI_NG */
                int idx = op - first;
                if (first <= op &&
-                   idx < (int) (sizeof(opnames) / sizeof(opnames[0])) &&
+                   idx < (int) ARRAY_SIZE(opnames) &&
                    opnames[idx])
                        perror(opnames[idx]);
                else
@@ -322,19 +321,16 @@ madwifi_set_ieee8021x(void *priv, struct wpa_bss_params *params)
                        IEEE80211_AUTH_AUTO);
        }
        if (!params->wpa && !params->ieee802_1x) {
-               hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
-                       HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!");
+               wpa_printf(MSG_WARNING, "No 802.1X or WPA enabled!");
                return -1;
        }
        if (params->wpa && madwifi_configure_wpa(drv, params) != 0) {
-               hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
-                       HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!");
+               wpa_printf(MSG_WARNING, "Error configuring WPA state!");
                return -1;
        }
        if (set80211param(priv, IEEE80211_PARAM_AUTHMODE,
                (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) {
-               hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER,
-                       HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!");
+               wpa_printf(MSG_WARNING, "Error enabling WPA/802.1X!");
                return -1;
        }
 
index 4656c1b..4953af6 100644 (file)
@@ -1074,8 +1074,8 @@ wpa_driver_ndis_associate(void *priv,
                /* Try to continue anyway */
        }
 
-       if (params->key_mgmt_suite == KEY_MGMT_NONE ||
-           params->key_mgmt_suite == KEY_MGMT_802_1X_NO_WPA) {
+       if (params->key_mgmt_suite == WPA_KEY_MGMT_NONE ||
+           params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
                /* Re-set WEP keys if static WEP configuration is used. */
                int i;
                for (i = 0; i < 4; i++) {
@@ -1102,12 +1102,12 @@ wpa_driver_ndis_associate(void *priv,
                priv_mode = Ndis802_11PrivFilterAcceptAll;
        } else if (params->wpa_ie[0] == WLAN_EID_RSN) {
                priv_mode = Ndis802_11PrivFilter8021xWEP;
-               if (params->key_mgmt_suite == KEY_MGMT_PSK)
+               if (params->key_mgmt_suite == WPA_KEY_MGMT_PSK)
                        auth_mode = Ndis802_11AuthModeWPA2PSK;
                else
                        auth_mode = Ndis802_11AuthModeWPA2;
 #ifdef CONFIG_WPS
-       } else if (params->key_mgmt_suite == KEY_MGMT_WPS) {
+       } else if (params->key_mgmt_suite == WPA_KEY_MGMT_WPS) {
                auth_mode = Ndis802_11AuthModeOpen;
                priv_mode = Ndis802_11PrivFilterAcceptAll;
                if (params->wps == WPS_MODE_PRIVACY) {
@@ -1129,35 +1129,35 @@ wpa_driver_ndis_associate(void *priv,
 #endif /* CONFIG_WPS */
        } else {
                priv_mode = Ndis802_11PrivFilter8021xWEP;
-               if (params->key_mgmt_suite == KEY_MGMT_WPA_NONE)
+               if (params->key_mgmt_suite == WPA_KEY_MGMT_WPA_NONE)
                        auth_mode = Ndis802_11AuthModeWPANone;
-               else if (params->key_mgmt_suite == KEY_MGMT_PSK)
+               else if (params->key_mgmt_suite == WPA_KEY_MGMT_PSK)
                        auth_mode = Ndis802_11AuthModeWPAPSK;
                else
                        auth_mode = Ndis802_11AuthModeWPA;
        }
 
        switch (params->pairwise_suite) {
-       case CIPHER_CCMP:
+       case WPA_CIPHER_CCMP:
                encr = Ndis802_11Encryption3Enabled;
                break;
-       case CIPHER_TKIP:
+       case WPA_CIPHER_TKIP:
                encr = Ndis802_11Encryption2Enabled;
                break;
-       case CIPHER_WEP40:
-       case CIPHER_WEP104:
+       case WPA_CIPHER_WEP40:
+       case WPA_CIPHER_WEP104:
                encr = Ndis802_11Encryption1Enabled;
                break;
-       case CIPHER_NONE:
+       case WPA_CIPHER_NONE:
 #ifdef CONFIG_WPS
                if (params->wps == WPS_MODE_PRIVACY) {
                        encr = Ndis802_11Encryption1Enabled;
                        break;
                }
 #endif /* CONFIG_WPS */
-               if (params->group_suite == CIPHER_CCMP)
+               if (params->group_suite == WPA_CIPHER_CCMP)
                        encr = Ndis802_11Encryption3Enabled;
-               else if (params->group_suite == CIPHER_TKIP)
+               else if (params->group_suite == WPA_CIPHER_TKIP)
                        encr = Ndis802_11Encryption2Enabled;
                else
                        encr = Ndis802_11EncryptionDisabled;
old mode 100644 (file)
new mode 100755 (executable)
index a7af256..2ca9162
@@ -1,6 +1,6 @@
 /*
  * Driver interaction with Linux nl80211/cfg80211
- * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
  * Copyright (c) 2003-2004, Instant802 Networks, Inc.
  * Copyright (c) 2005-2006, Devicescape Software, Inc.
  * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
  */
 
 #include "includes.h"
-#include <sys/ioctl.h>
 #include <sys/types.h>
-#include <sys/stat.h>
+#ifdef BCM_DRIVER_V115
+#include <sys/ioctl.h>
+#endif
 #include <fcntl.h>
 #include <net/if.h>
 #include <netlink/genl/genl.h>
-#include <netlink/genl/family.h>
 #include <netlink/genl/ctrl.h>
+#ifdef CONFIG_LIBNL3_ROUTE
+#include <netlink/route/neighbour.h>
+#endif /* CONFIG_LIBNL3_ROUTE */
 #include <linux/rtnetlink.h>
 #include <netpacket/packet.h>
-#include <linux/filter.h>
 #include <linux/errqueue.h>
-#include "nl80211_copy.h"
 
 #include "common.h"
 #include "eloop.h"
-#include "utils/list.h"
+#include "common/qca-vendor.h"
+#include "common/qca-vendor-attr.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "l2_packet/l2_packet.h"
 #include "netlink.h"
+#include "linux_defines.h"
 #include "linux_ioctl.h"
 #include "radiotap.h"
 #include "radiotap_iter.h"
 #include "rfkill.h"
-#include "driver.h"
-
-#ifndef SO_WIFI_STATUS
-# if defined(__sparc__)
-#  define SO_WIFI_STATUS       0x0025
-# elif defined(__parisc__)
-#  define SO_WIFI_STATUS       0x4022
-# else
-#  define SO_WIFI_STATUS       41
-# endif
-
-# define SCM_WIFI_STATUS       SO_WIFI_STATUS
-#endif
-
-#ifndef SO_EE_ORIGIN_TXSTATUS
-#define SO_EE_ORIGIN_TXSTATUS  4
-#endif
-
-#ifndef PACKET_TX_TIMESTAMP
-#define PACKET_TX_TIMESTAMP    16
+#include "driver_nl80211.h"
+#if defined(TIZEN_WLAN_BOARD_SPRD)
+#include <sys/ioctl.h>
+#include "../ap/hostapd.h"
+#include "../ap/ap_config.h"
 #endif
 
-#ifdef TIZEN_EXT
-#ifdef BCM_DRIVER_V115
-#define GROUP_INTERFACE_PREFIX "p2p-wlan0"
-#endif
-#endif
 
-#ifdef ANDROID
-#include "android_drv.h"
-#endif /* ANDROID */
-#ifdef CONFIG_LIBNL20
-/* libnl 2.0 compatibility code */
-#define nl_handle nl_sock
-#define nl80211_handle_alloc nl_socket_alloc_cb
-#define nl80211_handle_destroy nl_socket_free
-#else
+#ifndef CONFIG_LIBNL20
 /*
  * libnl 1.1 has a bug, it tries to allocate socket numbers densely
  * but when you free a socket again it will mess up its bitmap and
@@ -115,6 +90,15 @@ static void nl80211_handle_destroy(struct nl_handle *handle)
 #endif /* CONFIG_LIBNL20 */
 
 
+#ifdef ANDROID
+/* system/core/libnl_2 does not include nl_socket_set_nonblocking() */
+#undef nl_socket_set_nonblocking
+#define nl_socket_set_nonblocking(h) android_nl_socket_set_nonblocking(h)
+
+#define genl_ctrl_resolve android_genl_ctrl_resolve
+#endif /* ANDROID */
+
+
 static struct nl_handle * nl_create_handle(struct nl_cb *cb, const char *dbg)
 {
        struct nl_handle *handle;
@@ -146,352 +130,144 @@ static void nl_destroy_handles(struct nl_handle **handle)
 }
 
 
-#ifndef IFF_LOWER_UP
-#define IFF_LOWER_UP   0x10000         /* driver signals L1 up         */
-#endif
-#ifndef IFF_DORMANT
-#define IFF_DORMANT    0x20000         /* driver signals dormant       */
+#if __WORDSIZE == 64
+#define ELOOP_SOCKET_INVALID   (intptr_t) 0x8888888888888889ULL
+#else
+#define ELOOP_SOCKET_INVALID   (intptr_t) 0x88888889ULL
 #endif
 
-#ifndef IF_OPER_DORMANT
-#define IF_OPER_DORMANT 5
-#endif
-#ifndef IF_OPER_UP
-#define IF_OPER_UP 6
-#endif
+static void nl80211_register_eloop_read(struct nl_handle **handle,
+                                       eloop_sock_handler handler,
+                                       void *eloop_data)
+{
+#ifdef CONFIG_LIBNL20
+       /*
+        * libnl uses a pretty small buffer (32 kB that gets converted to 64 kB)
+        * by default. It is possible to hit that limit in some cases where
+        * operations are blocked, e.g., with a burst of Deauthentication frames
+        * to hostapd and STA entry deletion. Try to increase the buffer to make
+        * this less likely to occur.
+        */
+       if (nl_socket_set_buffer_size(*handle, 262144, 0) < 0) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Could not set nl_socket RX buffer size: %s",
+                          strerror(errno));
+               /* continue anyway with the default (smaller) buffer */
+       }
+#endif /* CONFIG_LIBNL20 */
 
-struct nl80211_global {
-       struct dl_list interfaces;
-       int if_add_ifindex;
-       u64 if_add_wdevid;
-       int if_add_wdevid_set;
-       struct netlink_data *netlink;
-       struct nl_cb *nl_cb;
-       struct nl_handle *nl;
-       int nl80211_id;
-       int ioctl_sock; /* socket for ioctl() use */
-
-       struct nl_handle *nl_event;
-};
+       nl_socket_set_nonblocking(*handle);
+       eloop_register_read_sock(nl_socket_get_fd(*handle), handler,
+                                eloop_data, *handle);
+       *handle = (void *) (((intptr_t) *handle) ^ ELOOP_SOCKET_INVALID);
+}
 
-struct nl80211_wiphy_data {
-       struct dl_list list;
-       struct dl_list bsss;
-       struct dl_list drvs;
 
-       struct nl_handle *nl_beacons;
-       struct nl_cb *nl_cb;
+static void nl80211_destroy_eloop_handle(struct nl_handle **handle)
+{
+       *handle = (void *) (((intptr_t) *handle) ^ ELOOP_SOCKET_INVALID);
+       eloop_unregister_read_sock(nl_socket_get_fd(*handle));
+       nl_destroy_handles(handle);
+}
 
-       int wiphy_idx;
-};
 
 static void nl80211_global_deinit(void *priv);
-
-struct i802_bss {
-       struct wpa_driver_nl80211_data *drv;
-       struct i802_bss *next;
-       int ifindex;
-       u64 wdev_id;
-       char ifname[IFNAMSIZ + 1];
-       char brname[IFNAMSIZ];
-       unsigned int beacon_set:1;
-       unsigned int added_if_into_bridge:1;
-       unsigned int added_bridge:1;
-       unsigned int in_deinit:1;
-       unsigned int wdev_id_set:1;
-
-       u8 addr[ETH_ALEN];
-#ifdef TIZEN_EXT_P2P
-       u8 dev_addr[ETH_ALEN];
-#endif /* TIZEN_EXT_P2P */
-
-       int freq;
-
-       void *ctx;
-       struct nl_handle *nl_preq, *nl_mgmt;
-       struct nl_cb *nl_cb;
-
-       struct nl80211_wiphy_data *wiphy_data;
-       struct dl_list wiphy_list;
-};
-
-struct wpa_driver_nl80211_data {
-       struct nl80211_global *global;
-       struct dl_list list;
-       struct dl_list wiphy_list;
-       char phyname[32];
-       void *ctx;
-       int ifindex;
-       int if_removed;
-       int if_disabled;
-       int ignore_if_down_event;
-       struct rfkill_data *rfkill;
-       struct wpa_driver_capa capa;
-       u8 *extended_capa, *extended_capa_mask;
-       unsigned int extended_capa_len;
-       int has_capability;
-
-       int operstate;
-
-       int scan_complete_events;
-
-       struct nl_cb *nl_cb;
-
-       u8 auth_bssid[ETH_ALEN];
-       u8 auth_attempt_bssid[ETH_ALEN];
-       u8 bssid[ETH_ALEN];
-       u8 prev_bssid[ETH_ALEN];
-       int associated;
-       u8 ssid[32];
-       size_t ssid_len;
-       enum nl80211_iftype nlmode;
-       enum nl80211_iftype ap_scan_as_station;
-       unsigned int assoc_freq;
-
-       int monitor_sock;
-       int monitor_ifidx;
-       int monitor_refcount;
-
-       unsigned int disabled_11b_rates:1;
-       unsigned int pending_remain_on_chan:1;
-       unsigned int in_interface_list:1;
-       unsigned int device_ap_sme:1;
-       unsigned int poll_command_supported:1;
-       unsigned int data_tx_status:1;
-       unsigned int scan_for_auth:1;
-       unsigned int retry_auth:1;
-       unsigned int use_monitor:1;
-       unsigned int ignore_next_local_disconnect:1;
-       unsigned int allow_p2p_device:1;
-
-       u64 remain_on_chan_cookie;
-       u64 send_action_cookie;
-
-       unsigned int last_mgmt_freq;
-
-       struct wpa_driver_scan_filter *filter_ssids;
-       size_t num_filter_ssids;
-
-       struct i802_bss first_bss;
-
-       int eapol_tx_sock;
-
-#ifdef HOSTAPD
-       int eapol_sock; /* socket for EAPOL frames */
-
-       int default_if_indices[16];
-       int *if_indices;
-       int num_if_indices;
-
-       int last_freq;
-       int last_freq_ht;
-#endif /* HOSTAPD */
-
-       /* From failed authentication command */
-       int auth_freq;
-       u8 auth_bssid_[ETH_ALEN];
-       u8 auth_ssid[32];
-       size_t auth_ssid_len;
-       int auth_alg;
-       u8 *auth_ie;
-       size_t auth_ie_len;
-       u8 auth_wep_key[4][16];
-       size_t auth_wep_key_len[4];
-       int auth_wep_tx_keyidx;
-       int auth_local_state_change;
-       int auth_p2p;
-};
-
+static void nl80211_check_global(struct nl80211_global *global);
 
 static void wpa_driver_nl80211_deinit(struct i802_bss *bss);
-static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx,
-                                           void *timeout_ctx);
-static int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
-                                      enum nl80211_iftype nlmode);
+static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss,
+                                           struct hostapd_freq_params *freq);
+
 static int
-wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv);
-static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
-                                  const u8 *addr, int cmd, u16 reason_code,
-                                  int local_state_change);
-static void nl80211_remove_monitor_interface(
-       struct wpa_driver_nl80211_data *drv);
+wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
+                                  const u8 *set_addr, int first,
+                                  const char *driver_params);
 static int nl80211_send_frame_cmd(struct i802_bss *bss,
                                  unsigned int freq, unsigned int wait,
                                  const u8 *buf, size_t buf_len, u64 *cookie,
                                  int no_cck, int no_ack, int offchanok);
 static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss,
                                               int report);
-#ifdef ANDROID
-static int android_pno_start(struct i802_bss *bss,
-                            struct wpa_driver_scan_params *params);
-static int android_pno_stop(struct i802_bss *bss);
-#endif /* ANDROID */
 
-#ifdef HOSTAPD
 static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
 static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
 static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx);
-static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
-                                       enum wpa_driver_if_type type,
-                                       const char *ifname);
-#else /* HOSTAPD */
-static inline void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
-{
-}
 
-static inline void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+static int nl80211_set_channel(struct i802_bss *bss,
+                              struct hostapd_freq_params *freq, int set_chan);
+static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
+                                    int ifindex, int disabled);
+
+static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv,
+                             int reset_mode);
+
+static int i802_set_iface_flags(struct i802_bss *bss, int up);
+static int nl80211_set_param(void *priv, const char *param);
+
+
+/* Converts nl80211_chan_width to a common format */
+enum chan_width convert2width(int width)
 {
+       switch (width) {
+       case NL80211_CHAN_WIDTH_20_NOHT:
+               return CHAN_WIDTH_20_NOHT;
+       case NL80211_CHAN_WIDTH_20:
+               return CHAN_WIDTH_20;
+       case NL80211_CHAN_WIDTH_40:
+               return CHAN_WIDTH_40;
+       case NL80211_CHAN_WIDTH_80:
+               return CHAN_WIDTH_80;
+       case NL80211_CHAN_WIDTH_80P80:
+               return CHAN_WIDTH_80P80;
+       case NL80211_CHAN_WIDTH_160:
+               return CHAN_WIDTH_160;
+       }
+       return CHAN_WIDTH_UNKNOWN;
 }
 
-static inline int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+
+int is_ap_interface(enum nl80211_iftype nlmode)
 {
-       return 0;
+       return nlmode == NL80211_IFTYPE_AP ||
+               nlmode == NL80211_IFTYPE_P2P_GO;
 }
-#endif /* HOSTAPD */
 
-static int wpa_driver_nl80211_set_freq(struct i802_bss *bss,
-                                      struct hostapd_freq_params *freq);
-static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
-                                    int ifindex, int disabled);
 
-static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv);
-static int wpa_driver_nl80211_authenticate_retry(
-       struct wpa_driver_nl80211_data *drv);
-
-
-static const char * nl80211_command_to_string(enum nl80211_commands cmd)
-{
-#define C2S(x) case x: return #x;
-       switch (cmd) {
-       C2S(NL80211_CMD_UNSPEC)
-       C2S(NL80211_CMD_GET_WIPHY)
-       C2S(NL80211_CMD_SET_WIPHY)
-       C2S(NL80211_CMD_NEW_WIPHY)
-       C2S(NL80211_CMD_DEL_WIPHY)
-       C2S(NL80211_CMD_GET_INTERFACE)
-       C2S(NL80211_CMD_SET_INTERFACE)
-       C2S(NL80211_CMD_NEW_INTERFACE)
-       C2S(NL80211_CMD_DEL_INTERFACE)
-       C2S(NL80211_CMD_GET_KEY)
-       C2S(NL80211_CMD_SET_KEY)
-       C2S(NL80211_CMD_NEW_KEY)
-       C2S(NL80211_CMD_DEL_KEY)
-       C2S(NL80211_CMD_GET_BEACON)
-       C2S(NL80211_CMD_SET_BEACON)
-       C2S(NL80211_CMD_START_AP)
-       C2S(NL80211_CMD_STOP_AP)
-       C2S(NL80211_CMD_GET_STATION)
-       C2S(NL80211_CMD_SET_STATION)
-       C2S(NL80211_CMD_NEW_STATION)
-       C2S(NL80211_CMD_DEL_STATION)
-       C2S(NL80211_CMD_GET_MPATH)
-       C2S(NL80211_CMD_SET_MPATH)
-       C2S(NL80211_CMD_NEW_MPATH)
-       C2S(NL80211_CMD_DEL_MPATH)
-       C2S(NL80211_CMD_SET_BSS)
-       C2S(NL80211_CMD_SET_REG)
-       C2S(NL80211_CMD_REQ_SET_REG)
-       C2S(NL80211_CMD_GET_MESH_CONFIG)
-       C2S(NL80211_CMD_SET_MESH_CONFIG)
-       C2S(NL80211_CMD_SET_MGMT_EXTRA_IE)
-       C2S(NL80211_CMD_GET_REG)
-       C2S(NL80211_CMD_GET_SCAN)
-       C2S(NL80211_CMD_TRIGGER_SCAN)
-       C2S(NL80211_CMD_NEW_SCAN_RESULTS)
-       C2S(NL80211_CMD_SCAN_ABORTED)
-       C2S(NL80211_CMD_REG_CHANGE)
-       C2S(NL80211_CMD_AUTHENTICATE)
-       C2S(NL80211_CMD_ASSOCIATE)
-       C2S(NL80211_CMD_DEAUTHENTICATE)
-       C2S(NL80211_CMD_DISASSOCIATE)
-       C2S(NL80211_CMD_MICHAEL_MIC_FAILURE)
-       C2S(NL80211_CMD_REG_BEACON_HINT)
-       C2S(NL80211_CMD_JOIN_IBSS)
-       C2S(NL80211_CMD_LEAVE_IBSS)
-       C2S(NL80211_CMD_TESTMODE)
-       C2S(NL80211_CMD_CONNECT)
-       C2S(NL80211_CMD_ROAM)
-       C2S(NL80211_CMD_DISCONNECT)
-       C2S(NL80211_CMD_SET_WIPHY_NETNS)
-       C2S(NL80211_CMD_GET_SURVEY)
-       C2S(NL80211_CMD_NEW_SURVEY_RESULTS)
-       C2S(NL80211_CMD_SET_PMKSA)
-       C2S(NL80211_CMD_DEL_PMKSA)
-       C2S(NL80211_CMD_FLUSH_PMKSA)
-       C2S(NL80211_CMD_REMAIN_ON_CHANNEL)
-       C2S(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL)
-       C2S(NL80211_CMD_SET_TX_BITRATE_MASK)
-       C2S(NL80211_CMD_REGISTER_FRAME)
-       C2S(NL80211_CMD_FRAME)
-       C2S(NL80211_CMD_FRAME_TX_STATUS)
-       C2S(NL80211_CMD_SET_POWER_SAVE)
-       C2S(NL80211_CMD_GET_POWER_SAVE)
-       C2S(NL80211_CMD_SET_CQM)
-       C2S(NL80211_CMD_NOTIFY_CQM)
-       C2S(NL80211_CMD_SET_CHANNEL)
-       C2S(NL80211_CMD_SET_WDS_PEER)
-       C2S(NL80211_CMD_FRAME_WAIT_CANCEL)
-       C2S(NL80211_CMD_JOIN_MESH)
-       C2S(NL80211_CMD_LEAVE_MESH)
-       C2S(NL80211_CMD_UNPROT_DEAUTHENTICATE)
-       C2S(NL80211_CMD_UNPROT_DISASSOCIATE)
-       C2S(NL80211_CMD_NEW_PEER_CANDIDATE)
-       C2S(NL80211_CMD_GET_WOWLAN)
-       C2S(NL80211_CMD_SET_WOWLAN)
-       C2S(NL80211_CMD_START_SCHED_SCAN)
-       C2S(NL80211_CMD_STOP_SCHED_SCAN)
-       C2S(NL80211_CMD_SCHED_SCAN_RESULTS)
-       C2S(NL80211_CMD_SCHED_SCAN_STOPPED)
-       C2S(NL80211_CMD_SET_REKEY_OFFLOAD)
-       C2S(NL80211_CMD_PMKSA_CANDIDATE)
-       C2S(NL80211_CMD_TDLS_OPER)
-       C2S(NL80211_CMD_TDLS_MGMT)
-       C2S(NL80211_CMD_UNEXPECTED_FRAME)
-       C2S(NL80211_CMD_PROBE_CLIENT)
-       C2S(NL80211_CMD_REGISTER_BEACONS)
-       C2S(NL80211_CMD_UNEXPECTED_4ADDR_FRAME)
-       C2S(NL80211_CMD_SET_NOACK_MAP)
-       C2S(NL80211_CMD_CH_SWITCH_NOTIFY)
-       C2S(NL80211_CMD_START_P2P_DEVICE)
-       C2S(NL80211_CMD_STOP_P2P_DEVICE)
-       C2S(NL80211_CMD_CONN_FAILED)
-       C2S(NL80211_CMD_SET_MCAST_RATE)
-       C2S(NL80211_CMD_SET_MAC_ACL)
-       C2S(NL80211_CMD_RADAR_DETECT)
-       C2S(NL80211_CMD_GET_PROTOCOL_FEATURES)
-       C2S(NL80211_CMD_UPDATE_FT_IES)
-       C2S(NL80211_CMD_FT_EVENT)
-       C2S(NL80211_CMD_CRIT_PROTOCOL_START)
-       C2S(NL80211_CMD_CRIT_PROTOCOL_STOP)
-       default:
-               return "NL80211_CMD_UNKNOWN";
-       }
-#undef C2S
+int is_sta_interface(enum nl80211_iftype nlmode)
+{
+       return nlmode == NL80211_IFTYPE_STATION ||
+               nlmode == NL80211_IFTYPE_P2P_CLIENT;
 }
 
 
-static int is_ap_interface(enum nl80211_iftype nlmode)
+static int is_p2p_net_interface(enum nl80211_iftype nlmode)
 {
-       return (nlmode == NL80211_IFTYPE_AP ||
-               nlmode == NL80211_IFTYPE_P2P_GO);
+       return nlmode == NL80211_IFTYPE_P2P_CLIENT ||
+               nlmode == NL80211_IFTYPE_P2P_GO;
 }
 
 
-static int is_sta_interface(enum nl80211_iftype nlmode)
+struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv,
+                                 int ifindex)
 {
-       return (nlmode == NL80211_IFTYPE_STATION ||
-               nlmode == NL80211_IFTYPE_P2P_CLIENT);
+       struct i802_bss *bss;
+
+       for (bss = drv->first_bss; bss; bss = bss->next) {
+               if (bss->ifindex == ifindex)
+                       return bss;
+       }
+
+       return NULL;
 }
 
 
-static int is_p2p_net_interface(enum nl80211_iftype nlmode)
+static int is_mesh_interface(enum nl80211_iftype nlmode)
 {
-       return (nlmode == NL80211_IFTYPE_P2P_CLIENT ||
-               nlmode == NL80211_IFTYPE_P2P_GO);
+       return nlmode == NL80211_IFTYPE_MESH_POINT;
 }
 
 
-static void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv)
+void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv)
 {
        if (drv->associated)
                os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
@@ -500,16 +276,6 @@ static void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv)
 }
 
 
-struct nl80211_bss_info_arg {
-       struct wpa_driver_nl80211_data *drv;
-       struct wpa_scan_results *res;
-       unsigned int assoc_freq;
-       u8 assoc_bssid[ETH_ALEN];
-};
-
-static int bss_info_handler(struct nl_msg *msg, void *arg);
-
-
 /* nl80211 code */
 static int ack_handler(struct nl_msg *msg, void *arg)
 {
@@ -540,6 +306,28 @@ static int no_seq_check(struct nl_msg *msg, void *arg)
 }
 
 
+static void nl80211_nlmsg_clear(struct nl_msg *msg)
+{
+       /*
+        * Clear nlmsg data, e.g., to make sure key material is not left in
+        * heap memory for unnecessarily long time.
+        */
+       if (msg) {
+               struct nlmsghdr *hdr = nlmsg_hdr(msg);
+               void *data = nlmsg_data(hdr);
+               /*
+                * This would use nlmsg_datalen() or the older nlmsg_len() if
+                * only libnl were to maintain a stable API.. Neither will work
+                * with all released versions, so just calculate the length
+                * here.
+                */
+               int len = hdr->nlmsg_len - NLMSG_HDRLEN;
+
+               os_memset(data, 0, len);
+       }
+}
+
+
 static int send_and_recv(struct nl80211_global *global,
                         struct nl_handle *nl_handle, struct nl_msg *msg,
                         int (*valid_handler)(struct nl_msg *, void *),
@@ -548,6 +336,9 @@ static int send_and_recv(struct nl80211_global *global,
        struct nl_cb *cb;
        int err = -ENOMEM;
 
+       if (!msg)
+               return -ENOMEM;
+
        cb = nl_cb_clone(global->nl_cb);
        if (!cb)
                goto out;
@@ -566,29 +357,27 @@ static int send_and_recv(struct nl80211_global *global,
                nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM,
                          valid_handler, valid_data);
 
-       while (err > 0)
-               nl_recvmsgs(nl_handle, cb);
+       while (err > 0) {
+               int res = nl_recvmsgs(nl_handle, cb);
+               if (res < 0) {
+                       wpa_printf(MSG_INFO,
+                                  "nl80211: %s->nl_recvmsgs failed: %d",
+                                  __func__, res);
+               }
+       }
  out:
        nl_cb_put(cb);
+       if (!valid_handler && valid_data == (void *) -1)
+               nl80211_nlmsg_clear(msg);
        nlmsg_free(msg);
        return err;
 }
 
 
-static int send_and_recv_msgs_global(struct nl80211_global *global,
-                                    struct nl_msg *msg,
-                                    int (*valid_handler)(struct nl_msg *, void *),
-                                    void *valid_data)
-{
-       return send_and_recv(global, global->nl, msg, valid_handler,
-                            valid_data);
-}
-
-
-static int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
-                             struct nl_msg *msg,
-                             int (*valid_handler)(struct nl_msg *, void *),
-                             void *valid_data)
+int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv,
+                      struct nl_msg *msg,
+                      int (*valid_handler)(struct nl_msg *, void *),
+                      void *valid_data)
 {
        return send_and_recv(drv->global, drv->global->nl, msg,
                             valid_handler, valid_data);
@@ -601,19 +390,6 @@ struct family_data {
 };
 
 
-static int nl80211_set_iface_id(struct nl_msg *msg, struct i802_bss *bss)
-{
-       if (bss->wdev_id_set)
-               NLA_PUT_U64(msg, NL80211_ATTR_WDEV, bss->wdev_id);
-       else
-               NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
-       return 0;
-
-nla_put_failure:
-       return -1;
-}
-
-
 static int family_handler(struct nl_msg *msg, void *arg)
 {
        struct family_data *res = arg;
@@ -649,35 +425,93 @@ static int nl_get_multicast_id(struct nl80211_global *global,
                               const char *family, const char *group)
 {
        struct nl_msg *msg;
-       int ret = -1;
+       int ret;
        struct family_data res = { group, -ENOENT };
 
        msg = nlmsg_alloc();
        if (!msg)
                return -ENOMEM;
-       genlmsg_put(msg, 0, 0, genl_ctrl_resolve(global->nl, "nlctrl"),
-                   0, 0, CTRL_CMD_GETFAMILY, 0);
-       NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family);
+       if (!genlmsg_put(msg, 0, 0, genl_ctrl_resolve(global->nl, "nlctrl"),
+                        0, 0, CTRL_CMD_GETFAMILY, 0) ||
+           nla_put_string(msg, CTRL_ATTR_FAMILY_NAME, family)) {
+               nlmsg_free(msg);
+               return -1;
+       }
 
-       ret = send_and_recv_msgs_global(global, msg, family_handler, &res);
-       msg = NULL;
+       ret = send_and_recv(global, global->nl, msg, family_handler, &res);
        if (ret == 0)
                ret = res.id;
-
-nla_put_failure:
-       nlmsg_free(msg);
        return ret;
 }
 
 
-static void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
-                         struct nl_msg *msg, int flags, uint8_t cmd)
+void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
+                  struct nl_msg *msg, int flags, uint8_t cmd)
 {
        return genlmsg_put(msg, 0, 0, drv->global->nl80211_id,
                           0, flags, cmd, 0);
 }
 
 
+static int nl80211_set_iface_id(struct nl_msg *msg, struct i802_bss *bss)
+{
+       if (bss->wdev_id_set)
+               return nla_put_u64(msg, NL80211_ATTR_WDEV, bss->wdev_id);
+       return nla_put_u32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+}
+
+
+struct nl_msg * nl80211_cmd_msg(struct i802_bss *bss, int flags, uint8_t cmd)
+{
+       struct nl_msg *msg;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return NULL;
+
+       if (!nl80211_cmd(bss->drv, msg, flags, cmd) ||
+           nl80211_set_iface_id(msg, bss) < 0) {
+               nlmsg_free(msg);
+               return NULL;
+       }
+
+       return msg;
+}
+
+
+static struct nl_msg *
+nl80211_ifindex_msg(struct wpa_driver_nl80211_data *drv, int ifindex,
+                   int flags, uint8_t cmd)
+{
+       struct nl_msg *msg;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return NULL;
+
+       if (!nl80211_cmd(drv, msg, flags, cmd) ||
+           nla_put_u32(msg, NL80211_ATTR_IFINDEX, ifindex)) {
+               nlmsg_free(msg);
+               return NULL;
+       }
+
+       return msg;
+}
+
+
+struct nl_msg * nl80211_drv_msg(struct wpa_driver_nl80211_data *drv, int flags,
+                               uint8_t cmd)
+{
+       return nl80211_ifindex_msg(drv, drv->ifindex, flags, cmd);
+}
+
+
+struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd)
+{
+       return nl80211_ifindex_msg(bss->drv, bss->ifindex, flags, cmd);
+}
+
+
 struct wiphy_idx_data {
        int wiphy_idx;
        enum nl80211_iftype nlmode;
@@ -708,7 +542,7 @@ static int netdev_info_handler(struct nl_msg *msg, void *arg)
 }
 
 
-static int nl80211_get_wiphy_index(struct i802_bss *bss)
+int nl80211_get_wiphy_index(struct i802_bss *bss)
 {
        struct nl_msg *msg;
        struct wiphy_idx_data data = {
@@ -716,20 +550,11 @@ static int nl80211_get_wiphy_index(struct i802_bss *bss)
                .macaddr = NULL,
        };
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return NL80211_IFTYPE_UNSPECIFIED;
-
-       nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE);
-
-       if (nl80211_set_iface_id(msg, bss) < 0)
-               goto nla_put_failure;
+       if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
+               return -1;
 
        if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0)
                return data.wiphy_idx;
-       msg = NULL;
-nla_put_failure:
-       nlmsg_free(msg);
        return -1;
 }
 
@@ -742,25 +567,15 @@ static enum nl80211_iftype nl80211_get_ifmode(struct i802_bss *bss)
                .macaddr = NULL,
        };
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -1;
-
-       nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE);
-
-       if (nl80211_set_iface_id(msg, bss) < 0)
-               goto nla_put_failure;
+       if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
+               return NL80211_IFTYPE_UNSPECIFIED;
 
        if (send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data) == 0)
                return data.nlmode;
-       msg = NULL;
-nla_put_failure:
-       nlmsg_free(msg);
        return NL80211_IFTYPE_UNSPECIFIED;
 }
 
 
-#ifndef HOSTAPD
 static int nl80211_get_macaddr(struct i802_bss *bss)
 {
        struct nl_msg *msg;
@@ -768,48 +583,35 @@ static int nl80211_get_macaddr(struct i802_bss *bss)
                .macaddr = bss->addr,
        };
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return NL80211_IFTYPE_UNSPECIFIED;
-
-       nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_GET_INTERFACE);
-       if (nl80211_set_iface_id(msg, bss) < 0)
-               goto nla_put_failure;
+       if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_GET_INTERFACE)))
+               return -1;
 
        return send_and_recv_msgs(bss->drv, msg, netdev_info_handler, &data);
-
-nla_put_failure:
-       nlmsg_free(msg);
-       return NL80211_IFTYPE_UNSPECIFIED;
 }
-#endif /* HOSTAPD */
 
 
 static int nl80211_register_beacons(struct wpa_driver_nl80211_data *drv,
                                    struct nl80211_wiphy_data *w)
 {
        struct nl_msg *msg;
-       int ret = -1;
+       int ret;
 
        msg = nlmsg_alloc();
        if (!msg)
                return -1;
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_BEACONS);
-
-       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, w->wiphy_idx);
+       if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_BEACONS) ||
+           nla_put_u32(msg, NL80211_ATTR_WIPHY, w->wiphy_idx)) {
+               nlmsg_free(msg);
+               return -1;
+       }
 
        ret = send_and_recv(drv->global, w->nl_beacons, msg, NULL, NULL);
-       msg = NULL;
        if (ret) {
                wpa_printf(MSG_DEBUG, "nl80211: Register beacons command "
                           "failed: ret=%d (%s)",
                           ret, strerror(-ret));
-               goto nla_put_failure;
        }
-       ret = 0;
-nla_put_failure:
-       nlmsg_free(msg);
        return ret;
 }
 
@@ -817,10 +619,15 @@ nla_put_failure:
 static void nl80211_recv_beacons(int sock, void *eloop_ctx, void *handle)
 {
        struct nl80211_wiphy_data *w = eloop_ctx;
+       int res;
 
        wpa_printf(MSG_EXCESSIVE, "nl80211: Beacon event message available");
 
-       nl_recvmsgs(handle, w->nl_cb);
+       res = nl_recvmsgs(handle, w->nl_cb);
+       if (res < 0) {
+               wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d",
+                          __func__, res);
+       }
 }
 
 
@@ -904,8 +711,7 @@ nl80211_get_wiphy_data_ap(struct i802_bss *bss)
                return NULL;
        }
 
-       eloop_register_read_sock(nl_socket_get_fd(w->nl_beacons),
-                                nl80211_recv_beacons, w, w->nl_beacons);
+       nl80211_register_eloop_read(&w->nl_beacons, nl80211_recv_beacons, w);
 
        dl_list_add(&nl80211_wiphys, &w->list);
 
@@ -952,10 +758,9 @@ static void nl80211_put_wiphy_data_ap(struct i802_bss *bss)
        if (!dl_list_empty(&w->bsss))
                return;
 
-       eloop_unregister_read_sock(nl_socket_get_fd(w->nl_beacons));
+       nl80211_destroy_eloop_handle(&w->nl_beacons);
 
        nl_cb_put(w->nl_cb);
-       nl_destroy_handles(&w->nl_beacons);
        dl_list_del(&w->list);
        os_free(w);
 }
@@ -983,48 +788,55 @@ static int wpa_driver_nl80211_get_ssid(void *priv, u8 *ssid)
 }
 
 
-static void wpa_driver_nl80211_event_link(struct wpa_driver_nl80211_data *drv,
-                                         char *buf, size_t len, int del)
+static void wpa_driver_nl80211_event_newlink(
+       struct wpa_driver_nl80211_data *drv, const char *ifname)
 {
        union wpa_event_data event;
 
+       if (os_strcmp(drv->first_bss->ifname, ifname) == 0) {
+               if (if_nametoindex(drv->first_bss->ifname) == 0) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Interface %s does not exist - ignore RTM_NEWLINK",
+                                  drv->first_bss->ifname);
+                       return;
+               }
+               if (!drv->if_removed)
+                       return;
+               wpa_printf(MSG_DEBUG, "nl80211: Mark if_removed=0 for %s based on RTM_NEWLINK event",
+                          drv->first_bss->ifname);
+               drv->if_removed = 0;
+       }
+
        os_memset(&event, 0, sizeof(event));
-       if (len > sizeof(event.interface_status.ifname))
-               len = sizeof(event.interface_status.ifname) - 1;
-       os_memcpy(event.interface_status.ifname, buf, len);
-       event.interface_status.ievent = del ? EVENT_INTERFACE_REMOVED :
-               EVENT_INTERFACE_ADDED;
-
-       wpa_printf(MSG_DEBUG, "RTM_%sLINK, IFLA_IFNAME: Interface '%s' %s",
-                  del ? "DEL" : "NEW",
-                  event.interface_status.ifname,
-                  del ? "removed" : "added");
-
-       if (os_strcmp(drv->first_bss.ifname, event.interface_status.ifname) == 0) {
-               if (del) {
-                       if (drv->if_removed) {
-                               wpa_printf(MSG_DEBUG, "nl80211: if_removed "
-                                          "already set - ignore event");
-                               return;
-                       }
-                       drv->if_removed = 1;
-               } else {
-                       if (if_nametoindex(drv->first_bss.ifname) == 0) {
-                               wpa_printf(MSG_DEBUG, "nl80211: Interface %s "
-                                          "does not exist - ignore "
-                                          "RTM_NEWLINK",
-                                          drv->first_bss.ifname);
-                               return;
-                       }
-                       if (!drv->if_removed) {
-                               wpa_printf(MSG_DEBUG, "nl80211: if_removed "
-                                          "already cleared - ignore event");
-                               return;
-                       }
-                       drv->if_removed = 0;
+       os_strlcpy(event.interface_status.ifname, ifname,
+                  sizeof(event.interface_status.ifname));
+       event.interface_status.ievent = EVENT_INTERFACE_ADDED;
+       wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
+}
+
+
+static void wpa_driver_nl80211_event_dellink(
+       struct wpa_driver_nl80211_data *drv, const char *ifname)
+{
+       union wpa_event_data event;
+
+       if (os_strcmp(drv->first_bss->ifname, ifname) == 0) {
+               if (drv->if_removed) {
+                       wpa_printf(MSG_DEBUG, "nl80211: if_removed already set - ignore RTM_DELLINK event for %s",
+                                  ifname);
+                       return;
                }
+               wpa_printf(MSG_DEBUG, "RTM_DELLINK: Interface '%s' removed - mark if_removed=1",
+                          ifname);
+               drv->if_removed = 1;
+       } else {
+               wpa_printf(MSG_DEBUG, "RTM_DELLINK: Interface '%s' removed",
+                          ifname);
        }
 
+       os_memset(&event, 0, sizeof(event));
+       os_strlcpy(event.interface_status.ifname, ifname,
+                  sizeof(event.interface_status.ifname));
+       event.interface_status.ievent = EVENT_INTERFACE_REMOVED;
        wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_STATUS, &event);
 }
 
@@ -1041,8 +853,8 @@ static int wpa_driver_nl80211_own_ifname(struct wpa_driver_nl80211_data *drv,
        rta_len = RTA_ALIGN(sizeof(struct rtattr));
        while (RTA_OK(attr, attrlen)) {
                if (attr->rta_type == IFLA_IFNAME) {
-                       if (os_strcmp(((char *) attr) + rta_len, drv->first_bss.ifname)
-                           == 0)
+                       if (os_strcmp(((char *) attr) + rta_len,
+                                     drv->first_bss->ifname) == 0)
                                return 1;
                        else
                                break;
@@ -1061,10 +873,10 @@ static int wpa_driver_nl80211_own_ifindex(struct wpa_driver_nl80211_data *drv,
                return 1;
 
        if (drv->if_removed && wpa_driver_nl80211_own_ifname(drv, buf, len)) {
-               drv->first_bss.ifindex = if_nametoindex(drv->first_bss.ifname);
+               nl80211_check_global(drv->global);
                wpa_printf(MSG_DEBUG, "nl80211: Update ifindex for a removed "
                           "interface");
-               wpa_driver_nl80211_finish_drv_init(drv);
+               wpa_driver_nl80211_finish_drv_init(drv, NULL, 0, NULL);
                return 1;
        }
 
@@ -1092,36 +904,79 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
 {
        struct nl80211_global *global = ctx;
        struct wpa_driver_nl80211_data *drv;
-       int attrlen, rta_len;
+       int attrlen;
        struct rtattr *attr;
        u32 brid = 0;
        char namebuf[IFNAMSIZ];
+       char ifname[IFNAMSIZ + 1];
+       char extra[100], *pos, *end;
 
        drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
        if (!drv) {
-               wpa_printf(MSG_DEBUG, "nl80211: Ignore event for foreign "
-                          "ifindex %d", ifi->ifi_index);
+               wpa_printf(MSG_DEBUG, "nl80211: Ignore RTM_NEWLINK event for foreign ifindex %d",
+                          ifi->ifi_index);
                return;
        }
 
-       wpa_printf(MSG_DEBUG, "RTM_NEWLINK: operstate=%d ifi_flags=0x%x "
-                  "(%s%s%s%s)",
-                  drv->operstate, ifi->ifi_flags,
-                  (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
-                  (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
-                  (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
-                  (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+       extra[0] = '\0';
+       pos = extra;
+       end = pos + sizeof(extra);
+       ifname[0] = '\0';
 
-       if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
-               if (if_indextoname(ifi->ifi_index, namebuf) &&
-                   linux_iface_up(drv->global->ioctl_sock,
-                                  drv->first_bss.ifname) > 0) {
-                       wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
-                                  "event since interface %s is up", namebuf);
+       attrlen = len;
+       attr = (struct rtattr *) buf;
+       while (RTA_OK(attr, attrlen)) {
+               switch (attr->rta_type) {
+               case IFLA_IFNAME:
+                       if (RTA_PAYLOAD(attr) >= IFNAMSIZ)
+                               break;
+                       os_memcpy(ifname, RTA_DATA(attr), RTA_PAYLOAD(attr));
+                       ifname[RTA_PAYLOAD(attr)] = '\0';
+                       break;
+               case IFLA_MASTER:
+                       brid = nla_get_u32((struct nlattr *) attr);
+                       pos += os_snprintf(pos, end - pos, " master=%u", brid);
+                       break;
+               case IFLA_WIRELESS:
+                       pos += os_snprintf(pos, end - pos, " wext");
+                       break;
+               case IFLA_OPERSTATE:
+                       pos += os_snprintf(pos, end - pos, " operstate=%u",
+                                          nla_get_u32((struct nlattr *) attr));
+                       break;
+               case IFLA_LINKMODE:
+                       pos += os_snprintf(pos, end - pos, " linkmode=%u",
+                                          nla_get_u32((struct nlattr *) attr));
+                       break;
+               }
+               attr = RTA_NEXT(attr, attrlen);
+       }
+       extra[sizeof(extra) - 1] = '\0';
+
+       wpa_printf(MSG_DEBUG, "RTM_NEWLINK: ifi_index=%d ifname=%s%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
+                  ifi->ifi_index, ifname, extra, ifi->ifi_family,
+                  ifi->ifi_flags,
+                  (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
+                  (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
+                  (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
+                  (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+
+       if (!drv->if_disabled && !(ifi->ifi_flags & IFF_UP)) {
+               namebuf[0] = '\0';
+               if (if_indextoname(ifi->ifi_index, namebuf) &&
+                   linux_iface_up(drv->global->ioctl_sock, namebuf) > 0) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
+                                  "event since interface %s is up", namebuf);
+                       drv->ignore_if_down_event = 0;
                        return;
                }
-               wpa_printf(MSG_DEBUG, "nl80211: Interface down");
-               if (drv->ignore_if_down_event) {
+               wpa_printf(MSG_DEBUG, "nl80211: Interface down (%s/%s)",
+                          namebuf, ifname);
+               if (os_strcmp(drv->first_bss->ifname, ifname) != 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "nl80211: Not the main interface (%s) - do not indicate interface down",
+                                  drv->first_bss->ifname);
+               } else if (drv->ignore_if_down_event) {
                        wpa_printf(MSG_DEBUG, "nl80211: Ignore interface down "
                                   "event generated by mode change");
                        drv->ignore_if_down_event = 0;
@@ -1129,25 +984,56 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
                        drv->if_disabled = 1;
                        wpa_supplicant_event(drv->ctx,
                                             EVENT_INTERFACE_DISABLED, NULL);
+
+                       /*
+                        * Try to get drv again, since it may be removed as
+                        * part of the EVENT_INTERFACE_DISABLED handling for
+                        * dynamic interfaces
+                        */
+                       drv = nl80211_find_drv(global, ifi->ifi_index,
+                                              buf, len);
+                       if (!drv)
+                               return;
                }
        }
 
        if (drv->if_disabled && (ifi->ifi_flags & IFF_UP)) {
                if (if_indextoname(ifi->ifi_index, namebuf) &&
-                   linux_iface_up(drv->global->ioctl_sock,
-                                  drv->first_bss.ifname) == 0) {
+                   linux_iface_up(drv->global->ioctl_sock, namebuf) == 0) {
                        wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
                                   "event since interface %s is down",
                                   namebuf);
-               } else if (if_nametoindex(drv->first_bss.ifname) == 0) {
+               } else if (if_nametoindex(drv->first_bss->ifname) == 0) {
                        wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
                                   "event since interface %s does not exist",
-                                  drv->first_bss.ifname);
+                                  drv->first_bss->ifname);
                } else if (drv->if_removed) {
                        wpa_printf(MSG_DEBUG, "nl80211: Ignore interface up "
                                   "event since interface %s is marked "
-                                  "removed", drv->first_bss.ifname);
+                                  "removed", drv->first_bss->ifname);
                } else {
+                       struct i802_bss *bss;
+                       u8 addr[ETH_ALEN];
+
+                       /* Re-read MAC address as it may have changed */
+                       bss = get_bss_ifindex(drv, ifi->ifi_index);
+                       if (bss &&
+                           linux_get_ifhwaddr(drv->global->ioctl_sock,
+                                              bss->ifname, addr) < 0) {
+                               wpa_printf(MSG_DEBUG,
+                                          "nl80211: %s: failed to re-read MAC address",
+                                          bss->ifname);
+                       } else if (bss &&
+                                  os_memcmp(addr, bss->addr, ETH_ALEN) != 0) {
+                               wpa_printf(MSG_DEBUG,
+                                          "nl80211: Own MAC address on ifindex %d (%s) changed from "
+                                          MACSTR " to " MACSTR,
+                                          ifi->ifi_index, bss->ifname,
+                                          MAC2STR(bss->addr),
+                                          MAC2STR(addr));
+                               os_memcpy(bss->addr, addr, ETH_ALEN);
+                       }
+
                        wpa_printf(MSG_DEBUG, "nl80211: Interface up");
                        drv->if_disabled = 0;
                        wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_ENABLED,
@@ -1163,30 +1049,35 @@ static void wpa_driver_nl80211_event_rtm_newlink(void *ctx,
         */
        if (drv->operstate == 1 &&
            (ifi->ifi_flags & (IFF_LOWER_UP | IFF_DORMANT)) == IFF_LOWER_UP &&
-           !(ifi->ifi_flags & IFF_RUNNING))
+           !(ifi->ifi_flags & IFF_RUNNING)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Set IF_OPER_UP again based on ifi_flags and expected operstate");
                netlink_send_oper_ifla(drv->global->netlink, drv->ifindex,
                                       -1, IF_OPER_UP);
-
-       attrlen = len;
-       attr = (struct rtattr *) buf;
-       rta_len = RTA_ALIGN(sizeof(struct rtattr));
-       while (RTA_OK(attr, attrlen)) {
-               if (attr->rta_type == IFLA_IFNAME) {
-                       wpa_driver_nl80211_event_link(
-                               drv,
-                               ((char *) attr) + rta_len,
-                               attr->rta_len - rta_len, 0);
-               } else if (attr->rta_type == IFLA_MASTER)
-                       brid = nla_get_u32((struct nlattr *) attr);
-               attr = RTA_NEXT(attr, attrlen);
        }
 
+       if (ifname[0])
+               wpa_driver_nl80211_event_newlink(drv, ifname);
+
        if (ifi->ifi_family == AF_BRIDGE && brid) {
+               struct i802_bss *bss;
+
                /* device has been added to bridge */
-               if_indextoname(brid, namebuf);
+               if (!if_indextoname(brid, namebuf)) {
+                       wpa_printf(MSG_DEBUG,
+                                  "nl80211: Could not find bridge ifname for ifindex %u",
+                                  brid);
+                       return;
+               }
                wpa_printf(MSG_DEBUG, "nl80211: Add ifindex %u for bridge %s",
                           brid, namebuf);
                add_ifidx(drv, brid);
+
+               for (bss = drv->first_bss; bss; bss = bss->next) {
+                       if (os_strcmp(ifname, bss->ifname) == 0) {
+                               os_strlcpy(bss->brname, namebuf, IFNAMSIZ);
+                               break;
+                       }
+               }
        }
 }
 
@@ -1197,4092 +1088,1721 @@ static void wpa_driver_nl80211_event_rtm_dellink(void *ctx,
 {
        struct nl80211_global *global = ctx;
        struct wpa_driver_nl80211_data *drv;
-       int attrlen, rta_len;
+       int attrlen;
        struct rtattr *attr;
        u32 brid = 0;
+       char ifname[IFNAMSIZ + 1];
+       char extra[100], *pos, *end;
 
        drv = nl80211_find_drv(global, ifi->ifi_index, buf, len);
        if (!drv) {
-               wpa_printf(MSG_DEBUG, "nl80211: Ignore dellink event for "
-                          "foreign ifindex %d", ifi->ifi_index);
+               wpa_printf(MSG_DEBUG, "nl80211: Ignore RTM_DELLINK event for foreign ifindex %d",
+                          ifi->ifi_index);
                return;
        }
 
+       extra[0] = '\0';
+       pos = extra;
+       end = pos + sizeof(extra);
+       ifname[0] = '\0';
+
        attrlen = len;
        attr = (struct rtattr *) buf;
-
-       rta_len = RTA_ALIGN(sizeof(struct rtattr));
        while (RTA_OK(attr, attrlen)) {
-               if (attr->rta_type == IFLA_IFNAME) {
-                       wpa_driver_nl80211_event_link(
-                               drv,
-                               ((char *) attr) + rta_len,
-                               attr->rta_len - rta_len, 1);
-               } else if (attr->rta_type == IFLA_MASTER)
+               switch (attr->rta_type) {
+               case IFLA_IFNAME:
+                       if (RTA_PAYLOAD(attr) >= IFNAMSIZ)
+                               break;
+                       os_memcpy(ifname, RTA_DATA(attr), RTA_PAYLOAD(attr));
+                       ifname[RTA_PAYLOAD(attr)] = '\0';
+                       break;
+               case IFLA_MASTER:
                        brid = nla_get_u32((struct nlattr *) attr);
+                       pos += os_snprintf(pos, end - pos, " master=%u", brid);
+                       break;
+               case IFLA_OPERSTATE:
+                       pos += os_snprintf(pos, end - pos, " operstate=%u",
+                                          nla_get_u32((struct nlattr *) attr));
+                       break;
+               case IFLA_LINKMODE:
+                       pos += os_snprintf(pos, end - pos, " linkmode=%u",
+                                          nla_get_u32((struct nlattr *) attr));
+                       break;
+               }
                attr = RTA_NEXT(attr, attrlen);
        }
+       extra[sizeof(extra) - 1] = '\0';
+
+       wpa_printf(MSG_DEBUG, "RTM_DELLINK: ifi_index=%d ifname=%s%s ifi_family=%d ifi_flags=0x%x (%s%s%s%s)",
+                  ifi->ifi_index, ifname, extra, ifi->ifi_family,
+                  ifi->ifi_flags,
+                  (ifi->ifi_flags & IFF_UP) ? "[UP]" : "",
+                  (ifi->ifi_flags & IFF_RUNNING) ? "[RUNNING]" : "",
+                  (ifi->ifi_flags & IFF_LOWER_UP) ? "[LOWER_UP]" : "",
+                  (ifi->ifi_flags & IFF_DORMANT) ? "[DORMANT]" : "");
+
+       if (ifname[0] && (ifi->ifi_family != AF_BRIDGE || !brid))
+               wpa_driver_nl80211_event_dellink(drv, ifname);
 
        if (ifi->ifi_family == AF_BRIDGE && brid) {
                /* device has been removed from bridge */
                char namebuf[IFNAMSIZ];
-               if_indextoname(brid, namebuf);
-               wpa_printf(MSG_DEBUG, "nl80211: Remove ifindex %u for bridge "
-                          "%s", brid, namebuf);
-               del_ifidx(drv, brid);
-       }
-}
-
-
-static void mlme_event_auth(struct wpa_driver_nl80211_data *drv,
-                           const u8 *frame, size_t len)
-{
-       const struct ieee80211_mgmt *mgmt;
-       union wpa_event_data event;
-
-       wpa_printf(MSG_DEBUG, "nl80211: Authenticate event");
-       mgmt = (const struct ieee80211_mgmt *) frame;
-       if (len < 24 + sizeof(mgmt->u.auth)) {
-               wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
-                          "frame");
-               return;
-       }
 
-       os_memcpy(drv->auth_bssid, mgmt->sa, ETH_ALEN);
-       os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN);
-       os_memset(&event, 0, sizeof(event));
-       os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN);
-       event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg);
-       event.auth.auth_transaction =
-               le_to_host16(mgmt->u.auth.auth_transaction);
-       event.auth.status_code = le_to_host16(mgmt->u.auth.status_code);
-       if (len > 24 + sizeof(mgmt->u.auth)) {
-               event.auth.ies = mgmt->u.auth.variable;
-               event.auth.ies_len = len - 24 - sizeof(mgmt->u.auth);
+               if (!if_indextoname(brid, namebuf)) {
+                       wpa_printf(MSG_DEBUG,
+                                  "nl80211: Could not find bridge ifname for ifindex %u",
+                                  brid);
+               } else {
+                       wpa_printf(MSG_DEBUG,
+                                  "nl80211: Remove ifindex %u for bridge %s",
+                                  brid, namebuf);
+               }
+               del_ifidx(drv, brid);
        }
-
-       wpa_supplicant_event(drv->ctx, EVENT_AUTH, &event);
 }
 
 
-static unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv)
+unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv)
 {
        struct nl_msg *msg;
        int ret;
        struct nl80211_bss_info_arg arg;
 
+       msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
        os_memset(&arg, 0, sizeof(arg));
-       msg = nlmsg_alloc();
-       if (!msg)
-               goto nla_put_failure;
-
-       nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-
        arg.drv = drv;
        ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
-       msg = NULL;
        if (ret == 0) {
+               unsigned int freq = drv->nlmode == NL80211_IFTYPE_ADHOC ?
+                       arg.ibss_freq : arg.assoc_freq;
                wpa_printf(MSG_DEBUG, "nl80211: Operating frequency for the "
-                          "associated BSS from scan results: %u MHz",
-                          arg.assoc_freq);
-               return arg.assoc_freq ? arg.assoc_freq : drv->assoc_freq;
+                          "associated BSS from scan results: %u MHz", freq);
+               if (freq)
+                       drv->assoc_freq = freq;
+               return drv->assoc_freq;
        }
        wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
                   "(%s)", ret, strerror(-ret));
-nla_put_failure:
-       nlmsg_free(msg);
        return drv->assoc_freq;
 }
 
 
-static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
-                           const u8 *frame, size_t len)
+static int get_link_signal(struct nl_msg *msg, void *arg)
 {
-       const struct ieee80211_mgmt *mgmt;
-       union wpa_event_data event;
-       u16 status;
-
-       wpa_printf(MSG_DEBUG, "nl80211: Associate event");
-       mgmt = (const struct ieee80211_mgmt *) frame;
-#ifdef TIZEN_EXT
-       if (drv->nlmode == NL80211_IFTYPE_AP || drv->nlmode == NL80211_IFTYPE_P2P_GO) {
-               if (len < 24 + sizeof(mgmt->u.assoc_req)) {
-                       wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
-                          "frame");
-                       return;
-               }
-               os_memset(&event, 0, sizeof(event));
-               event.assoc_info.freq = drv->assoc_freq;
-               event.assoc_info.req_ies = (u8 *) mgmt->u.assoc_req.variable;
-               event.assoc_info.req_ies_len = len - 24 - sizeof(mgmt->u.assoc_req);
-               event.assoc_info.addr = mgmt->sa;
-       } else {
-#endif
-       if (len < 24 + sizeof(mgmt->u.assoc_resp)) {
-               wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
-                          "frame");
-               return;
-       }
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
+       static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = {
+               [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
+               [NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 },
+       };
+       struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
+       static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
+               [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
+               [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
+               [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
+               [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
+       };
+       struct wpa_signal_info *sig_change = arg;
 
-       status = le_to_host16(mgmt->u.assoc_resp.status_code);
-       if (status != WLAN_STATUS_SUCCESS) {
-               os_memset(&event, 0, sizeof(event));
-               event.assoc_reject.bssid = mgmt->bssid;
-               if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
-                       event.assoc_reject.resp_ies =
-                               (u8 *) mgmt->u.assoc_resp.variable;
-                       event.assoc_reject.resp_ies_len =
-                               len - 24 - sizeof(mgmt->u.assoc_resp);
-               }
-               event.assoc_reject.status_code = status;
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+       if (!tb[NL80211_ATTR_STA_INFO] ||
+           nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
+                            tb[NL80211_ATTR_STA_INFO], policy))
+               return NL_SKIP;
+       if (!sinfo[NL80211_STA_INFO_SIGNAL])
+               return NL_SKIP;
 
-               wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
-               return;
-       }
+       sig_change->current_signal =
+               (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
 
-       drv->associated = 1;
-       os_memcpy(drv->bssid, mgmt->sa, ETH_ALEN);
-       os_memcpy(drv->prev_bssid, mgmt->sa, ETH_ALEN);
+       if (sinfo[NL80211_STA_INFO_SIGNAL_AVG])
+               sig_change->avg_signal =
+                       (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]);
+       else
+               sig_change->avg_signal = 0;
 
-       os_memset(&event, 0, sizeof(event));
-       if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
-               event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable;
-               event.assoc_info.resp_ies_len =
-                       len - 24 - sizeof(mgmt->u.assoc_resp);
+       if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
+               if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
+                                    sinfo[NL80211_STA_INFO_TX_BITRATE],
+                                    rate_policy)) {
+                       sig_change->current_txrate = 0;
+               } else {
+                       if (rinfo[NL80211_RATE_INFO_BITRATE]) {
+                               sig_change->current_txrate =
+                                       nla_get_u16(rinfo[
+                                            NL80211_RATE_INFO_BITRATE]) * 100;
+                       }
+               }
        }
 
-       event.assoc_info.freq = drv->assoc_freq;
-#ifdef TIZEN_EXT
-       }
-#endif
-       wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+       return NL_SKIP;
 }
 
 
-static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
-                              enum nl80211_commands cmd, struct nlattr *status,
-                              struct nlattr *addr, struct nlattr *req_ie,
-                              struct nlattr *resp_ie)
+int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
+                           struct wpa_signal_info *sig)
 {
-       union wpa_event_data event;
-
-       if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
-               /*
-                * Avoid reporting two association events that would confuse
-                * the core code.
-                */
-               wpa_printf(MSG_DEBUG, "nl80211: Ignore connect event (cmd=%d) "
-                          "when using userspace SME", cmd);
-               return;
-       }
-
-       if (cmd == NL80211_CMD_CONNECT)
-               wpa_printf(MSG_DEBUG, "nl80211: Connect event");
-       else if (cmd == NL80211_CMD_ROAM)
-               wpa_printf(MSG_DEBUG, "nl80211: Roam event");
-
-       os_memset(&event, 0, sizeof(event));
-       if (cmd == NL80211_CMD_CONNECT &&
-           nla_get_u16(status) != WLAN_STATUS_SUCCESS) {
-               if (addr)
-                       event.assoc_reject.bssid = nla_data(addr);
-               if (resp_ie) {
-                       event.assoc_reject.resp_ies = nla_data(resp_ie);
-                       event.assoc_reject.resp_ies_len = nla_len(resp_ie);
-               }
-               event.assoc_reject.status_code = nla_get_u16(status);
-               wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
-               return;
-       }
+       struct nl_msg *msg;
 
-       drv->associated = 1;
-       if (addr) {
-               os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN);
-               os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
-       }
+       sig->current_signal = -9999;
+       sig->current_txrate = 0;
 
-       if (req_ie) {
-               event.assoc_info.req_ies = nla_data(req_ie);
-               event.assoc_info.req_ies_len = nla_len(req_ie);
-       }
-       if (resp_ie) {
-               event.assoc_info.resp_ies = nla_data(resp_ie);
-               event.assoc_info.resp_ies_len = nla_len(resp_ie);
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_STATION)) ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid)) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
        }
 
-       event.assoc_info.freq = nl80211_get_assoc_freq(drv);
-
-       wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+       return send_and_recv_msgs(drv, msg, get_link_signal, sig);
 }
 
 
-static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
-                                 struct nlattr *reason, struct nlattr *addr,
-                                 struct nlattr *by_ap)
+static int get_link_noise(struct nl_msg *msg, void *arg)
 {
-       union wpa_event_data data;
-       unsigned int locally_generated = by_ap == NULL;
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
+       static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
+               [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
+               [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
+       };
+       struct wpa_signal_info *sig_change = arg;
 
-       if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
-               /*
-                * Avoid reporting two disassociation events that could
-                * confuse the core code.
-                */
-               wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
-                          "event when using userspace SME");
-               return;
-       }
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
 
-       if (drv->ignore_next_local_disconnect) {
-               drv->ignore_next_local_disconnect = 0;
-               if (locally_generated) {
-                       wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
-                                  "event triggered during reassociation");
-                       return;
-               }
-               wpa_printf(MSG_WARNING, "nl80211: Was expecting local "
-                          "disconnect but got another disconnect "
-                          "event first");
+       if (!tb[NL80211_ATTR_SURVEY_INFO]) {
+               wpa_printf(MSG_DEBUG, "nl80211: survey data missing!");
+               return NL_SKIP;
        }
 
-       wpa_printf(MSG_DEBUG, "nl80211: Disconnect event");
-       nl80211_mark_disconnected(drv);
-       os_memset(&data, 0, sizeof(data));
-       if (reason)
-               data.deauth_info.reason_code = nla_get_u16(reason);
-       data.deauth_info.locally_generated = by_ap == NULL;
-       wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, &data);
-}
-
-
-static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
-                                struct nlattr *freq, struct nlattr *type)
-{
-       union wpa_event_data data;
-       int ht_enabled = 1;
-       int chan_offset = 0;
+       if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
+                            tb[NL80211_ATTR_SURVEY_INFO],
+                            survey_policy)) {
+               wpa_printf(MSG_DEBUG, "nl80211: failed to parse nested "
+                          "attributes!");
+               return NL_SKIP;
+       }
 
-       wpa_printf(MSG_DEBUG, "nl80211: Channel switch event");
+       if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
+               return NL_SKIP;
 
-       if (!freq || !type)
-               return;
+       if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
+           sig_change->frequency)
+               return NL_SKIP;
 
-       switch (nla_get_u32(type)) {
-       case NL80211_CHAN_NO_HT:
-               ht_enabled = 0;
-               break;
-       case NL80211_CHAN_HT20:
-               break;
-       case NL80211_CHAN_HT40PLUS:
-               chan_offset = 1;
-               break;
-       case NL80211_CHAN_HT40MINUS:
-               chan_offset = -1;
-               break;
-       }
+       if (!sinfo[NL80211_SURVEY_INFO_NOISE])
+               return NL_SKIP;
 
-       data.ch_switch.freq = nla_get_u32(freq);
-       data.ch_switch.ht_enabled = ht_enabled;
-       data.ch_switch.ch_offset = chan_offset;
+       sig_change->current_noise =
+               (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
 
-       wpa_supplicant_event(drv->ctx, EVENT_CH_SWITCH, &data);
+       return NL_SKIP;
 }
 
 
-static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv,
-                              enum nl80211_commands cmd, struct nlattr *addr)
+int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
+                          struct wpa_signal_info *sig_change)
 {
-       union wpa_event_data event;
-       enum wpa_event_type ev;
-
-       if (nla_len(addr) != ETH_ALEN)
-               return;
-
-       wpa_printf(MSG_DEBUG, "nl80211: MLME event %d; timeout with " MACSTR,
-                  cmd, MAC2STR((u8 *) nla_data(addr)));
+       struct nl_msg *msg;
 
-       if (cmd == NL80211_CMD_AUTHENTICATE)
-               ev = EVENT_AUTH_TIMED_OUT;
-       else if (cmd == NL80211_CMD_ASSOCIATE)
-               ev = EVENT_ASSOC_TIMED_OUT;
-       else
-               return;
+       sig_change->current_noise = 9999;
+       sig_change->frequency = drv->assoc_freq;
 
-       os_memset(&event, 0, sizeof(event));
-       os_memcpy(event.timeout_event.addr, nla_data(addr), ETH_ALEN);
-       wpa_supplicant_event(drv->ctx, ev, &event);
+       msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+       return send_and_recv_msgs(drv, msg, get_link_noise, sig_change);
 }
 
-#ifdef TIZEN_EXT
-#ifdef BCM_DRIVER_V115
-static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
-                                      enum wpa_event_type type,
-                                      const u8 *frame, size_t len);
-#endif
-#endif
 
-static void mlme_event_mgmt(struct wpa_driver_nl80211_data *drv,
-                           struct nlattr *freq, struct nlattr *sig,
-                           const u8 *frame, size_t len)
+static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx,
+                                            void *handle)
 {
-       const struct ieee80211_mgmt *mgmt;
-       union wpa_event_data event;
-       u16 fc, stype;
-       int ssi_signal = 0;
-
-       wpa_printf(MSG_MSGDUMP, "nl80211: Frame event");
-       mgmt = (const struct ieee80211_mgmt *) frame;
-       if (len < 24) {
-               wpa_printf(MSG_DEBUG, "nl80211: Too short action frame");
-               return;
-       }
-
-       fc = le_to_host16(mgmt->frame_control);
-       stype = WLAN_FC_GET_STYPE(fc);
+       struct nl_cb *cb = eloop_ctx;
+       int res;
 
-       if (sig)
-               ssi_signal = (s32) nla_get_u32(sig);
+       wpa_printf(MSG_MSGDUMP, "nl80211: Event message available");
 
-       os_memset(&event, 0, sizeof(event));
-       if (freq) {
-               event.rx_action.freq = nla_get_u32(freq);
-               drv->last_mgmt_freq = event.rx_action.freq;
-       }
-       if (stype == WLAN_FC_STYPE_ACTION) {
-               event.rx_action.da = mgmt->da;
-               event.rx_action.sa = mgmt->sa;
-               event.rx_action.bssid = mgmt->bssid;
-               event.rx_action.category = mgmt->u.action.category;
-               event.rx_action.data = &mgmt->u.action.category + 1;
-               event.rx_action.len = frame + len - event.rx_action.data;
-               wpa_supplicant_event(drv->ctx, EVENT_RX_ACTION, &event);
-#ifdef TIZEN_EXT
-#ifdef BCM_DRIVER_V115
-       } else if (stype == WLAN_FC_STYPE_ASSOC_REQ) {
-               mlme_event_assoc(drv, frame, len);
-       } else if (stype == WLAN_FC_STYPE_DISASSOC) {
-               mlme_event_deauth_disassoc(drv, EVENT_DISASSOC, frame, len);
-       } else if (stype == WLAN_FC_STYPE_DEAUTH) {
-               mlme_event_deauth_disassoc(drv, EVENT_DEAUTH, frame, len);
-#endif
-#endif
-       } else {
-               event.rx_mgmt.frame = frame;
-               event.rx_mgmt.frame_len = len;
-               event.rx_mgmt.ssi_signal = ssi_signal;
-               wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+       res = nl_recvmsgs(handle, cb);
+       if (res < 0) {
+               wpa_printf(MSG_INFO, "nl80211: %s->nl_recvmsgs failed: %d",
+                          __func__, res);
        }
 }
 
 
-static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
-                                     struct nlattr *cookie, const u8 *frame,
-                                     size_t len, struct nlattr *ack)
-{
-       union wpa_event_data event;
-       const struct ieee80211_hdr *hdr;
-       u16 fc;
-
-       wpa_printf(MSG_DEBUG, "nl80211: Frame TX status event");
-       if (!is_ap_interface(drv->nlmode)) {
-               u64 cookie_val;
-
-               if (!cookie)
-                       return;
+/**
+ * wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain
+ * @priv: driver_nl80211 private data
+ * @alpha2_arg: country to which to switch to
+ * Returns: 0 on success, -1 on failure
+ *
+ * This asks nl80211 to set the regulatory domain for given
+ * country ISO / IEC alpha2.
+ */
+static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       char alpha2[3];
+       struct nl_msg *msg;
 
-               cookie_val = nla_get_u64(cookie);
-               wpa_printf(MSG_DEBUG, "nl80211: Action TX status:"
-                          " cookie=0%llx%s (ack=%d)",
-                          (long long unsigned int) cookie_val,
-                          cookie_val == drv->send_action_cookie ?
-                          " (match)" : " (unknown)", ack != NULL);
-               if (cookie_val != drv->send_action_cookie)
-                       return;
-       }
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
 
-       hdr = (const struct ieee80211_hdr *) frame;
-       fc = le_to_host16(hdr->frame_control);
+       alpha2[0] = alpha2_arg[0];
+       alpha2[1] = alpha2_arg[1];
+       alpha2[2] = '\0';
 
-       os_memset(&event, 0, sizeof(event));
-       event.tx_status.type = WLAN_FC_GET_TYPE(fc);
-       event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
-       event.tx_status.dst = hdr->addr1;
-       event.tx_status.data = frame;
-       event.tx_status.data_len = len;
-       event.tx_status.ack = ack != NULL;
-       wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
+       if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_REQ_SET_REG) ||
+           nla_put_string(msg, NL80211_ATTR_REG_ALPHA2, alpha2)) {
+               nlmsg_free(msg);
+               return -EINVAL;
+       }
+       if (send_and_recv_msgs(drv, msg, NULL, NULL))
+               return -EINVAL;
+       return 0;
 }
 
 
-static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
-                                      enum wpa_event_type type,
-                                      const u8 *frame, size_t len)
+static int nl80211_get_country(struct nl_msg *msg, void *arg)
 {
-       const struct ieee80211_mgmt *mgmt;
-       union wpa_event_data event;
-       const u8 *bssid = NULL;
-       u16 reason_code = 0;
-
-       if (type == EVENT_DEAUTH)
-               wpa_printf(MSG_DEBUG, "nl80211: Deauthenticate event");
-       else
-               wpa_printf(MSG_DEBUG, "nl80211: Disassociate event");
-
-       mgmt = (const struct ieee80211_mgmt *) frame;
-       if (len >= 24) {
-               bssid = mgmt->bssid;
-
-               if ((drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
-                   !drv->associated &&
-                   os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0 &&
-                   os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0 &&
-                   os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0) {
-                       /*
-                        * Avoid issues with some roaming cases where
-                        * disconnection event for the old AP may show up after
-                        * we have started connection with the new AP.
-                        */
-                       wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR,
-                                  MAC2STR(bssid),
-                                  MAC2STR(drv->auth_attempt_bssid));
-                       return;
-               }
-
-               if (drv->associated != 0 &&
-                   os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 &&
-                   os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) {
-                       /*
-                        * We have presumably received this deauth as a
-                        * response to a clear_state_mismatch() outgoing
-                        * deauth.  Don't let it take us offline!
-                        */
-                       wpa_printf(MSG_DEBUG, "nl80211: Deauth received "
-                                  "from Unknown BSSID " MACSTR " -- ignoring",
-                                  MAC2STR(bssid));
-                       return;
-               }
-       }
-
-       nl80211_mark_disconnected(drv);
-       os_memset(&event, 0, sizeof(event));
+       char *alpha2 = arg;
+       struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
 
-       /* Note: Same offset for Reason Code in both frame subtypes */
-       if (len >= 24 + sizeof(mgmt->u.deauth))
-               reason_code = le_to_host16(mgmt->u.deauth.reason_code);
-
-       if (type == EVENT_DISASSOC) {
-               event.disassoc_info.locally_generated =
-                       !os_memcmp(mgmt->sa, drv->first_bss.addr, ETH_ALEN);
-#if (defined(TIZEN_EXT_P2P) || defined(TIZEN_EXT_SOFTAP)) && defined(BCM_DRIVER_V115)
-               if (drv->nlmode == NL80211_IFTYPE_AP ||
-                       drv->nlmode == NL80211_IFTYPE_P2P_GO) {
-                       event.disassoc_info.addr = mgmt->sa;
-               } else
-#endif
-               event.disassoc_info.addr = bssid;
-               event.disassoc_info.reason_code = reason_code;
-               if (frame + len > mgmt->u.disassoc.variable) {
-                       event.disassoc_info.ie = mgmt->u.disassoc.variable;
-                       event.disassoc_info.ie_len = frame + len -
-                               mgmt->u.disassoc.variable;
-               }
-       } else {
-               event.deauth_info.locally_generated =
-                       !os_memcmp(mgmt->sa, drv->first_bss.addr, ETH_ALEN);
-#if (defined(TIZEN_EXT_P2P) || defined(TIZEN_EXT_SOFTAP)) && defined(BCM_DRIVER_V115)
-               if (drv->nlmode == NL80211_IFTYPE_AP ||
-                       drv->nlmode == NL80211_IFTYPE_P2P_GO) {
-               event.deauth_info.addr = mgmt->sa;
-               } else
-#endif
-               event.deauth_info.addr = bssid;
-               event.deauth_info.reason_code = reason_code;
-               if (frame + len > mgmt->u.deauth.variable) {
-                       event.deauth_info.ie = mgmt->u.deauth.variable;
-                       event.deauth_info.ie_len = frame + len -
-                               mgmt->u.deauth.variable;
-               }
+       nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+       if (!tb_msg[NL80211_ATTR_REG_ALPHA2]) {
+               wpa_printf(MSG_DEBUG, "nl80211: No country information available");
+               return NL_SKIP;
        }
-
-       wpa_supplicant_event(drv->ctx, type, &event);
+       os_strlcpy(alpha2, nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]), 3);
+       return NL_SKIP;
 }
 
 
-static void mlme_event_unprot_disconnect(struct wpa_driver_nl80211_data *drv,
-                                        enum wpa_event_type type,
-                                        const u8 *frame, size_t len)
+static int wpa_driver_nl80211_get_country(void *priv, char *alpha2)
 {
-       const struct ieee80211_mgmt *mgmt;
-       union wpa_event_data event;
-       u16 reason_code = 0;
-
-       if (type == EVENT_UNPROT_DEAUTH)
-               wpa_printf(MSG_DEBUG, "nl80211: Unprot Deauthenticate event");
-       else
-               wpa_printf(MSG_DEBUG, "nl80211: Unprot Disassociate event");
-
-       if (len < 24)
-               return;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret;
 
-       mgmt = (const struct ieee80211_mgmt *) frame;
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
 
-       os_memset(&event, 0, sizeof(event));
-       /* Note: Same offset for Reason Code in both frame subtypes */
-       if (len >= 24 + sizeof(mgmt->u.deauth))
-               reason_code = le_to_host16(mgmt->u.deauth.reason_code);
-
-       if (type == EVENT_UNPROT_DISASSOC) {
-               event.unprot_disassoc.sa = mgmt->sa;
-               event.unprot_disassoc.da = mgmt->da;
-               event.unprot_disassoc.reason_code = reason_code;
-       } else {
-               event.unprot_deauth.sa = mgmt->sa;
-               event.unprot_deauth.da = mgmt->da;
-               event.unprot_deauth.reason_code = reason_code;
-       }
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG);
+       alpha2[0] = '\0';
+       ret = send_and_recv_msgs(drv, msg, nl80211_get_country, alpha2);
+       if (!alpha2[0])
+               ret = -1;
 
-       wpa_supplicant_event(drv->ctx, type, &event);
+       return ret;
 }
 
 
-static void mlme_event(struct i802_bss *bss,
-                      enum nl80211_commands cmd, struct nlattr *frame,
-                      struct nlattr *addr, struct nlattr *timed_out,
-                      struct nlattr *freq, struct nlattr *ack,
-                      struct nlattr *cookie, struct nlattr *sig)
+static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
 {
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       const u8 *data;
-       size_t len;
+       int ret;
 
-       if (timed_out && addr) {
-               mlme_timeout_event(drv, cmd, addr);
-               return;
+       global->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+       if (global->nl_cb == NULL) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
+                          "callbacks");
+               return -1;
        }
 
-       if (frame == NULL) {
-               wpa_printf(MSG_DEBUG,
-                          "nl80211: MLME event %d (%s) without frame data",
-                          cmd, nl80211_command_to_string(cmd));
-               return;
+       global->nl = nl_create_handle(global->nl_cb, "nl");
+       if (global->nl == NULL)
+               goto err;
+
+       global->nl80211_id = genl_ctrl_resolve(global->nl, "nl80211");
+       if (global->nl80211_id < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not "
+                          "found");
+               goto err;
        }
 
-       data = nla_data(frame);
-       len = nla_len(frame);
-       if (len < 4 + 2 * ETH_ALEN) {
-               wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s("
-                          MACSTR ") - too short",
-                          cmd, nl80211_command_to_string(cmd), bss->ifname,
-                          MAC2STR(bss->addr));
-               return;
+       global->nl_event = nl_create_handle(global->nl_cb, "event");
+       if (global->nl_event == NULL)
+               goto err;
+
+       ret = nl_get_multicast_id(global, "nl80211", "scan");
+       if (ret >= 0)
+               ret = nl_socket_add_membership(global->nl_event, ret);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
+                          "membership for scan events: %d (%s)",
+                          ret, strerror(-ret));
+               goto err;
        }
-       wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" MACSTR
-                  ") A1=" MACSTR " A2=" MACSTR, cmd,
-                  nl80211_command_to_string(cmd), bss->ifname,
-                  MAC2STR(bss->addr), MAC2STR(data + 4),
-                  MAC2STR(data + 4 + ETH_ALEN));
-#ifdef TIZEN_EXT_P2P
-       if (os_strstr(bss->ifname, GROUP_INTERFACE_PREFIX )== NULL){
-               if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) &&
-                       os_memcmp(bss->dev_addr, data + 4, ETH_ALEN) != 0 &&
-                       os_memcmp(bss->dev_addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) {
-                       wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event on wlan0"
-                                  "for foreign address", bss->ifname);
-                       return;
-               }
-       }else{
-               if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) &&
-                       os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 &&
-                       os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) {
-                       wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event on p2p"
-                                  "for foreign address", bss->ifname);
-                       return;
-               }
+
+       ret = nl_get_multicast_id(global, "nl80211", "mlme");
+       if (ret >= 0)
+               ret = nl_socket_add_membership(global->nl_event, ret);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
+                          "membership for mlme events: %d (%s)",
+                          ret, strerror(-ret));
+               goto err;
        }
-#else /* TIZEN_EXT_P2P */
-       if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) &&
-           os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 &&
-           os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) {
-               wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event "
-                          "for foreign address", bss->ifname);
-               return;
+
+       ret = nl_get_multicast_id(global, "nl80211", "regulatory");
+       if (ret >= 0)
+               ret = nl_socket_add_membership(global->nl_event, ret);
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast "
+                          "membership for regulatory events: %d (%s)",
+                          ret, strerror(-ret));
+               /* Continue without regulatory events */
        }
-#endif /* TIZEN_EXT_P2P */
-       wpa_hexdump(MSG_MSGDUMP, "nl80211: MLME event frame",
-                   nla_data(frame), nla_len(frame));
 
-       switch (cmd) {
-       case NL80211_CMD_AUTHENTICATE:
-               mlme_event_auth(drv, nla_data(frame), nla_len(frame));
-               break;
-       case NL80211_CMD_ASSOCIATE:
-               mlme_event_assoc(drv, nla_data(frame), nla_len(frame));
-               break;
-       case NL80211_CMD_DEAUTHENTICATE:
-               mlme_event_deauth_disassoc(drv, EVENT_DEAUTH,
-                                          nla_data(frame), nla_len(frame));
-               break;
-       case NL80211_CMD_DISASSOCIATE:
-               mlme_event_deauth_disassoc(drv, EVENT_DISASSOC,
-                                          nla_data(frame), nla_len(frame));
-               break;
-       case NL80211_CMD_FRAME:
-               mlme_event_mgmt(drv, freq, sig, nla_data(frame),
-                               nla_len(frame));
-               break;
-       case NL80211_CMD_FRAME_TX_STATUS:
-               mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame),
-                                         nla_len(frame), ack);
-               break;
-       case NL80211_CMD_UNPROT_DEAUTHENTICATE:
-               mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH,
-                                            nla_data(frame), nla_len(frame));
-               break;
-       case NL80211_CMD_UNPROT_DISASSOCIATE:
-               mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DISASSOC,
-                                            nla_data(frame), nla_len(frame));
-               break;
-       default:
-               break;
+       ret = nl_get_multicast_id(global, "nl80211", "vendor");
+       if (ret >= 0)
+               ret = nl_socket_add_membership(global->nl_event, ret);
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast "
+                          "membership for vendor events: %d (%s)",
+                          ret, strerror(-ret));
+               /* Continue without vendor events */
        }
+
+       nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+                 no_seq_check, NULL);
+       nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+                 process_global_event, global);
+
+       nl80211_register_eloop_read(&global->nl_event,
+                                   wpa_driver_nl80211_event_receive,
+                                   global->nl_cb);
+
+       return 0;
+
+err:
+       nl_destroy_handles(&global->nl_event);
+       nl_destroy_handles(&global->nl);
+       nl_cb_put(global->nl_cb);
+       global->nl_cb = NULL;
+       return -1;
 }
 
 
-static void mlme_event_michael_mic_failure(struct i802_bss *bss,
-                                          struct nlattr *tb[])
+static void nl80211_check_global(struct nl80211_global *global)
 {
-       union wpa_event_data data;
-
-       wpa_printf(MSG_DEBUG, "nl80211: MLME event Michael MIC failure");
-       os_memset(&data, 0, sizeof(data));
-       if (tb[NL80211_ATTR_MAC]) {
-               wpa_hexdump(MSG_DEBUG, "nl80211: Source MAC address",
-                           nla_data(tb[NL80211_ATTR_MAC]),
-                           nla_len(tb[NL80211_ATTR_MAC]));
-               data.michael_mic_failure.src = nla_data(tb[NL80211_ATTR_MAC]);
-       }
-       if (tb[NL80211_ATTR_KEY_SEQ]) {
-               wpa_hexdump(MSG_DEBUG, "nl80211: TSC",
-                           nla_data(tb[NL80211_ATTR_KEY_SEQ]),
-                           nla_len(tb[NL80211_ATTR_KEY_SEQ]));
-       }
-       if (tb[NL80211_ATTR_KEY_TYPE]) {
-               enum nl80211_key_type key_type =
-                       nla_get_u32(tb[NL80211_ATTR_KEY_TYPE]);
-               wpa_printf(MSG_DEBUG, "nl80211: Key Type %d", key_type);
-               if (key_type == NL80211_KEYTYPE_PAIRWISE)
-                       data.michael_mic_failure.unicast = 1;
-       } else
-               data.michael_mic_failure.unicast = 1;
+       struct nl_handle *handle;
+       const char *groups[] = { "scan", "mlme", "regulatory", "vendor", NULL };
+       int ret;
+       unsigned int i;
 
-       if (tb[NL80211_ATTR_KEY_IDX]) {
-               u8 key_id = nla_get_u8(tb[NL80211_ATTR_KEY_IDX]);
-               wpa_printf(MSG_DEBUG, "nl80211: Key Id %d", key_id);
+       /*
+        * Try to re-add memberships to handle case of cfg80211 getting reloaded
+        * and all registration having been cleared.
+        */
+       handle = (void *) (((intptr_t) global->nl_event) ^
+                          ELOOP_SOCKET_INVALID);
+
+       for (i = 0; groups[i]; i++) {
+               ret = nl_get_multicast_id(global, "nl80211", groups[i]);
+               if (ret >= 0)
+                       ret = nl_socket_add_membership(handle, ret);
+               if (ret < 0) {
+                       wpa_printf(MSG_INFO,
+                                  "nl80211: Could not re-add multicast membership for %s events: %d (%s)",
+                                  groups[i], ret, strerror(-ret));
+               }
        }
+}
+
 
-       wpa_supplicant_event(bss->ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+static void wpa_driver_nl80211_rfkill_blocked(void *ctx)
+{
+       wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked");
+       /*
+        * This may be for any interface; use ifdown event to disable
+        * interface.
+        */
 }
 
 
-static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv,
-                                struct nlattr *tb[])
+static void wpa_driver_nl80211_rfkill_unblocked(void *ctx)
 {
-       if (tb[NL80211_ATTR_MAC] == NULL) {
-               wpa_printf(MSG_DEBUG, "nl80211: No address in IBSS joined "
-                          "event");
+       struct wpa_driver_nl80211_data *drv = ctx;
+       wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked");
+       if (i802_set_iface_flags(drv->first_bss, 1)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP "
+                          "after rfkill unblock");
                return;
        }
-       os_memcpy(drv->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
-       drv->associated = 1;
-       wpa_printf(MSG_DEBUG, "nl80211: IBSS " MACSTR " joined",
-                  MAC2STR(drv->bssid));
-
-       wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+       /* rtnetlink ifup handler will report interface as enabled */
 }
 
 
-static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv,
-                                        int cancel_event, struct nlattr *tb[])
+static void wpa_driver_nl80211_handle_eapol_tx_status(int sock,
+                                                     void *eloop_ctx,
+                                                     void *handle)
 {
-       unsigned int freq, chan_type, duration;
-       union wpa_event_data data;
-       u64 cookie;
+       struct wpa_driver_nl80211_data *drv = eloop_ctx;
+       u8 data[2048];
+       struct msghdr msg;
+       struct iovec entry;
+       u8 control[512];
+       struct cmsghdr *cmsg;
+       int res, found_ee = 0, found_wifi = 0, acked = 0;
+       union wpa_event_data event;
 
-       if (tb[NL80211_ATTR_WIPHY_FREQ])
-               freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
-       else
-               freq = 0;
+       memset(&msg, 0, sizeof(msg));
+       msg.msg_iov = &entry;
+       msg.msg_iovlen = 1;
+       entry.iov_base = data;
+       entry.iov_len = sizeof(data);
+       msg.msg_control = &control;
+       msg.msg_controllen = sizeof(control);
 
-       if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
-               chan_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
-       else
-               chan_type = 0;
+       res = recvmsg(sock, &msg, MSG_ERRQUEUE);
+       /* if error or not fitting 802.3 header, return */
+       if (res < 14)
+               return;
 
-       if (tb[NL80211_ATTR_DURATION])
-               duration = nla_get_u32(tb[NL80211_ATTR_DURATION]);
-       else
-               duration = 0;
+       for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
+       {
+               if (cmsg->cmsg_level == SOL_SOCKET &&
+                   cmsg->cmsg_type == SCM_WIFI_STATUS) {
+                       int *ack;
 
-       if (tb[NL80211_ATTR_COOKIE])
-               cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
-       else
-               cookie = 0;
+                       found_wifi = 1;
+                       ack = (void *)CMSG_DATA(cmsg);
+                       acked = *ack;
+               }
 
-       wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel event (cancel=%d "
-                  "freq=%u channel_type=%u duration=%u cookie=0x%llx (%s))",
-                  cancel_event, freq, chan_type, duration,
-                  (long long unsigned int) cookie,
-                  cookie == drv->remain_on_chan_cookie ? "match" : "unknown");
+               if (cmsg->cmsg_level == SOL_PACKET &&
+                   cmsg->cmsg_type == PACKET_TX_TIMESTAMP) {
+                       struct sock_extended_err *err =
+                               (struct sock_extended_err *)CMSG_DATA(cmsg);
 
-       if (cookie != drv->remain_on_chan_cookie)
-               return; /* not for us */
+                       if (err->ee_origin == SO_EE_ORIGIN_TXSTATUS)
+                               found_ee = 1;
+               }
+       }
 
-       if (cancel_event)
-               drv->pending_remain_on_chan = 0;
+       if (!found_ee || !found_wifi)
+               return;
 
-       os_memset(&data, 0, sizeof(data));
-       data.remain_on_channel.freq = freq;
-       data.remain_on_channel.duration = duration;
-       wpa_supplicant_event(drv->ctx, cancel_event ?
-                            EVENT_CANCEL_REMAIN_ON_CHANNEL :
-                            EVENT_REMAIN_ON_CHANNEL, &data);
+       memset(&event, 0, sizeof(event));
+       event.eapol_tx_status.dst = data;
+       event.eapol_tx_status.data = data + 14;
+       event.eapol_tx_status.data_len = res - 14;
+       event.eapol_tx_status.ack = acked;
+       wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event);
 }
 
 
-static void mlme_event_ft_event(struct wpa_driver_nl80211_data *drv,
-                               struct nlattr *tb[])
+static int nl80211_init_bss(struct i802_bss *bss)
 {
-       union wpa_event_data data;
-
-       os_memset(&data, 0, sizeof(data));
+       bss->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
+       if (!bss->nl_cb)
+               return -1;
 
-       if (tb[NL80211_ATTR_IE]) {
-               data.ft_ies.ies = nla_data(tb[NL80211_ATTR_IE]);
-               data.ft_ies.ies_len = nla_len(tb[NL80211_ATTR_IE]);
-       }
-
-       if (tb[NL80211_ATTR_IE_RIC]) {
-               data.ft_ies.ric_ies = nla_data(tb[NL80211_ATTR_IE_RIC]);
-               data.ft_ies.ric_ies_len = nla_len(tb[NL80211_ATTR_IE_RIC]);
-       }
-
-       if (tb[NL80211_ATTR_MAC])
-               os_memcpy(data.ft_ies.target_ap,
-                         nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
-
-       wpa_printf(MSG_DEBUG, "nl80211: FT event target_ap " MACSTR,
-                  MAC2STR(data.ft_ies.target_ap));
+       nl_cb_set(bss->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
+                 no_seq_check, NULL);
+       nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
+                 process_bss_event, bss);
 
-       wpa_supplicant_event(drv->ctx, EVENT_FT_RESPONSE, &data);
+       return 0;
 }
 
 
-static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
-                           struct nlattr *tb[])
+static void nl80211_destroy_bss(struct i802_bss *bss)
 {
-       union wpa_event_data event;
-       struct nlattr *nl;
-       int rem;
-       struct scan_info *info;
-#define MAX_REPORT_FREQS 50
-       int freqs[MAX_REPORT_FREQS];
-       int num_freqs = 0;
-
-       if (drv->scan_for_auth) {
-               drv->scan_for_auth = 0;
-               wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing "
-                          "cfg80211 BSS entry");
-               wpa_driver_nl80211_authenticate_retry(drv);
-               return;
-       }
-
-       os_memset(&event, 0, sizeof(event));
-       info = &event.scan_info;
-       info->aborted = aborted;
-
-       if (tb[NL80211_ATTR_SCAN_SSIDS]) {
-               nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) {
-                       struct wpa_driver_scan_ssid *s =
-                               &info->ssids[info->num_ssids];
-                       s->ssid = nla_data(nl);
-                       s->ssid_len = nla_len(nl);
-                       info->num_ssids++;
-                       if (info->num_ssids == WPAS_MAX_SCAN_SSIDS)
-                               break;
-               }
-       }
-       if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
-               nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem)
-               {
-                       freqs[num_freqs] = nla_get_u32(nl);
-                       num_freqs++;
-                       if (num_freqs == MAX_REPORT_FREQS - 1)
-                               break;
-               }
-               info->freqs = freqs;
-               info->num_freqs = num_freqs;
-       }
-       wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event);
+       nl_cb_put(bss->nl_cb);
+       bss->nl_cb = NULL;
 }
 
 
-static int get_link_signal(struct nl_msg *msg, void *arg)
+static void * wpa_driver_nl80211_drv_init(void *ctx, const char *ifname,
+                                         void *global_priv, int hostapd,
+                                         const u8 *set_addr,
+                                         const char *driver_params)
 {
-       struct nlattr *tb[NL80211_ATTR_MAX + 1];
-       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-       struct nlattr *sinfo[NL80211_STA_INFO_MAX + 1];
-       static struct nla_policy policy[NL80211_STA_INFO_MAX + 1] = {
-               [NL80211_STA_INFO_SIGNAL] = { .type = NLA_U8 },
-               [NL80211_STA_INFO_SIGNAL_AVG] = { .type = NLA_U8 },
-       };
-       struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
-       static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
-               [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
-               [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
-               [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
-               [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
-       };
-       struct wpa_signal_info *sig_change = arg;
+       struct wpa_driver_nl80211_data *drv;
+       struct rfkill_config *rcfg;
+       struct i802_bss *bss;
 
-       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-                 genlmsg_attrlen(gnlh, 0), NULL);
-       if (!tb[NL80211_ATTR_STA_INFO] ||
-           nla_parse_nested(sinfo, NL80211_STA_INFO_MAX,
-                            tb[NL80211_ATTR_STA_INFO], policy))
-               return NL_SKIP;
-       if (!sinfo[NL80211_STA_INFO_SIGNAL])
-               return NL_SKIP;
+       if (global_priv == NULL)
+               return NULL;
+       drv = os_zalloc(sizeof(*drv));
+       if (drv == NULL)
+               return NULL;
+       drv->global = global_priv;
+       drv->ctx = ctx;
+       drv->hostapd = !!hostapd;
+       drv->eapol_sock = -1;
 
-       sig_change->current_signal =
-               (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
+       /*
+        * There is no driver capability flag for this, so assume it is
+        * supported and disable this on first attempt to use if the driver
+        * rejects the command due to missing support.
+        */
+       drv->set_rekey_offload = 1;
 
-       if (sinfo[NL80211_STA_INFO_SIGNAL_AVG])
-               sig_change->avg_signal =
-                       (s8) nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL_AVG]);
-       else
-               sig_change->avg_signal = 0;
+       drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int);
+       drv->if_indices = drv->default_if_indices;
 
-       if (sinfo[NL80211_STA_INFO_TX_BITRATE]) {
-               if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
-                                    sinfo[NL80211_STA_INFO_TX_BITRATE],
-                                    rate_policy)) {
-                       sig_change->current_txrate = 0;
-               } else {
-                       if (rinfo[NL80211_RATE_INFO_BITRATE]) {
-                               sig_change->current_txrate =
-                                       nla_get_u16(rinfo[
-                                            NL80211_RATE_INFO_BITRATE]) * 100;
-                       }
-               }
+       drv->first_bss = os_zalloc(sizeof(*drv->first_bss));
+       if (!drv->first_bss) {
+               os_free(drv);
+               return NULL;
        }
+       bss = drv->first_bss;
+       bss->drv = drv;
+       bss->ctx = ctx;
 
-       return NL_SKIP;
-}
-
-
-static int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
-                                  struct wpa_signal_info *sig)
-{
-       struct nl_msg *msg;
-
-       sig->current_signal = -9999;
-       sig->current_txrate = 0;
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
+       os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname));
+       drv->monitor_ifidx = -1;
+       drv->monitor_sock = -1;
+       drv->eapol_tx_sock = -1;
+       drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_STATION);
+       if (nl80211_init_bss(bss))
+               goto failed;
 
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid);
+       rcfg = os_zalloc(sizeof(*rcfg));
+       if (rcfg == NULL)
+               goto failed;
+       rcfg->ctx = drv;
+       os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname));
+       rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked;
+       rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked;
+       drv->rfkill = rfkill_init(rcfg);
+       if (drv->rfkill == NULL) {
+               wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available");
+               os_free(rcfg);
+       }
 
-       return send_and_recv_msgs(drv, msg, get_link_signal, sig);
- nla_put_failure:
-       nlmsg_free(msg);
-       return -ENOBUFS;
-}
+       if (linux_iface_up(drv->global->ioctl_sock, ifname) > 0)
+               drv->start_iface_up = 1;
 
+       if (wpa_driver_nl80211_finish_drv_init(drv, set_addr, 1, driver_params))
+               goto failed;
 
-static int get_link_noise(struct nl_msg *msg, void *arg)
-{
-       struct nlattr *tb[NL80211_ATTR_MAX + 1];
-       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-       struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
-       static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
-               [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
-               [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
-       };
-       struct wpa_signal_info *sig_change = arg;
+       drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
+       if (drv->eapol_tx_sock < 0)
+               goto failed;
 
-       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-                 genlmsg_attrlen(gnlh, 0), NULL);
+       if (drv->data_tx_status) {
+               int enabled = 1;
 
-       if (!tb[NL80211_ATTR_SURVEY_INFO]) {
-               wpa_printf(MSG_DEBUG, "nl80211: survey data missing!");
-               return NL_SKIP;
+               if (setsockopt(drv->eapol_tx_sock, SOL_SOCKET, SO_WIFI_STATUS,
+                              &enabled, sizeof(enabled)) < 0) {
+                       wpa_printf(MSG_DEBUG,
+                               "nl80211: wifi status sockopt failed\n");
+                       drv->data_tx_status = 0;
+                       if (!drv->use_monitor)
+                               drv->capa.flags &=
+                                       ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+               } else {
+                       eloop_register_read_sock(drv->eapol_tx_sock,
+                               wpa_driver_nl80211_handle_eapol_tx_status,
+                               drv, NULL);
+               }
        }
 
-       if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
-                            tb[NL80211_ATTR_SURVEY_INFO],
-                            survey_policy)) {
-               wpa_printf(MSG_DEBUG, "nl80211: failed to parse nested "
-                          "attributes!");
-               return NL_SKIP;
+       if (drv->global) {
+               nl80211_check_global(drv->global);
+               dl_list_add(&drv->global->interfaces, &drv->list);
+               drv->in_interface_list = 1;
        }
 
-       if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
-               return NL_SKIP;
-
-       if (nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
-           sig_change->frequency)
-               return NL_SKIP;
+       return bss;
 
-       if (!sinfo[NL80211_SURVEY_INFO_NOISE])
-               return NL_SKIP;
+failed:
+       wpa_driver_nl80211_deinit(bss);
+       return NULL;
+}
 
-       sig_change->current_noise =
-               (s8) nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
 
-       return NL_SKIP;
+/**
+ * wpa_driver_nl80211_init - Initialize nl80211 driver interface
+ * @ctx: context to be used when calling wpa_supplicant functions,
+ * e.g., wpa_supplicant_event()
+ * @ifname: interface name, e.g., wlan0
+ * @global_priv: private driver global data from global_init()
+ * Returns: Pointer to private data, %NULL on failure
+ */
+static void * wpa_driver_nl80211_init(void *ctx, const char *ifname,
+                                     void *global_priv)
+{
+       return wpa_driver_nl80211_drv_init(ctx, ifname, global_priv, 0, NULL,
+                                          NULL);
 }
 
 
-static int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
-                                 struct wpa_signal_info *sig_change)
+static int nl80211_register_frame(struct i802_bss *bss,
+                                 struct nl_handle *nl_handle,
+                                 u16 type, const u8 *match, size_t match_len)
 {
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
+       int ret;
+       char buf[30];
 
-       sig_change->current_noise = 9999;
-       sig_change->frequency = drv->assoc_freq;
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
-
-       nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+       buf[0] = '\0';
+       wpa_snprintf_hex(buf, sizeof(buf), match, match_len);
+       wpa_printf(MSG_DEBUG, "nl80211: Register frame type=0x%x (%s) nl_handle=%p match=%s",
+                  type, fc2str(type), nl_handle, buf);
 
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_REGISTER_ACTION)) ||
+           nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE, type) ||
+           nla_put(msg, NL80211_ATTR_FRAME_MATCH, match_len, match)) {
+               nlmsg_free(msg);
+               return -1;
+       }
 
-       return send_and_recv_msgs(drv, msg, get_link_noise, sig_change);
- nla_put_failure:
-       nlmsg_free(msg);
-       return -ENOBUFS;
+       ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL);
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Register frame command "
+                          "failed (type=%u): ret=%d (%s)",
+                          type, ret, strerror(-ret));
+               wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match",
+                           match, match_len);
+       }
+       return ret;
 }
 
 
-static int get_noise_for_scan_results(struct nl_msg *msg, void *arg)
+static int nl80211_alloc_mgmt_handle(struct i802_bss *bss)
 {
-       struct nlattr *tb[NL80211_ATTR_MAX + 1];
-       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-       struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
-       static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
-               [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
-               [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
-       };
-       struct wpa_scan_results *scan_results = arg;
-       struct wpa_scan_res *scan_res;
-       size_t i;
+       if (bss->nl_mgmt) {
+               wpa_printf(MSG_DEBUG, "nl80211: Mgmt reporting "
+                          "already on! (nl_mgmt=%p)", bss->nl_mgmt);
+               return -1;
+       }
 
-       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-                 genlmsg_attrlen(gnlh, 0), NULL);
+       bss->nl_mgmt = nl_create_handle(bss->nl_cb, "mgmt");
+       if (bss->nl_mgmt == NULL)
+               return -1;
 
-       if (!tb[NL80211_ATTR_SURVEY_INFO]) {
-               wpa_printf(MSG_DEBUG, "nl80211: Survey data missing");
-               return NL_SKIP;
-       }
+       return 0;
+}
 
-       if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
-                            tb[NL80211_ATTR_SURVEY_INFO],
-                            survey_policy)) {
-               wpa_printf(MSG_DEBUG, "nl80211: Failed to parse nested "
-                          "attributes");
-               return NL_SKIP;
-       }
 
-       if (!sinfo[NL80211_SURVEY_INFO_NOISE])
-               return NL_SKIP;
+static void nl80211_mgmt_handle_register_eloop(struct i802_bss *bss)
+{
+       nl80211_register_eloop_read(&bss->nl_mgmt,
+                                   wpa_driver_nl80211_event_receive,
+                                   bss->nl_cb);
+}
 
-       if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
-               return NL_SKIP;
 
-       for (i = 0; i < scan_results->num; ++i) {
-               scan_res = scan_results->res[i];
-               if (!scan_res)
-                       continue;
-               if ((int) nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
-                   scan_res->freq)
-                       continue;
-               if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID))
-                       continue;
-               scan_res->noise = (s8)
-                       nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
-               scan_res->flags &= ~WPA_SCAN_NOISE_INVALID;
-       }
-
-       return NL_SKIP;
+static int nl80211_register_action_frame(struct i802_bss *bss,
+                                        const u8 *match, size_t match_len)
+{
+       u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4);
+       return nl80211_register_frame(bss, bss->nl_mgmt,
+                                     type, match, match_len);
 }
 
 
-static int nl80211_get_noise_for_scan_results(
-       struct wpa_driver_nl80211_data *drv,
-       struct wpa_scan_results *scan_res)
+static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
 {
-       struct nl_msg *msg;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ret = 0;
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
+       if (nl80211_alloc_mgmt_handle(bss))
+               return -1;
+       wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with non-AP "
+                  "handle %p", bss->nl_mgmt);
 
-       nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+       if (drv->nlmode == NL80211_IFTYPE_ADHOC) {
+               u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4);
 
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+               /* register for any AUTH message */
+               nl80211_register_frame(bss, bss->nl_mgmt, type, NULL, 0);
+       }
 
-       return send_and_recv_msgs(drv, msg, get_noise_for_scan_results,
-                                 scan_res);
- nla_put_failure:
-       nlmsg_free(msg);
-       return -ENOBUFS;
-}
+#ifdef CONFIG_INTERWORKING
+       /* QoS Map Configure */
+       if (nl80211_register_action_frame(bss, (u8 *) "\x01\x04", 2) < 0)
+               ret = -1;
+#endif /* CONFIG_INTERWORKING */
+#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING)
+       /* GAS Initial Request */
+       if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0a", 2) < 0)
+               ret = -1;
+       /* GAS Initial Response */
+       if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0b", 2) < 0)
+               ret = -1;
+       /* GAS Comeback Request */
+       if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0c", 2) < 0)
+               ret = -1;
+       /* GAS Comeback Response */
+       if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0d", 2) < 0)
+               ret = -1;
+       /* Protected GAS Initial Request */
+       if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0a", 2) < 0)
+               ret = -1;
+       /* Protected GAS Initial Response */
+       if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0b", 2) < 0)
+               ret = -1;
+       /* Protected GAS Comeback Request */
+       if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0c", 2) < 0)
+               ret = -1;
+       /* Protected GAS Comeback Response */
+       if (nl80211_register_action_frame(bss, (u8 *) "\x09\x0d", 2) < 0)
+               ret = -1;
+#endif /* CONFIG_P2P || CONFIG_INTERWORKING */
+#ifdef CONFIG_P2P
+       /* P2P Public Action */
+       if (nl80211_register_action_frame(bss,
+                                         (u8 *) "\x04\x09\x50\x6f\x9a\x09",
+                                         6) < 0)
+               ret = -1;
+       /* P2P Action */
+       if (nl80211_register_action_frame(bss,
+                                         (u8 *) "\x7f\x50\x6f\x9a\x09",
+                                         5) < 0)
+               ret = -1;
+#endif /* CONFIG_P2P */
+#ifdef CONFIG_IEEE80211W
+       /* SA Query Response */
+       if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0)
+               ret = -1;
+#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_TDLS
+       if ((drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) {
+               /* TDLS Discovery Response */
+               if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0e", 2) <
+                   0)
+                       ret = -1;
+       }
+#endif /* CONFIG_TDLS */
 
+       /* FT Action frames */
+       if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0)
+               ret = -1;
+       else
+               drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT |
+                       WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
 
-static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
-                             struct nlattr *tb[])
-{
-       static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
-               [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
-               [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 },
-               [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
-               [NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 },
-       };
-       struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
-       enum nl80211_cqm_rssi_threshold_event event;
-       union wpa_event_data ed;
-       struct wpa_signal_info sig;
-       int res;
+       /* WNM - BSS Transition Management Request */
+       if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x07", 2) < 0)
+               ret = -1;
+       /* WNM-Sleep Mode Response */
+       if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0)
+               ret = -1;
 
-       if (tb[NL80211_ATTR_CQM] == NULL ||
-           nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM],
-                            cqm_policy)) {
-               wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid CQM event");
-               return;
-       }
+#ifdef CONFIG_HS20
+       /* WNM-Notification */
+       if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x1a", 2) < 0)
+               ret = -1;
+#endif /* CONFIG_HS20 */
 
-       os_memset(&ed, 0, sizeof(ed));
+       /* WMM-AC ADDTS Response */
+       if (nl80211_register_action_frame(bss, (u8 *) "\x11\x01", 2) < 0)
+               ret = -1;
 
-       if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) {
-               if (!tb[NL80211_ATTR_MAC])
-                       return;
-               os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]),
-                         ETH_ALEN);
-               wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed);
-               return;
-       }
+       /* WMM-AC DELTS */
+       if (nl80211_register_action_frame(bss, (u8 *) "\x11\x02", 2) < 0)
+               ret = -1;
 
-       if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL)
-               return;
-       event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
-
-       if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) {
-               wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
-                          "event: RSSI high");
-               ed.signal_change.above_threshold = 1;
-       } else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) {
-               wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
-                          "event: RSSI low");
-               ed.signal_change.above_threshold = 0;
-       } else
-               return;
+       /* Radio Measurement - Neighbor Report Response */
+       if (nl80211_register_action_frame(bss, (u8 *) "\x05\x05", 2) < 0)
+               ret = -1;
 
-       res = nl80211_get_link_signal(drv, &sig);
-       if (res == 0) {
-               ed.signal_change.current_signal = sig.current_signal;
-               ed.signal_change.current_txrate = sig.current_txrate;
-               wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm  txrate: %d",
-                          sig.current_signal, sig.current_txrate);
-       }
+       /* Radio Measurement - Link Measurement Request */
+       if ((drv->capa.rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION) &&
+           (nl80211_register_action_frame(bss, (u8 *) "\x05\x02", 2) < 0))
+               ret = -1;
 
-       res = nl80211_get_link_noise(drv, &sig);
-       if (res == 0) {
-               ed.signal_change.current_noise = sig.current_noise;
-               wpa_printf(MSG_DEBUG, "nl80211: Noise: %d dBm",
-                          sig.current_noise);
-       }
+       nl80211_mgmt_handle_register_eloop(bss);
 
-       wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed);
+       return ret;
 }
 
 
-static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv,
-                                     struct nlattr **tb)
+static int nl80211_mgmt_subscribe_mesh(struct i802_bss *bss)
 {
-       u8 *addr;
-       union wpa_event_data data;
+       int ret = 0;
 
-       if (tb[NL80211_ATTR_MAC] == NULL)
-               return;
-       addr = nla_data(tb[NL80211_ATTR_MAC]);
-       wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr));
-
-       if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
-               u8 *ies = NULL;
-               size_t ies_len = 0;
-               if (tb[NL80211_ATTR_IE]) {
-                       ies = nla_data(tb[NL80211_ATTR_IE]);
-                       ies_len = nla_len(tb[NL80211_ATTR_IE]);
-               }
-               wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len);
-               drv_event_assoc(drv->ctx, addr, ies, ies_len, 0);
-               return;
-       }
+       if (nl80211_alloc_mgmt_handle(bss))
+               return -1;
 
-       if (drv->nlmode != NL80211_IFTYPE_ADHOC)
-               return;
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: Subscribe to mgmt frames with mesh handle %p",
+                  bss->nl_mgmt);
 
-       os_memset(&data, 0, sizeof(data));
-       os_memcpy(data.ibss_rsn_start.peer, addr, ETH_ALEN);
-       wpa_supplicant_event(drv->ctx, EVENT_IBSS_RSN_START, &data);
+       /* Auth frames for mesh SAE */
+       if (nl80211_register_frame(bss, bss->nl_mgmt,
+                                  (WLAN_FC_TYPE_MGMT << 2) |
+                                  (WLAN_FC_STYPE_AUTH << 4),
+                                  NULL, 0) < 0)
+               ret = -1;
+
+       /* Mesh peering open */
+       if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x01", 2) < 0)
+               ret = -1;
+       /* Mesh peering confirm */
+       if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x02", 2) < 0)
+               ret = -1;
+       /* Mesh peering close */
+       if (nl80211_register_action_frame(bss, (u8 *) "\x0f\x03", 2) < 0)
+               ret = -1;
+
+       nl80211_mgmt_handle_register_eloop(bss);
+
+       return ret;
 }
 
 
-static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv,
-                                     struct nlattr **tb)
+static int nl80211_register_spurious_class3(struct i802_bss *bss)
 {
-       u8 *addr;
-       union wpa_event_data data;
-
-       if (tb[NL80211_ATTR_MAC] == NULL)
-               return;
-       addr = nla_data(tb[NL80211_ATTR_MAC]);
-       wpa_printf(MSG_DEBUG, "nl80211: Delete station " MACSTR,
-                  MAC2STR(addr));
+       struct nl_msg *msg;
+       int ret;
 
-       if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
-               drv_event_disassoc(drv->ctx, addr);
-               return;
+       msg = nl80211_bss_msg(bss, 0, NL80211_CMD_UNEXPECTED_FRAME);
+       ret = send_and_recv(bss->drv->global, bss->nl_mgmt, msg, NULL, NULL);
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 "
+                          "failed: ret=%d (%s)",
+                          ret, strerror(-ret));
        }
+       return ret;
+}
 
-       if (drv->nlmode != NL80211_IFTYPE_ADHOC)
-               return;
 
-       os_memset(&data, 0, sizeof(data));
-       os_memcpy(data.ibss_peer_lost.peer, addr, ETH_ALEN);
-       wpa_supplicant_event(drv->ctx, EVENT_IBSS_PEER_LOST, &data);
-}
-
-
-static void nl80211_rekey_offload_event(struct wpa_driver_nl80211_data *drv,
-                                       struct nlattr **tb)
-{
-       struct nlattr *rekey_info[NUM_NL80211_REKEY_DATA];
-       static struct nla_policy rekey_policy[NUM_NL80211_REKEY_DATA] = {
-               [NL80211_REKEY_DATA_KEK] = {
-                       .minlen = NL80211_KEK_LEN,
-                       .maxlen = NL80211_KEK_LEN,
-               },
-               [NL80211_REKEY_DATA_KCK] = {
-                       .minlen = NL80211_KCK_LEN,
-                       .maxlen = NL80211_KCK_LEN,
-               },
-               [NL80211_REKEY_DATA_REPLAY_CTR] = {
-                       .minlen = NL80211_REPLAY_CTR_LEN,
-                       .maxlen = NL80211_REPLAY_CTR_LEN,
-               },
+static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss)
+{
+       static const int stypes[] = {
+               WLAN_FC_STYPE_AUTH,
+               WLAN_FC_STYPE_ASSOC_REQ,
+               WLAN_FC_STYPE_REASSOC_REQ,
+               WLAN_FC_STYPE_DISASSOC,
+               WLAN_FC_STYPE_DEAUTH,
+               WLAN_FC_STYPE_ACTION,
+               WLAN_FC_STYPE_PROBE_REQ,
+/* Beacon doesn't work as mac80211 doesn't currently allow
+ * it, but it wouldn't really be the right thing anyway as
+ * it isn't per interface ... maybe just dump the scan
+ * results periodically for OLBC?
+ */
+               /* WLAN_FC_STYPE_BEACON, */
        };
-       union wpa_event_data data;
+       unsigned int i;
 
-       if (!tb[NL80211_ATTR_MAC])
-               return;
-       if (!tb[NL80211_ATTR_REKEY_DATA])
-               return;
-       if (nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA,
-                            tb[NL80211_ATTR_REKEY_DATA], rekey_policy))
-               return;
-       if (!rekey_info[NL80211_REKEY_DATA_REPLAY_CTR])
-               return;
+       if (nl80211_alloc_mgmt_handle(bss))
+               return -1;
+       wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP "
+                  "handle %p", bss->nl_mgmt);
 
-       os_memset(&data, 0, sizeof(data));
-       data.driver_gtk_rekey.bssid = nla_data(tb[NL80211_ATTR_MAC]);
-       wpa_printf(MSG_DEBUG, "nl80211: Rekey offload event for BSSID " MACSTR,
-                  MAC2STR(data.driver_gtk_rekey.bssid));
-       data.driver_gtk_rekey.replay_ctr =
-               nla_data(rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]);
-       wpa_hexdump(MSG_DEBUG, "nl80211: Rekey offload - Replay Counter",
-                   data.driver_gtk_rekey.replay_ctr, NL80211_REPLAY_CTR_LEN);
-       wpa_supplicant_event(drv->ctx, EVENT_DRIVER_GTK_REKEY, &data);
-}
-
-
-static void nl80211_pmksa_candidate_event(struct wpa_driver_nl80211_data *drv,
-                                         struct nlattr **tb)
-{
-       struct nlattr *cand[NUM_NL80211_PMKSA_CANDIDATE];
-       static struct nla_policy cand_policy[NUM_NL80211_PMKSA_CANDIDATE] = {
-               [NL80211_PMKSA_CANDIDATE_INDEX] = { .type = NLA_U32 },
-               [NL80211_PMKSA_CANDIDATE_BSSID] = {
-                       .minlen = ETH_ALEN,
-                       .maxlen = ETH_ALEN,
-               },
-               [NL80211_PMKSA_CANDIDATE_PREAUTH] = { .type = NLA_FLAG },
-       };
-       union wpa_event_data data;
+       for (i = 0; i < ARRAY_SIZE(stypes); i++) {
+               if (nl80211_register_frame(bss, bss->nl_mgmt,
+                                          (WLAN_FC_TYPE_MGMT << 2) |
+                                          (stypes[i] << 4),
+                                          NULL, 0) < 0) {
+                       goto out_err;
+               }
+       }
 
-       wpa_printf(MSG_DEBUG, "nl80211: PMKSA candidate event");
+       if (nl80211_register_spurious_class3(bss))
+               goto out_err;
 
-       if (!tb[NL80211_ATTR_PMKSA_CANDIDATE])
-               return;
-       if (nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE,
-                            tb[NL80211_ATTR_PMKSA_CANDIDATE], cand_policy))
-               return;
-       if (!cand[NL80211_PMKSA_CANDIDATE_INDEX] ||
-           !cand[NL80211_PMKSA_CANDIDATE_BSSID])
-               return;
+       if (nl80211_get_wiphy_data_ap(bss) == NULL)
+               goto out_err;
 
-       os_memset(&data, 0, sizeof(data));
-       os_memcpy(data.pmkid_candidate.bssid,
-                 nla_data(cand[NL80211_PMKSA_CANDIDATE_BSSID]), ETH_ALEN);
-       data.pmkid_candidate.index =
-               nla_get_u32(cand[NL80211_PMKSA_CANDIDATE_INDEX]);
-       data.pmkid_candidate.preauth =
-               cand[NL80211_PMKSA_CANDIDATE_PREAUTH] != NULL;
-       wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data);
+       nl80211_mgmt_handle_register_eloop(bss);
+       return 0;
+
+out_err:
+       nl_destroy_handles(&bss->nl_mgmt);
+       return -1;
 }
 
 
-static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv,
-                                      struct nlattr **tb)
+static int nl80211_mgmt_subscribe_ap_dev_sme(struct i802_bss *bss)
 {
-       union wpa_event_data data;
-
-       wpa_printf(MSG_DEBUG, "nl80211: Probe client event");
+       if (nl80211_alloc_mgmt_handle(bss))
+               return -1;
+       wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP "
+                  "handle %p (device SME)", bss->nl_mgmt);
 
-       if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_ACK])
-               return;
+       if (nl80211_register_frame(bss, bss->nl_mgmt,
+                                  (WLAN_FC_TYPE_MGMT << 2) |
+                                  (WLAN_FC_STYPE_ACTION << 4),
+                                  NULL, 0) < 0)
+               goto out_err;
 
-       os_memset(&data, 0, sizeof(data));
-       os_memcpy(data.client_poll.addr,
-                 nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+       nl80211_mgmt_handle_register_eloop(bss);
+       return 0;
 
-       wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data);
+out_err:
+       nl_destroy_handles(&bss->nl_mgmt);
+       return -1;
 }
 
 
-static void nl80211_tdls_oper_event(struct wpa_driver_nl80211_data *drv,
-                                   struct nlattr **tb)
+static void nl80211_mgmt_unsubscribe(struct i802_bss *bss, const char *reason)
 {
-       union wpa_event_data data;
-
-       wpa_printf(MSG_DEBUG, "nl80211: TDLS operation event");
-
-       if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_TDLS_OPERATION])
-               return;
-
-       os_memset(&data, 0, sizeof(data));
-       os_memcpy(data.tdls.peer, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
-       switch (nla_get_u8(tb[NL80211_ATTR_TDLS_OPERATION])) {
-       case NL80211_TDLS_SETUP:
-               wpa_printf(MSG_DEBUG, "nl80211: TDLS setup request for peer "
-                          MACSTR, MAC2STR(data.tdls.peer));
-               data.tdls.oper = TDLS_REQUEST_SETUP;
-               break;
-       case NL80211_TDLS_TEARDOWN:
-               wpa_printf(MSG_DEBUG, "nl80211: TDLS teardown request for peer "
-                          MACSTR, MAC2STR(data.tdls.peer));
-               data.tdls.oper = TDLS_REQUEST_TEARDOWN;
-               break;
-       default:
-               wpa_printf(MSG_DEBUG, "nl80211: Unsupported TDLS operatione "
-                          "event");
+       if (bss->nl_mgmt == NULL)
                return;
-       }
-       if (tb[NL80211_ATTR_REASON_CODE]) {
-               data.tdls.reason_code =
-                       nla_get_u16(tb[NL80211_ATTR_REASON_CODE]);
-       }
+       wpa_printf(MSG_DEBUG, "nl80211: Unsubscribe mgmt frames handle %p "
+                  "(%s)", bss->nl_mgmt, reason);
+       nl80211_destroy_eloop_handle(&bss->nl_mgmt);
 
-       wpa_supplicant_event(drv->ctx, EVENT_TDLS, &data);
+       nl80211_put_wiphy_data_ap(bss);
 }
 
 
-static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv,
-                                        struct nlattr **tb)
+static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx)
 {
-       union wpa_event_data data;
-       u32 reason;
+       wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
+}
 
-       wpa_printf(MSG_DEBUG, "nl80211: Connect failed event");
 
-       if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_CONN_FAILED_REASON])
-               return;
+static void nl80211_del_p2pdev(struct i802_bss *bss)
+{
+       struct nl_msg *msg;
+       int ret;
 
-       os_memset(&data, 0, sizeof(data));
-       os_memcpy(data.connect_failed_reason.addr,
-                 nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
-
-       reason = nla_get_u32(tb[NL80211_ATTR_CONN_FAILED_REASON]);
-       switch (reason) {
-       case NL80211_CONN_FAIL_MAX_CLIENTS:
-               wpa_printf(MSG_DEBUG, "nl80211: Max client reached");
-               data.connect_failed_reason.code = MAX_CLIENT_REACHED;
-               break;
-       case NL80211_CONN_FAIL_BLOCKED_CLIENT:
-               wpa_printf(MSG_DEBUG, "nl80211: Blocked client " MACSTR
-                          " tried to connect",
-                          MAC2STR(data.connect_failed_reason.addr));
-               data.connect_failed_reason.code = BLOCKED_CLIENT;
-               break;
-       default:
-               wpa_printf(MSG_DEBUG, "nl8021l: Unknown connect failed reason "
-                          "%u", reason);
-               return;
-       }
+       msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_INTERFACE);
+       ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
 
-       wpa_supplicant_event(drv->ctx, EVENT_CONNECT_FAILED_REASON, &data);
+       wpa_printf(MSG_DEBUG, "nl80211: Delete P2P Device %s (0x%llx): %s",
+                  bss->ifname, (long long unsigned int) bss->wdev_id,
+                  strerror(-ret));
 }
 
 
-static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
-                               struct nlattr **tb)
+static int nl80211_set_p2pdev(struct i802_bss *bss, int start)
 {
-       union wpa_event_data data;
-       enum nl80211_radar_event event_type;
-
-       if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT])
-               return;
-
-       os_memset(&data, 0, sizeof(data));
-       data.dfs_event.freq = nla_get_u16(tb[NL80211_ATTR_WIPHY_FREQ]);
-       event_type = nla_get_u8(tb[NL80211_ATTR_RADAR_EVENT]);
+       struct nl_msg *msg;
+       int ret;
 
-       wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz",
-                  data.dfs_event.freq);
+       msg = nl80211_cmd_msg(bss, 0, start ? NL80211_CMD_START_P2P_DEVICE :
+                             NL80211_CMD_STOP_P2P_DEVICE);
+       ret = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
 
-       switch (event_type) {
-       case NL80211_RADAR_DETECTED:
-               wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data);
-               break;
-       case NL80211_RADAR_CAC_FINISHED:
-               wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data);
-               break;
-       case NL80211_RADAR_CAC_ABORTED:
-               wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data);
-               break;
-       case NL80211_RADAR_NOP_FINISHED:
-               wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data);
-               break;
-       default:
-               wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
-                          "received", event_type);
-               break;
-       }
+       wpa_printf(MSG_DEBUG, "nl80211: %s P2P Device %s (0x%llx): %s",
+                  start ? "Start" : "Stop",
+                  bss->ifname, (long long unsigned int) bss->wdev_id,
+                  strerror(-ret));
+       return ret;
 }
 
 
-static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb,
-                                  int wds)
+static int i802_set_iface_flags(struct i802_bss *bss, int up)
 {
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       union wpa_event_data event;
-
-       if (!tb[NL80211_ATTR_MAC])
-               return;
+       enum nl80211_iftype nlmode;
 
-       os_memset(&event, 0, sizeof(event));
-       event.rx_from_unknown.bssid = bss->addr;
-       event.rx_from_unknown.addr = nla_data(tb[NL80211_ATTR_MAC]);
-       event.rx_from_unknown.wds = wds;
+       nlmode = nl80211_get_ifmode(bss);
+       if (nlmode != NL80211_IFTYPE_P2P_DEVICE) {
+               return linux_set_iface_flags(bss->drv->global->ioctl_sock,
+                                            bss->ifname, up);
+       }
 
-       wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
+       /* P2P Device has start/stop which is equivalent */
+       return nl80211_set_p2pdev(bss, up);
 }
 
 
-static void do_process_drv_event(struct i802_bss *bss, int cmd,
-                                struct nlattr **tb)
+#ifdef CONFIG_TESTING_OPTIONS
+static int qca_vendor_test_cmd_handler(struct nl_msg *msg, void *arg)
 {
-       struct wpa_driver_nl80211_data *drv = bss->drv;
+       /* struct wpa_driver_nl80211_data *drv = arg; */
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
 
-       wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s",
-                  cmd, nl80211_command_to_string(cmd), bss->ifname);
 
-       if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED &&
-           (cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
-            cmd == NL80211_CMD_SCAN_ABORTED)) {
-               wpa_driver_nl80211_set_mode(&drv->first_bss,
-                                           drv->ap_scan_as_station);
-               drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: QCA vendor test command response received");
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+       if (!tb[NL80211_ATTR_VENDOR_DATA]) {
+               wpa_printf(MSG_DEBUG, "nl80211: No vendor data attribute");
+               return NL_SKIP;
        }
 
-       switch (cmd) {
-       case NL80211_CMD_TRIGGER_SCAN:
-               wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan trigger");
-               break;
-       case NL80211_CMD_START_SCHED_SCAN:
-               wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan started");
-               break;
-       case NL80211_CMD_SCHED_SCAN_STOPPED:
-               wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan stopped");
-               wpa_supplicant_event(drv->ctx, EVENT_SCHED_SCAN_STOPPED, NULL);
-               break;
-       case NL80211_CMD_NEW_SCAN_RESULTS:
-               wpa_dbg(drv->ctx, MSG_DEBUG,
-                       "nl80211: New scan results available");
-               drv->scan_complete_events = 1;
-               eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
-                                    drv->ctx);
-               send_scan_event(drv, 0, tb);
-               break;
-       case NL80211_CMD_SCHED_SCAN_RESULTS:
-               wpa_dbg(drv->ctx, MSG_DEBUG,
-                       "nl80211: New sched scan results available");
-               send_scan_event(drv, 0, tb);
-               break;
-       case NL80211_CMD_SCAN_ABORTED:
-               wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted");
-               /*
-                * Need to indicate that scan results are available in order
-                * not to make wpa_supplicant stop its scanning.
-                */
-               eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
-                                    drv->ctx);
-               send_scan_event(drv, 1, tb);
-               break;
-       case NL80211_CMD_AUTHENTICATE:
-       case NL80211_CMD_ASSOCIATE:
-       case NL80211_CMD_DEAUTHENTICATE:
-       case NL80211_CMD_DISASSOCIATE:
-       case NL80211_CMD_FRAME_TX_STATUS:
-       case NL80211_CMD_UNPROT_DEAUTHENTICATE:
-       case NL80211_CMD_UNPROT_DISASSOCIATE:
-               mlme_event(bss, cmd, tb[NL80211_ATTR_FRAME],
-                          tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
-                          tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
-                          tb[NL80211_ATTR_COOKIE],
-                          tb[NL80211_ATTR_RX_SIGNAL_DBM]);
-               break;
-       case NL80211_CMD_CONNECT:
-       case NL80211_CMD_ROAM:
-               mlme_event_connect(drv, cmd,
-                                  tb[NL80211_ATTR_STATUS_CODE],
-                                  tb[NL80211_ATTR_MAC],
-                                  tb[NL80211_ATTR_REQ_IE],
-                                  tb[NL80211_ATTR_RESP_IE]);
-               break;
-       case NL80211_CMD_CH_SWITCH_NOTIFY:
-               mlme_event_ch_switch(drv, tb[NL80211_ATTR_WIPHY_FREQ],
-                                    tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
-               break;
-       case NL80211_CMD_DISCONNECT:
-               mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
-                                     tb[NL80211_ATTR_MAC],
-                                     tb[NL80211_ATTR_DISCONNECTED_BY_AP]);
-               break;
-       case NL80211_CMD_MICHAEL_MIC_FAILURE:
-               mlme_event_michael_mic_failure(bss, tb);
-               break;
-       case NL80211_CMD_JOIN_IBSS:
-               mlme_event_join_ibss(drv, tb);
-               break;
-       case NL80211_CMD_REMAIN_ON_CHANNEL:
-               mlme_event_remain_on_channel(drv, 0, tb);
-               break;
-       case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL:
-               mlme_event_remain_on_channel(drv, 1, tb);
-               break;
-       case NL80211_CMD_NOTIFY_CQM:
-               nl80211_cqm_event(drv, tb);
-               break;
-       case NL80211_CMD_REG_CHANGE:
-               wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change");
-               wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED,
-                                    NULL);
-               break;
-       case NL80211_CMD_REG_BEACON_HINT:
-               wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint");
-               wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED,
-                                    NULL);
-               break;
-       case NL80211_CMD_NEW_STATION:
-               nl80211_new_station_event(drv, tb);
-               break;
-       case NL80211_CMD_DEL_STATION:
-               nl80211_del_station_event(drv, tb);
-               break;
-       case NL80211_CMD_SET_REKEY_OFFLOAD:
-               nl80211_rekey_offload_event(drv, tb);
-               break;
-       case NL80211_CMD_PMKSA_CANDIDATE:
-               nl80211_pmksa_candidate_event(drv, tb);
-               break;
-       case NL80211_CMD_PROBE_CLIENT:
-               nl80211_client_probe_event(drv, tb);
-               break;
-       case NL80211_CMD_TDLS_OPER:
-               nl80211_tdls_oper_event(drv, tb);
-               break;
-       case NL80211_CMD_CONN_FAILED:
-               nl80211_connect_failed_event(drv, tb);
-               break;
-       case NL80211_CMD_FT_EVENT:
-               mlme_event_ft_event(drv, tb);
-               break;
-       case NL80211_CMD_RADAR_DETECT:
-               nl80211_radar_event(drv, tb);
-               break;
-       default:
-               wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
-                       "(cmd=%d)", cmd);
-               break;
-       }
+       wpa_hexdump(MSG_DEBUG,
+                   "nl80211: Received QCA vendor test command response",
+                   nla_data(tb[NL80211_ATTR_VENDOR_DATA]),
+                   nla_len(tb[NL80211_ATTR_VENDOR_DATA]));
+
+       return NL_SKIP;
 }
+#endif /* CONFIG_TESTING_OPTIONS */
 
 
-static int process_drv_event(struct nl_msg *msg, void *arg)
+static void qca_vendor_test(struct wpa_driver_nl80211_data *drv)
 {
-       struct wpa_driver_nl80211_data *drv = arg;
-       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-       struct nlattr *tb[NL80211_ATTR_MAX + 1];
-       struct i802_bss *bss;
-       int ifidx = -1;
-
-       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-                 genlmsg_attrlen(gnlh, 0), NULL);
-
-       if (tb[NL80211_ATTR_IFINDEX]) {
-               ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+#ifdef CONFIG_TESTING_OPTIONS
+       struct nl_msg *msg;
+       struct nlattr *params;
+       int ret;
 
-               for (bss = &drv->first_bss; bss; bss = bss->next)
-                       if (ifidx == -1 || ifidx == bss->ifindex) {
-                               do_process_drv_event(bss, gnlh->cmd, tb);
-                               return NL_SKIP;
-                       }
-               wpa_printf(MSG_DEBUG,
-                          "nl80211: Ignored event (cmd=%d) for foreign interface (ifindex %d)",
-                          gnlh->cmd, ifidx);
-       } else if (tb[NL80211_ATTR_WDEV]) {
-               u64 wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
-               wpa_printf(MSG_DEBUG, "nl80211: Process event on P2P device");
-               for (bss = &drv->first_bss; bss; bss = bss->next) {
-                       if (bss->wdev_id_set && wdev_id == bss->wdev_id) {
-                               do_process_drv_event(bss, gnlh->cmd, tb);
-                               return NL_SKIP;
-                       }
-               }
-               wpa_printf(MSG_DEBUG,
-                          "nl80211: Ignored event (cmd=%d) for foreign interface (wdev 0x%llx)",
-                          gnlh->cmd, (long long unsigned int) wdev_id);
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+                       QCA_NL80211_VENDOR_SUBCMD_TEST) ||
+           !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+           nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_TEST, 123)) {
+               nlmsg_free(msg);
+               return;
        }
+       nla_nest_end(msg, params);
 
-       return NL_SKIP;
+       ret = send_and_recv_msgs(drv, msg, qca_vendor_test_cmd_handler, drv);
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: QCA vendor test command returned %d (%s)",
+                  ret, strerror(-ret));
+#endif /* CONFIG_TESTING_OPTIONS */
 }
 
 
-static int process_global_event(struct nl_msg *msg, void *arg)
+static int
+wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv,
+                                  const u8 *set_addr, int first,
+                                  const char *driver_params)
 {
-       struct nl80211_global *global = arg;
-       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-       struct nlattr *tb[NL80211_ATTR_MAX + 1];
-       struct wpa_driver_nl80211_data *drv, *tmp;
-       int ifidx = -1;
-       struct i802_bss *bss;
-       u64 wdev_id = 0;
-       int wdev_id_set = 0;
-
-       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-                 genlmsg_attrlen(gnlh, 0), NULL);
-
-       if (tb[NL80211_ATTR_IFINDEX])
-               ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
-       else if (tb[NL80211_ATTR_WDEV]) {
-               wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
-               wdev_id_set = 1;
-       }
-
-       dl_list_for_each_safe(drv, tmp, &global->interfaces,
-                             struct wpa_driver_nl80211_data, list) {
-               for (bss = &drv->first_bss; bss; bss = bss->next) {
-                       if ((ifidx == -1 && !wdev_id_set) ||
-                           ifidx == bss->ifindex ||
-                           (wdev_id_set && bss->wdev_id_set &&
-                            wdev_id == bss->wdev_id)) {
-                               do_process_drv_event(bss, gnlh->cmd, tb);
-                               return NL_SKIP;
-                       }
-               }
-       }
+       struct i802_bss *bss = drv->first_bss;
+       int send_rfkill_event = 0;
+       enum nl80211_iftype nlmode;
 
-       return NL_SKIP;
-}
+       drv->ifindex = if_nametoindex(bss->ifname);
+       bss->ifindex = drv->ifindex;
+       bss->wdev_id = drv->global->if_add_wdevid;
+       bss->wdev_id_set = drv->global->if_add_wdevid_set;
 
+       bss->if_dynamic = drv->ifindex == drv->global->if_add_ifindex;
+       bss->if_dynamic = bss->if_dynamic || drv->global->if_add_wdevid_set;
+       drv->global->if_add_wdevid_set = 0;
 
-static int process_bss_event(struct nl_msg *msg, void *arg)
-{
-       struct i802_bss *bss = arg;
-       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       if (!bss->if_dynamic && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP)
+               bss->static_ap = 1;
 
-       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-                 genlmsg_attrlen(gnlh, 0), NULL);
+       if (wpa_driver_nl80211_capa(drv))
+               return -1;
 
-       wpa_printf(MSG_DEBUG, "nl80211: BSS Event %d (%s) received for %s",
-                  gnlh->cmd, nl80211_command_to_string(gnlh->cmd),
-                  bss->ifname);
+       if (driver_params && nl80211_set_param(bss, driver_params) < 0)
+               return -1;
 
-       switch (gnlh->cmd) {
-       case NL80211_CMD_FRAME:
-       case NL80211_CMD_FRAME_TX_STATUS:
-               mlme_event(bss, gnlh->cmd, tb[NL80211_ATTR_FRAME],
-                          tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
-                          tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
-                          tb[NL80211_ATTR_COOKIE],
-                          tb[NL80211_ATTR_RX_SIGNAL_DBM]);
-               break;
-       case NL80211_CMD_UNEXPECTED_FRAME:
-               nl80211_spurious_frame(bss, tb, 0);
-               break;
-       case NL80211_CMD_UNEXPECTED_4ADDR_FRAME:
-               nl80211_spurious_frame(bss, tb, 1);
-               break;
-       default:
-               wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
-                          "(cmd=%d)", gnlh->cmd);
-               break;
-       }
+       wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s",
+                  bss->ifname, drv->phyname);
 
-       return NL_SKIP;
-}
+       if (set_addr &&
+           (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0) ||
+            linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+                               set_addr)))
+               return -1;
 
+       if (first && nl80211_get_ifmode(bss) == NL80211_IFTYPE_AP)
+               drv->start_mode_ap = 1;
 
-static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx,
-                                            void *handle)
-{
-       struct nl_cb *cb = eloop_ctx;
+       if (drv->hostapd || bss->static_ap)
+               nlmode = NL80211_IFTYPE_AP;
+       else if (bss->if_dynamic)
+               nlmode = nl80211_get_ifmode(bss);
+       else
+               nlmode = NL80211_IFTYPE_STATION;
 
-       wpa_printf(MSG_MSGDUMP, "nl80211: Event message available");
+       if (wpa_driver_nl80211_set_mode(bss, nlmode) < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: Could not configure driver mode");
+               return -1;
+       }
 
-       nl_recvmsgs(handle, cb);
-}
+       if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
+               nl80211_get_macaddr(bss);
 
+       if (!rfkill_is_blocked(drv->rfkill)) {
+               int ret = i802_set_iface_flags(bss, 1);
+               if (ret) {
+                       wpa_printf(MSG_ERROR, "nl80211: Could not set "
+                                  "interface '%s' UP", bss->ifname);
+                       return ret;
+               }
+               if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
+                       return ret;
+       } else {
+               wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable "
+                          "interface '%s' due to rfkill", bss->ifname);
+               if (nlmode == NL80211_IFTYPE_P2P_DEVICE)
+                       return 0;
+               drv->if_disabled = 1;
+               send_rfkill_event = 1;
+       }
 
-/**
- * wpa_driver_nl80211_set_country - ask nl80211 to set the regulatory domain
- * @priv: driver_nl80211 private data
- * @alpha2_arg: country to which to switch to
- * Returns: 0 on success, -1 on failure
- *
- * This asks nl80211 to set the regulatory domain for given
- * country ISO / IEC alpha2.
- */
-static int wpa_driver_nl80211_set_country(void *priv, const char *alpha2_arg)
-{
-       struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       char alpha2[3];
-       struct nl_msg *msg;
+       if (!drv->hostapd)
+               netlink_send_oper_ifla(drv->global->netlink, drv->ifindex,
+                                      1, IF_OPER_DORMANT);
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
+       if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+                              bss->addr))
+               return -1;
+       os_memcpy(drv->perm_addr, bss->addr, ETH_ALEN);
 
-       alpha2[0] = alpha2_arg[0];
-       alpha2[1] = alpha2_arg[1];
-       alpha2[2] = '\0';
+#ifdef BCM_DRIVER_V115
+       wpa_printf(MSG_DEBUG, "nl80211: USE private CMD P2P_DEV_ADDR");
+       int wpa_driver_nl80211_priv_cmd_bcm(void *priv, char *cmd, char *buf,
+                       size_t buf_len );
+       char buf[64];
+       wpa_driver_nl80211_priv_cmd_bcm(bss,"P2P_DEV_ADDR",buf, sizeof(buf));
+       os_memcpy(bss->dev_addr, buf, ETH_ALEN);
+       wpa_printf(MSG_MSGDUMP, "nl80211: MLME event A1=" MACSTR, MAC2STR(bss->dev_addr));
+#endif /* BCM_DRIVER_V115 */
+       if (send_rfkill_event) {
+               eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill,
+                                      drv, drv->ctx);
+       }
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_REQ_SET_REG);
+       if (drv->vendor_cmd_test_avail)
+               qca_vendor_test(drv);
 
-       NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, alpha2);
-       if (send_and_recv_msgs(drv, msg, NULL, NULL))
-               return -EINVAL;
        return 0;
-nla_put_failure:
-       nlmsg_free(msg);
-       return -EINVAL;
 }
 
 
-static int protocol_feature_handler(struct nl_msg *msg, void *arg)
+static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv)
 {
-       u32 *feat = arg;
-       struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
-       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-
-       nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-                 genlmsg_attrlen(gnlh, 0), NULL);
-
-       if (tb_msg[NL80211_ATTR_PROTOCOL_FEATURES])
-               *feat = nla_get_u32(tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]);
+       struct nl_msg *msg;
 
-       return NL_SKIP;
+       wpa_printf(MSG_DEBUG, "nl80211: Remove beacon (ifindex=%d)",
+                  drv->ifindex);
+       msg = nl80211_drv_msg(drv, 0, NL80211_CMD_DEL_BEACON);
+       return send_and_recv_msgs(drv, msg, NULL, NULL);
 }
 
 
-static u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv)
+/**
+ * wpa_driver_nl80211_deinit - Deinitialize nl80211 driver interface
+ * @bss: Pointer to private nl80211 data from wpa_driver_nl80211_init()
+ *
+ * Shut down driver interface and processing of driver events. Free
+ * private data buffer if one was allocated in wpa_driver_nl80211_init().
+ */
+static void wpa_driver_nl80211_deinit(struct i802_bss *bss)
 {
-       u32 feat = 0;
-       struct nl_msg *msg;
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               goto nla_put_failure;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_PROTOCOL_FEATURES);
-       if (send_and_recv_msgs(drv, msg, protocol_feature_handler, &feat) == 0)
-               return feat;
+       wpa_printf(MSG_INFO, "nl80211: deinit ifname=%s disabled_11b_rates=%d",
+                  bss->ifname, drv->disabled_11b_rates);
 
-       msg = NULL;
-nla_put_failure:
-       nlmsg_free(msg);
-       return 0;
-}
+       bss->in_deinit = 1;
+       if (drv->data_tx_status)
+               eloop_unregister_read_sock(drv->eapol_tx_sock);
+       if (drv->eapol_tx_sock >= 0)
+               close(drv->eapol_tx_sock);
 
+       if (bss->nl_preq)
+               wpa_driver_nl80211_probe_req_report(bss, 0);
+       if (bss->added_if_into_bridge) {
+               if (linux_br_del_if(drv->global->ioctl_sock, bss->brname,
+                                   bss->ifname) < 0)
+                       wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+                                  "interface %s from bridge %s: %s",
+                                  bss->ifname, bss->brname, strerror(errno));
+               if (drv->rtnl_sk)
+                       nl80211_handle_destroy(drv->rtnl_sk);
+       }
+       if (bss->added_bridge) {
+               if (linux_set_iface_flags(drv->global->ioctl_sock, bss->brname,
+                                         0) < 0)
+                       wpa_printf(MSG_INFO,
+                                  "nl80211: Could not set bridge %s down",
+                                  bss->brname);
+               if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0)
+                       wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+                                  "bridge %s: %s",
+                                  bss->brname, strerror(errno));
+       }
 
-struct wiphy_info_data {
-       struct wpa_driver_nl80211_data *drv;
-       struct wpa_driver_capa *capa;
-
-       unsigned int error:1;
-       unsigned int device_ap_sme:1;
-       unsigned int poll_command_supported:1;
-       unsigned int data_tx_status:1;
-       unsigned int monitor_supported:1;
-       unsigned int auth_supported:1;
-       unsigned int connect_supported:1;
-       unsigned int p2p_go_supported:1;
-       unsigned int p2p_client_supported:1;
-       unsigned int p2p_concurrent:1;
-       unsigned int p2p_multichan_concurrent:1;
-};
+       nl80211_remove_monitor_interface(drv);
 
+       if (is_ap_interface(drv->nlmode))
+               wpa_driver_nl80211_del_beacon(drv);
 
-static unsigned int probe_resp_offload_support(int supp_protocols)
-{
-       unsigned int prot = 0;
+       if (drv->eapol_sock >= 0) {
+               eloop_unregister_read_sock(drv->eapol_sock);
+               close(drv->eapol_sock);
+       }
 
-       if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS)
-               prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS;
-       if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2)
-               prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2;
-       if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P)
-               prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P;
-       if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U)
-               prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING;
+       if (drv->if_indices != drv->default_if_indices)
+               os_free(drv->if_indices);
 
-       return prot;
-}
+       if (drv->disabled_11b_rates)
+               nl80211_disable_11b_rates(drv, drv->ifindex, 0);
 
+       netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 0,
+                              IF_OPER_UP);
+       eloop_cancel_timeout(wpa_driver_nl80211_send_rfkill, drv, drv->ctx);
+       rfkill_deinit(drv->rfkill);
 
-static void wiphy_info_supported_iftypes(struct wiphy_info_data *info,
-                                        struct nlattr *tb)
-{
-       struct nlattr *nl_mode;
-       int i;
+       eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
 
-       if (tb == NULL)
-               return;
+       if (!drv->start_iface_up)
+               (void) i802_set_iface_flags(bss, 0);
 
-       nla_for_each_nested(nl_mode, tb, i) {
-               switch (nla_type(nl_mode)) {
-               case NL80211_IFTYPE_AP:
-                       info->capa->flags |= WPA_DRIVER_FLAGS_AP;
-                       break;
-               case NL80211_IFTYPE_ADHOC:
-                       info->capa->flags |= WPA_DRIVER_FLAGS_IBSS;
-                       break;
-               case NL80211_IFTYPE_P2P_DEVICE:
-                       info->capa->flags |=
-                               WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE;
-                       break;
-               case NL80211_IFTYPE_P2P_GO:
-                       info->p2p_go_supported = 1;
-                       break;
-               case NL80211_IFTYPE_P2P_CLIENT:
-                       info->p2p_client_supported = 1;
-                       break;
-               case NL80211_IFTYPE_MONITOR:
-                       info->monitor_supported = 1;
-                       break;
+       if (drv->addr_changed) {
+               if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname,
+                                         0) < 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "nl80211: Could not set interface down to restore permanent MAC address");
+               }
+               if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+                                      drv->perm_addr) < 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "nl80211: Could not restore permanent MAC address");
                }
        }
-}
 
+       if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) {
+               if (!drv->hostapd || !drv->start_mode_ap)
+                       wpa_driver_nl80211_set_mode(bss,
+                                                   NL80211_IFTYPE_STATION);
+               nl80211_mgmt_unsubscribe(bss, "deinit");
+       } else {
+               nl80211_mgmt_unsubscribe(bss, "deinit");
+               nl80211_del_p2pdev(bss);
+       }
 
-static int wiphy_info_iface_comb_process(struct wiphy_info_data *info,
-                                        struct nlattr *nl_combi)
-{
-       struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB];
-       struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT];
-       struct nlattr *nl_limit, *nl_mode;
-       int err, rem_limit, rem_mode;
-       int combination_has_p2p = 0, combination_has_mgd = 0;
-       static struct nla_policy
-       iface_combination_policy[NUM_NL80211_IFACE_COMB] = {
-               [NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED },
-               [NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 },
-               [NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG },
-               [NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 },
-               [NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS] = { .type = NLA_U32 },
-       },
-       iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = {
-               [NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED },
-               [NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 },
-       };
+       nl80211_destroy_bss(drv->first_bss);
 
-       err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB,
-                              nl_combi, iface_combination_policy);
-       if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] ||
-           !tb_comb[NL80211_IFACE_COMB_MAXNUM] ||
-           !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS])
-               return 0; /* broken combination */
-
-       if (tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS])
-               info->capa->flags |= WPA_DRIVER_FLAGS_RADAR;
-
-       nla_for_each_nested(nl_limit, tb_comb[NL80211_IFACE_COMB_LIMITS],
-                           rem_limit) {
-               err = nla_parse_nested(tb_limit, MAX_NL80211_IFACE_LIMIT,
-                                      nl_limit, iface_limit_policy);
-               if (err || !tb_limit[NL80211_IFACE_LIMIT_TYPES])
-                       return 0; /* broken combination */
-
-               nla_for_each_nested(nl_mode,
-                                   tb_limit[NL80211_IFACE_LIMIT_TYPES],
-                                   rem_mode) {
-                       int ift = nla_type(nl_mode);
-                       if (ift == NL80211_IFTYPE_P2P_GO ||
-                           ift == NL80211_IFTYPE_P2P_CLIENT)
-                               combination_has_p2p = 1;
-                       if (ift == NL80211_IFTYPE_STATION)
-                               combination_has_mgd = 1;
-               }
-               if (combination_has_p2p && combination_has_mgd)
-                       break;
-       }
+       os_free(drv->filter_ssids);
 
-       if (combination_has_p2p && combination_has_mgd) {
-               info->p2p_concurrent = 1;
-               if (nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]) > 1)
-                       info->p2p_multichan_concurrent = 1;
-               return 1;
-       }
+       os_free(drv->auth_ie);
 
-       return 0;
+       if (drv->in_interface_list)
+               dl_list_del(&drv->list);
+
+       os_free(drv->extended_capa);
+       os_free(drv->extended_capa_mask);
+       os_free(drv->first_bss);
+       os_free(drv);
 }
 
 
-static void wiphy_info_iface_comb(struct wiphy_info_data *info,
-                                 struct nlattr *tb)
+static u32 wpa_alg_to_cipher_suite(enum wpa_alg alg, size_t key_len)
 {
-       struct nlattr *nl_combi;
-       int rem_combi;
-
-       if (tb == NULL)
-               return;
-
-       nla_for_each_nested(nl_combi, tb, rem_combi) {
-               if (wiphy_info_iface_comb_process(info, nl_combi) > 0)
-                       break;
+       switch (alg) {
+       case WPA_ALG_WEP:
+               if (key_len == 5)
+                       return WLAN_CIPHER_SUITE_WEP40;
+               return WLAN_CIPHER_SUITE_WEP104;
+       case WPA_ALG_TKIP:
+               return WLAN_CIPHER_SUITE_TKIP;
+       case WPA_ALG_CCMP:
+               return WLAN_CIPHER_SUITE_CCMP;
+       case WPA_ALG_GCMP:
+               return WLAN_CIPHER_SUITE_GCMP;
+       case WPA_ALG_CCMP_256:
+               return WLAN_CIPHER_SUITE_CCMP_256;
+       case WPA_ALG_GCMP_256:
+               return WLAN_CIPHER_SUITE_GCMP_256;
+       case WPA_ALG_IGTK:
+               return WLAN_CIPHER_SUITE_AES_CMAC;
+       case WPA_ALG_BIP_GMAC_128:
+               return WLAN_CIPHER_SUITE_BIP_GMAC_128;
+       case WPA_ALG_BIP_GMAC_256:
+               return WLAN_CIPHER_SUITE_BIP_GMAC_256;
+       case WPA_ALG_BIP_CMAC_256:
+               return WLAN_CIPHER_SUITE_BIP_CMAC_256;
+       case WPA_ALG_SMS4:
+               return WLAN_CIPHER_SUITE_SMS4;
+       case WPA_ALG_KRK:
+               return WLAN_CIPHER_SUITE_KRK;
+       case WPA_ALG_NONE:
+       case WPA_ALG_PMK:
+               wpa_printf(MSG_ERROR, "nl80211: Unexpected encryption algorithm %d",
+                          alg);
+               return 0;
        }
+
+       wpa_printf(MSG_ERROR, "nl80211: Unsupported encryption algorithm %d",
+                  alg);
+       return 0;
 }
 
 
-static void wiphy_info_supp_cmds(struct wiphy_info_data *info,
-                                struct nlattr *tb)
+static u32 wpa_cipher_to_cipher_suite(unsigned int cipher)
 {
-       struct nlattr *nl_cmd;
-       int i;
-
-       if (tb == NULL)
-               return;
-
-       nla_for_each_nested(nl_cmd, tb, i) {
-               switch (nla_get_u32(nl_cmd)) {
-               case NL80211_CMD_AUTHENTICATE:
-                       info->auth_supported = 1;
-                       break;
-               case NL80211_CMD_CONNECT:
-                       info->connect_supported = 1;
-                       break;
-               case NL80211_CMD_START_SCHED_SCAN:
-                       info->capa->sched_scan_supported = 1;
-                       break;
-               case NL80211_CMD_PROBE_CLIENT:
-                       info->poll_command_supported = 1;
-                       break;
-               }
+       switch (cipher) {
+       case WPA_CIPHER_CCMP_256:
+               return WLAN_CIPHER_SUITE_CCMP_256;
+       case WPA_CIPHER_GCMP_256:
+               return WLAN_CIPHER_SUITE_GCMP_256;
+       case WPA_CIPHER_CCMP:
+               return WLAN_CIPHER_SUITE_CCMP;
+       case WPA_CIPHER_GCMP:
+               return WLAN_CIPHER_SUITE_GCMP;
+       case WPA_CIPHER_TKIP:
+               return WLAN_CIPHER_SUITE_TKIP;
+       case WPA_CIPHER_WEP104:
+               return WLAN_CIPHER_SUITE_WEP104;
+       case WPA_CIPHER_WEP40:
+               return WLAN_CIPHER_SUITE_WEP40;
+       case WPA_CIPHER_GTK_NOT_USED:
+               return WLAN_CIPHER_SUITE_NO_GROUP_ADDR;
        }
-}
 
-
-static void wiphy_info_max_roc(struct wpa_driver_capa *capa,
-                              struct nlattr *tb)
-{
-       if (tb)
-               capa->max_remain_on_chan = nla_get_u32(tb);
+       return 0;
 }
 
 
-static void wiphy_info_tdls(struct wpa_driver_capa *capa, struct nlattr *tdls,
-                           struct nlattr *ext_setup)
+static int wpa_cipher_to_cipher_suites(unsigned int ciphers, u32 suites[],
+                                      int max_suites)
 {
-       if (tdls == NULL)
-               return;
+       int num_suites = 0;
 
-       wpa_printf(MSG_DEBUG, "nl80211: TDLS supported");
-       capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT;
+       if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP_256)
+               suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP_256;
+       if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP_256)
+               suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP_256;
+       if (num_suites < max_suites && ciphers & WPA_CIPHER_CCMP)
+               suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP;
+       if (num_suites < max_suites && ciphers & WPA_CIPHER_GCMP)
+               suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP;
+       if (num_suites < max_suites && ciphers & WPA_CIPHER_TKIP)
+               suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP;
+       if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP104)
+               suites[num_suites++] = WLAN_CIPHER_SUITE_WEP104;
+       if (num_suites < max_suites && ciphers & WPA_CIPHER_WEP40)
+               suites[num_suites++] = WLAN_CIPHER_SUITE_WEP40;
 
-       if (ext_setup) {
-               wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup");
-               capa->flags |= WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP;
-       }
+       return num_suites;
 }
 
 
-static void wiphy_info_feature_flags(struct wiphy_info_data *info,
-                                    struct nlattr *tb)
+static int issue_key_mgmt_set_key(struct wpa_driver_nl80211_data *drv,
+                                 const u8 *key, size_t key_len)
 {
-       u32 flags;
-       struct wpa_driver_capa *capa = info->capa;
+       struct nl_msg *msg;
+       int ret;
 
-       if (tb == NULL)
-               return;
+       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD))
+               return 0;
 
-       flags = nla_get_u32(tb);
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+                       QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_SET_KEY) ||
+           nla_put(msg, NL80211_ATTR_VENDOR_DATA, key_len, key)) {
+               nl80211_nlmsg_clear(msg);
+               nlmsg_free(msg);
+               return -1;
+       }
+       ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
+       if (ret) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Key management set key failed: ret=%d (%s)",
+                          ret, strerror(-ret));
+       }
 
-       if (flags & NL80211_FEATURE_SK_TX_STATUS)
-               info->data_tx_status = 1;
+       return ret;
+}
 
-       if (flags & NL80211_FEATURE_INACTIVITY_TIMER)
-               capa->flags |= WPA_DRIVER_FLAGS_INACTIVITY_TIMER;
 
-       if (flags & NL80211_FEATURE_SAE)
-               capa->flags |= WPA_DRIVER_FLAGS_SAE;
+static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
+                                     enum wpa_alg alg, const u8 *addr,
+                                     int key_idx, int set_tx,
+                                     const u8 *seq, size_t seq_len,
+                                     const u8 *key, size_t key_len)
+{
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ifindex;
+       struct nl_msg *msg;
+       int ret;
+       int tdls = 0;
 
-       if (flags & NL80211_FEATURE_NEED_OBSS_SCAN)
-               capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN;
-}
+       /* Ignore for P2P Device */
+       if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
+               return 0;
 
+       ifindex = if_nametoindex(ifname);
+       wpa_printf(MSG_DEBUG, "%s: ifindex=%d (%s) alg=%d addr=%p key_idx=%d "
+                  "set_tx=%d seq_len=%lu key_len=%lu",
+                  __func__, ifindex, ifname, alg, addr, key_idx, set_tx,
+                  (unsigned long) seq_len, (unsigned long) key_len);
+#ifdef CONFIG_TDLS
+       if (key_idx == -1) {
+               key_idx = 0;
+               tdls = 1;
+       }
+#endif /* CONFIG_TDLS */
 
-static void wiphy_info_probe_resp_offload(struct wpa_driver_capa *capa,
-                                         struct nlattr *tb)
-{
-       u32 protocols;
+       if (alg == WPA_ALG_PMK &&
+           (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) {
+               wpa_printf(MSG_DEBUG, "%s: calling issue_key_mgmt_set_key",
+                          __func__);
+               ret = issue_key_mgmt_set_key(drv, key, key_len);
+               return ret;
+       }
 
-       if (tb == NULL)
-               return;
+       if (alg == WPA_ALG_NONE) {
+               msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_DEL_KEY);
+               if (!msg)
+                       return -ENOBUFS;
+       } else {
+               msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_NEW_KEY);
+               if (!msg ||
+                   nla_put(msg, NL80211_ATTR_KEY_DATA, key_len, key) ||
+                   nla_put_u32(msg, NL80211_ATTR_KEY_CIPHER,
+                               wpa_alg_to_cipher_suite(alg, key_len)))
+                       goto fail;
+               wpa_hexdump_key(MSG_DEBUG, "nl80211: KEY_DATA", key, key_len);
+       }
 
-       protocols = nla_get_u32(tb);
-       wpa_printf(MSG_DEBUG, "nl80211: Supports Probe Response offload in AP "
-                  "mode");
-       capa->flags |= WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD;
-       capa->probe_resp_offloads = probe_resp_offload_support(protocols);
-}
+       if (seq && seq_len) {
+               if (nla_put(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq))
+                       goto fail;
+               wpa_hexdump(MSG_DEBUG, "nl80211: KEY_SEQ", seq, seq_len);
+       }
 
+       if (addr && !is_broadcast_ether_addr(addr)) {
+               wpa_printf(MSG_DEBUG, "   addr=" MACSTR, MAC2STR(addr));
+               if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
+                       goto fail;
 
-static int wiphy_info_handler(struct nl_msg *msg, void *arg)
-{
-       struct nlattr *tb[NL80211_ATTR_MAX + 1];
-       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-       struct wiphy_info_data *info = arg;
-       struct wpa_driver_capa *capa = info->capa;
-       struct wpa_driver_nl80211_data *drv = info->drv;
+               if (alg != WPA_ALG_WEP && key_idx && !set_tx) {
+                       wpa_printf(MSG_DEBUG, "   RSN IBSS RX GTK");
+                       if (nla_put_u32(msg, NL80211_ATTR_KEY_TYPE,
+                                       NL80211_KEYTYPE_GROUP))
+                               goto fail;
+               }
+       } else if (addr && is_broadcast_ether_addr(addr)) {
+               struct nlattr *types;
 
-       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-                 genlmsg_attrlen(gnlh, 0), NULL);
+               wpa_printf(MSG_DEBUG, "   broadcast key");
 
-       if (tb[NL80211_ATTR_WIPHY_NAME])
-               os_strncpy(drv->phyname,
-                          nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]),
-                          sizeof(drv->phyname));
-       if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS])
-               capa->max_scan_ssids =
-                       nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]);
+               types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES);
+               if (!types ||
+                   nla_put_flag(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST))
+                       goto fail;
+               nla_nest_end(msg, types);
+       }
+       if (nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx))
+               goto fail;
 
-       if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS])
-               capa->max_sched_scan_ssids =
-                       nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]);
+       ret = send_and_recv_msgs(drv, msg, NULL, key ? (void *) -1 : NULL);
+       if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE)
+               ret = 0;
+       if (ret)
+               wpa_printf(MSG_DEBUG, "nl80211: set_key failed; err=%d %s)",
+                          ret, strerror(-ret));
 
-       if (tb[NL80211_ATTR_MAX_MATCH_SETS])
-               capa->max_match_sets =
-                       nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
+       /*
+        * If we failed or don't need to set the default TX key (below),
+        * we're done here.
+        */
+       if (ret || !set_tx || alg == WPA_ALG_NONE || tdls)
+               return ret;
+       if (is_ap_interface(drv->nlmode) && addr &&
+           !is_broadcast_ether_addr(addr))
+               return ret;
 
-       if (tb[NL80211_ATTR_MAC_ACL_MAX])
-               capa->max_acl_mac_addrs =
-                       nla_get_u8(tb[NL80211_ATTR_MAC_ACL_MAX]);
+       msg = nl80211_ifindex_msg(drv, ifindex, 0, NL80211_CMD_SET_KEY);
+       if (!msg ||
+           nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx) ||
+           nla_put_flag(msg, (alg == WPA_ALG_IGTK ||
+                              alg == WPA_ALG_BIP_GMAC_128 ||
+                              alg == WPA_ALG_BIP_GMAC_256 ||
+                              alg == WPA_ALG_BIP_CMAC_256) ?
+                        NL80211_ATTR_KEY_DEFAULT_MGMT :
+                        NL80211_ATTR_KEY_DEFAULT))
+               goto fail;
+       if (addr && is_broadcast_ether_addr(addr)) {
+               struct nlattr *types;
 
-       wiphy_info_supported_iftypes(info, tb[NL80211_ATTR_SUPPORTED_IFTYPES]);
-       wiphy_info_iface_comb(info, tb[NL80211_ATTR_INTERFACE_COMBINATIONS]);
-       wiphy_info_supp_cmds(info, tb[NL80211_ATTR_SUPPORTED_COMMANDS]);
+               types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES);
+               if (!types ||
+                   nla_put_flag(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST))
+                       goto fail;
+               nla_nest_end(msg, types);
+       } else if (addr) {
+               struct nlattr *types;
 
-       if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) {
-               wpa_printf(MSG_DEBUG, "nl80211: Using driver-based "
-                          "off-channel TX");
-               capa->flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
+               types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES);
+               if (!types ||
+                   nla_put_flag(msg, NL80211_KEY_DEFAULT_TYPE_UNICAST))
+                       goto fail;
+               nla_nest_end(msg, types);
        }
 
-       if (tb[NL80211_ATTR_ROAM_SUPPORT]) {
-               wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming");
-               capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION;
-       }
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret == -ENOENT)
+               ret = 0;
+       if (ret)
+               wpa_printf(MSG_DEBUG, "nl80211: set_key default failed; "
+                          "err=%d %s)", ret, strerror(-ret));
+       return ret;
 
-       wiphy_info_max_roc(capa,
-                          tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]);
+fail:
+       nl80211_nlmsg_clear(msg);
+       nlmsg_free(msg);
+       return -ENOBUFS;
+}
 
-       if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD])
-               capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD;
 
-       wiphy_info_tdls(capa, tb[NL80211_ATTR_TDLS_SUPPORT],
-                       tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]);
+static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg,
+                     int key_idx, int defkey,
+                     const u8 *seq, size_t seq_len,
+                     const u8 *key, size_t key_len)
+{
+       struct nlattr *key_attr = nla_nest_start(msg, NL80211_ATTR_KEY);
+       if (!key_attr)
+               return -1;
 
-       if (tb[NL80211_ATTR_DEVICE_AP_SME])
-               info->device_ap_sme = 1;
+       if (defkey && alg == WPA_ALG_IGTK) {
+               if (nla_put_flag(msg, NL80211_KEY_DEFAULT_MGMT))
+                       return -1;
+       } else if (defkey) {
+               if (nla_put_flag(msg, NL80211_KEY_DEFAULT))
+                       return -1;
+       }
 
-       wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]);
-       wiphy_info_probe_resp_offload(capa,
-                                     tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]);
+       if (nla_put_u8(msg, NL80211_KEY_IDX, key_idx) ||
+           nla_put_u32(msg, NL80211_KEY_CIPHER,
+                       wpa_alg_to_cipher_suite(alg, key_len)) ||
+           (seq && seq_len &&
+            nla_put(msg, NL80211_KEY_SEQ, seq_len, seq)) ||
+           nla_put(msg, NL80211_KEY_DATA, key_len, key))
+               return -1;
 
-       if (tb[NL80211_ATTR_EXT_CAPA] && tb[NL80211_ATTR_EXT_CAPA_MASK] &&
-           drv->extended_capa == NULL) {
-               drv->extended_capa =
-                       os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA]));
-               if (drv->extended_capa) {
-                       os_memcpy(drv->extended_capa,
-                                 nla_data(tb[NL80211_ATTR_EXT_CAPA]),
-                                 nla_len(tb[NL80211_ATTR_EXT_CAPA]));
-                       drv->extended_capa_len =
-                               nla_len(tb[NL80211_ATTR_EXT_CAPA]);
-               }
-               drv->extended_capa_mask =
-                       os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA]));
-               if (drv->extended_capa_mask) {
-                       os_memcpy(drv->extended_capa_mask,
-                                 nla_data(tb[NL80211_ATTR_EXT_CAPA]),
-                                 nla_len(tb[NL80211_ATTR_EXT_CAPA]));
-               } else {
-                       os_free(drv->extended_capa);
-                       drv->extended_capa = NULL;
-                       drv->extended_capa_len = 0;
-               }
-       }
+       nla_nest_end(msg, key_attr);
 
-       return NL_SKIP;
+       return 0;
 }
 
 
-static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
-                                      struct wiphy_info_data *info)
+static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params,
+                                struct nl_msg *msg)
 {
-       u32 feat;
-       struct nl_msg *msg;
+       int i, privacy = 0;
+       struct nlattr *nl_keys, *nl_key;
 
-       os_memset(info, 0, sizeof(*info));
-       info->capa = &drv->capa;
-       info->drv = drv;
+       for (i = 0; i < 4; i++) {
+               if (!params->wep_key[i])
+                       continue;
+               privacy = 1;
+               break;
+       }
+       if (params->wps == WPS_MODE_PRIVACY)
+               privacy = 1;
+       if (params->pairwise_suite &&
+           params->pairwise_suite != WPA_CIPHER_NONE)
+               privacy = 1;
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -1;
+       if (!privacy)
+               return 0;
 
-       feat = get_nl80211_protocol_features(drv);
-       if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
-               nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_WIPHY);
-       else
-               nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY);
+       if (nla_put_flag(msg, NL80211_ATTR_PRIVACY))
+               return -ENOBUFS;
 
-       NLA_PUT_FLAG(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP);
-       if (nl80211_set_iface_id(msg, &drv->first_bss) < 0)
-               goto nla_put_failure;
+       nl_keys = nla_nest_start(msg, NL80211_ATTR_KEYS);
+       if (!nl_keys)
+               return -ENOBUFS;
 
-       if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info))
-               return -1;
+       for (i = 0; i < 4; i++) {
+               if (!params->wep_key[i])
+                       continue;
 
-       if (info->auth_supported)
-               drv->capa.flags |= WPA_DRIVER_FLAGS_SME;
-       else if (!info->connect_supported) {
-               wpa_printf(MSG_INFO, "nl80211: Driver does not support "
-                          "authentication/association or connect commands");
-               info->error = 1;
-       }
+               nl_key = nla_nest_start(msg, i);
+               if (!nl_key ||
+                   nla_put(msg, NL80211_KEY_DATA, params->wep_key_len[i],
+                           params->wep_key[i]) ||
+                   nla_put_u32(msg, NL80211_KEY_CIPHER,
+                               params->wep_key_len[i] == 5 ?
+                               WLAN_CIPHER_SUITE_WEP40 :
+                               WLAN_CIPHER_SUITE_WEP104) ||
+                   nla_put_u8(msg, NL80211_KEY_IDX, i) ||
+                   (i == params->wep_tx_keyidx &&
+                    nla_put_flag(msg, NL80211_KEY_DEFAULT)))
+                       return -ENOBUFS;
 
-       if (info->p2p_go_supported && info->p2p_client_supported)
-               drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
-       if (info->p2p_concurrent) {
-               wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
-                          "interface (driver advertised support)");
-               drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
-               drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
-       }
-       if (info->p2p_multichan_concurrent) {
-               wpa_printf(MSG_DEBUG, "nl80211: Enable multi-channel "
-                          "concurrent (driver advertised support)");
-               drv->capa.flags |= WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT;
+               nla_nest_end(msg, nl_key);
        }
-
-       /* default to 5000 since early versions of mac80211 don't set it */
-       if (!drv->capa.max_remain_on_chan)
-               drv->capa.max_remain_on_chan = 5000;
-#ifdef TIZEN_EXT
-#ifdef BCM_DRIVER_V115
-       info->device_ap_sme = 1;
-#endif
-#endif
+       nla_nest_end(msg, nl_keys);
 
        return 0;
-nla_put_failure:
-       nlmsg_free(msg);
-       return -1;
 }
 
 
-static int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
+int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
+                           const u8 *addr, int cmd, u16 reason_code,
+                           int local_state_change)
 {
-       struct wiphy_info_data info;
-       if (wpa_driver_nl80211_get_info(drv, &info))
-               return -1;
+       int ret;
+       struct nl_msg *msg;
 
-       if (info.error)
+       if (!(msg = nl80211_drv_msg(drv, 0, cmd)) ||
+           nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code) ||
+           (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) ||
+           (local_state_change &&
+            nla_put_flag(msg, NL80211_ATTR_LOCAL_STATE_CHANGE))) {
+               nlmsg_free(msg);
                return -1;
-
-       drv->has_capability = 1;
-       /* For now, assume TKIP, CCMP, WPA, WPA2 are supported */
-       drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
-               WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
-               WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
-               WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
-       drv->capa.enc = WPA_DRIVER_CAPA_ENC_WEP40 |
-               WPA_DRIVER_CAPA_ENC_WEP104 |
-               WPA_DRIVER_CAPA_ENC_TKIP |
-               WPA_DRIVER_CAPA_ENC_CCMP;
-       drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
-               WPA_DRIVER_AUTH_SHARED |
-               WPA_DRIVER_AUTH_LEAP;
-
-       drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES;
-       drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
-       drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
-
-       if (!info.device_ap_sme) {
-               drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS;
-
-               /*
-                * No AP SME is currently assumed to also indicate no AP MLME
-                * in the driver/firmware.
-                */
-               drv->capa.flags |= WPA_DRIVER_FLAGS_AP_MLME;
        }
 
-       drv->device_ap_sme = info.device_ap_sme;
-       drv->poll_command_supported = info.poll_command_supported;
-       drv->data_tx_status = info.data_tx_status;
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret) {
+               wpa_dbg(drv->ctx, MSG_DEBUG,
+                       "nl80211: MLME command failed: reason=%u ret=%d (%s)",
+                       reason_code, ret, strerror(-ret));
+       }
+       return ret;
+}
 
-       /*
-        * If poll command and tx status are supported, mac80211 is new enough
-        * to have everything we need to not need monitor interfaces.
-        */
-       //drv->use_monitor = !info.poll_command_supported || !info.data_tx_status;
-       drv->use_monitor = 0;
 
-       if (drv->device_ap_sme && drv->use_monitor) {
-               /*
-                * Non-mac80211 drivers may not support monitor interface.
-                * Make sure we do not get stuck with incorrect capability here
-                * by explicitly testing this.
-                */
-               if (!info.monitor_supported) {
-                       wpa_printf(MSG_DEBUG, "nl80211: Disable use_monitor "
-                                  "with device_ap_sme since no monitor mode "
-                                  "support detected");
-                       drv->use_monitor = 0;
-               }
-       }
+static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv,
+                                        int reason_code)
+{
+       int ret;
 
+       wpa_printf(MSG_DEBUG, "%s(reason_code=%d)", __func__, reason_code);
+       nl80211_mark_disconnected(drv);
+       /* Disconnect command doesn't need BSSID - it uses cached value */
+       ret = wpa_driver_nl80211_mlme(drv, NULL, NL80211_CMD_DISCONNECT,
+                                     reason_code, 0);
        /*
-        * If we aren't going to use monitor interfaces, but the
-        * driver doesn't support data TX status, we won't get TX
-        * status for EAPOL frames.
+        * For locally generated disconnect, supplicant already generates a
+        * DEAUTH event, so ignore the event from NL80211.
         */
-       if (!drv->use_monitor && !info.data_tx_status)
-               drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+       drv->ignore_next_local_disconnect = ret == 0;
 
-       return 0;
+       return ret;
 }
 
 
-#ifdef ANDROID
-static int android_genl_ctrl_resolve(struct nl_handle *handle,
-                                    const char *name)
+static int wpa_driver_nl80211_deauthenticate(struct i802_bss *bss,
+                                            const u8 *addr, int reason_code)
 {
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ret;
+
+       if (drv->nlmode == NL80211_IFTYPE_ADHOC) {
+               nl80211_mark_disconnected(drv);
+               return nl80211_leave_ibss(drv, 1);
+       }
+       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME))
+               return wpa_driver_nl80211_disconnect(drv, reason_code);
+       wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)",
+                  __func__, MAC2STR(addr), reason_code);
+       nl80211_mark_disconnected(drv);
+       ret = wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE,
+                                     reason_code, 0);
        /*
-        * Android ICS has very minimal genl_ctrl_resolve() implementation, so
-        * need to work around that.
+        * For locally generated deauthenticate, supplicant already generates a
+        * DEAUTH event, so ignore the event from NL80211.
         */
-       struct nl_cache *cache = NULL;
-       struct genl_family *nl80211 = NULL;
-       int id = -1;
+       drv->ignore_next_local_deauth = ret == 0;
+       return ret;
+}
 
-       if (genl_ctrl_alloc_cache(handle, &cache) < 0) {
-               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
-                          "netlink cache");
-               goto fail;
-       }
 
-       nl80211 = genl_ctrl_search_by_name(cache, name);
-       if (nl80211 == NULL)
-               goto fail;
-
-       id = genl_family_get_id(nl80211);
-
-fail:
-       if (nl80211)
-               genl_family_put(nl80211);
-       if (cache)
-               nl_cache_free(cache);
-
-       return id;
-}
-#define genl_ctrl_resolve android_genl_ctrl_resolve
-#endif /* ANDROID */
-
-
-static int wpa_driver_nl80211_init_nl_global(struct nl80211_global *global)
-{
-       int ret;
-
-       global->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
-       if (global->nl_cb == NULL) {
-               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate netlink "
-                          "callbacks");
-               return -1;
-       }
-
-       global->nl = nl_create_handle(global->nl_cb, "nl");
-       if (global->nl == NULL)
-               goto err;
-
-       global->nl80211_id = genl_ctrl_resolve(global->nl, "nl80211");
-       if (global->nl80211_id < 0) {
-               wpa_printf(MSG_ERROR, "nl80211: 'nl80211' generic netlink not "
-                          "found");
-               goto err;
-       }
-
-       global->nl_event = nl_create_handle(global->nl_cb, "event");
-       if (global->nl_event == NULL)
-               goto err;
-
-       ret = nl_get_multicast_id(global, "nl80211", "scan");
-       if (ret >= 0)
-               ret = nl_socket_add_membership(global->nl_event, ret);
-       if (ret < 0) {
-               wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
-                          "membership for scan events: %d (%s)",
-                          ret, strerror(-ret));
-               goto err;
-       }
-
-       ret = nl_get_multicast_id(global, "nl80211", "mlme");
-       if (ret >= 0)
-               ret = nl_socket_add_membership(global->nl_event, ret);
-       if (ret < 0) {
-               wpa_printf(MSG_ERROR, "nl80211: Could not add multicast "
-                          "membership for mlme events: %d (%s)",
-                          ret, strerror(-ret));
-               goto err;
-       }
-
-       ret = nl_get_multicast_id(global, "nl80211", "regulatory");
-       if (ret >= 0)
-               ret = nl_socket_add_membership(global->nl_event, ret);
-       if (ret < 0) {
-               wpa_printf(MSG_DEBUG, "nl80211: Could not add multicast "
-                          "membership for regulatory events: %d (%s)",
-                          ret, strerror(-ret));
-               /* Continue without regulatory events */
-       }
-
-       nl_cb_set(global->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
-                 no_seq_check, NULL);
-       nl_cb_set(global->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
-                 process_global_event, global);
-
-       eloop_register_read_sock(nl_socket_get_fd(global->nl_event),
-                                wpa_driver_nl80211_event_receive,
-                                global->nl_cb, global->nl_event);
-
-       return 0;
-
-err:
-       nl_destroy_handles(&global->nl_event);
-       nl_destroy_handles(&global->nl);
-       nl_cb_put(global->nl_cb);
-       global->nl_cb = NULL;
-       return -1;
-}
-
-
-static int wpa_driver_nl80211_init_nl(struct wpa_driver_nl80211_data *drv)
-{
-       drv->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
-       if (!drv->nl_cb) {
-               wpa_printf(MSG_ERROR, "nl80211: Failed to alloc cb struct");
-               return -1;
-       }
-
-       nl_cb_set(drv->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
-                 no_seq_check, NULL);
-       nl_cb_set(drv->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
-                 process_drv_event, drv);
-
-       return 0;
-}
-
-
-static void wpa_driver_nl80211_rfkill_blocked(void *ctx)
-{
-       wpa_printf(MSG_DEBUG, "nl80211: RFKILL blocked");
-       /*
-        * This may be for any interface; use ifdown event to disable
-        * interface.
-        */
-}
-
-
-static void wpa_driver_nl80211_rfkill_unblocked(void *ctx)
-{
-       struct wpa_driver_nl80211_data *drv = ctx;
-       wpa_printf(MSG_DEBUG, "nl80211: RFKILL unblocked");
-       if (linux_set_iface_flags(drv->global->ioctl_sock,
-                                 drv->first_bss.ifname, 1)) {
-               wpa_printf(MSG_DEBUG, "nl80211: Could not set interface UP "
-                          "after rfkill unblock");
-               return;
-       }
-       /* rtnetlink ifup handler will report interface as enabled */
-}
-
-
-static void wpa_driver_nl80211_handle_eapol_tx_status(int sock,
-                                                     void *eloop_ctx,
-                                                     void *handle)
-{
-       struct wpa_driver_nl80211_data *drv = eloop_ctx;
-       u8 data[2048];
-       struct msghdr msg;
-       struct iovec entry;
-       u8 control[512];
-       struct cmsghdr *cmsg;
-       int res, found_ee = 0, found_wifi = 0, acked = 0;
-       union wpa_event_data event;
-
-       memset(&msg, 0, sizeof(msg));
-       msg.msg_iov = &entry;
-       msg.msg_iovlen = 1;
-       entry.iov_base = data;
-       entry.iov_len = sizeof(data);
-       msg.msg_control = &control;
-       msg.msg_controllen = sizeof(control);
-
-       res = recvmsg(sock, &msg, MSG_ERRQUEUE);
-       /* if error or not fitting 802.3 header, return */
-       if (res < 14)
-               return;
-
-       for (cmsg = CMSG_FIRSTHDR(&msg); cmsg; cmsg = CMSG_NXTHDR(&msg, cmsg))
-       {
-               if (cmsg->cmsg_level == SOL_SOCKET &&
-                   cmsg->cmsg_type == SCM_WIFI_STATUS) {
-                       int *ack;
-
-                       found_wifi = 1;
-                       ack = (void *)CMSG_DATA(cmsg);
-                       acked = *ack;
-               }
-
-               if (cmsg->cmsg_level == SOL_PACKET &&
-                   cmsg->cmsg_type == PACKET_TX_TIMESTAMP) {
-                       struct sock_extended_err *err =
-                               (struct sock_extended_err *)CMSG_DATA(cmsg);
-
-                       if (err->ee_origin == SO_EE_ORIGIN_TXSTATUS)
-                               found_ee = 1;
-               }
-       }
-
-       if (!found_ee || !found_wifi)
-               return;
-
-       memset(&event, 0, sizeof(event));
-       event.eapol_tx_status.dst = data;
-       event.eapol_tx_status.data = data + 14;
-       event.eapol_tx_status.data_len = res - 14;
-       event.eapol_tx_status.ack = acked;
-       wpa_supplicant_event(drv->ctx, EVENT_EAPOL_TX_STATUS, &event);
-}
-
-
-static int nl80211_init_bss(struct i802_bss *bss)
-{
-       bss->nl_cb = nl_cb_alloc(NL_CB_DEFAULT);
-       if (!bss->nl_cb)
-               return -1;
-
-       nl_cb_set(bss->nl_cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM,
-                 no_seq_check, NULL);
-       nl_cb_set(bss->nl_cb, NL_CB_VALID, NL_CB_CUSTOM,
-                 process_bss_event, bss);
-
-       return 0;
-}
-
-
-static void nl80211_destroy_bss(struct i802_bss *bss)
-{
-       nl_cb_put(bss->nl_cb);
-       bss->nl_cb = NULL;
-}
-
-
-/**
- * wpa_driver_nl80211_init - Initialize nl80211 driver interface
- * @ctx: context to be used when calling wpa_supplicant functions,
- * e.g., wpa_supplicant_event()
- * @ifname: interface name, e.g., wlan0
- * @global_priv: private driver global data from global_init()
- * Returns: Pointer to private data, %NULL on failure
- */
-static void * wpa_driver_nl80211_init(void *ctx, const char *ifname,
-                                     void *global_priv)
-{
-       struct wpa_driver_nl80211_data *drv;
-       struct rfkill_config *rcfg;
-       struct i802_bss *bss;
-
-       if (global_priv == NULL)
-               return NULL;
-       drv = os_zalloc(sizeof(*drv));
-       if (drv == NULL)
-               return NULL;
-       drv->global = global_priv;
-       drv->ctx = ctx;
-       bss = &drv->first_bss;
-       bss->drv = drv;
-       bss->ctx = ctx;
-
-       os_strlcpy(bss->ifname, ifname, sizeof(bss->ifname));
-       drv->monitor_ifidx = -1;
-       drv->monitor_sock = -1;
-       drv->eapol_tx_sock = -1;
-       drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
-
-       if (wpa_driver_nl80211_init_nl(drv)) {
-               os_free(drv);
-               return NULL;
-       }
-
-       if (nl80211_init_bss(bss))
-               goto failed;
-
-       rcfg = os_zalloc(sizeof(*rcfg));
-       if (rcfg == NULL)
-               goto failed;
-       rcfg->ctx = drv;
-       os_strlcpy(rcfg->ifname, ifname, sizeof(rcfg->ifname));
-       rcfg->blocked_cb = wpa_driver_nl80211_rfkill_blocked;
-       rcfg->unblocked_cb = wpa_driver_nl80211_rfkill_unblocked;
-       drv->rfkill = rfkill_init(rcfg);
-       if (drv->rfkill == NULL) {
-               wpa_printf(MSG_DEBUG, "nl80211: RFKILL status not available");
-               os_free(rcfg);
-       }
-
-       if (wpa_driver_nl80211_finish_drv_init(drv))
-               goto failed;
-
-       drv->eapol_tx_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
-       if (drv->eapol_tx_sock < 0)
-               goto failed;
-
-       if (drv->data_tx_status) {
-               int enabled = 1;
-
-               if (setsockopt(drv->eapol_tx_sock, SOL_SOCKET, SO_WIFI_STATUS,
-                              &enabled, sizeof(enabled)) < 0) {
-                       wpa_printf(MSG_DEBUG,
-                               "nl80211: wifi status sockopt failed\n");
-                       drv->data_tx_status = 0;
-                       if (!drv->use_monitor)
-                               drv->capa.flags &=
-                                       ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
-               } else {
-                       eloop_register_read_sock(drv->eapol_tx_sock,
-                               wpa_driver_nl80211_handle_eapol_tx_status,
-                               drv, NULL);
-               }
-       }
-
-       if (drv->global) {
-               dl_list_add(&drv->global->interfaces, &drv->list);
-               drv->in_interface_list = 1;
-       }
-
-       return bss;
-
-failed:
-       wpa_driver_nl80211_deinit(bss);
-       return NULL;
-}
-
-
-static int nl80211_register_frame(struct i802_bss *bss,
-                                 struct nl_handle *nl_handle,
-                                 u16 type, const u8 *match, size_t match_len)
-{
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct nl_msg *msg;
-       int ret = -1;
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -1;
-
-       wpa_printf(MSG_DEBUG, "nl80211: Register frame type=0x%x nl_handle=%p",
-                  type, nl_handle);
-       wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match",
-                   match, match_len);
-
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_REGISTER_ACTION);
-
-       if (nl80211_set_iface_id(msg, bss) < 0)
-               goto nla_put_failure;
-
-       NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE, type);
-       NLA_PUT(msg, NL80211_ATTR_FRAME_MATCH, match_len, match);
-
-       ret = send_and_recv(drv->global, nl_handle, msg, NULL, NULL);
-       msg = NULL;
-       if (ret) {
-               wpa_printf(MSG_DEBUG, "nl80211: Register frame command "
-                          "failed (type=%u): ret=%d (%s)",
-                          type, ret, strerror(-ret));
-               wpa_hexdump(MSG_DEBUG, "nl80211: Register frame match",
-                           match, match_len);
-               goto nla_put_failure;
-       }
-       ret = 0;
-nla_put_failure:
-       nlmsg_free(msg);
-       return ret;
-}
-
-
-static int nl80211_alloc_mgmt_handle(struct i802_bss *bss)
-{
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-
-       if (bss->nl_mgmt) {
-               wpa_printf(MSG_DEBUG, "nl80211: Mgmt reporting "
-                          "already on! (nl_mgmt=%p)", bss->nl_mgmt);
-               return -1;
-       }
-
-       bss->nl_mgmt = nl_create_handle(drv->nl_cb, "mgmt");
-       if (bss->nl_mgmt == NULL)
-               return -1;
-
-       eloop_register_read_sock(nl_socket_get_fd(bss->nl_mgmt),
-                                wpa_driver_nl80211_event_receive, bss->nl_cb,
-                                bss->nl_mgmt);
-
-       return 0;
-}
-
-
-static int nl80211_register_action_frame(struct i802_bss *bss,
-                                        const u8 *match, size_t match_len)
-{
-       u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_ACTION << 4);
-       return nl80211_register_frame(bss, bss->nl_mgmt,
-                                     type, match, match_len);
-}
-
-
-static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
-{
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-
-       if (nl80211_alloc_mgmt_handle(bss))
-               return -1;
-       wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with non-AP "
-                  "handle %p", bss->nl_mgmt);
-
-#if defined(CONFIG_P2P) || defined(CONFIG_INTERWORKING)
-       /* GAS Initial Request */
-       if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0a", 2) < 0)
-               return -1;
-       /* GAS Initial Response */
-       if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0b", 2) < 0)
-               return -1;
-       /* GAS Comeback Request */
-       if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0c", 2) < 0)
-               return -1;
-       /* GAS Comeback Response */
-       if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0d", 2) < 0)
-               return -1;
-#endif /* CONFIG_P2P || CONFIG_INTERWORKING */
-#ifdef CONFIG_P2P
-       /* P2P Public Action */
-       if (nl80211_register_action_frame(bss,
-                                         (u8 *) "\x04\x09\x50\x6f\x9a\x09",
-                                         6) < 0)
-               return -1;
-       /* P2P Action */
-       if (nl80211_register_action_frame(bss,
-                                         (u8 *) "\x7f\x50\x6f\x9a\x09",
-                                         5) < 0)
-               return -1;
-#endif /* CONFIG_P2P */
-#ifdef CONFIG_IEEE80211W
-       /* SA Query Response */
-       if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0)
-               return -1;
-#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_TDLS
-       if ((drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT)) {
-               /* TDLS Discovery Response */
-               if (nl80211_register_action_frame(bss, (u8 *) "\x04\x0e", 2) <
-                   0)
-                       return -1;
-       }
-#endif /* CONFIG_TDLS */
-
-       /* FT Action frames */
-       if (nl80211_register_action_frame(bss, (u8 *) "\x06", 1) < 0)
-               return -1;
-       else
-               drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT |
-                       WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
-
-       /* WNM - BSS Transition Management Request */
-       if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x07", 2) < 0)
-               return -1;
-       /* WNM-Sleep Mode Response */
-       if (nl80211_register_action_frame(bss, (u8 *) "\x0a\x11", 2) < 0)
-               return -1;
-
-       return 0;
-}
-
-
-static int nl80211_register_spurious_class3(struct i802_bss *bss)
-{
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct nl_msg *msg;
-       int ret = -1;
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -1;
-
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_UNEXPECTED_FRAME);
-
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
-
-       ret = send_and_recv(drv->global, bss->nl_mgmt, msg, NULL, NULL);
-       msg = NULL;
-       if (ret) {
-               wpa_printf(MSG_DEBUG, "nl80211: Register spurious class3 "
-                          "failed: ret=%d (%s)",
-                          ret, strerror(-ret));
-               goto nla_put_failure;
-       }
-       ret = 0;
-nla_put_failure:
-       nlmsg_free(msg);
-       return ret;
-}
-
-
-static int nl80211_mgmt_subscribe_ap(struct i802_bss *bss)
-{
-       static const int stypes[] = {
-               WLAN_FC_STYPE_AUTH,
-               WLAN_FC_STYPE_ASSOC_REQ,
-               WLAN_FC_STYPE_REASSOC_REQ,
-               WLAN_FC_STYPE_DISASSOC,
-               WLAN_FC_STYPE_DEAUTH,
-               WLAN_FC_STYPE_ACTION,
-               WLAN_FC_STYPE_PROBE_REQ,
-/* Beacon doesn't work as mac80211 doesn't currently allow
- * it, but it wouldn't really be the right thing anyway as
- * it isn't per interface ... maybe just dump the scan
- * results periodically for OLBC?
- */
-//             WLAN_FC_STYPE_BEACON,
-       };
-       unsigned int i;
-
-       if (nl80211_alloc_mgmt_handle(bss))
-               return -1;
-       wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP "
-                  "handle %p", bss->nl_mgmt);
-
-       for (i = 0; i < sizeof(stypes) / sizeof(stypes[0]); i++) {
-               if (nl80211_register_frame(bss, bss->nl_mgmt,
-                                          (WLAN_FC_TYPE_MGMT << 2) |
-                                          (stypes[i] << 4),
-                                          NULL, 0) < 0) {
-                       goto out_err;
-               }
-       }
-
-       if (nl80211_register_spurious_class3(bss))
-               goto out_err;
-
-       if (nl80211_get_wiphy_data_ap(bss) == NULL)
-               goto out_err;
-
-       return 0;
-
-out_err:
-       eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt));
-       nl_destroy_handles(&bss->nl_mgmt);
-       return -1;
-}
-
-
-static int nl80211_mgmt_subscribe_ap_dev_sme(struct i802_bss *bss)
-{
-       if (nl80211_alloc_mgmt_handle(bss))
-               return -1;
-       wpa_printf(MSG_DEBUG, "nl80211: Subscribe to mgmt frames with AP "
-                  "handle %p (device SME)", bss->nl_mgmt);
-
-       if (nl80211_register_frame(bss, bss->nl_mgmt,
-                                  (WLAN_FC_TYPE_MGMT << 2) |
-                                  (WLAN_FC_STYPE_ACTION << 4),
-                                  NULL, 0) < 0)
-               goto out_err;
-
-       return 0;
-
-out_err:
-       eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt));
-       nl_destroy_handles(&bss->nl_mgmt);
-       return -1;
-}
-
-
-static void nl80211_mgmt_unsubscribe(struct i802_bss *bss, const char *reason)
-{
-       if (bss->nl_mgmt == NULL)
-               return;
-       wpa_printf(MSG_DEBUG, "nl80211: Unsubscribe mgmt frames handle %p "
-                  "(%s)", bss->nl_mgmt, reason);
-       eloop_unregister_read_sock(nl_socket_get_fd(bss->nl_mgmt));
-       nl_destroy_handles(&bss->nl_mgmt);
-
-       nl80211_put_wiphy_data_ap(bss);
-}
-
-
-static void wpa_driver_nl80211_send_rfkill(void *eloop_ctx, void *timeout_ctx)
-{
-       wpa_supplicant_event(timeout_ctx, EVENT_INTERFACE_DISABLED, NULL);
-}
-
-
-static void nl80211_del_p2pdev(struct i802_bss *bss)
-{
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct nl_msg *msg;
-       int ret;
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return;
-
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_INTERFACE);
-       NLA_PUT_U64(msg, NL80211_ATTR_WDEV, bss->wdev_id);
-
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       msg = NULL;
-
-       wpa_printf(MSG_DEBUG, "nl80211: Delete P2P Device %s (0x%llx): %s",
-                  bss->ifname, (long long unsigned int) bss->wdev_id,
-                  strerror(ret));
-
-nla_put_failure:
-       nlmsg_free(msg);
-}
-
-
-static int nl80211_set_p2pdev(struct i802_bss *bss, int start)
-{
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct nl_msg *msg;
-       int ret = -1;
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -1;
-
-       if (start)
-               nl80211_cmd(drv, msg, 0, NL80211_CMD_START_P2P_DEVICE);
-       else
-               nl80211_cmd(drv, msg, 0, NL80211_CMD_STOP_P2P_DEVICE);
-
-       NLA_PUT_U64(msg, NL80211_ATTR_WDEV, bss->wdev_id);
-
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       msg = NULL;
-
-       wpa_printf(MSG_DEBUG, "nl80211: %s P2P Device %s (0x%llx): %s",
-                  start ? "Start" : "Stop",
-                  bss->ifname, (long long unsigned int) bss->wdev_id,
-                  strerror(ret));
-
-nla_put_failure:
-       nlmsg_free(msg);
-       return ret;
-}
-
-
-static int i802_set_iface_flags(struct i802_bss *bss, int up)
-{
-       enum nl80211_iftype nlmode;
-
-       nlmode = nl80211_get_ifmode(bss);
-       if (nlmode != NL80211_IFTYPE_P2P_DEVICE) {
-               return linux_set_iface_flags(bss->drv->global->ioctl_sock,
-                                            bss->ifname, up);
-       }
-
-       /* P2P Device has start/stop which is equivalent */
-       return nl80211_set_p2pdev(bss, up);
-}
-
-
-static int
-wpa_driver_nl80211_finish_drv_init(struct wpa_driver_nl80211_data *drv)
-{
-#ifndef HOSTAPD
-       enum nl80211_iftype nlmode = NL80211_IFTYPE_STATION;
-#endif /* HOSTAPD */
-       struct i802_bss *bss = &drv->first_bss;
-       int send_rfkill_event = 0;
-       int dynamic_if;
-
-       drv->ifindex = if_nametoindex(bss->ifname);
-       bss->ifindex = drv->ifindex;
-       bss->wdev_id = drv->global->if_add_wdevid;
-       bss->wdev_id_set = drv->global->if_add_wdevid_set;
-
-       dynamic_if = drv->ifindex == drv->global->if_add_ifindex;
-       dynamic_if = dynamic_if || drv->global->if_add_wdevid_set;
-       drv->global->if_add_wdevid_set = 0;
-
-       if (wpa_driver_nl80211_capa(drv))
-               return -1;
-
-       wpa_printf(MSG_DEBUG, "nl80211: interface %s in phy %s",
-                  bss->ifname, drv->phyname);
-
-#ifndef HOSTAPD
-       if (dynamic_if)
-               nlmode = nl80211_get_ifmode(bss);
-
-       /*
-        * Make sure the interface starts up in station mode unless this is a
-        * dynamically added interface (e.g., P2P) that was already configured
-        * with proper iftype.
-        */
-       if (wpa_driver_nl80211_set_mode(bss, nlmode) < 0) {
-               wpa_printf(MSG_ERROR, "nl80211: Could not configure driver to use managed mode");
-               return -1;
-       }
-       drv->nlmode = nlmode;
-
-       if (nlmode == NL80211_IFTYPE_P2P_DEVICE) {
-               int ret = nl80211_set_p2pdev(bss, 1);
-               if (ret < 0)
-                       wpa_printf(MSG_ERROR, "nl80211: Could not start P2P device");
-               nl80211_get_macaddr(bss);
-               return ret;
-       }
-
-       if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1)) {
-               if (rfkill_is_blocked(drv->rfkill)) {
-                       wpa_printf(MSG_DEBUG, "nl80211: Could not yet enable "
-                                  "interface '%s' due to rfkill",
-                                  bss->ifname);
-                       drv->if_disabled = 1;
-                       send_rfkill_event = 1;
-               } else {
-                       wpa_printf(MSG_ERROR, "nl80211: Could not set "
-                                  "interface '%s' UP", bss->ifname);
-                       return -1;
-               }
-       }
-
-       netlink_send_oper_ifla(drv->global->netlink, drv->ifindex,
-                              1, IF_OPER_DORMANT);
-#endif /* HOSTAPD */
-
-       if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
-                              bss->addr))
-               return -1;
-
-#ifdef TIZEN_EXT_P2P
-#ifdef BCM_DRIVER_V115
-       int wpa_driver_nl80211_priv_cmd_bcm(void *priv, char *cmd, char *buf,
-                       size_t buf_len );
-       char buf[64];
-       wpa_driver_nl80211_priv_cmd_bcm(bss,"P2P_DEV_ADDR",buf, sizeof(buf));
-       os_memcpy(bss->dev_addr, buf, ETH_ALEN);
-#endif /* BCM_DRIVER_V115 */
-#endif /* TIZEN_EXT_P2P */
-       if (send_rfkill_event) {
-               eloop_register_timeout(0, 0, wpa_driver_nl80211_send_rfkill,
-                                      drv, drv->ctx);
-       }
-
-       return 0;
-}
-
-
-static int wpa_driver_nl80211_del_beacon(struct wpa_driver_nl80211_data *drv)
-{
-       struct nl_msg *msg;
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
-
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_BEACON);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-
-       return send_and_recv_msgs(drv, msg, NULL, NULL);
- nla_put_failure:
-       nlmsg_free(msg);
-       return -ENOBUFS;
-}
-
-
-/**
- * wpa_driver_nl80211_deinit - Deinitialize nl80211 driver interface
- * @bss: Pointer to private nl80211 data from wpa_driver_nl80211_init()
- *
- * Shut down driver interface and processing of driver events. Free
- * private data buffer if one was allocated in wpa_driver_nl80211_init().
- */
-static void wpa_driver_nl80211_deinit(struct i802_bss *bss)
-{
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-
-       bss->in_deinit = 1;
-       if (drv->data_tx_status)
-               eloop_unregister_read_sock(drv->eapol_tx_sock);
-       if (drv->eapol_tx_sock >= 0)
-               close(drv->eapol_tx_sock);
-
-       if (bss->nl_preq)
-               wpa_driver_nl80211_probe_req_report(bss, 0);
-       if (bss->added_if_into_bridge) {
-               if (linux_br_del_if(drv->global->ioctl_sock, bss->brname,
-                                   bss->ifname) < 0)
-                       wpa_printf(MSG_INFO, "nl80211: Failed to remove "
-                                  "interface %s from bridge %s: %s",
-                                  bss->ifname, bss->brname, strerror(errno));
-       }
-       if (bss->added_bridge) {
-               if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0)
-                       wpa_printf(MSG_INFO, "nl80211: Failed to remove "
-                                  "bridge %s: %s",
-                                  bss->brname, strerror(errno));
-       }
-
-       nl80211_remove_monitor_interface(drv);
-
-       if (is_ap_interface(drv->nlmode))
-               wpa_driver_nl80211_del_beacon(drv);
-
-#ifdef HOSTAPD
-       if (drv->last_freq_ht) {
-               /* Clear HT flags from the driver */
-               struct hostapd_freq_params freq;
-               os_memset(&freq, 0, sizeof(freq));
-               freq.freq = drv->last_freq;
-               wpa_driver_nl80211_set_freq(bss, &freq);
-       }
-
-       if (drv->eapol_sock >= 0) {
-               eloop_unregister_read_sock(drv->eapol_sock);
-               close(drv->eapol_sock);
-       }
-
-       if (drv->if_indices != drv->default_if_indices)
-               os_free(drv->if_indices);
-#endif /* HOSTAPD */
-
-       if (drv->disabled_11b_rates)
-               nl80211_disable_11b_rates(drv, drv->ifindex, 0);
-
-       netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, 0,
-                              IF_OPER_UP);
-       rfkill_deinit(drv->rfkill);
-
-       eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
-
-       (void) i802_set_iface_flags(bss, 0);
-       if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE) {
-               wpa_driver_nl80211_set_mode(bss, NL80211_IFTYPE_STATION);
-       } else {
-               nl80211_mgmt_unsubscribe(bss, "deinit");
-               nl80211_del_p2pdev(bss);
-       }
-       nl_cb_put(drv->nl_cb);
-
-       nl80211_destroy_bss(&drv->first_bss);
-
-       os_free(drv->filter_ssids);
-
-       os_free(drv->auth_ie);
-
-       if (drv->in_interface_list)
-               dl_list_del(&drv->list);
-
-       os_free(drv->extended_capa);
-       os_free(drv->extended_capa_mask);
-       os_free(drv);
-}
-
-
-/**
- * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion
- * @eloop_ctx: Driver private data
- * @timeout_ctx: ctx argument given to wpa_driver_nl80211_init()
- *
- * This function can be used as registered timeout when starting a scan to
- * generate a scan completed event if the driver does not report this.
- */
-static void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx)
-{
-       struct wpa_driver_nl80211_data *drv = eloop_ctx;
-       if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) {
-               wpa_driver_nl80211_set_mode(&drv->first_bss,
-                                           drv->ap_scan_as_station);
-               drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
-       }
-       wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
-       wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
-}
-
-
-static struct nl_msg *
-nl80211_scan_common(struct wpa_driver_nl80211_data *drv, u8 cmd,
-                   struct wpa_driver_scan_params *params, u64 *wdev_id)
-{
-       struct nl_msg *msg;
-       size_t i;
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return NULL;
-
-       nl80211_cmd(drv, msg, 0, cmd);
-
-       if (!wdev_id)
-               NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-       else
-               NLA_PUT_U64(msg, NL80211_ATTR_WDEV, *wdev_id);
-
-       if (params->num_ssids) {
-               struct nlattr *ssids;
-
-               ssids = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
-               if (ssids == NULL)
-                       goto fail;
-               for (i = 0; i < params->num_ssids; i++) {
-                       wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID",
-                                         params->ssids[i].ssid,
-                                         params->ssids[i].ssid_len);
-                       if (nla_put(msg, i + 1, params->ssids[i].ssid_len,
-                                   params->ssids[i].ssid) < 0)
-                               goto fail;
-               }
-               nla_nest_end(msg, ssids);
-       }
-
-       if (params->extra_ies) {
-               wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
-                           params->extra_ies, params->extra_ies_len);
-               if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len,
-                           params->extra_ies) < 0)
-                       goto fail;
-       }
-
-       if (params->freqs) {
-               struct nlattr *freqs;
-               freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
-               if (freqs == NULL)
-                       goto fail;
-               for (i = 0; params->freqs[i]; i++) {
-                       wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u "
-                                  "MHz", params->freqs[i]);
-                       if (nla_put_u32(msg, i + 1, params->freqs[i]) < 0)
-                               goto fail;
-               }
-               nla_nest_end(msg, freqs);
-       }
-
-       os_free(drv->filter_ssids);
-       drv->filter_ssids = params->filter_ssids;
-       params->filter_ssids = NULL;
-       drv->num_filter_ssids = params->num_filter_ssids;
-
-       return msg;
-
-fail:
-nla_put_failure:
-       nlmsg_free(msg);
-       return NULL;
-}
-
-
-/**
- * wpa_driver_nl80211_scan - Request the driver to initiate scan
- * @bss: Pointer to private driver data from wpa_driver_nl80211_init()
- * @params: Scan parameters
- * Returns: 0 on success, -1 on failure
- */
-static int wpa_driver_nl80211_scan(struct i802_bss *bss,
-                                  struct wpa_driver_scan_params *params)
-{
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       int ret = -1, timeout;
-       struct nl_msg *msg = NULL;
-
-       wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request");
-       drv->scan_for_auth = 0;
-
-       msg = nl80211_scan_common(drv, NL80211_CMD_TRIGGER_SCAN, params,
-                                 bss->wdev_id_set ? &bss->wdev_id : NULL);
-       if (!msg)
-               return -1;
-
-       if (params->p2p_probe) {
-               struct nlattr *rates;
-
-               wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates");
-
-               rates = nla_nest_start(msg, NL80211_ATTR_SCAN_SUPP_RATES);
-               if (rates == NULL)
-                       goto nla_put_failure;
-
-               /*
-                * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates
-                * by masking out everything else apart from the OFDM rates 6,
-                * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz
-                * rates are left enabled.
-                */
-               NLA_PUT(msg, NL80211_BAND_2GHZ, 8,
-                       "\x0c\x12\x18\x24\x30\x48\x60\x6c");
-               nla_nest_end(msg, rates);
-
-               NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE);
-       }
-
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       msg = NULL;
-       if (ret) {
-               wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
-                          "(%s)", ret, strerror(-ret));
-#ifdef HOSTAPD
-               if (is_ap_interface(drv->nlmode)) {
-                       /*
-                        * mac80211 does not allow scan requests in AP mode, so
-                        * try to do this in station mode.
-                        */
-                       if (wpa_driver_nl80211_set_mode(
-                                   bss, NL80211_IFTYPE_STATION))
-                               goto nla_put_failure;
-
-                       if (wpa_driver_nl80211_scan(bss, params)) {
-                               wpa_driver_nl80211_set_mode(bss, drv->nlmode);
-                               goto nla_put_failure;
-                       }
-
-                       /* Restore AP mode when processing scan results */
-                       drv->ap_scan_as_station = drv->nlmode;
-                       ret = 0;
-               } else
-                       goto nla_put_failure;
-#else /* HOSTAPD */
-               goto nla_put_failure;
-#endif /* HOSTAPD */
-       }
-
-       /* Not all drivers generate "scan completed" wireless event, so try to
-        * read results after a timeout. */
-       timeout = 10;
-       if (drv->scan_complete_events) {
-               /*
-                * The driver seems to deliver events to notify when scan is
-                * complete, so use longer timeout to avoid race conditions
-                * with scanning and following association request.
-                */
-               timeout = 30;
-       }
-       wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d "
-                  "seconds", ret, timeout);
-       eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
-       eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout,
-                              drv, drv->ctx);
-
-nla_put_failure:
-       nlmsg_free(msg);
-       return ret;
-}
-
-
-/**
- * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan
- * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
- * @params: Scan parameters
- * @interval: Interval between scan cycles in milliseconds
- * Returns: 0 on success, -1 on failure or if not supported
- */
-static int wpa_driver_nl80211_sched_scan(void *priv,
-                                        struct wpa_driver_scan_params *params,
-                                        u32 interval)
-{
-       struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       int ret = -1;
-       struct nl_msg *msg;
-       size_t i;
-
-       wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: sched_scan request");
-
-#ifdef ANDROID
-       if (!drv->capa.sched_scan_supported)
-               return android_pno_start(bss, params);
-#endif /* ANDROID */
-
-       msg = nl80211_scan_common(drv, NL80211_CMD_START_SCHED_SCAN, params,
-                                 bss->wdev_id_set ? &bss->wdev_id : NULL);
-       if (!msg)
-               goto nla_put_failure;
-
-       NLA_PUT_U32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval);
-
-       if ((drv->num_filter_ssids &&
-           (int) drv->num_filter_ssids <= drv->capa.max_match_sets) ||
-           params->filter_rssi) {
-               struct nlattr *match_sets;
-               match_sets = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH);
-               if (match_sets == NULL)
-                       goto nla_put_failure;
-
-               for (i = 0; i < drv->num_filter_ssids; i++) {
-                       struct nlattr *match_set_ssid;
-                       wpa_hexdump_ascii(MSG_MSGDUMP,
-                                         "nl80211: Sched scan filter SSID",
-                                         drv->filter_ssids[i].ssid,
-                                         drv->filter_ssids[i].ssid_len);
-
-                       match_set_ssid = nla_nest_start(msg, i + 1);
-                       if (match_set_ssid == NULL)
-                               goto nla_put_failure;
-                       NLA_PUT(msg, NL80211_ATTR_SCHED_SCAN_MATCH_SSID,
-                               drv->filter_ssids[i].ssid_len,
-                               drv->filter_ssids[i].ssid);
-
-                       nla_nest_end(msg, match_set_ssid);
-               }
-
-               if (params->filter_rssi) {
-                       struct nlattr *match_set_rssi;
-                       match_set_rssi = nla_nest_start(msg, 0);
-                       if (match_set_rssi == NULL)
-                               goto nla_put_failure;
-                       NLA_PUT_U32(msg, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
-                                   params->filter_rssi);
-                       wpa_printf(MSG_MSGDUMP,
-                                  "nl80211: Sched scan RSSI filter %d dBm",
-                                  params->filter_rssi);
-                       nla_nest_end(msg, match_set_rssi);
-               }
-
-               nla_nest_end(msg, match_sets);
-       }
-
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-
-       /* TODO: if we get an error here, we should fall back to normal scan */
-
-       msg = NULL;
-       if (ret) {
-               wpa_printf(MSG_DEBUG, "nl80211: Sched scan start failed: "
-                          "ret=%d (%s)", ret, strerror(-ret));
-               goto nla_put_failure;
-       }
-
-       wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - "
-                  "scan interval %d msec", ret, interval);
-
-nla_put_failure:
-       nlmsg_free(msg);
-       return ret;
-}
-
-
-/**
- * wpa_driver_nl80211_stop_sched_scan - Stop a scheduled scan
- * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
- * Returns: 0 on success, -1 on failure or if not supported
- */
-static int wpa_driver_nl80211_stop_sched_scan(void *priv)
-{
-       struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       int ret = 0;
-       struct nl_msg *msg;
-
-#ifdef ANDROID
-       if (!drv->capa.sched_scan_supported)
-               return android_pno_stop(bss);
-#endif /* ANDROID */
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -1;
-
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_STOP_SCHED_SCAN);
-
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       msg = NULL;
-       if (ret) {
-               wpa_printf(MSG_DEBUG, "nl80211: Sched scan stop failed: "
-                          "ret=%d (%s)", ret, strerror(-ret));
-               goto nla_put_failure;
-       }
-
-       wpa_printf(MSG_DEBUG, "nl80211: Sched scan stop sent (ret=%d)", ret);
-
-nla_put_failure:
-       nlmsg_free(msg);
-       return ret;
-}
-
-
-static const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie)
-{
-       const u8 *end, *pos;
-
-       if (ies == NULL)
-               return NULL;
-
-       pos = ies;
-       end = ies + ies_len;
-
-       while (pos + 1 < end) {
-               if (pos + 2 + pos[1] > end)
-                       break;
-               if (pos[0] == ie)
-                       return pos;
-               pos += 2 + pos[1];
-       }
-
-       return NULL;
-}
-
-
-static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv,
-                                const u8 *ie, size_t ie_len)
-{
-       const u8 *ssid;
-       size_t i;
-
-       if (drv->filter_ssids == NULL)
-               return 0;
-
-       ssid = nl80211_get_ie(ie, ie_len, WLAN_EID_SSID);
-       if (ssid == NULL)
-               return 1;
-
-       for (i = 0; i < drv->num_filter_ssids; i++) {
-               if (ssid[1] == drv->filter_ssids[i].ssid_len &&
-                   os_memcmp(ssid + 2, drv->filter_ssids[i].ssid, ssid[1]) ==
-                   0)
-                       return 0;
-       }
-
-       return 1;
-}
-
-
-static int bss_info_handler(struct nl_msg *msg, void *arg)
-{
-       struct nlattr *tb[NL80211_ATTR_MAX + 1];
-       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-       struct nlattr *bss[NL80211_BSS_MAX + 1];
-       static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
-               [NL80211_BSS_BSSID] = { .type = NLA_UNSPEC },
-               [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
-               [NL80211_BSS_TSF] = { .type = NLA_U64 },
-               [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
-               [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
-               [NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
-               [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
-               [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
-               [NL80211_BSS_STATUS] = { .type = NLA_U32 },
-               [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
-               [NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC },
-       };
-       struct nl80211_bss_info_arg *_arg = arg;
-       struct wpa_scan_results *res = _arg->res;
-       struct wpa_scan_res **tmp;
-       struct wpa_scan_res *r;
-       const u8 *ie, *beacon_ie;
-       size_t ie_len, beacon_ie_len;
-       u8 *pos;
-       size_t i;
-
-       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-                 genlmsg_attrlen(gnlh, 0), NULL);
-       if (!tb[NL80211_ATTR_BSS])
-               return NL_SKIP;
-       if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
-                            bss_policy))
-               return NL_SKIP;
-       if (bss[NL80211_BSS_STATUS]) {
-               enum nl80211_bss_status status;
-               status = nla_get_u32(bss[NL80211_BSS_STATUS]);
-               if (status == NL80211_BSS_STATUS_ASSOCIATED &&
-                   bss[NL80211_BSS_FREQUENCY]) {
-                       _arg->assoc_freq =
-                               nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
-                       wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
-                                  _arg->assoc_freq);
-               }
-               if (status == NL80211_BSS_STATUS_ASSOCIATED &&
-                   bss[NL80211_BSS_BSSID]) {
-                       os_memcpy(_arg->assoc_bssid,
-                                 nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN);
-                       wpa_printf(MSG_DEBUG, "nl80211: Associated with "
-                                  MACSTR, MAC2STR(_arg->assoc_bssid));
-               }
-       }
-       if (!res)
-               return NL_SKIP;
-       if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
-               ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
-               ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
-       } else {
-               ie = NULL;
-               ie_len = 0;
-       }
-       if (bss[NL80211_BSS_BEACON_IES]) {
-               beacon_ie = nla_data(bss[NL80211_BSS_BEACON_IES]);
-               beacon_ie_len = nla_len(bss[NL80211_BSS_BEACON_IES]);
-       } else {
-               beacon_ie = NULL;
-               beacon_ie_len = 0;
-       }
-
-       if (nl80211_scan_filtered(_arg->drv, ie ? ie : beacon_ie,
-                                 ie ? ie_len : beacon_ie_len))
-               return NL_SKIP;
-
-       r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len);
-       if (r == NULL)
-               return NL_SKIP;
-       if (bss[NL80211_BSS_BSSID])
-               os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]),
-                         ETH_ALEN);
-       if (bss[NL80211_BSS_FREQUENCY])
-               r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
-       if (bss[NL80211_BSS_BEACON_INTERVAL])
-               r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]);
-       if (bss[NL80211_BSS_CAPABILITY])
-               r->caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
-       r->flags |= WPA_SCAN_NOISE_INVALID;
-       if (bss[NL80211_BSS_SIGNAL_MBM]) {
-               r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
-               r->level /= 100; /* mBm to dBm */
-               r->flags |= WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID;
-       } else if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
-               r->level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
-               r->flags |= WPA_SCAN_QUAL_INVALID;
-       } else
-               r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID;
-       if (bss[NL80211_BSS_TSF])
-               r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]);
-       if (bss[NL80211_BSS_SEEN_MS_AGO])
-               r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
-       r->ie_len = ie_len;
-       pos = (u8 *) (r + 1);
-       if (ie) {
-               os_memcpy(pos, ie, ie_len);
-               pos += ie_len;
-       }
-       r->beacon_ie_len = beacon_ie_len;
-       if (beacon_ie)
-               os_memcpy(pos, beacon_ie, beacon_ie_len);
-
-       if (bss[NL80211_BSS_STATUS]) {
-               enum nl80211_bss_status status;
-               status = nla_get_u32(bss[NL80211_BSS_STATUS]);
-               switch (status) {
-               case NL80211_BSS_STATUS_AUTHENTICATED:
-                       r->flags |= WPA_SCAN_AUTHENTICATED;
-                       break;
-               case NL80211_BSS_STATUS_ASSOCIATED:
-                       r->flags |= WPA_SCAN_ASSOCIATED;
-                       break;
-               default:
-                       break;
-               }
-       }
-
-       /*
-        * cfg80211 maintains separate BSS table entries for APs if the same
-        * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does
-        * not use frequency as a separate key in the BSS table, so filter out
-        * duplicated entries. Prefer associated BSS entry in such a case in
-        * order to get the correct frequency into the BSS table.
-        */
-       for (i = 0; i < res->num; i++) {
-               const u8 *s1, *s2;
-               if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0)
-                       continue;
-
-               s1 = nl80211_get_ie((u8 *) (res->res[i] + 1),
-                                   res->res[i]->ie_len, WLAN_EID_SSID);
-               s2 = nl80211_get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID);
-               if (s1 == NULL || s2 == NULL || s1[1] != s2[1] ||
-                   os_memcmp(s1, s2, 2 + s1[1]) != 0)
-                       continue;
-
-               /* Same BSSID,SSID was already included in scan results */
-               wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result "
-                          "for " MACSTR, MAC2STR(r->bssid));
-
-               if ((r->flags & WPA_SCAN_ASSOCIATED) &&
-                   !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) {
-                       os_free(res->res[i]);
-                       res->res[i] = r;
-               } else
-                       os_free(r);
-               return NL_SKIP;
-       }
-
-       tmp = os_realloc_array(res->res, res->num + 1,
-                              sizeof(struct wpa_scan_res *));
-       if (tmp == NULL) {
-               os_free(r);
-               return NL_SKIP;
-       }
-       tmp[res->num++] = r;
-       res->res = tmp;
-
-       return NL_SKIP;
-}
-
-
-static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv,
-                                const u8 *addr)
-{
-       if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
-               wpa_printf(MSG_DEBUG, "nl80211: Clear possible state "
-                          "mismatch (" MACSTR ")", MAC2STR(addr));
-               wpa_driver_nl80211_mlme(drv, addr,
-                                       NL80211_CMD_DEAUTHENTICATE,
-                                       WLAN_REASON_PREV_AUTH_NOT_VALID, 1);
-       }
-}
-
-
-static void wpa_driver_nl80211_check_bss_status(
-       struct wpa_driver_nl80211_data *drv, struct wpa_scan_results *res)
-{
-       size_t i;
-
-       for (i = 0; i < res->num; i++) {
-               struct wpa_scan_res *r = res->res[i];
-               if (r->flags & WPA_SCAN_AUTHENTICATED) {
-                       wpa_printf(MSG_DEBUG, "nl80211: Scan results "
-                                  "indicates BSS status with " MACSTR
-                                  " as authenticated",
-                                  MAC2STR(r->bssid));
-                       if (is_sta_interface(drv->nlmode) &&
-                           os_memcmp(r->bssid, drv->bssid, ETH_ALEN) != 0 &&
-                           os_memcmp(r->bssid, drv->auth_bssid, ETH_ALEN) !=
-                           0) {
-                               wpa_printf(MSG_DEBUG, "nl80211: Unknown BSSID"
-                                          " in local state (auth=" MACSTR
-                                          " assoc=" MACSTR ")",
-                                          MAC2STR(drv->auth_bssid),
-                                          MAC2STR(drv->bssid));
-                               clear_state_mismatch(drv, r->bssid);
-                       }
-               }
-
-               if (r->flags & WPA_SCAN_ASSOCIATED) {
-                       wpa_printf(MSG_DEBUG, "nl80211: Scan results "
-                                  "indicate BSS status with " MACSTR
-                                  " as associated",
-                                  MAC2STR(r->bssid));
-                       if (is_sta_interface(drv->nlmode) &&
-                           !drv->associated) {
-                               wpa_printf(MSG_DEBUG, "nl80211: Local state "
-                                          "(not associated) does not match "
-                                          "with BSS state");
-                               clear_state_mismatch(drv, r->bssid);
-                       } else if (is_sta_interface(drv->nlmode) &&
-                                  os_memcmp(drv->bssid, r->bssid, ETH_ALEN) !=
-                                  0) {
-                               wpa_printf(MSG_DEBUG, "nl80211: Local state "
-                                          "(associated with " MACSTR ") does "
-                                          "not match with BSS state",
-                                          MAC2STR(drv->bssid));
-                               clear_state_mismatch(drv, r->bssid);
-                               clear_state_mismatch(drv, drv->bssid);
-                       }
-               }
-       }
-}
-
-
-static struct wpa_scan_results *
-nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
-{
-       struct nl_msg *msg;
-       struct wpa_scan_results *res;
-       int ret;
-       struct nl80211_bss_info_arg arg;
-
-       res = os_zalloc(sizeof(*res));
-       if (res == NULL)
-               return NULL;
-       msg = nlmsg_alloc();
-       if (!msg)
-               goto nla_put_failure;
-
-       nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_SCAN);
-       if (nl80211_set_iface_id(msg, &drv->first_bss) < 0)
-               goto nla_put_failure;
-
-       arg.drv = drv;
-       arg.res = res;
-       ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
-       msg = NULL;
-       if (ret == 0) {
-               wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu "
-                          "BSSes)", (unsigned long) res->num);
-               nl80211_get_noise_for_scan_results(drv, res);
-               return res;
-       }
-       wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
-                  "(%s)", ret, strerror(-ret));
-nla_put_failure:
-       nlmsg_free(msg);
-       wpa_scan_results_free(res);
-       return NULL;
-}
-
-
-/**
- * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results
- * @priv: Pointer to private wext data from wpa_driver_nl80211_init()
- * Returns: Scan results on success, -1 on failure
- */
-static struct wpa_scan_results *
-wpa_driver_nl80211_get_scan_results(void *priv)
-{
-       struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct wpa_scan_results *res;
-
-       res = nl80211_get_scan_results(drv);
-       if (res)
-               wpa_driver_nl80211_check_bss_status(drv, res);
-       return res;
-}
-
-
-static void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv)
-{
-       struct wpa_scan_results *res;
-       size_t i;
-
-       res = nl80211_get_scan_results(drv);
-       if (res == NULL) {
-               wpa_printf(MSG_DEBUG, "nl80211: Failed to get scan results");
-               return;
-       }
-
-       wpa_printf(MSG_DEBUG, "nl80211: Scan result dump");
-       for (i = 0; i < res->num; i++) {
-               struct wpa_scan_res *r = res->res[i];
-               wpa_printf(MSG_DEBUG, "nl80211: %d/%d " MACSTR "%s%s",
-                          (int) i, (int) res->num, MAC2STR(r->bssid),
-                          r->flags & WPA_SCAN_AUTHENTICATED ? " [auth]" : "",
-                          r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : "");
-       }
-
-       wpa_scan_results_free(res);
-}
-
-
-static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
-                                     enum wpa_alg alg, const u8 *addr,
-                                     int key_idx, int set_tx,
-                                     const u8 *seq, size_t seq_len,
-                                     const u8 *key, size_t key_len)
-{
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       int ifindex;
-       struct nl_msg *msg;
-       int ret;
-
-       /* Ignore for P2P Device */
-       if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
-               return 0;
-
-       ifindex = if_nametoindex(ifname);
-       wpa_printf(MSG_DEBUG, "%s: ifindex=%d (%s) alg=%d addr=%p key_idx=%d "
-                  "set_tx=%d seq_len=%lu key_len=%lu",
-                  __func__, ifindex, ifname, alg, addr, key_idx, set_tx,
-                  (unsigned long) seq_len, (unsigned long) key_len);
-#ifdef CONFIG_TDLS
-       if (key_idx == -1)
-               key_idx = 0;
-#endif /* CONFIG_TDLS */
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
-
-       if (alg == WPA_ALG_NONE) {
-               nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_KEY);
-       } else {
-               nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_KEY);
-               NLA_PUT(msg, NL80211_ATTR_KEY_DATA, key_len, key);
-               switch (alg) {
-               case WPA_ALG_WEP:
-                       if (key_len == 5)
-                               NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
-                                           WLAN_CIPHER_SUITE_WEP40);
-                       else
-                               NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
-                                           WLAN_CIPHER_SUITE_WEP104);
-                       break;
-               case WPA_ALG_TKIP:
-                       NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
-                                   WLAN_CIPHER_SUITE_TKIP);
-                       break;
-               case WPA_ALG_CCMP:
-                       NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
-                                   WLAN_CIPHER_SUITE_CCMP);
-                       break;
-               case WPA_ALG_GCMP:
-                       NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
-                                   WLAN_CIPHER_SUITE_GCMP);
-                       break;
-               case WPA_ALG_IGTK:
-                       NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
-                                   WLAN_CIPHER_SUITE_AES_CMAC);
-                       break;
-               case WPA_ALG_SMS4:
-                       NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
-                                   WLAN_CIPHER_SUITE_SMS4);
-                       break;
-               case WPA_ALG_KRK:
-                       NLA_PUT_U32(msg, NL80211_ATTR_KEY_CIPHER,
-                                   WLAN_CIPHER_SUITE_KRK);
-                       break;
-               default:
-                       wpa_printf(MSG_ERROR, "%s: Unsupported encryption "
-                                  "algorithm %d", __func__, alg);
-                       nlmsg_free(msg);
-                       return -1;
-               }
-       }
-
-       if (seq && seq_len)
-               NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, seq_len, seq);
-
-       if (addr && !is_broadcast_ether_addr(addr)) {
-               wpa_printf(MSG_DEBUG, "   addr=" MACSTR, MAC2STR(addr));
-               NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
-
-               if (alg != WPA_ALG_WEP && key_idx && !set_tx) {
-                       wpa_printf(MSG_DEBUG, "   RSN IBSS RX GTK");
-                       NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE,
-                                   NL80211_KEYTYPE_GROUP);
-               }
-       } else if (addr && is_broadcast_ether_addr(addr)) {
-               struct nlattr *types;
-
-               wpa_printf(MSG_DEBUG, "   broadcast key");
-
-               types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES);
-               if (!types)
-                       goto nla_put_failure;
-               NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST);
-               nla_nest_end(msg, types);
-       }
-       NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
-
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       if ((ret == -ENOENT || ret == -ENOLINK) && alg == WPA_ALG_NONE)
-               ret = 0;
-       if (ret)
-               wpa_printf(MSG_DEBUG, "nl80211: set_key failed; err=%d %s)",
-                          ret, strerror(-ret));
-
-       /*
-        * If we failed or don't need to set the default TX key (below),
-        * we're done here.
-        */
-       if (ret || !set_tx || alg == WPA_ALG_NONE)
-               return ret;
-       if (is_ap_interface(drv->nlmode) && addr &&
-           !is_broadcast_ether_addr(addr))
-               return ret;
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
-
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_KEY);
-       NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
-       if (alg == WPA_ALG_IGTK)
-               NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT_MGMT);
-       else
-               NLA_PUT_FLAG(msg, NL80211_ATTR_KEY_DEFAULT);
-       if (addr && is_broadcast_ether_addr(addr)) {
-               struct nlattr *types;
-
-               types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES);
-               if (!types)
-                       goto nla_put_failure;
-               NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_TYPE_MULTICAST);
-               nla_nest_end(msg, types);
-       } else if (addr) {
-               struct nlattr *types;
-
-               types = nla_nest_start(msg, NL80211_ATTR_KEY_DEFAULT_TYPES);
-               if (!types)
-                       goto nla_put_failure;
-               NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_TYPE_UNICAST);
-               nla_nest_end(msg, types);
-       }
-
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       if (ret == -ENOENT)
-               ret = 0;
-       if (ret)
-               wpa_printf(MSG_DEBUG, "nl80211: set_key default failed; "
-                          "err=%d %s)", ret, strerror(-ret));
-       return ret;
-
-nla_put_failure:
-       nlmsg_free(msg);
-       return -ENOBUFS;
-}
-
-
-static int nl_add_key(struct nl_msg *msg, enum wpa_alg alg,
-                     int key_idx, int defkey,
-                     const u8 *seq, size_t seq_len,
-                     const u8 *key, size_t key_len)
-{
-       struct nlattr *key_attr = nla_nest_start(msg, NL80211_ATTR_KEY);
-       if (!key_attr)
-               return -1;
-
-       if (defkey && alg == WPA_ALG_IGTK)
-               NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT_MGMT);
-       else if (defkey)
-               NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT);
-
-       NLA_PUT_U8(msg, NL80211_KEY_IDX, key_idx);
-
-       switch (alg) {
-       case WPA_ALG_WEP:
-               if (key_len == 5)
-                       NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
-                                   WLAN_CIPHER_SUITE_WEP40);
-               else
-                       NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
-                                   WLAN_CIPHER_SUITE_WEP104);
-               break;
-       case WPA_ALG_TKIP:
-               NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_TKIP);
-               break;
-       case WPA_ALG_CCMP:
-               NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_CCMP);
-               break;
-       case WPA_ALG_GCMP:
-               NLA_PUT_U32(msg, NL80211_KEY_CIPHER, WLAN_CIPHER_SUITE_GCMP);
-               break;
-       case WPA_ALG_IGTK:
-               NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
-                           WLAN_CIPHER_SUITE_AES_CMAC);
-               break;
-       default:
-               wpa_printf(MSG_ERROR, "%s: Unsupported encryption "
-                          "algorithm %d", __func__, alg);
-               return -1;
-       }
-
-       if (seq && seq_len)
-               NLA_PUT(msg, NL80211_KEY_SEQ, seq_len, seq);
-
-       NLA_PUT(msg, NL80211_KEY_DATA, key_len, key);
-
-       nla_nest_end(msg, key_attr);
-
-       return 0;
- nla_put_failure:
-       return -1;
-}
-
-
-static int nl80211_set_conn_keys(struct wpa_driver_associate_params *params,
-                                struct nl_msg *msg)
-{
-       int i, privacy = 0;
-       struct nlattr *nl_keys, *nl_key;
-
-       for (i = 0; i < 4; i++) {
-               if (!params->wep_key[i])
-                       continue;
-               privacy = 1;
-               break;
-       }
-       if (params->wps == WPS_MODE_PRIVACY)
-               privacy = 1;
-       if (params->pairwise_suite &&
-           params->pairwise_suite != WPA_CIPHER_NONE)
-               privacy = 1;
-
-       if (!privacy)
-               return 0;
-
-       NLA_PUT_FLAG(msg, NL80211_ATTR_PRIVACY);
-
-       nl_keys = nla_nest_start(msg, NL80211_ATTR_KEYS);
-       if (!nl_keys)
-               goto nla_put_failure;
-
-       for (i = 0; i < 4; i++) {
-               if (!params->wep_key[i])
-                       continue;
-
-               nl_key = nla_nest_start(msg, i);
-               if (!nl_key)
-                       goto nla_put_failure;
-
-               NLA_PUT(msg, NL80211_KEY_DATA, params->wep_key_len[i],
-                       params->wep_key[i]);
-               if (params->wep_key_len[i] == 5)
-                       NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
-                                   WLAN_CIPHER_SUITE_WEP40);
-               else
-                       NLA_PUT_U32(msg, NL80211_KEY_CIPHER,
-                                   WLAN_CIPHER_SUITE_WEP104);
-
-               NLA_PUT_U8(msg, NL80211_KEY_IDX, i);
-
-               if (i == params->wep_tx_keyidx)
-                       NLA_PUT_FLAG(msg, NL80211_KEY_DEFAULT);
-
-               nla_nest_end(msg, nl_key);
-       }
-       nla_nest_end(msg, nl_keys);
-
-       return 0;
-
-nla_put_failure:
-       return -ENOBUFS;
-}
-
-
-static int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
-                                  const u8 *addr, int cmd, u16 reason_code,
-                                  int local_state_change)
-{
-       int ret = -1;
-       struct nl_msg *msg;
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -1;
-
-       nl80211_cmd(drv, msg, 0, cmd);
-
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-       NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason_code);
-       if (addr)
-               NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
-       if (local_state_change)
-               NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE);
-
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       msg = NULL;
-       if (ret) {
-               wpa_dbg(drv->ctx, MSG_DEBUG,
-                       "nl80211: MLME command failed: reason=%u ret=%d (%s)",
-                       reason_code, ret, strerror(-ret));
-               goto nla_put_failure;
-       }
-       ret = 0;
-
-nla_put_failure:
-       nlmsg_free(msg);
-       return ret;
-}
-
-
-static int wpa_driver_nl80211_disconnect(struct wpa_driver_nl80211_data *drv,
-                                        int reason_code)
-{
-       wpa_printf(MSG_DEBUG, "%s(reason_code=%d)", __func__, reason_code);
-       nl80211_mark_disconnected(drv);
-       drv->ignore_next_local_disconnect = 0;
-       /* Disconnect command doesn't need BSSID - it uses cached value */
-       return wpa_driver_nl80211_mlme(drv, NULL, NL80211_CMD_DISCONNECT,
-                                      reason_code, 0);
-}
-
-
-static int wpa_driver_nl80211_deauthenticate(struct i802_bss *bss,
-                                            const u8 *addr, int reason_code)
-{
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME))
-               return wpa_driver_nl80211_disconnect(drv, reason_code);
-       wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " reason_code=%d)",
-                  __func__, MAC2STR(addr), reason_code);
-       nl80211_mark_disconnected(drv);
-       if (drv->nlmode == NL80211_IFTYPE_ADHOC)
-               return nl80211_leave_ibss(drv);
-       return wpa_driver_nl80211_mlme(drv, addr, NL80211_CMD_DEAUTHENTICATE,
-                                      reason_code, 0);
-}
-
-
-static void nl80211_copy_auth_params(struct wpa_driver_nl80211_data *drv,
-                                    struct wpa_driver_auth_params *params)
-{
-       int i;
+static void nl80211_copy_auth_params(struct wpa_driver_nl80211_data *drv,
+                                    struct wpa_driver_auth_params *params)
+{
+       int i;
 
        drv->auth_freq = params->freq;
        drv->auth_alg = params->auth_alg;
@@ -5325,6 +2845,25 @@ static void nl80211_copy_auth_params(struct wpa_driver_nl80211_data *drv,
 }
 
 
+static void nl80211_unmask_11b_rates(struct i802_bss *bss)
+{
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+
+       if (is_p2p_net_interface(drv->nlmode) || !drv->disabled_11b_rates)
+               return;
+
+       /*
+        * Looks like we failed to unmask 11b rates previously. This could
+        * happen, e.g., if the interface was down at the point in time when a
+        * P2P group was terminated.
+        */
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: Interface %s mode is for non-P2P, but 11b rates were disabled - re-enable them",
+                  bss->ifname);
+       nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+}
+
+
 static int wpa_driver_nl80211_authenticate(
        struct i802_bss *bss, struct wpa_driver_auth_params *params)
 {
@@ -5336,8 +2875,11 @@ static int wpa_driver_nl80211_authenticate(
        int count = 0;
        int is_retry;
 
+       nl80211_unmask_11b_rates(bss);
+
        is_retry = drv->retry_auth;
        drv->retry_auth = 0;
+       drv->ignore_deauth_event = 0;
 
        nl80211_mark_disconnected(drv);
        os_memset(drv->auth_bssid, 0, ETH_ALEN);
@@ -5346,7 +2888,7 @@ static int wpa_driver_nl80211_authenticate(
        else
                os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN);
        /* FIX: IBSS mode */
-#ifdef TIZEN_EXT_P2P
+#ifdef BCM_DRIVER_V115
        nlmode = NL80211_IFTYPE_STATION;
 #else
        nlmode = params->p2p ?
@@ -5357,14 +2899,12 @@ static int wpa_driver_nl80211_authenticate(
                return -1;
 
 retry:
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -1;
-
        wpa_printf(MSG_DEBUG, "nl80211: Authenticate (ifindex=%d)",
                   drv->ifindex);
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_AUTHENTICATE);
+       msg = nl80211_drv_msg(drv, 0, NL80211_CMD_AUTHENTICATE);
+       if (!msg)
+               goto fail;
 
        for (i = 0; i < 4; i++) {
                if (!params->wep_key[i])
@@ -5377,36 +2917,38 @@ retry:
                if (params->wep_tx_keyidx != i)
                        continue;
                if (nl_add_key(msg, WPA_ALG_WEP, i, 1, NULL, 0,
-                              params->wep_key[i], params->wep_key_len[i])) {
-                       nlmsg_free(msg);
-                       return -1;
-               }
+                              params->wep_key[i], params->wep_key_len[i]))
+                       goto fail;
        }
 
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
        if (params->bssid) {
                wpa_printf(MSG_DEBUG, "  * bssid=" MACSTR,
                           MAC2STR(params->bssid));
-               NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
+               if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
+                       goto fail;
        }
        if (params->freq) {
                wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
-               NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
+               if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq))
+                       goto fail;
        }
        if (params->ssid) {
                wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
                                  params->ssid, params->ssid_len);
-               NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
-                       params->ssid);
+               if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len,
+                           params->ssid))
+                       goto fail;
        }
        wpa_hexdump(MSG_DEBUG, "  * IEs", params->ie, params->ie_len);
-       if (params->ie)
-               NLA_PUT(msg, NL80211_ATTR_IE, params->ie_len, params->ie);
+       if (params->ie &&
+           nla_put(msg, NL80211_ATTR_IE, params->ie_len, params->ie))
+               goto fail;
        if (params->sae_data) {
                wpa_hexdump(MSG_DEBUG, "  * SAE data", params->sae_data,
                            params->sae_data_len);
-               NLA_PUT(msg, NL80211_ATTR_SAE_DATA, params->sae_data_len,
-                       params->sae_data);
+               if (nla_put(msg, NL80211_ATTR_SAE_DATA, params->sae_data_len,
+                           params->sae_data))
+                       goto fail;
        }
        if (params->auth_alg & WPA_AUTH_ALG_OPEN)
                type = NL80211_AUTHTYPE_OPEN_SYSTEM;
@@ -5419,12 +2961,14 @@ retry:
        else if (params->auth_alg & WPA_AUTH_ALG_SAE)
                type = NL80211_AUTHTYPE_SAE;
        else
-               goto nla_put_failure;
+               goto fail;
        wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
-       NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type);
+       if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+               goto fail;
        if (params->local_state_change) {
                wpa_printf(MSG_DEBUG, "  * Local state change only");
-               NLA_PUT_FLAG(msg, NL80211_ATTR_LOCAL_STATE_CHANGE);
+               if (nla_put_flag(msg, NL80211_ATTR_LOCAL_STATE_CHANGE))
+                       goto fail;
        }
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
@@ -5443,6 +2987,7 @@ retry:
                         */
                        wpa_printf(MSG_DEBUG, "nl80211: Retry authentication "
                                   "after forced deauthentication");
+                       drv->ignore_deauth_event = 1;
                        wpa_driver_nl80211_deauthenticate(
                                bss, params->bssid,
                                WLAN_REASON_PREV_AUTH_NOT_VALID);
@@ -5491,5225 +3036,5658 @@ retry:
                        wpa_supplicant_event(drv->ctx, EVENT_AUTH_TIMED_OUT,
                                             &event);
                }
+       } else {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Authentication request send successfully");
+       }
+
+fail:
+       nlmsg_free(msg);
+       return ret;
+}
+
+
+int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv)
+{
+       struct wpa_driver_auth_params params;
+       struct i802_bss *bss = drv->first_bss;
+       int i;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Try to authenticate again");
+
+       os_memset(&params, 0, sizeof(params));
+       params.freq = drv->auth_freq;
+       params.auth_alg = drv->auth_alg;
+       params.wep_tx_keyidx = drv->auth_wep_tx_keyidx;
+       params.local_state_change = drv->auth_local_state_change;
+       params.p2p = drv->auth_p2p;
+
+       if (!is_zero_ether_addr(drv->auth_bssid_))
+               params.bssid = drv->auth_bssid_;
+
+       if (drv->auth_ssid_len) {
+               params.ssid = drv->auth_ssid;
+               params.ssid_len = drv->auth_ssid_len;
+       }
+
+       params.ie = drv->auth_ie;
+       params.ie_len = drv->auth_ie_len;
+
+       for (i = 0; i < 4; i++) {
+               if (drv->auth_wep_key_len[i]) {
+                       params.wep_key[i] = drv->auth_wep_key[i];
+                       params.wep_key_len[i] = drv->auth_wep_key_len[i];
+               }
+       }
+
+       drv->retry_auth = 1;
+       return wpa_driver_nl80211_authenticate(bss, &params);
+}
+
+
+static int wpa_driver_nl80211_send_frame(struct i802_bss *bss,
+                                        const void *data, size_t len,
+                                        int encrypt, int noack,
+                                        unsigned int freq, int no_cck,
+                                        int offchanok, unsigned int wait_time)
+{
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       u64 cookie;
+       int res;
+
+       if (freq == 0 && drv->nlmode == NL80211_IFTYPE_ADHOC) {
+               freq = nl80211_get_assoc_freq(drv);
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: send_frame - Use assoc_freq=%u for IBSS",
+                          freq);
+       }
+       if (freq == 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: send_frame - Use bss->freq=%u",
+                          bss->freq);
+               freq = bss->freq;
+       }
+
+       if (drv->use_monitor) {
+               wpa_printf(MSG_DEBUG, "nl80211: send_frame(freq=%u bss->freq=%u) -> send_monitor",
+                          freq, bss->freq);
+               return nl80211_send_monitor(drv, data, len, encrypt, noack);
+       }
+
+       wpa_printf(MSG_DEBUG, "nl80211: send_frame -> send_frame_cmd");
+       res = nl80211_send_frame_cmd(bss, freq, wait_time, data, len,
+                                    &cookie, no_cck, noack, offchanok);
+       if (res == 0 && !noack) {
+               const struct ieee80211_mgmt *mgmt;
+               u16 fc;
+
+               mgmt = (const struct ieee80211_mgmt *) data;
+               fc = le_to_host16(mgmt->frame_control);
+               if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+                   WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) {
+                       wpa_printf(MSG_MSGDUMP,
+                                  "nl80211: Update send_action_cookie from 0x%llx to 0x%llx",
+                                  (long long unsigned int)
+                                  drv->send_action_cookie,
+                                  (long long unsigned int) cookie);
+                       drv->send_action_cookie = cookie;
+               }
+       }
+
+       return res;
+}
+
+
+static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data,
+                                       size_t data_len, int noack,
+                                       unsigned int freq, int no_cck,
+                                       int offchanok,
+                                       unsigned int wait_time)
+{
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct ieee80211_mgmt *mgmt;
+       int encrypt = 1;
+       u16 fc;
+
+       mgmt = (struct ieee80211_mgmt *) data;
+       fc = le_to_host16(mgmt->frame_control);
+       wpa_printf(MSG_DEBUG, "nl80211: send_mlme - da= " MACSTR
+                  " noack=%d freq=%u no_cck=%d offchanok=%d wait_time=%u fc=0x%x (%s) nlmode=%d",
+                  MAC2STR(mgmt->da), noack, freq, no_cck, offchanok, wait_time,
+                  fc, fc2str(fc), drv->nlmode);
+
+       if ((is_sta_interface(drv->nlmode) ||
+            drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) &&
+           WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+           WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) {
+               /*
+                * The use of last_mgmt_freq is a bit of a hack,
+                * but it works due to the single-threaded nature
+                * of wpa_supplicant.
+                */
+               if (freq == 0) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Use last_mgmt_freq=%d",
+                                  drv->last_mgmt_freq);
+                       freq = drv->last_mgmt_freq;
+               }
+               return nl80211_send_frame_cmd(bss, freq, 0,
+                                             data, data_len, NULL, 1, noack,
+                                             1);
+       }
+
+       if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) {
+               if (freq == 0) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Use bss->freq=%d",
+                                  bss->freq);
+                       freq = bss->freq;
+               }
+               return nl80211_send_frame_cmd(bss, freq,
+                                             (int) freq == bss->freq ? 0 :
+                                             wait_time,
+                                             data, data_len,
+                                             &drv->send_action_cookie,
+                                             no_cck, noack, offchanok);
+       }
 
-               goto nla_put_failure;
+       if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
+           WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
+               /*
+                * Only one of the authentication frame types is encrypted.
+                * In order for static WEP encryption to work properly (i.e.,
+                * to not encrypt the frame), we need to tell mac80211 about
+                * the frames that must not be encrypted.
+                */
+               u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
+               u16 auth_trans = le_to_host16(mgmt->u.auth.auth_transaction);
+               if (auth_alg != WLAN_AUTH_SHARED_KEY || auth_trans != 3)
+                       encrypt = 0;
        }
-       ret = 0;
-       wpa_printf(MSG_DEBUG, "nl80211: Authentication request send "
-                  "successfully");
 
-nla_put_failure:
-       nlmsg_free(msg);
-       return ret;
+       wpa_printf(MSG_DEBUG, "nl80211: send_mlme -> send_frame");
+       return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt,
+                                            noack, freq, no_cck, offchanok,
+                                            wait_time);
 }
 
 
-static int wpa_driver_nl80211_authenticate_retry(
-       struct wpa_driver_nl80211_data *drv)
+static int nl80211_put_basic_rates(struct nl_msg *msg, const int *basic_rates)
 {
-       struct wpa_driver_auth_params params;
-       struct i802_bss *bss = &drv->first_bss;
+       u8 rates[NL80211_MAX_SUPP_RATES];
+       u8 rates_len = 0;
        int i;
 
-       wpa_printf(MSG_DEBUG, "nl80211: Try to authenticate again");
+       if (!basic_rates)
+               return 0;
 
-       os_memset(&params, 0, sizeof(params));
-       params.freq = drv->auth_freq;
-       params.auth_alg = drv->auth_alg;
-       params.wep_tx_keyidx = drv->auth_wep_tx_keyidx;
-       params.local_state_change = drv->auth_local_state_change;
-       params.p2p = drv->auth_p2p;
+       for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0; i++)
+               rates[rates_len++] = basic_rates[i] / 5;
 
-       if (!is_zero_ether_addr(drv->auth_bssid_))
-               params.bssid = drv->auth_bssid_;
+       return nla_put(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates);
+}
 
-       if (drv->auth_ssid_len) {
-               params.ssid = drv->auth_ssid;
-               params.ssid_len = drv->auth_ssid_len;
-       }
 
-       params.ie = drv->auth_ie;
-       params.ie_len = drv->auth_ie_len;
+static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble,
+                          int slot, int ht_opmode, int ap_isolate,
+                          const int *basic_rates)
+{
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
 
-       for (i = 0; i < 4; i++) {
-               if (drv->auth_wep_key_len[i]) {
-                       params.wep_key[i] = drv->auth_wep_key[i];
-                       params.wep_key_len[i] = drv->auth_wep_key_len[i];
-               }
+       if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_BSS)) ||
+           (cts >= 0 &&
+            nla_put_u8(msg, NL80211_ATTR_BSS_CTS_PROT, cts)) ||
+           (preamble >= 0 &&
+            nla_put_u8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble)) ||
+           (slot >= 0 &&
+            nla_put_u8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot)) ||
+           (ht_opmode >= 0 &&
+            nla_put_u16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode)) ||
+           (ap_isolate >= 0 &&
+            nla_put_u8(msg, NL80211_ATTR_AP_ISOLATE, ap_isolate)) ||
+           nl80211_put_basic_rates(msg, basic_rates)) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
        }
 
-       drv->retry_auth = 1;
-       return wpa_driver_nl80211_authenticate(bss, &params);
+       return send_and_recv_msgs(drv, msg, NULL, NULL);
 }
 
 
-struct phy_info_arg {
-       u16 *num_modes;
-       struct hostapd_hw_modes *modes;
-       int last_mode, last_chan_idx;
-};
-
-static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa,
-                            struct nlattr *ampdu_factor,
-                            struct nlattr *ampdu_density,
-                            struct nlattr *mcs_set)
+static int wpa_driver_nl80211_set_acl(void *priv,
+                                     struct hostapd_acl_params *params)
 {
-       if (capa)
-               mode->ht_capab = nla_get_u16(capa);
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       struct nlattr *acl;
+       unsigned int i;
+       int ret;
+
+       if (!(drv->capa.max_acl_mac_addrs))
+               return -ENOTSUP;
+
+       if (params->num_mac_acl > drv->capa.max_acl_mac_addrs)
+               return -ENOTSUP;
 
-       if (ampdu_factor)
-               mode->a_mpdu_params |= nla_get_u8(ampdu_factor) & 0x03;
+       wpa_printf(MSG_DEBUG, "nl80211: Set %s ACL (num_mac_acl=%u)",
+                  params->acl_policy ? "Accept" : "Deny", params->num_mac_acl);
+
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_MAC_ACL)) ||
+           nla_put_u32(msg, NL80211_ATTR_ACL_POLICY, params->acl_policy ?
+                       NL80211_ACL_POLICY_DENY_UNLESS_LISTED :
+                       NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED) ||
+           (acl = nla_nest_start(msg, NL80211_ATTR_MAC_ADDRS)) == NULL) {
+               nlmsg_free(msg);
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < params->num_mac_acl; i++) {
+               if (nla_put(msg, i + 1, ETH_ALEN, params->mac_acl[i].addr)) {
+                       nlmsg_free(msg);
+                       return -ENOMEM;
+               }
+       }
 
-       if (ampdu_density)
-               mode->a_mpdu_params |= nla_get_u8(ampdu_density) << 2;
+       nla_nest_end(msg, acl);
 
-       if (mcs_set && nla_len(mcs_set) >= 16) {
-               u8 *mcs;
-               mcs = nla_data(mcs_set);
-               os_memcpy(mode->mcs_set, mcs, 16);
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Failed to set MAC ACL: %d (%s)",
+                          ret, strerror(-ret));
        }
+
+       return ret;
 }
 
 
-static void phy_info_vht_capa(struct hostapd_hw_modes *mode,
-                             struct nlattr *capa,
-                             struct nlattr *mcs_set)
+static int nl80211_put_beacon_int(struct nl_msg *msg, int beacon_int)
 {
-       if (capa)
-               mode->vht_capab = nla_get_u32(capa);
-
-       if (mcs_set && nla_len(mcs_set) >= 8) {
-               u8 *mcs;
-               mcs = nla_data(mcs_set);
-               os_memcpy(mode->vht_mcs_set, mcs, 8);
+       if (beacon_int > 0) {
+               wpa_printf(MSG_DEBUG, "  * beacon_int=%d", beacon_int);
+               return nla_put_u32(msg, NL80211_ATTR_BEACON_INTERVAL,
+                                  beacon_int);
        }
+
+       return 0;
 }
 
 
-static void phy_info_freq(struct hostapd_hw_modes *mode,
-                         struct hostapd_channel_data *chan,
-                         struct nlattr *tb_freq[])
+static int wpa_driver_nl80211_set_ap(void *priv,
+                                    struct wpa_driver_ap_params *params)
 {
-       u8 channel;
-       chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
-       chan->flag = 0;
-       if (ieee80211_freq_to_chan(chan->freq, &channel) != NUM_HOSTAPD_MODES)
-               chan->chan = channel;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       u8 cmd = NL80211_CMD_NEW_BEACON;
+       int ret;
+       int beacon_set;
+       int num_suites;
+       int smps_mode;
+       u32 suites[10], suite;
+       u32 ver;
 
-       if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
-               chan->flag |= HOSTAPD_CHAN_DISABLED;
-       if (tb_freq[NL80211_FREQUENCY_ATTR_PASSIVE_SCAN])
-               chan->flag |= HOSTAPD_CHAN_PASSIVE_SCAN;
-       if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IBSS])
-               chan->flag |= HOSTAPD_CHAN_NO_IBSS;
-       if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
-               chan->flag |= HOSTAPD_CHAN_RADAR;
+       beacon_set = params->reenable ? 0 : bss->beacon_set;
 
-       if (tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] &&
-           !tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
-               chan->max_tx_power = nla_get_u32(
-                       tb_freq[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]) / 100;
-       if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
-               enum nl80211_dfs_state state =
-                       nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]);
+       wpa_printf(MSG_DEBUG, "nl80211: Set beacon (beacon_set=%d)",
+                  beacon_set);
+       if (beacon_set)
+               cmd = NL80211_CMD_SET_BEACON;
 
-               switch (state) {
-               case NL80211_DFS_USABLE:
-                       chan->flag |= HOSTAPD_CHAN_DFS_USABLE;
-                       break;
-               case NL80211_DFS_AVAILABLE:
-                       chan->flag |= HOSTAPD_CHAN_DFS_AVAILABLE;
-                       break;
-               case NL80211_DFS_UNAVAILABLE:
-                       chan->flag |= HOSTAPD_CHAN_DFS_UNAVAILABLE;
-                       break;
+       wpa_hexdump(MSG_DEBUG, "nl80211: Beacon head",
+                   params->head, params->head_len);
+       wpa_hexdump(MSG_DEBUG, "nl80211: Beacon tail",
+                   params->tail, params->tail_len);
+       wpa_printf(MSG_DEBUG, "nl80211: ifindex=%d", bss->ifindex);
+       wpa_printf(MSG_DEBUG, "nl80211: beacon_int=%d", params->beacon_int);
+       wpa_printf(MSG_DEBUG, "nl80211: dtim_period=%d", params->dtim_period);
+       wpa_hexdump_ascii(MSG_DEBUG, "nl80211: ssid",
+                         params->ssid, params->ssid_len);
+       if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
+           nla_put(msg, NL80211_ATTR_BEACON_HEAD, params->head_len,
+                   params->head) ||
+           nla_put(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len,
+                   params->tail) ||
+           nl80211_put_beacon_int(msg, params->beacon_int) ||
+           nla_put_u32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period) ||
+           nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid))
+               goto fail;
+       if (params->proberesp && params->proberesp_len) {
+               wpa_hexdump(MSG_DEBUG, "nl80211: proberesp (offload)",
+                           params->proberesp, params->proberesp_len);
+               if (nla_put(msg, NL80211_ATTR_PROBE_RESP, params->proberesp_len,
+                           params->proberesp))
+                       goto fail;
+       }
+       switch (params->hide_ssid) {
+       case NO_SSID_HIDING:
+               wpa_printf(MSG_DEBUG, "nl80211: hidden SSID not in use");
+               if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID,
+                               NL80211_HIDDEN_SSID_NOT_IN_USE))
+                       goto fail;
+               break;
+       case HIDDEN_SSID_ZERO_LEN:
+               wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero len");
+               if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID,
+                               NL80211_HIDDEN_SSID_ZERO_LEN))
+                       goto fail;
+               break;
+       case HIDDEN_SSID_ZERO_CONTENTS:
+               wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero contents");
+               if (nla_put_u32(msg, NL80211_ATTR_HIDDEN_SSID,
+                               NL80211_HIDDEN_SSID_ZERO_CONTENTS))
+                       goto fail;
+               break;
+       }
+       wpa_printf(MSG_DEBUG, "nl80211: privacy=%d", params->privacy);
+       if (params->privacy &&
+           nla_put_flag(msg, NL80211_ATTR_PRIVACY))
+               goto fail;
+       wpa_printf(MSG_DEBUG, "nl80211: auth_algs=0x%x", params->auth_algs);
+       if ((params->auth_algs & (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) ==
+           (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) {
+               /* Leave out the attribute */
+       } else if (params->auth_algs & WPA_AUTH_ALG_SHARED) {
+               if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE,
+                               NL80211_AUTHTYPE_SHARED_KEY))
+                       goto fail;
+       } else {
+               if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE,
+                               NL80211_AUTHTYPE_OPEN_SYSTEM))
+                       goto fail;
+       }
+
+       wpa_printf(MSG_DEBUG, "nl80211: wpa_version=0x%x", params->wpa_version);
+       ver = 0;
+       if (params->wpa_version & WPA_PROTO_WPA)
+               ver |= NL80211_WPA_VERSION_1;
+       if (params->wpa_version & WPA_PROTO_RSN)
+               ver |= NL80211_WPA_VERSION_2;
+       if (ver &&
+           nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, ver))
+               goto fail;
+
+       wpa_printf(MSG_DEBUG, "nl80211: key_mgmt_suites=0x%x",
+                  params->key_mgmt_suites);
+       num_suites = 0;
+       if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X)
+               suites[num_suites++] = WLAN_AKM_SUITE_8021X;
+       if (params->key_mgmt_suites & WPA_KEY_MGMT_PSK)
+               suites[num_suites++] = WLAN_AKM_SUITE_PSK;
+       if (num_suites &&
+           nla_put(msg, NL80211_ATTR_AKM_SUITES, num_suites * sizeof(u32),
+                   suites))
+               goto fail;
+
+       if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
+           params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40) &&
+           nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT))
+               goto fail;
+
+       wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x",
+                  params->pairwise_ciphers);
+       num_suites = wpa_cipher_to_cipher_suites(params->pairwise_ciphers,
+                                                suites, ARRAY_SIZE(suites));
+       if (num_suites &&
+           nla_put(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
+                   num_suites * sizeof(u32), suites))
+               goto fail;
+
+       wpa_printf(MSG_DEBUG, "nl80211: group_cipher=0x%x",
+                  params->group_cipher);
+       suite = wpa_cipher_to_cipher_suite(params->group_cipher);
+       if (suite &&
+           nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, suite))
+               goto fail;
+
+       switch (params->smps_mode) {
+       case HT_CAP_INFO_SMPS_DYNAMIC:
+               wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - dynamic");
+               smps_mode = NL80211_SMPS_DYNAMIC;
+               break;
+       case HT_CAP_INFO_SMPS_STATIC:
+               wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - static");
+               smps_mode = NL80211_SMPS_STATIC;
+               break;
+       default:
+               /* invalid - fallback to smps off */
+       case HT_CAP_INFO_SMPS_DISABLED:
+               wpa_printf(MSG_DEBUG, "nl80211: SMPS mode - off");
+               smps_mode = NL80211_SMPS_OFF;
+               break;
+       }
+       if (nla_put_u32(msg, NL80211_ATTR_SMPS_MODE, smps_mode))
+               goto fail;
+
+       if (params->beacon_ies) {
+               wpa_hexdump_buf(MSG_DEBUG, "nl80211: beacon_ies",
+                               params->beacon_ies);
+               if (nla_put(msg, NL80211_ATTR_IE,
+                           wpabuf_len(params->beacon_ies),
+                           wpabuf_head(params->beacon_ies)))
+                       goto fail;
+       }
+       if (params->proberesp_ies) {
+               wpa_hexdump_buf(MSG_DEBUG, "nl80211: proberesp_ies",
+                               params->proberesp_ies);
+               if (nla_put(msg, NL80211_ATTR_IE_PROBE_RESP,
+                           wpabuf_len(params->proberesp_ies),
+                           wpabuf_head(params->proberesp_ies)))
+                       goto fail;
+       }
+       if (params->assocresp_ies) {
+               wpa_hexdump_buf(MSG_DEBUG, "nl80211: assocresp_ies",
+                               params->assocresp_ies);
+               if (nla_put(msg, NL80211_ATTR_IE_ASSOC_RESP,
+                           wpabuf_len(params->assocresp_ies),
+                           wpabuf_head(params->assocresp_ies)))
+                       goto fail;
+       }
+
+       if (drv->capa.flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)  {
+               wpa_printf(MSG_DEBUG, "nl80211: ap_max_inactivity=%d",
+                          params->ap_max_inactivity);
+               if (nla_put_u16(msg, NL80211_ATTR_INACTIVITY_TIMEOUT,
+                               params->ap_max_inactivity))
+                       goto fail;
+       }
+
+#ifdef CONFIG_P2P
+       if (params->p2p_go_ctwindow > 0) {
+               if (drv->p2p_go_ctwindow_supported) {
+                       wpa_printf(MSG_DEBUG, "nl80211: P2P GO ctwindow=%d",
+                                  params->p2p_go_ctwindow);
+                       if (nla_put_u8(msg, NL80211_ATTR_P2P_CTWINDOW,
+                                      params->p2p_go_ctwindow))
+                               goto fail;
+               } else {
+                       wpa_printf(MSG_INFO,
+                                  "nl80211: Driver does not support CTWindow configuration - ignore this parameter");
+               }
+       }
+#endif /* CONFIG_P2P */
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
+                          ret, strerror(-ret));
+       } else {
+               bss->beacon_set = 1;
+               nl80211_set_bss(bss, params->cts_protect, params->preamble,
+                               params->short_slot_time, params->ht_opmode,
+                               params->isolate, params->basic_rates);
+               if (beacon_set && params->freq &&
+                   params->freq->bandwidth != bss->bandwidth) {
+                       wpa_printf(MSG_DEBUG,
+                                  "nl80211: Update BSS %s bandwidth: %d -> %d",
+                                  bss->ifname, bss->bandwidth,
+                                  params->freq->bandwidth);
+                       ret = nl80211_set_channel(bss, params->freq, 1);
+                       if (ret) {
+                               wpa_printf(MSG_DEBUG,
+                                          "nl80211: Frequency set failed: %d (%s)",
+                                          ret, strerror(-ret));
+                       } else {
+                               wpa_printf(MSG_DEBUG,
+                                          "nl80211: Frequency set succeeded for ht2040 coex");
+                               bss->bandwidth = params->freq->bandwidth;
+                       }
+               } else if (!beacon_set) {
+                       /*
+                        * cfg80211 updates the driver on frequence change in AP
+                        * mode only at the point when beaconing is started, so
+                        * set the initial value here.
+                        */
+                       bss->bandwidth = params->freq->bandwidth;
                }
        }
+
+#ifdef BCM_DRIVER_V115
+       wpa_driver_nl80211_probe_req_report(priv, 1);
+#endif
+
+       return ret;
+fail:
+       nlmsg_free(msg);
+       return -ENOBUFS;
 }
 
 
-static int phy_info_freqs(struct phy_info_arg *phy_info,
-                         struct hostapd_hw_modes *mode, struct nlattr *tb)
+static int nl80211_put_freq_params(struct nl_msg *msg,
+                                  const struct hostapd_freq_params *freq)
 {
-       static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
-               [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
-               [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
-               [NL80211_FREQUENCY_ATTR_PASSIVE_SCAN] = { .type = NLA_FLAG },
-               [NL80211_FREQUENCY_ATTR_NO_IBSS] = { .type = NLA_FLAG },
-               [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
-               [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
-               [NL80211_FREQUENCY_ATTR_DFS_STATE] = { .type = NLA_U32 },
-       };
-       int new_channels = 0;
-       struct hostapd_channel_data *channel;
-       struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
-       struct nlattr *nl_freq;
-       int rem_freq, idx;
-
-       if (tb == NULL)
-               return NL_OK;
-
-       nla_for_each_nested(nl_freq, tb, rem_freq) {
-               nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
-                         nla_data(nl_freq), nla_len(nl_freq), freq_policy);
-               if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
-                       continue;
-               new_channels++;
-       }
+       wpa_printf(MSG_DEBUG, "  * freq=%d", freq->freq);
+       if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq))
+               return -ENOBUFS;
 
-       channel = os_realloc_array(mode->channels,
-                                  mode->num_channels + new_channels,
-                                  sizeof(struct hostapd_channel_data));
-       if (!channel)
-               return NL_SKIP;
+       wpa_printf(MSG_DEBUG, "  * vht_enabled=%d", freq->vht_enabled);
+       wpa_printf(MSG_DEBUG, "  * ht_enabled=%d", freq->ht_enabled);
+
+       if (freq->vht_enabled) {
+               enum nl80211_chan_width cw;
 
-       mode->channels = channel;
-       mode->num_channels += new_channels;
+               wpa_printf(MSG_DEBUG, "  * bandwidth=%d", freq->bandwidth);
+               switch (freq->bandwidth) {
+               case 20:
+                       cw = NL80211_CHAN_WIDTH_20;
+                       break;
+               case 40:
+                       cw = NL80211_CHAN_WIDTH_40;
+                       break;
+               case 80:
+                       if (freq->center_freq2)
+                               cw = NL80211_CHAN_WIDTH_80P80;
+                       else
+                               cw = NL80211_CHAN_WIDTH_80;
+                       break;
+               case 160:
+                       cw = NL80211_CHAN_WIDTH_160;
+                       break;
+               default:
+                       return -EINVAL;
+               }
 
-       idx = phy_info->last_chan_idx;
+               wpa_printf(MSG_DEBUG, "  * channel_width=%d", cw);
+               wpa_printf(MSG_DEBUG, "  * center_freq1=%d",
+                          freq->center_freq1);
+               wpa_printf(MSG_DEBUG, "  * center_freq2=%d",
+                          freq->center_freq2);
+               if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, cw) ||
+                   nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1,
+                               freq->center_freq1) ||
+                   (freq->center_freq2 &&
+                    nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2,
+                                freq->center_freq2)))
+                       return -ENOBUFS;
+       } else if (freq->ht_enabled) {
+               enum nl80211_channel_type ct;
 
-       nla_for_each_nested(nl_freq, tb, rem_freq) {
-               nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
-                         nla_data(nl_freq), nla_len(nl_freq), freq_policy);
-               if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
-                       continue;
-               phy_info_freq(mode, &mode->channels[idx], tb_freq);
-               idx++;
-       }
-       phy_info->last_chan_idx = idx;
+               wpa_printf(MSG_DEBUG, "  * sec_channel_offset=%d",
+                          freq->sec_channel_offset);
+               switch (freq->sec_channel_offset) {
+               case -1:
+                       ct = NL80211_CHAN_HT40MINUS;
+                       break;
+               case 1:
+                       ct = NL80211_CHAN_HT40PLUS;
+                       break;
+               default:
+                       ct = NL80211_CHAN_HT20;
+                       break;
+               }
 
-       return NL_OK;
+               wpa_printf(MSG_DEBUG, "  * channel_type=%d", ct);
+               if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, ct))
+                       return -ENOBUFS;
+       }
+       return 0;
 }
 
 
-static int phy_info_rates(struct hostapd_hw_modes *mode, struct nlattr *tb)
+static int nl80211_set_channel(struct i802_bss *bss,
+                              struct hostapd_freq_params *freq, int set_chan)
 {
-       static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = {
-               [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 },
-               [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] =
-               { .type = NLA_FLAG },
-       };
-       struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1];
-       struct nlattr *nl_rate;
-       int rem_rate, idx;
-
-       if (tb == NULL)
-               return NL_OK;
-
-       nla_for_each_nested(nl_rate, tb, rem_rate) {
-               nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX,
-                         nla_data(nl_rate), nla_len(nl_rate),
-                         rate_policy);
-               if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
-                       continue;
-               mode->num_rates++;
-       }
-
-       mode->rates = os_calloc(mode->num_rates, sizeof(int));
-       if (!mode->rates)
-               return NL_SKIP;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret;
 
-       idx = 0;
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: Set freq %d (ht_enabled=%d, vht_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)",
+                  freq->freq, freq->ht_enabled, freq->vht_enabled,
+                  freq->bandwidth, freq->center_freq1, freq->center_freq2);
 
-       nla_for_each_nested(nl_rate, tb, rem_rate) {
-               nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX,
-                         nla_data(nl_rate), nla_len(nl_rate),
-                         rate_policy);
-               if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
-                       continue;
-               mode->rates[idx] = nla_get_u32(
-                       tb_rate[NL80211_BITRATE_ATTR_RATE]);
-               idx++;
+       msg = nl80211_drv_msg(drv, 0, set_chan ? NL80211_CMD_SET_CHANNEL :
+                             NL80211_CMD_SET_WIPHY);
+       if (!msg || nl80211_put_freq_params(msg, freq) < 0) {
+               nlmsg_free(msg);
+               return -1;
        }
 
-       return NL_OK;
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret == 0) {
+               bss->freq = freq->freq;
+               return 0;
+       }
+       wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): "
+                  "%d (%s)", freq->freq, ret, strerror(-ret));
+       return -1;
 }
 
 
-static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band)
+static u32 sta_flags_nl80211(int flags)
 {
-       struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
-       struct hostapd_hw_modes *mode;
-       int ret;
+       u32 f = 0;
 
-       if (phy_info->last_mode != nl_band->nla_type) {
-               mode = os_realloc_array(phy_info->modes,
-                                       *phy_info->num_modes + 1,
-                                       sizeof(*mode));
-               if (!mode)
-                       return NL_SKIP;
-               phy_info->modes = mode;
-
-               mode = &phy_info->modes[*(phy_info->num_modes)];
-               os_memset(mode, 0, sizeof(*mode));
-               mode->mode = NUM_HOSTAPD_MODES;
-               mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN;
-               *(phy_info->num_modes) += 1;
-               phy_info->last_mode = nl_band->nla_type;
-               phy_info->last_chan_idx = 0;
-       } else
-               mode = &phy_info->modes[*(phy_info->num_modes) - 1];
-
-       nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
-                 nla_len(nl_band), NULL);
-
-       phy_info_ht_capa(mode, tb_band[NL80211_BAND_ATTR_HT_CAPA],
-                        tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR],
-                        tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY],
-                        tb_band[NL80211_BAND_ATTR_HT_MCS_SET]);
-       phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA],
-                         tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]);
-       ret = phy_info_freqs(phy_info, mode, tb_band[NL80211_BAND_ATTR_FREQS]);
-       if (ret != NL_OK)
-               return ret;
-       ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]);
-       if (ret != NL_OK)
-               return ret;
+       if (flags & WPA_STA_AUTHORIZED)
+               f |= BIT(NL80211_STA_FLAG_AUTHORIZED);
+       if (flags & WPA_STA_WMM)
+               f |= BIT(NL80211_STA_FLAG_WME);
+       if (flags & WPA_STA_SHORT_PREAMBLE)
+               f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
+       if (flags & WPA_STA_MFP)
+               f |= BIT(NL80211_STA_FLAG_MFP);
+       if (flags & WPA_STA_TDLS_PEER)
+               f |= BIT(NL80211_STA_FLAG_TDLS_PEER);
+       if (flags & WPA_STA_AUTHENTICATED)
+               f |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
 
-       return NL_OK;
+       return f;
 }
 
 
-static int phy_info_handler(struct nl_msg *msg, void *arg)
-{
-       struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
-       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-       struct phy_info_arg *phy_info = arg;
-       struct nlattr *nl_band;
-       int rem_band;
-
-       nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-                 genlmsg_attrlen(gnlh, 0), NULL);
-
-       if (!tb_msg[NL80211_ATTR_WIPHY_BANDS])
-               return NL_SKIP;
-
-       nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band)
-       {
-               int res = phy_info_band(phy_info, nl_band);
-               if (res != NL_OK)
-                       return res;
+#ifdef CONFIG_MESH
+static u32 sta_plink_state_nl80211(enum mesh_plink_state state)
+{
+       switch (state) {
+       case PLINK_LISTEN:
+               return NL80211_PLINK_LISTEN;
+       case PLINK_OPEN_SENT:
+               return NL80211_PLINK_OPN_SNT;
+       case PLINK_OPEN_RCVD:
+               return NL80211_PLINK_OPN_RCVD;
+       case PLINK_CNF_RCVD:
+               return NL80211_PLINK_CNF_RCVD;
+       case PLINK_ESTAB:
+               return NL80211_PLINK_ESTAB;
+       case PLINK_HOLDING:
+               return NL80211_PLINK_HOLDING;
+       case PLINK_BLOCKED:
+               return NL80211_PLINK_BLOCKED;
+       default:
+               wpa_printf(MSG_ERROR, "nl80211: Invalid mesh plink state %d",
+                          state);
        }
-
-       return NL_SKIP;
+       return -1;
 }
+#endif /* CONFIG_MESH */
 
 
-static struct hostapd_hw_modes *
-wpa_driver_nl80211_postprocess_modes(struct hostapd_hw_modes *modes,
-                                    u16 *num_modes)
+static int wpa_driver_nl80211_sta_add(void *priv,
+                                     struct hostapd_sta_add_params *params)
 {
-       u16 m;
-       struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode;
-       int i, mode11g_idx = -1;
-
-       /* heuristic to set up modes */
-       for (m = 0; m < *num_modes; m++) {
-               if (!modes[m].num_channels)
-                       continue;
-               if (modes[m].channels[0].freq < 4000) {
-                       modes[m].mode = HOSTAPD_MODE_IEEE80211B;
-                       for (i = 0; i < modes[m].num_rates; i++) {
-                               if (modes[m].rates[i] > 200) {
-                                       modes[m].mode = HOSTAPD_MODE_IEEE80211G;
-                                       break;
-                               }
-                       }
-               } else if (modes[m].channels[0].freq > 50000)
-                       modes[m].mode = HOSTAPD_MODE_IEEE80211AD;
-               else
-                       modes[m].mode = HOSTAPD_MODE_IEEE80211A;
-       }
-
-       /* If only 802.11g mode is included, use it to construct matching
-        * 802.11b mode data. */
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       struct nl80211_sta_flag_update upd;
+       int ret = -ENOBUFS;
 
-       for (m = 0; m < *num_modes; m++) {
-               if (modes[m].mode == HOSTAPD_MODE_IEEE80211B)
-                       return modes; /* 802.11b already included */
-               if (modes[m].mode == HOSTAPD_MODE_IEEE80211G)
-                       mode11g_idx = m;
-       }
+       if ((params->flags & WPA_STA_TDLS_PEER) &&
+           !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+               return -EOPNOTSUPP;
 
-       if (mode11g_idx < 0)
-               return modes; /* 2.4 GHz band not supported at all */
+       wpa_printf(MSG_DEBUG, "nl80211: %s STA " MACSTR,
+                  params->set ? "Set" : "Add", MAC2STR(params->addr));
+       msg = nl80211_bss_msg(bss, 0, params->set ? NL80211_CMD_SET_STATION :
+                             NL80211_CMD_NEW_STATION);
+       if (!msg || nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr))
+               goto fail;
 
-       nmodes = os_realloc_array(modes, *num_modes + 1, sizeof(*nmodes));
-       if (nmodes == NULL)
-               return modes; /* Could not add 802.11b mode */
+       if (!params->set || (params->flags & WPA_STA_TDLS_PEER)) {
+               wpa_hexdump(MSG_DEBUG, "  * supported rates",
+                           params->supp_rates, params->supp_rates_len);
+               wpa_printf(MSG_DEBUG, "  * capability=0x%x",
+                          params->capability);
+               if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_RATES,
+                           params->supp_rates_len, params->supp_rates) ||
+                   nla_put_u16(msg, NL80211_ATTR_STA_CAPABILITY,
+                               params->capability))
+                       goto fail;
 
-       mode = &nmodes[*num_modes];
-       os_memset(mode, 0, sizeof(*mode));
-       (*num_modes)++;
-       modes = nmodes;
+               if (params->ht_capabilities) {
+                       wpa_hexdump(MSG_DEBUG, "  * ht_capabilities",
+                                   (u8 *) params->ht_capabilities,
+                                   sizeof(*params->ht_capabilities));
+                       if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY,
+                                   sizeof(*params->ht_capabilities),
+                                   params->ht_capabilities))
+                               goto fail;
+               }
 
-       mode->mode = HOSTAPD_MODE_IEEE80211B;
+               if (params->vht_capabilities) {
+                       wpa_hexdump(MSG_DEBUG, "  * vht_capabilities",
+                                   (u8 *) params->vht_capabilities,
+                                   sizeof(*params->vht_capabilities));
+                       if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY,
+                                   sizeof(*params->vht_capabilities),
+                                   params->vht_capabilities))
+                               goto fail;
+               }
 
-       mode11g = &modes[mode11g_idx];
-       mode->num_channels = mode11g->num_channels;
-       mode->channels = os_malloc(mode11g->num_channels *
-                                  sizeof(struct hostapd_channel_data));
-       if (mode->channels == NULL) {
-               (*num_modes)--;
-               return modes; /* Could not add 802.11b mode */
+               if (params->ext_capab) {
+                       wpa_hexdump(MSG_DEBUG, "  * ext_capab",
+                                   params->ext_capab, params->ext_capab_len);
+                       if (nla_put(msg, NL80211_ATTR_STA_EXT_CAPABILITY,
+                                   params->ext_capab_len, params->ext_capab))
+                               goto fail;
+               }
        }
-       os_memcpy(mode->channels, mode11g->channels,
-                 mode11g->num_channels * sizeof(struct hostapd_channel_data));
-
-       mode->num_rates = 0;
-       mode->rates = os_malloc(4 * sizeof(int));
-       if (mode->rates == NULL) {
-               os_free(mode->channels);
-               (*num_modes)--;
-               return modes; /* Could not add 802.11b mode */
+       if (!params->set) {
+               if (params->aid) {
+                       wpa_printf(MSG_DEBUG, "  * aid=%u", params->aid);
+                       if (nla_put_u16(msg, NL80211_ATTR_STA_AID, params->aid))
+                               goto fail;
+               } else {
+                       /*
+                        * cfg80211 validates that AID is non-zero, so we have
+                        * to make this a non-zero value for the TDLS case where
+                        * a dummy STA entry is used for now.
+                        */
+                       wpa_printf(MSG_DEBUG, "  * aid=1 (TDLS workaround)");
+                       if (nla_put_u16(msg, NL80211_ATTR_STA_AID, 1))
+                               goto fail;
+               }
+               wpa_printf(MSG_DEBUG, "  * listen_interval=%u",
+                          params->listen_interval);
+               if (nla_put_u16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
+                               params->listen_interval))
+                       goto fail;
+       } else if (params->aid && (params->flags & WPA_STA_TDLS_PEER)) {
+               wpa_printf(MSG_DEBUG, "  * peer_aid=%u", params->aid);
+               if (nla_put_u16(msg, NL80211_ATTR_PEER_AID, params->aid))
+                       goto fail;
        }
 
-       for (i = 0; i < mode11g->num_rates; i++) {
-               if (mode11g->rates[i] != 10 && mode11g->rates[i] != 20 &&
-                   mode11g->rates[i] != 55 && mode11g->rates[i] != 110)
-                       continue;
-               mode->rates[mode->num_rates] = mode11g->rates[i];
-               mode->num_rates++;
-               if (mode->num_rates == 4)
-                       break;
+       if (params->vht_opmode_enabled) {
+               wpa_printf(MSG_DEBUG, "  * opmode=%u", params->vht_opmode);
+               if (nla_put_u8(msg, NL80211_ATTR_OPMODE_NOTIF,
+                              params->vht_opmode))
+                       goto fail;
        }
 
-       if (mode->num_rates == 0) {
-               os_free(mode->channels);
-               os_free(mode->rates);
-               (*num_modes)--;
-               return modes; /* No 802.11b rates */
+       if (params->supp_channels) {
+               wpa_hexdump(MSG_DEBUG, "  * supported channels",
+                           params->supp_channels, params->supp_channels_len);
+               if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_CHANNELS,
+                           params->supp_channels_len, params->supp_channels))
+                       goto fail;
        }
 
-       wpa_printf(MSG_DEBUG, "nl80211: Added 802.11b mode based on 802.11g "
-                  "information");
+       if (params->supp_oper_classes) {
+               wpa_hexdump(MSG_DEBUG, "  * supported operating classes",
+                           params->supp_oper_classes,
+                           params->supp_oper_classes_len);
+               if (nla_put(msg, NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
+                           params->supp_oper_classes_len,
+                           params->supp_oper_classes))
+                       goto fail;
+       }
 
-       return modes;
-}
+       os_memset(&upd, 0, sizeof(upd));
+       upd.set = sta_flags_nl80211(params->flags);
+       upd.mask = upd.set | sta_flags_nl80211(params->flags_mask);
+       wpa_printf(MSG_DEBUG, "  * flags set=0x%x mask=0x%x",
+                  upd.set, upd.mask);
+       if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd))
+               goto fail;
 
+#ifdef CONFIG_MESH
+       if (params->plink_state &&
+           nla_put_u8(msg, NL80211_ATTR_STA_PLINK_STATE,
+                      sta_plink_state_nl80211(params->plink_state)))
+               goto fail;
+#endif /* CONFIG_MESH */
 
-static void nl80211_set_ht40_mode(struct hostapd_hw_modes *mode, int start,
-                                 int end)
-{
-       int c;
+       if (params->flags & WPA_STA_WMM) {
+               struct nlattr *wme = nla_nest_start(msg, NL80211_ATTR_STA_WME);
 
-       for (c = 0; c < mode->num_channels; c++) {
-               struct hostapd_channel_data *chan = &mode->channels[c];
-               if (chan->freq - 10 >= start && chan->freq + 10 <= end)
-                       chan->flag |= HOSTAPD_CHAN_HT40;
+               wpa_printf(MSG_DEBUG, "  * qosinfo=0x%x", params->qosinfo);
+               if (!wme ||
+                   nla_put_u8(msg, NL80211_STA_WME_UAPSD_QUEUES,
+                              params->qosinfo & WMM_QOSINFO_STA_AC_MASK) ||
+                   nla_put_u8(msg, NL80211_STA_WME_MAX_SP,
+                              (params->qosinfo >> WMM_QOSINFO_STA_SP_SHIFT) &
+                              WMM_QOSINFO_STA_SP_MASK))
+                       goto fail;
+               nla_nest_end(msg, wme);
        }
-}
-
-
-static void nl80211_set_ht40_mode_sec(struct hostapd_hw_modes *mode, int start,
-                                     int end)
-{
-       int c;
 
-       for (c = 0; c < mode->num_channels; c++) {
-               struct hostapd_channel_data *chan = &mode->channels[c];
-               if (!(chan->flag & HOSTAPD_CHAN_HT40))
-                       continue;
-               if (chan->freq - 30 >= start && chan->freq - 10 <= end)
-                       chan->flag |= HOSTAPD_CHAN_HT40MINUS;
-               if (chan->freq + 10 >= start && chan->freq + 30 <= end)
-                       chan->flag |= HOSTAPD_CHAN_HT40PLUS;
-       }
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
+       if (ret)
+               wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_%s_STATION "
+                          "result: %d (%s)", params->set ? "SET" : "NEW", ret,
+                          strerror(-ret));
+       if (ret == -EEXIST)
+               ret = 0;
+fail:
+       nlmsg_free(msg);
+       return ret;
 }
 
 
-static void nl80211_reg_rule_ht40(struct nlattr *tb[],
-                                 struct phy_info_arg *results)
+static void rtnl_neigh_delete_fdb_entry(struct i802_bss *bss, const u8 *addr)
 {
-       u32 start, end, max_bw;
-       u16 m;
+#ifdef CONFIG_LIBNL3_ROUTE
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct rtnl_neigh *rn;
+       struct nl_addr *nl_addr;
+       int err;
 
-       if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
-           tb[NL80211_ATTR_FREQ_RANGE_END] == NULL ||
-           tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL)
+       rn = rtnl_neigh_alloc();
+       if (!rn)
                return;
 
-       start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
-       end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
-       max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
-
-       wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz",
-                  start, end, max_bw);
-       if (max_bw < 40)
+       rtnl_neigh_set_family(rn, AF_BRIDGE);
+       rtnl_neigh_set_ifindex(rn, bss->ifindex);
+       nl_addr = nl_addr_build(AF_BRIDGE, (void *) addr, ETH_ALEN);
+       if (!nl_addr) {
+               rtnl_neigh_put(rn);
                return;
+       }
+       rtnl_neigh_set_lladdr(rn, nl_addr);
 
-       for (m = 0; m < *results->num_modes; m++) {
-               if (!(results->modes[m].ht_capab &
-                     HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
-                       continue;
-               nl80211_set_ht40_mode(&results->modes[m], start, end);
+       err = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
+       if (err < 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: bridge FDB entry delete for "
+                          MACSTR " ifindex=%d failed: %s", MAC2STR(addr),
+                          bss->ifindex, nl_geterror(err));
+       } else {
+               wpa_printf(MSG_DEBUG, "nl80211: deleted bridge FDB entry for "
+                          MACSTR, MAC2STR(addr));
        }
+
+       nl_addr_put(nl_addr);
+       rtnl_neigh_put(rn);
+#endif /* CONFIG_LIBNL3_ROUTE */
 }
 
 
-static void nl80211_reg_rule_sec(struct nlattr *tb[],
-                                struct phy_info_arg *results)
+static int wpa_driver_nl80211_sta_remove(struct i802_bss *bss, const u8 *addr,
+                                        int deauth, u16 reason_code)
 {
-       u32 start, end, max_bw;
-       u16 m;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret;
 
-       if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
-           tb[NL80211_ATTR_FREQ_RANGE_END] == NULL ||
-           tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL)
-               return;
+       if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION)) ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+           (deauth == 0 &&
+            nla_put_u8(msg, NL80211_ATTR_MGMT_SUBTYPE,
+                       WLAN_FC_STYPE_DISASSOC)) ||
+           (deauth == 1 &&
+            nla_put_u8(msg, NL80211_ATTR_MGMT_SUBTYPE,
+                       WLAN_FC_STYPE_DEAUTH)) ||
+           (reason_code &&
+            nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code))) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
+       }
 
-       start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
-       end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
-       max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       wpa_printf(MSG_DEBUG, "nl80211: sta_remove -> DEL_STATION %s " MACSTR
+                  " --> %d (%s)",
+                  bss->ifname, MAC2STR(addr), ret, strerror(-ret));
 
-       if (max_bw < 20)
-               return;
+       if (drv->rtnl_sk)
+               rtnl_neigh_delete_fdb_entry(bss, addr);
 
-       for (m = 0; m < *results->num_modes; m++) {
-               if (!(results->modes[m].ht_capab &
-                     HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
-                       continue;
-               nl80211_set_ht40_mode_sec(&results->modes[m], start, end);
-       }
+       if (ret == -ENOENT)
+               return 0;
+       return ret;
 }
 
 
-static int nl80211_get_reg(struct nl_msg *msg, void *arg)
+void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx)
 {
-       struct phy_info_arg *results = arg;
-       struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
-       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-       struct nlattr *nl_rule;
-       struct nlattr *tb_rule[NL80211_FREQUENCY_ATTR_MAX + 1];
-       int rem_rule;
-       static struct nla_policy reg_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
-               [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
-               [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
-               [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
-               [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
-               [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
-               [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
-       };
-
-       nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-                 genlmsg_attrlen(gnlh, 0), NULL);
-       if (!tb_msg[NL80211_ATTR_REG_ALPHA2] ||
-           !tb_msg[NL80211_ATTR_REG_RULES]) {
-               wpa_printf(MSG_DEBUG, "nl80211: No regulatory information "
-                          "available");
-               return NL_SKIP;
-       }
-
-       wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s",
-                  (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]));
+       struct nl_msg *msg;
+       struct wpa_driver_nl80211_data *drv2;
 
-       nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
-       {
-               nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
-                         nla_data(nl_rule), nla_len(nl_rule), reg_policy);
-               nl80211_reg_rule_ht40(tb_rule, results);
-       }
+       wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx);
 
-       nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
-       {
-               nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
-                         nla_data(nl_rule), nla_len(nl_rule), reg_policy);
-               nl80211_reg_rule_sec(tb_rule, results);
-       }
+       /* stop listening for EAPOL on this interface */
+       dl_list_for_each(drv2, &drv->global->interfaces,
+                        struct wpa_driver_nl80211_data, list)
+               del_ifidx(drv2, ifidx);
 
-       return NL_SKIP;
+       msg = nl80211_ifindex_msg(drv, ifidx, 0, NL80211_CMD_DEL_INTERFACE);
+       if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
+               return;
+       wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx);
 }
 
 
-static int nl80211_set_ht40_flags(struct wpa_driver_nl80211_data *drv,
-                                 struct phy_info_arg *results)
+static const char * nl80211_iftype_str(enum nl80211_iftype mode)
 {
-       struct nl_msg *msg;
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
-
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG);
-       return send_and_recv_msgs(drv, msg, nl80211_get_reg, results);
+       switch (mode) {
+       case NL80211_IFTYPE_ADHOC:
+               return "ADHOC";
+       case NL80211_IFTYPE_STATION:
+               return "STATION";
+       case NL80211_IFTYPE_AP:
+               return "AP";
+       case NL80211_IFTYPE_AP_VLAN:
+               return "AP_VLAN";
+       case NL80211_IFTYPE_WDS:
+               return "WDS";
+       case NL80211_IFTYPE_MONITOR:
+               return "MONITOR";
+       case NL80211_IFTYPE_MESH_POINT:
+               return "MESH_POINT";
+       case NL80211_IFTYPE_P2P_CLIENT:
+               return "P2P_CLIENT";
+       case NL80211_IFTYPE_P2P_GO:
+               return "P2P_GO";
+       case NL80211_IFTYPE_P2P_DEVICE:
+               return "P2P_DEVICE";
+       default:
+               return "unknown";
+       }
 }
 
 
-static struct hostapd_hw_modes *
-wpa_driver_nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
+static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
+                                    const char *ifname,
+                                    enum nl80211_iftype iftype,
+                                    const u8 *addr, int wds,
+                                    int (*handler)(struct nl_msg *, void *),
+                                    void *arg)
 {
-       u32 feat;
-       struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
-       struct phy_info_arg result = {
-               .num_modes = num_modes,
-               .modes = NULL,
-               .last_mode = -1,
-       };
+       int ifidx;
+       int ret = -ENOBUFS;
 
-       *num_modes = 0;
-       *flags = 0;
+       wpa_printf(MSG_DEBUG, "nl80211: Create interface iftype %d (%s)",
+                  iftype, nl80211_iftype_str(iftype));
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return NULL;
+       msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_NEW_INTERFACE);
+       if (!msg ||
+           nla_put_string(msg, NL80211_ATTR_IFNAME, ifname) ||
+           nla_put_u32(msg, NL80211_ATTR_IFTYPE, iftype))
+               goto fail;
 
-       feat = get_nl80211_protocol_features(drv);
-       if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
-               nl80211_cmd(drv, msg, NLM_F_DUMP, NL80211_CMD_GET_WIPHY);
-       else
-               nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_WIPHY);
+       if (iftype == NL80211_IFTYPE_MONITOR) {
+               struct nlattr *flags;
 
-       NLA_PUT_FLAG(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+               flags = nla_nest_start(msg, NL80211_ATTR_MNTR_FLAGS);
+               if (!flags ||
+                   nla_put_flag(msg, NL80211_MNTR_FLAG_COOK_FRAMES))
+                       goto fail;
 
-       if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
-               nl80211_set_ht40_flags(drv, &result);
-               return wpa_driver_nl80211_postprocess_modes(result.modes,
-                                                           num_modes);
+               nla_nest_end(msg, flags);
+       } else if (wds) {
+               if (nla_put_u8(msg, NL80211_ATTR_4ADDR, wds))
+                       goto fail;
        }
-       msg = NULL;
- nla_put_failure:
-       nlmsg_free(msg);
-       return NULL;
-}
 
+       /*
+        * Tell cfg80211 that the interface belongs to the socket that created
+        * it, and the interface should be deleted when the socket is closed.
+        */
+       if (nla_put_flag(msg, NL80211_ATTR_IFACE_SOCKET_OWNER))
+               goto fail;
 
-static int wpa_driver_nl80211_send_mntr(struct wpa_driver_nl80211_data *drv,
-                                       const void *data, size_t len,
-                                       int encrypt, int noack)
-{
-       __u8 rtap_hdr[] = {
-               0x00, 0x00, /* radiotap version */
-               0x0e, 0x00, /* radiotap length */
-               0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */
-               IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */
-               0x00,       /* padding */
-               0x00, 0x00, /* RX and TX flags to indicate that */
-               0x00, 0x00, /* this is the injected frame directly */
-       };
-       struct iovec iov[2] = {
-               {
-                       .iov_base = &rtap_hdr,
-                       .iov_len = sizeof(rtap_hdr),
-               },
-               {
-                       .iov_base = (void *) data,
-                       .iov_len = len,
-               }
-       };
-       struct msghdr msg = {
-               .msg_name = NULL,
-               .msg_namelen = 0,
-               .msg_iov = iov,
-               .msg_iovlen = 2,
-               .msg_control = NULL,
-               .msg_controllen = 0,
-               .msg_flags = 0,
-       };
-       int res;
-       u16 txflags = 0;
+       ret = send_and_recv_msgs(drv, msg, handler, arg);
+       msg = NULL;
+       if (ret) {
+       fail:
+               nlmsg_free(msg);
+               wpa_printf(MSG_ERROR, "Failed to create interface %s: %d (%s)",
+                          ifname, ret, strerror(-ret));
+               return ret;
+       }
 
-       if (encrypt)
-               rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP;
+       if (iftype == NL80211_IFTYPE_P2P_DEVICE)
+               return 0;
+
+       ifidx = if_nametoindex(ifname);
+       wpa_printf(MSG_DEBUG, "nl80211: New interface %s created: ifindex=%d",
+                  ifname, ifidx);
 
-       if (drv->monitor_sock < 0) {
-               wpa_printf(MSG_DEBUG, "nl80211: No monitor socket available "
-                          "for %s", __func__);
+       if (ifidx <= 0)
                return -1;
-       }
 
-       if (noack)
-               txflags |= IEEE80211_RADIOTAP_F_TX_NOACK;
-       WPA_PUT_LE16(&rtap_hdr[12], txflags);
+       /*
+        * Some virtual interfaces need to process EAPOL packets and events on
+        * the parent interface. This is used mainly with hostapd.
+        */
+       if (drv->hostapd ||
+           iftype == NL80211_IFTYPE_AP_VLAN ||
+           iftype == NL80211_IFTYPE_WDS ||
+           iftype == NL80211_IFTYPE_MONITOR) {
+               /* start listening for EAPOL on this interface */
+               add_ifidx(drv, ifidx);
+       }
 
-       res = sendmsg(drv->monitor_sock, &msg, 0);
-       if (res < 0) {
-               wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno));
+       if (addr && iftype != NL80211_IFTYPE_MONITOR &&
+           linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, addr)) {
+               nl80211_remove_iface(drv, ifidx);
                return -1;
        }
-       return 0;
+
+       return ifidx;
 }
 
 
-static int wpa_driver_nl80211_send_frame(struct i802_bss *bss,
-                                        const void *data, size_t len,
-                                        int encrypt, int noack,
-                                        unsigned int freq, int no_cck,
-                                        int offchanok, unsigned int wait_time)
+int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
+                        const char *ifname, enum nl80211_iftype iftype,
+                        const u8 *addr, int wds,
+                        int (*handler)(struct nl_msg *, void *),
+                        void *arg, int use_existing)
 {
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       u64 cookie;
+       int ret;
 
-       if (freq == 0)
-               freq = bss->freq;
+       ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds, handler,
+                                       arg);
+
+       /* if error occurred and interface exists already */
+       if (ret == -ENFILE && if_nametoindex(ifname)) {
+               if (use_existing) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Continue using existing interface %s",
+                                  ifname);
+                       if (addr && iftype != NL80211_IFTYPE_MONITOR &&
+                           linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
+                                              addr) < 0 &&
+                           (linux_set_iface_flags(drv->global->ioctl_sock,
+                                                  ifname, 0) < 0 ||
+                            linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
+                                               addr) < 0 ||
+                            linux_set_iface_flags(drv->global->ioctl_sock,
+                                                  ifname, 1) < 0))
+                                       return -1;
+                       return -ENFILE;
+               }
+               wpa_printf(MSG_INFO, "Try to remove and re-create %s", ifname);
+
+               /* Try to remove the interface that was already there. */
+               nl80211_remove_iface(drv, if_nametoindex(ifname));
 
-       if (drv->use_monitor)
-               return wpa_driver_nl80211_send_mntr(drv, data, len,
-                                                   encrypt, noack);
+               /* Try to create the interface again */
+               ret = nl80211_create_iface_once(drv, ifname, iftype, addr,
+                                               wds, handler, arg);
+       }
+
+       if (ret >= 0 && is_p2p_net_interface(iftype)) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Interface %s created for P2P - disable 11b rates",
+                          ifname);
+               nl80211_disable_11b_rates(drv, ret, 1);
+       }
 
-       return nl80211_send_frame_cmd(bss, freq, wait_time, data, len,
-                                     &cookie, no_cck, noack, offchanok);
+       return ret;
 }
 
 
-static int wpa_driver_nl80211_send_mlme(struct i802_bss *bss, const u8 *data,
-                                       size_t data_len, int noack,
-                                       unsigned int freq, int no_cck,
-                                       int offchanok,
-                                       unsigned int wait_time)
+static int nl80211_setup_ap(struct i802_bss *bss)
 {
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct ieee80211_mgmt *mgmt;
-       int encrypt = 1;
-       u16 fc;
 
-       mgmt = (struct ieee80211_mgmt *) data;
-       fc = le_to_host16(mgmt->frame_control);
+       wpa_printf(MSG_DEBUG, "nl80211: Setup AP(%s) - device_ap_sme=%d use_monitor=%d",
+                  bss->ifname, drv->device_ap_sme, drv->use_monitor);
 
-       if ((is_sta_interface(drv->nlmode) ||
-            drv->nlmode == NL80211_IFTYPE_P2P_DEVICE) &&
-           WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
-           WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_PROBE_RESP) {
-               /*
-                * The use of last_mgmt_freq is a bit of a hack,
-                * but it works due to the single-threaded nature
-                * of wpa_supplicant.
-                */
-               if (freq == 0)
-                       freq = drv->last_mgmt_freq;
-               return nl80211_send_frame_cmd(bss, freq, 0,
-                                             data, data_len, NULL, 1, noack,
-                                             1);
-       }
+       /*
+        * Disable Probe Request reporting unless we need it in this way for
+        * devices that include the AP SME, in the other case (unless using
+        * monitor iface) we'll get it through the nl_mgmt socket instead.
+        */
+       if (!drv->device_ap_sme)
+               wpa_driver_nl80211_probe_req_report(bss, 0);
 
-       if (drv->device_ap_sme && is_ap_interface(drv->nlmode)) {
-               if (freq == 0)
-                       freq = bss->freq;
-               return nl80211_send_frame_cmd(bss, freq,
-                                             (int) freq == bss->freq ? 0 :
-                                             wait_time,
-                                             data, data_len,
-                                             &drv->send_action_cookie,
-                                             no_cck, noack, offchanok);
-       }
+       if (!drv->device_ap_sme && !drv->use_monitor)
+               if (nl80211_mgmt_subscribe_ap(bss))
+                       return -1;
 
-       if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
-           WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_AUTH) {
-               /*
-                * Only one of the authentication frame types is encrypted.
-                * In order for static WEP encryption to work properly (i.e.,
-                * to not encrypt the frame), we need to tell mac80211 about
-                * the frames that must not be encrypted.
-                */
-               u16 auth_alg = le_to_host16(mgmt->u.auth.auth_alg);
-               u16 auth_trans = le_to_host16(mgmt->u.auth.auth_transaction);
-               if (auth_alg != WLAN_AUTH_SHARED_KEY || auth_trans != 3)
-                       encrypt = 0;
+       if (drv->device_ap_sme && !drv->use_monitor)
+               if (nl80211_mgmt_subscribe_ap_dev_sme(bss))
+                       return -1;
+
+       if (!drv->device_ap_sme && drv->use_monitor &&
+           nl80211_create_monitor_interface(drv) &&
+           !drv->device_ap_sme)
+               return -1;
+
+       if (drv->device_ap_sme &&
+           wpa_driver_nl80211_probe_req_report(bss, 1) < 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Failed to enable "
+                          "Probe Request frame reporting in AP mode");
+               /* Try to survive without this */
        }
 
-       return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt,
-                                            noack, freq, no_cck, offchanok,
-                                            wait_time);
+       return 0;
 }
 
 
-static int nl80211_set_bss(struct i802_bss *bss, int cts, int preamble,
-                          int slot, int ht_opmode, int ap_isolate,
-                          int *basic_rates)
+static void nl80211_teardown_ap(struct i802_bss *bss)
 {
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct nl_msg *msg;
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_BSS);
+       wpa_printf(MSG_DEBUG, "nl80211: Teardown AP(%s) - device_ap_sme=%d use_monitor=%d",
+                  bss->ifname, drv->device_ap_sme, drv->use_monitor);
+       if (drv->device_ap_sme) {
+               wpa_driver_nl80211_probe_req_report(bss, 0);
+               if (!drv->use_monitor)
+                       nl80211_mgmt_unsubscribe(bss, "AP teardown (dev SME)");
+       } else if (drv->use_monitor)
+               nl80211_remove_monitor_interface(drv);
+       else
+               nl80211_mgmt_unsubscribe(bss, "AP teardown");
 
-       if (cts >= 0)
-               NLA_PUT_U8(msg, NL80211_ATTR_BSS_CTS_PROT, cts);
-       if (preamble >= 0)
-               NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_PREAMBLE, preamble);
-       if (slot >= 0)
-               NLA_PUT_U8(msg, NL80211_ATTR_BSS_SHORT_SLOT_TIME, slot);
-       if (ht_opmode >= 0)
-               NLA_PUT_U16(msg, NL80211_ATTR_BSS_HT_OPMODE, ht_opmode);
-       if (ap_isolate >= 0)
-               NLA_PUT_U8(msg, NL80211_ATTR_AP_ISOLATE, ap_isolate);
+       bss->beacon_set = 0;
+}
 
-       if (basic_rates) {
-               u8 rates[NL80211_MAX_SUPP_RATES];
-               u8 rates_len = 0;
-               int i;
 
-               for (i = 0; i < NL80211_MAX_SUPP_RATES && basic_rates[i] >= 0;
-                    i++)
-                       rates[rates_len++] = basic_rates[i] / 5;
+static int nl80211_send_eapol_data(struct i802_bss *bss,
+                                  const u8 *addr, const u8 *data,
+                                  size_t data_len)
+{
+       struct sockaddr_ll ll;
+       int ret;
 
-               NLA_PUT(msg, NL80211_ATTR_BSS_BASIC_RATES, rates_len, rates);
+       if (bss->drv->eapol_tx_sock < 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: No socket to send EAPOL");
+               return -1;
        }
 
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+       os_memset(&ll, 0, sizeof(ll));
+       ll.sll_family = AF_PACKET;
+       ll.sll_ifindex = bss->ifindex;
+       ll.sll_protocol = htons(ETH_P_PAE);
+       ll.sll_halen = ETH_ALEN;
+       os_memcpy(ll.sll_addr, addr, ETH_ALEN);
+       ret = sendto(bss->drv->eapol_tx_sock, data, data_len, 0,
+                    (struct sockaddr *) &ll, sizeof(ll));
+       if (ret < 0)
+               wpa_printf(MSG_ERROR, "nl80211: EAPOL TX: %s",
+                          strerror(errno));
 
-       return send_and_recv_msgs(drv, msg, NULL, NULL);
- nla_put_failure:
-       nlmsg_free(msg);
-       return -ENOBUFS;
+       return ret;
 }
 
 
-static int wpa_driver_nl80211_set_acl(void *priv,
-                                     struct hostapd_acl_params *params)
+static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
+
+static int wpa_driver_nl80211_hapd_send_eapol(
+       void *priv, const u8 *addr, const u8 *data,
+       size_t data_len, int encrypt, const u8 *own_addr, u32 flags)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct nl_msg *msg;
-       struct nlattr *acl;
-       unsigned int i;
-       int ret = 0;
-
-       if (!(drv->capa.max_acl_mac_addrs))
-               return -ENOTSUP;
-
-       if (params->num_mac_acl > drv->capa.max_acl_mac_addrs)
-               return -ENOTSUP;
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
-
-       wpa_printf(MSG_DEBUG, "nl80211: Set %s ACL (num_mac_acl=%u)",
-                  params->acl_policy ? "Accept" : "Deny", params->num_mac_acl);
+       struct ieee80211_hdr *hdr;
+       size_t len;
+       u8 *pos;
+       int res;
+       int qos = flags & WPA_STA_WMM;
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_MAC_ACL);
+       if (drv->device_ap_sme || !drv->use_monitor)
+               return nl80211_send_eapol_data(bss, addr, data, data_len);
 
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 +
+               data_len;
+       hdr = os_zalloc(len);
+       if (hdr == NULL) {
+               wpa_printf(MSG_INFO, "nl80211: Failed to allocate EAPOL buffer(len=%lu)",
+                          (unsigned long) len);
+               return -1;
+       }
 
-       NLA_PUT_U32(msg, NL80211_ATTR_ACL_POLICY, params->acl_policy ?
-                   NL80211_ACL_POLICY_DENY_UNLESS_LISTED :
-                   NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED);
+       hdr->frame_control =
+               IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA);
+       hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS);
+       if (encrypt)
+               hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
+       if (qos) {
+               hdr->frame_control |=
+                       host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4);
+       }
 
-       acl = nla_nest_start(msg, NL80211_ATTR_MAC_ADDRS);
-       if (acl == NULL)
-               goto nla_put_failure;
+       memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN);
+       memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
+       memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
+       pos = (u8 *) (hdr + 1);
 
-       for (i = 0; i < params->num_mac_acl; i++)
-               NLA_PUT(msg, i + 1, ETH_ALEN, params->mac_acl[i].addr);
+       if (qos) {
+               /* Set highest priority in QoS header */
+               pos[0] = 7;
+               pos[1] = 0;
+               pos += 2;
+       }
 
-       nla_nest_end(msg, acl);
+       memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
+       pos += sizeof(rfc1042_header);
+       WPA_PUT_BE16(pos, ETH_P_PAE);
+       pos += 2;
+       memcpy(pos, data, data_len);
 
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       msg = NULL;
-       if (ret) {
-               wpa_printf(MSG_DEBUG, "nl80211: Failed to set MAC ACL: %d (%s)",
-                          ret, strerror(-ret));
+       res = wpa_driver_nl80211_send_frame(bss, (u8 *) hdr, len, encrypt, 0,
+                                           0, 0, 0, 0);
+       if (res < 0) {
+               wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - "
+                          "failed: %d (%s)",
+                          (unsigned long) len, errno, strerror(errno));
        }
+       os_free(hdr);
 
-nla_put_failure:
-       nlmsg_free(msg);
-
-       return ret;
+       return res;
 }
 
 
-static int wpa_driver_nl80211_set_ap(void *priv,
-                                    struct wpa_driver_ap_params *params)
+static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
+                                           int total_flags,
+                                           int flags_or, int flags_and)
 {
-       struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct nl_msg *msg;
-       u8 cmd = NL80211_CMD_NEW_BEACON;
-       int ret;
-       int beacon_set;
-       int ifindex = if_nametoindex(bss->ifname);
-       int num_suites;
-       u32 suites[10];
-       u32 ver;
+       struct i802_bss *bss = priv;
+       struct nl_msg *msg;
+       struct nlattr *flags;
+       struct nl80211_sta_flag_update upd;
 
-       beacon_set = bss->beacon_set;
+       wpa_printf(MSG_DEBUG, "nl80211: Set STA flags - ifname=%s addr=" MACSTR
+                  " total_flags=0x%x flags_or=0x%x flags_and=0x%x authorized=%d",
+                  bss->ifname, MAC2STR(addr), total_flags, flags_or, flags_and,
+                  !!(total_flags & WPA_STA_AUTHORIZED));
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
+       if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
+               goto fail;
 
-       wpa_printf(MSG_DEBUG, "nl80211: Set beacon (beacon_set=%d)",
-                  beacon_set);
-       if (beacon_set)
-               cmd = NL80211_CMD_SET_BEACON;
+       /*
+        * Backwards compatibility version using NL80211_ATTR_STA_FLAGS. This
+        * can be removed eventually.
+        */
+       flags = nla_nest_start(msg, NL80211_ATTR_STA_FLAGS);
+       if (!flags ||
+           ((total_flags & WPA_STA_AUTHORIZED) &&
+            nla_put_flag(msg, NL80211_STA_FLAG_AUTHORIZED)) ||
+           ((total_flags & WPA_STA_WMM) &&
+            nla_put_flag(msg, NL80211_STA_FLAG_WME)) ||
+           ((total_flags & WPA_STA_SHORT_PREAMBLE) &&
+            nla_put_flag(msg, NL80211_STA_FLAG_SHORT_PREAMBLE)) ||
+           ((total_flags & WPA_STA_MFP) &&
+            nla_put_flag(msg, NL80211_STA_FLAG_MFP)) ||
+           ((total_flags & WPA_STA_TDLS_PEER) &&
+            nla_put_flag(msg, NL80211_STA_FLAG_TDLS_PEER)))
+               goto fail;
 
-       nl80211_cmd(drv, msg, 0, cmd);
-       wpa_hexdump(MSG_DEBUG, "nl80211: Beacon head",
-                   params->head, params->head_len);
-       NLA_PUT(msg, NL80211_ATTR_BEACON_HEAD, params->head_len, params->head);
-       wpa_hexdump(MSG_DEBUG, "nl80211: Beacon tail",
-                   params->tail, params->tail_len);
-       NLA_PUT(msg, NL80211_ATTR_BEACON_TAIL, params->tail_len, params->tail);
-       wpa_printf(MSG_DEBUG, "nl80211: ifindex=%d", ifindex);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
-       wpa_printf(MSG_DEBUG, "nl80211: beacon_int=%d", params->beacon_int);
-       NLA_PUT_U32(msg, NL80211_ATTR_BEACON_INTERVAL, params->beacon_int);
-       wpa_printf(MSG_DEBUG, "nl80211: dtim_period=%d", params->dtim_period);
-       NLA_PUT_U32(msg, NL80211_ATTR_DTIM_PERIOD, params->dtim_period);
-       wpa_hexdump_ascii(MSG_DEBUG, "nl80211: ssid",
-                         params->ssid, params->ssid_len);
-       NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
-               params->ssid);
-       if (params->proberesp && params->proberesp_len) {
-               wpa_hexdump(MSG_DEBUG, "nl80211: proberesp (offload)",
-                           params->proberesp, params->proberesp_len);
-               NLA_PUT(msg, NL80211_ATTR_PROBE_RESP, params->proberesp_len,
-                       params->proberesp);
-       }
-       switch (params->hide_ssid) {
-       case NO_SSID_HIDING:
-               wpa_printf(MSG_DEBUG, "nl80211: hidden SSID not in use");
-               NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID,
-                           NL80211_HIDDEN_SSID_NOT_IN_USE);
-               break;
-       case HIDDEN_SSID_ZERO_LEN:
-               wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero len");
-               NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID,
-                           NL80211_HIDDEN_SSID_ZERO_LEN);
-               break;
-       case HIDDEN_SSID_ZERO_CONTENTS:
-               wpa_printf(MSG_DEBUG, "nl80211: hidden SSID zero contents");
-               NLA_PUT_U32(msg, NL80211_ATTR_HIDDEN_SSID,
-                           NL80211_HIDDEN_SSID_ZERO_CONTENTS);
-               break;
-       }
-       wpa_printf(MSG_DEBUG, "nl80211: privacy=%d", params->privacy);
-       if (params->privacy)
-               NLA_PUT_FLAG(msg, NL80211_ATTR_PRIVACY);
-       wpa_printf(MSG_DEBUG, "nl80211: auth_algs=0x%x", params->auth_algs);
-       if ((params->auth_algs & (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) ==
-           (WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED)) {
-               /* Leave out the attribute */
-       } else if (params->auth_algs & WPA_AUTH_ALG_SHARED)
-               NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE,
-                           NL80211_AUTHTYPE_SHARED_KEY);
-       else
-               NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE,
-                           NL80211_AUTHTYPE_OPEN_SYSTEM);
+       nla_nest_end(msg, flags);
 
-       wpa_printf(MSG_DEBUG, "nl80211: wpa_version=0x%x", params->wpa_version);
-       ver = 0;
-       if (params->wpa_version & WPA_PROTO_WPA)
-               ver |= NL80211_WPA_VERSION_1;
-       if (params->wpa_version & WPA_PROTO_RSN)
-               ver |= NL80211_WPA_VERSION_2;
-       if (ver)
-               NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver);
+       os_memset(&upd, 0, sizeof(upd));
+       upd.mask = sta_flags_nl80211(flags_or | ~flags_and);
+       upd.set = sta_flags_nl80211(flags_or);
+       if (nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd))
+               goto fail;
 
-       wpa_printf(MSG_DEBUG, "nl80211: key_mgmt_suites=0x%x",
-                  params->key_mgmt_suites);
-       num_suites = 0;
-       if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X)
-               suites[num_suites++] = WLAN_AKM_SUITE_8021X;
-       if (params->key_mgmt_suites & WPA_KEY_MGMT_PSK)
-               suites[num_suites++] = WLAN_AKM_SUITE_PSK;
-       if (num_suites) {
-               NLA_PUT(msg, NL80211_ATTR_AKM_SUITES,
-                       num_suites * sizeof(u32), suites);
-       }
+       return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+fail:
+       nlmsg_free(msg);
+       return -ENOBUFS;
+}
 
-       if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X &&
-           params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40))
-               NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT);
 
-       wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x",
-                  params->pairwise_ciphers);
-       num_suites = 0;
-       if (params->pairwise_ciphers & WPA_CIPHER_CCMP)
-               suites[num_suites++] = WLAN_CIPHER_SUITE_CCMP;
-       if (params->pairwise_ciphers & WPA_CIPHER_GCMP)
-               suites[num_suites++] = WLAN_CIPHER_SUITE_GCMP;
-       if (params->pairwise_ciphers & WPA_CIPHER_TKIP)
-               suites[num_suites++] = WLAN_CIPHER_SUITE_TKIP;
-       if (params->pairwise_ciphers & WPA_CIPHER_WEP104)
-               suites[num_suites++] = WLAN_CIPHER_SUITE_WEP104;
-       if (params->pairwise_ciphers & WPA_CIPHER_WEP40)
-               suites[num_suites++] = WLAN_CIPHER_SUITE_WEP40;
-       if (num_suites) {
-               NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
-                       num_suites * sizeof(u32), suites);
-       }
+static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
+                                struct wpa_driver_associate_params *params)
+{
+       enum nl80211_iftype nlmode, old_mode;
 
-       wpa_printf(MSG_DEBUG, "nl80211: group_cipher=0x%x",
-                  params->group_cipher);
-       switch (params->group_cipher) {
-       case WPA_CIPHER_CCMP:
-               NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
-                           WLAN_CIPHER_SUITE_CCMP);
-               break;
-       case WPA_CIPHER_GCMP:
-               NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
-                           WLAN_CIPHER_SUITE_GCMP);
-               break;
-       case WPA_CIPHER_TKIP:
-               NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
-                           WLAN_CIPHER_SUITE_TKIP);
-               break;
-       case WPA_CIPHER_WEP104:
-               NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
-                           WLAN_CIPHER_SUITE_WEP104);
-               break;
-       case WPA_CIPHER_WEP40:
-               NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP,
-                           WLAN_CIPHER_SUITE_WEP40);
-               break;
+#ifdef BCM_DRIVER_V115
+       nlmode = NL80211_IFTYPE_AP;
+#else
+       if (params->p2p) {
+               wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P "
+                          "group (GO)");
+               nlmode = NL80211_IFTYPE_P2P_GO;
+       } else
+               nlmode = NL80211_IFTYPE_AP;
+#endif
+       old_mode = drv->nlmode;
+       if (wpa_driver_nl80211_set_mode(drv->first_bss, nlmode)) {
+               nl80211_remove_monitor_interface(drv);
+               return -1;
        }
 
-       if (params->beacon_ies) {
-               wpa_hexdump_buf(MSG_DEBUG, "nl80211: beacon_ies",
-                               params->beacon_ies);
-               NLA_PUT(msg, NL80211_ATTR_IE, wpabuf_len(params->beacon_ies),
-                       wpabuf_head(params->beacon_ies));
-       }
-       if (params->proberesp_ies) {
-               wpa_hexdump_buf(MSG_DEBUG, "nl80211: proberesp_ies",
-                               params->proberesp_ies);
-               NLA_PUT(msg, NL80211_ATTR_IE_PROBE_RESP,
-                       wpabuf_len(params->proberesp_ies),
-                       wpabuf_head(params->proberesp_ies));
-       }
-       if (params->assocresp_ies) {
-               wpa_hexdump_buf(MSG_DEBUG, "nl80211: assocresp_ies",
-                               params->assocresp_ies);
-               NLA_PUT(msg, NL80211_ATTR_IE_ASSOC_RESP,
-                       wpabuf_len(params->assocresp_ies),
-                       wpabuf_head(params->assocresp_ies));
+       if (params->freq.freq &&
+           nl80211_set_channel(drv->first_bss, &params->freq, 0)) {
+               if (old_mode != nlmode)
+                       wpa_driver_nl80211_set_mode(drv->first_bss, old_mode);
+               nl80211_remove_monitor_interface(drv);
+               return -1;
        }
 
-       if (drv->capa.flags & WPA_DRIVER_FLAGS_INACTIVITY_TIMER)  {
-               wpa_printf(MSG_DEBUG, "nl80211: ap_max_inactivity=%d",
-                          params->ap_max_inactivity);
-               NLA_PUT_U16(msg, NL80211_ATTR_INACTIVITY_TIMEOUT,
-                           params->ap_max_inactivity);
-       }
+       return 0;
+}
+
+
+static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv,
+                             int reset_mode)
+{
+       struct nl_msg *msg;
+       int ret;
 
+       msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_IBSS);
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
        if (ret) {
-               wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
-                          ret, strerror(-ret));
+               wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d "
+                          "(%s)", ret, strerror(-ret));
        } else {
-               bss->beacon_set = 1;
-               nl80211_set_bss(bss, params->cts_protect, params->preamble,
-                               params->short_slot_time, params->ht_opmode,
-                               params->isolate, params->basic_rates);
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Leave IBSS request sent successfully");
        }
 
-#ifdef TIZEN_EXT
-       wpa_driver_nl80211_probe_req_report(priv, 1);
-#endif
+       if (reset_mode &&
+           wpa_driver_nl80211_set_mode(drv->first_bss,
+                                       NL80211_IFTYPE_STATION)) {
+               wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
+                          "station mode");
+       }
 
        return ret;
- nla_put_failure:
-       nlmsg_free(msg);
-       return -ENOBUFS;
 }
 
 
-static int wpa_driver_nl80211_set_freq(struct i802_bss *bss,
-                                      struct hostapd_freq_params *freq)
+static int nl80211_ht_vht_overrides(struct nl_msg *msg,
+                                   struct wpa_driver_associate_params *params)
 {
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct nl_msg *msg;
-       int ret;
-
-       wpa_printf(MSG_DEBUG, "nl80211: Set freq %d (ht_enabled=%d, vht_enabled=%d,"
-                  " bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)",
-                  freq->freq, freq->ht_enabled, freq->vht_enabled,
-                  freq->bandwidth, freq->center_freq1, freq->center_freq2);
-       msg = nlmsg_alloc();
-       if (!msg)
+       if (params->disable_ht && nla_put_flag(msg, NL80211_ATTR_DISABLE_HT))
                return -1;
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY);
-
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq->freq);
-       if (freq->vht_enabled) {
-               switch (freq->bandwidth) {
-               case 20:
-                       NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
-                                   NL80211_CHAN_WIDTH_20);
-                       break;
-               case 40:
-                       NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
-                                   NL80211_CHAN_WIDTH_40);
-                       break;
-               case 80:
-                       if (freq->center_freq2)
-                               NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
-                                           NL80211_CHAN_WIDTH_80P80);
-                       else
-                               NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
-                                           NL80211_CHAN_WIDTH_80);
-                       break;
-               case 160:
-                       NLA_PUT_U32(msg, NL80211_ATTR_CHANNEL_WIDTH,
-                                   NL80211_CHAN_WIDTH_160);
-                       break;
-               default:
+       if (params->htcaps && params->htcaps_mask) {
+               int sz = sizeof(struct ieee80211_ht_capabilities);
+               wpa_hexdump(MSG_DEBUG, "  * htcaps", params->htcaps, sz);
+               wpa_hexdump(MSG_DEBUG, "  * htcaps_mask",
+                           params->htcaps_mask, sz);
+               if (nla_put(msg, NL80211_ATTR_HT_CAPABILITY, sz,
+                           params->htcaps) ||
+                   nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
+                           params->htcaps_mask))
                        return -1;
-               }
-               NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ1, freq->center_freq1);
-               if (freq->center_freq2)
-                       NLA_PUT_U32(msg, NL80211_ATTR_CENTER_FREQ2,
-                                   freq->center_freq2);
-       } else if (freq->ht_enabled) {
-               switch (freq->sec_channel_offset) {
-               case -1:
-                       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
-                                   NL80211_CHAN_HT40MINUS);
-                       break;
-               case 1:
-                       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
-                                   NL80211_CHAN_HT40PLUS);
-                       break;
-               default:
-                       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
-                                   NL80211_CHAN_HT20);
-                       break;
-               }
        }
 
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       msg = NULL;
-       if (ret == 0) {
-               bss->freq = freq->freq;
-               return 0;
+#ifdef CONFIG_VHT_OVERRIDES
+       if (params->disable_vht) {
+               wpa_printf(MSG_DEBUG, "  * VHT disabled");
+               if (nla_put_flag(msg, NL80211_ATTR_DISABLE_VHT))
+                       return -1;
        }
-       wpa_printf(MSG_DEBUG, "nl80211: Failed to set channel (freq=%d): "
-                  "%d (%s)", freq->freq, ret, strerror(-ret));
-nla_put_failure:
-       nlmsg_free(msg);
-       return -1;
-}
-
-
-static u32 sta_flags_nl80211(int flags)
-{
-       u32 f = 0;
 
-       if (flags & WPA_STA_AUTHORIZED)
-               f |= BIT(NL80211_STA_FLAG_AUTHORIZED);
-       if (flags & WPA_STA_WMM)
-               f |= BIT(NL80211_STA_FLAG_WME);
-       if (flags & WPA_STA_SHORT_PREAMBLE)
-               f |= BIT(NL80211_STA_FLAG_SHORT_PREAMBLE);
-       if (flags & WPA_STA_MFP)
-               f |= BIT(NL80211_STA_FLAG_MFP);
-       if (flags & WPA_STA_TDLS_PEER)
-               f |= BIT(NL80211_STA_FLAG_TDLS_PEER);
+       if (params->vhtcaps && params->vhtcaps_mask) {
+               int sz = sizeof(struct ieee80211_vht_capabilities);
+               wpa_hexdump(MSG_DEBUG, "  * vhtcaps", params->vhtcaps, sz);
+               wpa_hexdump(MSG_DEBUG, "  * vhtcaps_mask",
+                           params->vhtcaps_mask, sz);
+               if (nla_put(msg, NL80211_ATTR_VHT_CAPABILITY, sz,
+                           params->vhtcaps) ||
+                   nla_put(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz,
+                           params->vhtcaps_mask))
+                       return -1;
+       }
+#endif /* CONFIG_VHT_OVERRIDES */
 
-       return f;
+       return 0;
 }
 
 
-static int wpa_driver_nl80211_sta_add(void *priv,
-                                     struct hostapd_sta_add_params *params)
+static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv,
+                                  struct wpa_driver_associate_params *params)
 {
-       struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
-       struct nl80211_sta_flag_update upd;
-       int ret = -ENOBUFS;
-
-       if ((params->flags & WPA_STA_TDLS_PEER) &&
-           !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
-               return -EOPNOTSUPP;
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
-
-       wpa_printf(MSG_DEBUG, "nl80211: %s STA " MACSTR,
-                  params->set ? "Set" : "Add", MAC2STR(params->addr));
-       nl80211_cmd(drv, msg, 0, params->set ? NL80211_CMD_SET_STATION :
-                   NL80211_CMD_NEW_STATION);
-
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
-       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->addr);
-       NLA_PUT(msg, NL80211_ATTR_STA_SUPPORTED_RATES, params->supp_rates_len,
-               params->supp_rates);
-       wpa_hexdump(MSG_DEBUG, "  * supported rates", params->supp_rates,
-                   params->supp_rates_len);
-       if (!params->set) {
-               if (params->aid) {
-                       wpa_printf(MSG_DEBUG, "  * aid=%u", params->aid);
-                       NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, params->aid);
-               } else {
-                       /*
-                        * cfg80211 validates that AID is non-zero, so we have
-                        * to make this a non-zero value for the TDLS case where
-                        * a dummy STA entry is used for now.
-                        */
-                       wpa_printf(MSG_DEBUG, "  * aid=1 (TDLS workaround)");
-                       NLA_PUT_U16(msg, NL80211_ATTR_STA_AID, 1);
-               }
-               wpa_printf(MSG_DEBUG, "  * listen_interval=%u",
-                          params->listen_interval);
-               NLA_PUT_U16(msg, NL80211_ATTR_STA_LISTEN_INTERVAL,
-                           params->listen_interval);
-       } else if (params->aid && (params->flags & WPA_STA_TDLS_PEER)) {
-               wpa_printf(MSG_DEBUG, "  * peer_aid=%u", params->aid);
-               NLA_PUT_U16(msg, NL80211_ATTR_PEER_AID, params->aid);
-       }
-       if (params->ht_capabilities) {
-               wpa_hexdump(MSG_DEBUG, "  * ht_capabilities",
-                           (u8 *) params->ht_capabilities,
-                           sizeof(*params->ht_capabilities));
-               NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY,
-                       sizeof(*params->ht_capabilities),
-                       params->ht_capabilities);
-       }
+       int ret = -1;
+       int count = 0;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex);
 
-       if (params->vht_capabilities) {
-               wpa_hexdump(MSG_DEBUG, "  * vht_capabilities",
-                           (u8 *) params->vht_capabilities,
-                           sizeof(*params->vht_capabilities));
-               NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY,
-                       sizeof(*params->vht_capabilities),
-                       params->vht_capabilities);
+       if (wpa_driver_nl80211_set_mode_ibss(drv->first_bss, &params->freq)) {
+               wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
+                          "IBSS mode");
+               return -1;
        }
 
-       wpa_printf(MSG_DEBUG, "  * capability=0x%x", params->capability);
-       NLA_PUT_U16(msg, NL80211_ATTR_STA_CAPABILITY, params->capability);
+retry:
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_IBSS)) ||
+           params->ssid == NULL || params->ssid_len > sizeof(drv->ssid))
+               goto fail;
 
-       if (params->ext_capab) {
-               wpa_hexdump(MSG_DEBUG, "  * ext_capab",
-                           params->ext_capab, params->ext_capab_len);
-               NLA_PUT(msg, NL80211_ATTR_STA_EXT_CAPABILITY,
-                       params->ext_capab_len, params->ext_capab);
-       }
+       wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
+                         params->ssid, params->ssid_len);
+       if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid))
+               goto fail;
+       os_memcpy(drv->ssid, params->ssid, params->ssid_len);
+       drv->ssid_len = params->ssid_len;
 
-       os_memset(&upd, 0, sizeof(upd));
-       upd.mask = sta_flags_nl80211(params->flags);
-       upd.set = upd.mask;
-       wpa_printf(MSG_DEBUG, "  * flags set=0x%x mask=0x%x",
-                  upd.set, upd.mask);
-       NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd);
+       if (nl80211_put_freq_params(msg, &params->freq) < 0 ||
+           nl80211_put_beacon_int(msg, params->beacon_int))
+               goto fail;
 
-       if (params->flags & WPA_STA_WMM) {
-               struct nlattr *wme = nla_nest_start(msg, NL80211_ATTR_STA_WME);
+       ret = nl80211_set_conn_keys(params, msg);
+       if (ret)
+               goto fail;
 
-               if (!wme)
-                       goto nla_put_failure;
+       if (params->bssid && params->fixed_bssid) {
+               wpa_printf(MSG_DEBUG, "  * BSSID=" MACSTR,
+                          MAC2STR(params->bssid));
+               if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
+                       goto fail;
+       }
 
-               wpa_printf(MSG_DEBUG, "  * qosinfo=0x%x", params->qosinfo);
-               NLA_PUT_U8(msg, NL80211_STA_WME_UAPSD_QUEUES,
-                               params->qosinfo & WMM_QOSINFO_STA_AC_MASK);
-               NLA_PUT_U8(msg, NL80211_STA_WME_MAX_SP,
-                               (params->qosinfo >> WMM_QOSINFO_STA_SP_SHIFT) &
-                               WMM_QOSINFO_STA_SP_MASK);
-               nla_nest_end(msg, wme);
+       if (params->fixed_freq) {
+               wpa_printf(MSG_DEBUG, "  * fixed_freq");
+               if (nla_put_flag(msg, NL80211_ATTR_FREQ_FIXED))
+                       goto fail;
+       }
+
+       if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
+           params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+           params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
+           params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256) {
+               wpa_printf(MSG_DEBUG, "  * control port");
+               if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT))
+                       goto fail;
+       }
+
+       if (params->wpa_ie) {
+               wpa_hexdump(MSG_DEBUG,
+                           "  * Extra IEs for Beacon/Probe Response frames",
+                           params->wpa_ie, params->wpa_ie_len);
+               if (nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len,
+                           params->wpa_ie))
+                       goto fail;
        }
 
+       if (nl80211_ht_vht_overrides(msg, params) < 0)
+               return -1;
+
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
        msg = NULL;
-       if (ret)
-               wpa_printf(MSG_DEBUG, "nl80211: NL80211_CMD_%s_STATION "
-                          "result: %d (%s)", params->set ? "SET" : "NEW", ret,
-                          strerror(-ret));
-       if (ret == -EEXIST)
-               ret = 0;
- nla_put_failure:
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Join IBSS failed: ret=%d (%s)",
+                          ret, strerror(-ret));
+               count++;
+               if (ret == -EALREADY && count == 1) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Retry IBSS join after "
+                                  "forced leave");
+                       nl80211_leave_ibss(drv, 0);
+                       nlmsg_free(msg);
+                       goto retry;
+               }
+       } else {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Join IBSS request sent successfully");
+       }
+
+fail:
        nlmsg_free(msg);
        return ret;
 }
 
 
-static int wpa_driver_nl80211_sta_remove(struct i802_bss *bss, const u8 *addr)
+static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
+                                 struct wpa_driver_associate_params *params,
+                                 struct nl_msg *msg)
 {
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct nl_msg *msg;
-       int ret;
+       if (params->bssid) {
+               wpa_printf(MSG_DEBUG, "  * bssid=" MACSTR,
+                          MAC2STR(params->bssid));
+               if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid))
+                       return -1;
+       }
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
+       if (params->bssid_hint) {
+               wpa_printf(MSG_DEBUG, "  * bssid_hint=" MACSTR,
+                          MAC2STR(params->bssid_hint));
+               if (nla_put(msg, NL80211_ATTR_MAC_HINT, ETH_ALEN,
+                           params->bssid_hint))
+                       return -1;
+       }
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION);
+       if (params->freq.freq) {
+               wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq.freq);
+               if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
+                               params->freq.freq))
+                       return -1;
+               drv->assoc_freq = params->freq.freq;
+       } else
+               drv->assoc_freq = 0;
 
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
-                   if_nametoindex(bss->ifname));
-       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+       if (params->freq_hint) {
+               wpa_printf(MSG_DEBUG, "  * freq_hint=%d", params->freq_hint);
+               if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ_HINT,
+                               params->freq_hint))
+                       return -1;
+       }
 
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       if (ret == -ENOENT)
-               return 0;
-       return ret;
- nla_put_failure:
-       nlmsg_free(msg);
-       return -ENOBUFS;
-}
+       if (params->bg_scan_period >= 0) {
+               wpa_printf(MSG_DEBUG, "  * bg scan period=%d",
+                          params->bg_scan_period);
+               if (nla_put_u16(msg, NL80211_ATTR_BG_SCAN_PERIOD,
+                               params->bg_scan_period))
+                       return -1;
+       }
 
+       if (params->ssid) {
+               wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
+                                 params->ssid, params->ssid_len);
+               if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len,
+                           params->ssid))
+                       return -1;
+               if (params->ssid_len > sizeof(drv->ssid))
+                       return -1;
+               os_memcpy(drv->ssid, params->ssid, params->ssid_len);
+               drv->ssid_len = params->ssid_len;
+       }
 
-static void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv,
-                                int ifidx)
-{
-       struct nl_msg *msg;
+       wpa_hexdump(MSG_DEBUG, "  * IEs", params->wpa_ie, params->wpa_ie_len);
+       if (params->wpa_ie &&
+           nla_put(msg, NL80211_ATTR_IE, params->wpa_ie_len, params->wpa_ie))
+               return -1;
 
-       wpa_printf(MSG_DEBUG, "nl80211: Remove interface ifindex=%d", ifidx);
+       if (params->wpa_proto) {
+               enum nl80211_wpa_versions ver = 0;
 
-       /* stop listening for EAPOL on this interface */
-       del_ifidx(drv, ifidx);
+               if (params->wpa_proto & WPA_PROTO_WPA)
+                       ver |= NL80211_WPA_VERSION_1;
+               if (params->wpa_proto & WPA_PROTO_RSN)
+                       ver |= NL80211_WPA_VERSION_2;
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               goto nla_put_failure;
+               wpa_printf(MSG_DEBUG, "  * WPA Versions 0x%x", ver);
+               if (nla_put_u32(msg, NL80211_ATTR_WPA_VERSIONS, ver))
+                       return -1;
+       }
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_INTERFACE);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifidx);
+       if (params->pairwise_suite != WPA_CIPHER_NONE) {
+               u32 cipher = wpa_cipher_to_cipher_suite(params->pairwise_suite);
+               wpa_printf(MSG_DEBUG, "  * pairwise=0x%x", cipher);
+               if (nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE,
+                               cipher))
+                       return -1;
+       }
 
-       if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
-               return;
-       msg = NULL;
- nla_put_failure:
-       nlmsg_free(msg);
-       wpa_printf(MSG_ERROR, "Failed to remove interface (ifidx=%d)", ifidx);
-}
+       if (params->group_suite == WPA_CIPHER_GTK_NOT_USED &&
+           !(drv->capa.enc & WPA_DRIVER_CAPA_ENC_GTK_NOT_USED)) {
+               /*
+                * This is likely to work even though many drivers do not
+                * advertise support for operations without GTK.
+                */
+               wpa_printf(MSG_DEBUG, "  * skip group cipher configuration for GTK_NOT_USED due to missing driver support advertisement");
+       } else if (params->group_suite != WPA_CIPHER_NONE) {
+               u32 cipher = wpa_cipher_to_cipher_suite(params->group_suite);
+               wpa_printf(MSG_DEBUG, "  * group=0x%x", cipher);
+               if (nla_put_u32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher))
+                       return -1;
+       }
 
+       if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
+           params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+           params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X ||
+           params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK ||
+           params->key_mgmt_suite == WPA_KEY_MGMT_CCKM ||
+           params->key_mgmt_suite == WPA_KEY_MGMT_OSEN ||
+           params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
+           params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
+           params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
+           params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+               int mgmt = WLAN_AKM_SUITE_PSK;
 
-static const char * nl80211_iftype_str(enum nl80211_iftype mode)
-{
-       switch (mode) {
-       case NL80211_IFTYPE_ADHOC:
-               return "ADHOC";
-       case NL80211_IFTYPE_STATION:
-               return "STATION";
-       case NL80211_IFTYPE_AP:
-               return "AP";
-       case NL80211_IFTYPE_AP_VLAN:
-               return "AP_VLAN";
-       case NL80211_IFTYPE_WDS:
-               return "WDS";
-       case NL80211_IFTYPE_MONITOR:
-               return "MONITOR";
-       case NL80211_IFTYPE_MESH_POINT:
-               return "MESH_POINT";
-       case NL80211_IFTYPE_P2P_CLIENT:
-               return "P2P_CLIENT";
-       case NL80211_IFTYPE_P2P_GO:
-               return "P2P_GO";
-       case NL80211_IFTYPE_P2P_DEVICE:
-               return "P2P_DEVICE";
-       default:
-               return "unknown";
+               switch (params->key_mgmt_suite) {
+               case WPA_KEY_MGMT_CCKM:
+                       mgmt = WLAN_AKM_SUITE_CCKM;
+                       break;
+               case WPA_KEY_MGMT_IEEE8021X:
+                       mgmt = WLAN_AKM_SUITE_8021X;
+                       break;
+               case WPA_KEY_MGMT_FT_IEEE8021X:
+                       mgmt = WLAN_AKM_SUITE_FT_8021X;
+                       break;
+               case WPA_KEY_MGMT_FT_PSK:
+                       mgmt = WLAN_AKM_SUITE_FT_PSK;
+                       break;
+               case WPA_KEY_MGMT_IEEE8021X_SHA256:
+                       mgmt = WLAN_AKM_SUITE_8021X_SHA256;
+                       break;
+               case WPA_KEY_MGMT_PSK_SHA256:
+                       mgmt = WLAN_AKM_SUITE_PSK_SHA256;
+                       break;
+               case WPA_KEY_MGMT_OSEN:
+                       mgmt = WLAN_AKM_SUITE_OSEN;
+                       break;
+               case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
+                       mgmt = WLAN_AKM_SUITE_8021X_SUITE_B;
+                       break;
+               case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+                       mgmt = WLAN_AKM_SUITE_8021X_SUITE_B_192;
+                       break;
+               case WPA_KEY_MGMT_PSK:
+               default:
+                       mgmt = WLAN_AKM_SUITE_PSK;
+                       break;
+               }
+               wpa_printf(MSG_DEBUG, "  * akm=0x%x", mgmt);
+               if (nla_put_u32(msg, NL80211_ATTR_AKM_SUITES, mgmt))
+                       return -1;
+       }
+
+       if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT))
+               return -1;
+
+       if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED &&
+           nla_put_u32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED))
+               return -1;
+
+       if (params->rrm_used) {
+               u32 drv_rrm_flags = drv->capa.rrm_flags;
+               if (!(drv_rrm_flags &
+                     WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) ||
+                   !(drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET) ||
+                   nla_put_flag(msg, NL80211_ATTR_USE_RRM))
+                       return -1;
        }
+
+       if (nl80211_ht_vht_overrides(msg, params) < 0)
+               return -1;
+
+       if (params->p2p)
+               wpa_printf(MSG_DEBUG, "  * P2P group");
+
+       return 0;
 }
 
 
-static int nl80211_create_iface_once(struct wpa_driver_nl80211_data *drv,
-                                    const char *ifname,
-                                    enum nl80211_iftype iftype,
-                                    const u8 *addr, int wds,
-                                    int (*handler)(struct nl_msg *, void *),
-                                    void *arg)
+static int wpa_driver_nl80211_try_connect(
+       struct wpa_driver_nl80211_data *drv,
+       struct wpa_driver_associate_params *params)
 {
        struct nl_msg *msg;
-       int ifidx;
-       int ret = -ENOBUFS;
+       enum nl80211_auth_type type;
+       int ret;
+       int algs;
 
-       wpa_printf(MSG_DEBUG, "nl80211: Create interface iftype %d (%s)",
-                  iftype, nl80211_iftype_str(iftype));
+       if (params->req_key_mgmt_offload && params->psk &&
+           (params->key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+            params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
+            params->key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Key management set PSK");
+               ret = issue_key_mgmt_set_key(drv, params->psk, 32);
+               if (ret)
+                       return ret;
+       }
 
-       msg = nlmsg_alloc();
+       wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
+       msg = nl80211_drv_msg(drv, 0, NL80211_CMD_CONNECT);
        if (!msg)
                return -1;
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_NEW_INTERFACE);
-       if (nl80211_set_iface_id(msg, &drv->first_bss) < 0)
-               goto nla_put_failure;
-       NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, ifname);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, iftype);
+       ret = nl80211_connect_common(drv, params, msg);
+       if (ret)
+               goto fail;
 
-       if (iftype == NL80211_IFTYPE_MONITOR) {
-               struct nlattr *flags;
+       algs = 0;
+       if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+               algs++;
+       if (params->auth_alg & WPA_AUTH_ALG_SHARED)
+               algs++;
+       if (params->auth_alg & WPA_AUTH_ALG_LEAP)
+               algs++;
+       if (algs > 1) {
+               wpa_printf(MSG_DEBUG, "  * Leave out Auth Type for automatic "
+                          "selection");
+               goto skip_auth_type;
+       }
 
-               flags = nla_nest_start(msg, NL80211_ATTR_MNTR_FLAGS);
-               if (!flags)
-                       goto nla_put_failure;
+       if (params->auth_alg & WPA_AUTH_ALG_OPEN)
+               type = NL80211_AUTHTYPE_OPEN_SYSTEM;
+       else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
+               type = NL80211_AUTHTYPE_SHARED_KEY;
+       else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
+               type = NL80211_AUTHTYPE_NETWORK_EAP;
+       else if (params->auth_alg & WPA_AUTH_ALG_FT)
+               type = NL80211_AUTHTYPE_FT;
+       else
+               goto fail;
 
-               NLA_PUT_FLAG(msg, NL80211_MNTR_FLAG_COOK_FRAMES);
+       wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
+       if (nla_put_u32(msg, NL80211_ATTR_AUTH_TYPE, type))
+               goto fail;
 
-               nla_nest_end(msg, flags);
-       } else if (wds) {
-               NLA_PUT_U8(msg, NL80211_ATTR_4ADDR, wds);
+skip_auth_type:
+       ret = nl80211_set_conn_keys(params, msg);
+       if (ret)
+               goto fail;
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
+                          "(%s)", ret, strerror(-ret));
+       } else {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Connect request send successfully");
        }
 
-       ret = send_and_recv_msgs(drv, msg, handler, arg);
-       msg = NULL;
-       if (ret) {
- nla_put_failure:
-               nlmsg_free(msg);
-               wpa_printf(MSG_ERROR, "Failed to create interface %s: %d (%s)",
-                          ifname, ret, strerror(-ret));
-               return ret;
-       }
+fail:
+       nlmsg_free(msg);
+       return ret;
 
-       if (iftype == NL80211_IFTYPE_P2P_DEVICE)
-               return 0;
+}
 
-       ifidx = if_nametoindex(ifname);
-       wpa_printf(MSG_DEBUG, "nl80211: New interface %s created: ifindex=%d",
-                  ifname, ifidx);
 
-       if (ifidx <= 0)
-               return -1;
+static int wpa_driver_nl80211_connect(
+       struct wpa_driver_nl80211_data *drv,
+       struct wpa_driver_associate_params *params)
+{
+       int ret;
 
-       /* start listening for EAPOL on this interface */
-       add_ifidx(drv, ifidx);
+       /* Store the connection attempted bssid for future use */
+       if (params->bssid)
+               os_memcpy(drv->auth_attempt_bssid, params->bssid, ETH_ALEN);
+       else
+               os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN);
 
-       if (addr && iftype != NL80211_IFTYPE_MONITOR &&
-           linux_set_ifhwaddr(drv->global->ioctl_sock, ifname, addr)) {
-               nl80211_remove_iface(drv, ifidx);
-               return -1;
+       ret = wpa_driver_nl80211_try_connect(drv, params);
+       if (ret == -EALREADY) {
+               /*
+                * cfg80211 does not currently accept new connections if
+                * we are already connected. As a workaround, force
+                * disconnection and try again.
+                */
+               wpa_printf(MSG_DEBUG, "nl80211: Explicitly "
+                          "disconnecting before reassociation "
+                          "attempt");
+               if (wpa_driver_nl80211_disconnect(
+                           drv, WLAN_REASON_PREV_AUTH_NOT_VALID))
+                       return -1;
+               ret = wpa_driver_nl80211_try_connect(drv, params);
        }
-
-       return ifidx;
+       return ret;
 }
 
 
-static int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
-                               const char *ifname, enum nl80211_iftype iftype,
-                               const u8 *addr, int wds,
-                               int (*handler)(struct nl_msg *, void *),
-                               void *arg)
+static int wpa_driver_nl80211_associate(
+       void *priv, struct wpa_driver_associate_params *params)
 {
-       int ret;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ret = -1;
+       struct nl_msg *msg;
 
-       ret = nl80211_create_iface_once(drv, ifname, iftype, addr, wds, handler,
-                                       arg);
+       nl80211_unmask_11b_rates(bss);
 
-       /* if error occurred and interface exists already */
-       if (ret == -ENFILE && if_nametoindex(ifname)) {
-               wpa_printf(MSG_INFO, "Try to remove and re-create %s", ifname);
+       if (params->mode == IEEE80211_MODE_AP)
+               return wpa_driver_nl80211_ap(drv, params);
 
-               /* Try to remove the interface that was already there. */
-               nl80211_remove_iface(drv, if_nametoindex(ifname));
+       if (params->mode == IEEE80211_MODE_IBSS)
+               return wpa_driver_nl80211_ibss(drv, params);
 
-               /* Try to create the interface again */
-               ret = nl80211_create_iface_once(drv, ifname, iftype, addr,
-                                               wds, handler, arg);
+       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
+               enum nl80211_iftype nlmode = params->p2p ?
+                       NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION;
+
+               if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0)
+                       return -1;
+               return wpa_driver_nl80211_connect(drv, params);
        }
 
-       if (ret >= 0 && is_p2p_net_interface(iftype))
-               nl80211_disable_11b_rates(drv, ret, 1);
+       nl80211_mark_disconnected(drv);
 
-       return ret;
-}
+       wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)",
+                  drv->ifindex);
+       msg = nl80211_drv_msg(drv, 0, NL80211_CMD_ASSOCIATE);
+       if (!msg)
+               return -1;
 
+       ret = nl80211_connect_common(drv, params, msg);
+       if (ret)
+               goto fail;
 
-static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok)
-{
-       struct ieee80211_hdr *hdr;
-       u16 fc;
-       union wpa_event_data event;
+       if (params->prev_bssid) {
+               wpa_printf(MSG_DEBUG, "  * prev_bssid=" MACSTR,
+                          MAC2STR(params->prev_bssid));
+               if (nla_put(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN,
+                           params->prev_bssid))
+                       goto fail;
+       }
 
-       hdr = (struct ieee80211_hdr *) buf;
-       fc = le_to_host16(hdr->frame_control);
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
+       if (ret) {
+               wpa_dbg(drv->ctx, MSG_DEBUG,
+                       "nl80211: MLME command failed (assoc): ret=%d (%s)",
+                       ret, strerror(-ret));
+               nl80211_dump_scan(drv);
+       } else {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Association request send successfully");
+       }
 
-       os_memset(&event, 0, sizeof(event));
-       event.tx_status.type = WLAN_FC_GET_TYPE(fc);
-       event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
-       event.tx_status.dst = hdr->addr1;
-       event.tx_status.data = buf;
-       event.tx_status.data_len = len;
-       event.tx_status.ack = ok;
-       wpa_supplicant_event(ctx, EVENT_TX_STATUS, &event);
+fail:
+       nlmsg_free(msg);
+       return ret;
 }
 
 
-static void from_unknown_sta(struct wpa_driver_nl80211_data *drv,
-                            u8 *buf, size_t len)
+static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
+                           int ifindex, enum nl80211_iftype mode)
 {
-       struct ieee80211_hdr *hdr = (void *)buf;
-       u16 fc;
-       union wpa_event_data event;
+       struct nl_msg *msg;
+       int ret = -ENOBUFS;
 
-       if (len < sizeof(*hdr))
-               return;
+       wpa_printf(MSG_DEBUG, "nl80211: Set mode ifindex %d iftype %d (%s)",
+                  ifindex, mode, nl80211_iftype_str(mode));
 
-       fc = le_to_host16(hdr->frame_control);
+       msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_SET_INTERFACE);
+       if (!msg || nla_put_u32(msg, NL80211_ATTR_IFTYPE, mode))
+               goto fail;
 
-       os_memset(&event, 0, sizeof(event));
-       event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len);
-       event.rx_from_unknown.addr = hdr->addr2;
-       event.rx_from_unknown.wds = (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) ==
-               (WLAN_FC_FROMDS | WLAN_FC_TODS);
-       wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
+       if (!ret)
+               return 0;
+fail:
+       nlmsg_free(msg);
+       wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:"
+                  " %d (%s)", ifindex, mode, ret, strerror(-ret));
+       return ret;
 }
 
 
-static void handle_frame(struct wpa_driver_nl80211_data *drv,
-                        u8 *buf, size_t len, int datarate, int ssi_signal)
+static int wpa_driver_nl80211_set_mode_impl(
+               struct i802_bss *bss,
+               enum nl80211_iftype nlmode,
+               struct hostapd_freq_params *desired_freq_params)
 {
-       struct ieee80211_hdr *hdr;
-       u16 fc;
-       union wpa_event_data event;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ret = -1;
+       int i;
+       int was_ap = is_ap_interface(drv->nlmode);
+       int res;
+       int mode_switch_res;
 
-       hdr = (struct ieee80211_hdr *) buf;
-       fc = le_to_host16(hdr->frame_control);
+       mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode);
+       if (mode_switch_res && nlmode == nl80211_get_ifmode(bss))
+               mode_switch_res = 0;
 
-       switch (WLAN_FC_GET_TYPE(fc)) {
-       case WLAN_FC_TYPE_MGMT:
-               os_memset(&event, 0, sizeof(event));
-               event.rx_mgmt.frame = buf;
-               event.rx_mgmt.frame_len = len;
-               event.rx_mgmt.datarate = datarate;
-               event.rx_mgmt.ssi_signal = ssi_signal;
-               wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
-               break;
-       case WLAN_FC_TYPE_CTRL:
-               /* can only get here with PS-Poll frames */
-               wpa_printf(MSG_DEBUG, "CTRL");
-               from_unknown_sta(drv, buf, len);
-               break;
-       case WLAN_FC_TYPE_DATA:
-               from_unknown_sta(drv, buf, len);
-               break;
+       if (mode_switch_res == 0) {
+               drv->nlmode = nlmode;
+               ret = 0;
+               goto done;
        }
-}
-
-
-static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
-{
-       struct wpa_driver_nl80211_data *drv = eloop_ctx;
-       int len;
-       unsigned char buf[3000];
-       struct ieee80211_radiotap_iterator iter;
-       int ret;
-       int datarate = 0, ssi_signal = 0;
-       int injected = 0, failed = 0, rxflags = 0;
 
-       len = recv(sock, buf, sizeof(buf), 0);
-       if (len < 0) {
-               perror("recv");
-               return;
-       }
+       if (mode_switch_res == -ENODEV)
+               return -1;
 
-       if (ieee80211_radiotap_iterator_init(&iter, (void*)buf, len)) {
-               printf("received invalid radiotap frame\n");
-               return;
+       if (nlmode == drv->nlmode) {
+               wpa_printf(MSG_DEBUG, "nl80211: Interface already in "
+                          "requested mode - ignore error");
+               ret = 0;
+               goto done; /* Already in the requested mode */
        }
 
-       while (1) {
-               ret = ieee80211_radiotap_iterator_next(&iter);
-               if (ret == -ENOENT)
-                       break;
-               if (ret) {
-                       printf("received invalid radiotap frame (%d)\n", ret);
-                       return;
-               }
-               switch (iter.this_arg_index) {
-               case IEEE80211_RADIOTAP_FLAGS:
-                       if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS)
-                               len -= 4;
-                       break;
-               case IEEE80211_RADIOTAP_RX_FLAGS:
-                       rxflags = 1;
-                       break;
-               case IEEE80211_RADIOTAP_TX_FLAGS:
-                       injected = 1;
-                       failed = le_to_host16((*(uint16_t *) iter.this_arg)) &
-                                       IEEE80211_RADIOTAP_F_TX_FAIL;
-                       break;
-               case IEEE80211_RADIOTAP_DATA_RETRIES:
-                       break;
-               case IEEE80211_RADIOTAP_CHANNEL:
-                       /* TODO: convert from freq/flags to channel number */
-                       break;
-               case IEEE80211_RADIOTAP_RATE:
-                       datarate = *iter.this_arg * 5;
-                       break;
-               case IEEE80211_RADIOTAP_DBM_ANTSIGNAL:
-                       ssi_signal = (s8) *iter.this_arg;
+       /* mac80211 doesn't allow mode changes while the device is up, so
+        * take the device down, try to set the mode again, and bring the
+        * device back up.
+        */
+       wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting "
+                  "interface down");
+       for (i = 0; i < 10; i++) {
+               res = i802_set_iface_flags(bss, 0);
+               if (res == -EACCES || res == -ENODEV)
                        break;
+               if (res != 0) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Failed to set "
+                                  "interface down");
+                       os_sleep(0, 100000);
+                       continue;
                }
-       }
-
-       if (rxflags && injected)
-               return;
-
-       if (!injected)
-               handle_frame(drv, buf + iter.max_length,
-                            len - iter.max_length, datarate, ssi_signal);
-       else
-               handle_tx_callback(drv->ctx, buf + iter.max_length,
-                                  len - iter.max_length, !failed);
-}
 
+               /*
+                * Setting the mode will fail for some drivers if the phy is
+                * on a frequency that the mode is disallowed in.
+                */
+               if (desired_freq_params) {
+                       res = nl80211_set_channel(bss, desired_freq_params, 0);
+                       if (res) {
+                               wpa_printf(MSG_DEBUG,
+                                          "nl80211: Failed to set frequency on interface");
+                       }
+               }
 
-/*
- * we post-process the filter code later and rewrite
- * this to the offset to the last instruction
- */
-#define PASS   0xFF
-#define FAIL   0xFE
+               /* Try to set the mode again while the interface is down */
+               mode_switch_res = nl80211_set_mode(drv, drv->ifindex, nlmode);
+               if (mode_switch_res == -EBUSY) {
+                       wpa_printf(MSG_DEBUG,
+                                  "nl80211: Delaying mode set while interface going down");
+                       os_sleep(0, 100000);
+                       continue;
+               }
+               ret = mode_switch_res;
+               break;
+       }
 
-static struct sock_filter msock_filter_insns[] = {
-       /*
-        * do a little-endian load of the radiotap length field
-        */
-       /* load lower byte into A */
-       BPF_STMT(BPF_LD  | BPF_B | BPF_ABS, 2),
-       /* put it into X (== index register) */
-       BPF_STMT(BPF_MISC| BPF_TAX, 0),
-       /* load upper byte into A */
-       BPF_STMT(BPF_LD  | BPF_B | BPF_ABS, 3),
-       /* left-shift it by 8 */
-       BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 8),
-       /* or with X */
-       BPF_STMT(BPF_ALU | BPF_OR | BPF_X, 0),
-       /* put result into X */
-       BPF_STMT(BPF_MISC| BPF_TAX, 0),
+       if (!ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while "
+                          "interface is down");
+               drv->nlmode = nlmode;
+               drv->ignore_if_down_event = 1;
+       }
 
-       /*
-        * Allow management frames through, this also gives us those
-        * management frames that we sent ourselves with status
-        */
-       /* load the lower byte of the IEEE 802.11 frame control field */
-       BPF_STMT(BPF_LD  | BPF_B | BPF_IND, 0),
-       /* mask off frame type and version */
-       BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xF),
-       /* accept frame if it's both 0, fall through otherwise */
-       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, PASS, 0),
+       /* Bring the interface back up */
+       res = linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1);
+       if (res != 0) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Failed to set interface up after switching mode");
+               ret = -1;
+       }
 
-       /*
-        * TODO: add a bit to radiotap RX flags that indicates
-        * that the sending station is not associated, then
-        * add a filter here that filters on our DA and that flag
-        * to allow us to deauth frames to that bad station.
-        *
-        * For now allow all To DS data frames through.
-        */
-       /* load the IEEE 802.11 frame control field */
-       BPF_STMT(BPF_LD  | BPF_H | BPF_IND, 0),
-       /* mask off frame type, version and DS status */
-       BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0F03),
-       /* accept frame if version 0, type 2 and To DS, fall through otherwise
-        */
-       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0801, PASS, 0),
+done:
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d "
+                          "from %d failed", nlmode, drv->nlmode);
+               return ret;
+       }
 
-#if 0
-       /*
-        * drop non-data frames
-        */
-       /* load the lower byte of the frame control field */
-       BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 0),
-       /* mask off QoS bit */
-       BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x0c),
-       /* drop non-data frames */
-       BPF_JUMP(BPF_JMP  | BPF_JEQ | BPF_K, 8, 0, FAIL),
-#endif
-       /* load the upper byte of the frame control field */
-       BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 1),
-       /* mask off toDS/fromDS */
-       BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x03),
-       /* accept WDS frames */
-       BPF_JUMP(BPF_JMP  | BPF_JEQ | BPF_K, 3, PASS, 0),
+       if (is_p2p_net_interface(nlmode)) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Interface %s mode change to P2P - disable 11b rates",
+                          bss->ifname);
+               nl80211_disable_11b_rates(drv, drv->ifindex, 1);
+       } else if (drv->disabled_11b_rates) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Interface %s mode changed to non-P2P - re-enable 11b rates",
+                          bss->ifname);
+               nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+       }
 
-       /*
-        * add header length to index
-        */
-       /* load the lower byte of the frame control field */
-       BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 0),
-       /* mask off QoS bit */
-       BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x80),
-       /* right shift it by 6 to give 0 or 2 */
-       BPF_STMT(BPF_ALU  | BPF_RSH | BPF_K, 6),
-       /* add data frame header length */
-       BPF_STMT(BPF_ALU  | BPF_ADD | BPF_K, 24),
-       /* add index, was start of 802.11 header */
-       BPF_STMT(BPF_ALU  | BPF_ADD | BPF_X, 0),
-       /* move to index, now start of LL header */
-       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+       if (is_ap_interface(nlmode)) {
+               nl80211_mgmt_unsubscribe(bss, "start AP");
+               /* Setup additional AP mode functionality if needed */
+               if (nl80211_setup_ap(bss))
+                       return -1;
+       } else if (was_ap) {
+               /* Remove additional AP mode functionality */
+               nl80211_teardown_ap(bss);
+       } else {
+               nl80211_mgmt_unsubscribe(bss, "mode change");
+       }
 
-       /*
-        * Accept empty data frames, we use those for
-        * polling activity.
-        */
-       BPF_STMT(BPF_LD  | BPF_W | BPF_LEN, 0),
-       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, PASS, 0),
+       if (is_mesh_interface(nlmode) &&
+           nl80211_mgmt_subscribe_mesh(bss))
+               return -1;
 
-       /*
-        * Accept EAPOL frames
-        */
-       BPF_STMT(BPF_LD  | BPF_W | BPF_IND, 0),
-       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA0300, 0, FAIL),
-       BPF_STMT(BPF_LD  | BPF_W | BPF_IND, 4),
-       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0000888E, PASS, FAIL),
-
-       /* keep these last two statements or change the code below */
-       /* return 0 == "DROP" */
-       BPF_STMT(BPF_RET | BPF_K, 0),
-       /* return ~0 == "keep all" */
-       BPF_STMT(BPF_RET | BPF_K, ~0),
-};
+       if (!bss->in_deinit && !is_ap_interface(nlmode) &&
+           !is_mesh_interface(nlmode) &&
+           nl80211_mgmt_subscribe_non_ap(bss) < 0)
+               wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action "
+                          "frame processing - ignore for now");
 
-static struct sock_fprog msock_filter = {
-       .len = sizeof(msock_filter_insns)/sizeof(msock_filter_insns[0]),
-       .filter = msock_filter_insns,
-};
+       return 0;
+}
 
 
-static int add_monitor_filter(int s)
+int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
+                               enum nl80211_iftype nlmode)
 {
-       int idx;
+       return wpa_driver_nl80211_set_mode_impl(bss, nlmode, NULL);
+}
 
-       /* rewrite all PASS/FAIL jump offsets */
-       for (idx = 0; idx < msock_filter.len; idx++) {
-               struct sock_filter *insn = &msock_filter_insns[idx];
 
-               if (BPF_CLASS(insn->code) == BPF_JMP) {
-                       if (insn->code == (BPF_JMP|BPF_JA)) {
-                               if (insn->k == PASS)
-                                       insn->k = msock_filter.len - idx - 2;
-                               else if (insn->k == FAIL)
-                                       insn->k = msock_filter.len - idx - 3;
-                       }
+static int wpa_driver_nl80211_set_mode_ibss(struct i802_bss *bss,
+                                           struct hostapd_freq_params *freq)
+{
+       return wpa_driver_nl80211_set_mode_impl(bss, NL80211_IFTYPE_ADHOC,
+                                               freq);
+}
 
-                       if (insn->jt == PASS)
-                               insn->jt = msock_filter.len - idx - 2;
-                       else if (insn->jt == FAIL)
-                               insn->jt = msock_filter.len - idx - 3;
 
-                       if (insn->jf == PASS)
-                               insn->jf = msock_filter.len - idx - 2;
-                       else if (insn->jf == FAIL)
-                               insn->jf = msock_filter.len - idx - 3;
-               }
-       }
+static int wpa_driver_nl80211_get_capa(void *priv,
+                                      struct wpa_driver_capa *capa)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
 
-       if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER,
-                      &msock_filter, sizeof(msock_filter))) {
-               perror("SO_ATTACH_FILTER");
+       if (!drv->has_capability)
                return -1;
+       os_memcpy(capa, &drv->capa, sizeof(*capa));
+       if (drv->extended_capa && drv->extended_capa_mask) {
+               capa->extended_capa = drv->extended_capa;
+               capa->extended_capa_mask = drv->extended_capa_mask;
+               capa->extended_capa_len = drv->extended_capa_len;
        }
 
        return 0;
 }
 
 
-static void nl80211_remove_monitor_interface(
-       struct wpa_driver_nl80211_data *drv)
+static int wpa_driver_nl80211_set_operstate(void *priv, int state)
 {
-       drv->monitor_refcount--;
-       if (drv->monitor_refcount > 0)
-               return;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
 
-       if (drv->monitor_ifidx >= 0) {
-               nl80211_remove_iface(drv, drv->monitor_ifidx);
-               drv->monitor_ifidx = -1;
-       }
-       if (drv->monitor_sock >= 0) {
-               eloop_unregister_read_sock(drv->monitor_sock);
-               close(drv->monitor_sock);
-               drv->monitor_sock = -1;
-       }
+       wpa_printf(MSG_DEBUG, "nl80211: Set %s operstate %d->%d (%s)",
+                  bss->ifname, drv->operstate, state,
+                  state ? "UP" : "DORMANT");
+       drv->operstate = state;
+       return netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, -1,
+                                     state ? IF_OPER_UP : IF_OPER_DORMANT);
 }
 
 
-static int
-nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
+static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized)
 {
-       char buf[IFNAMSIZ];
-       struct sockaddr_ll ll;
-       int optval;
-       socklen_t optlen;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       struct nl80211_sta_flag_update upd;
+       int ret;
 
-       if (drv->monitor_ifidx >= 0) {
-               drv->monitor_refcount++;
+       if (!drv->associated && is_zero_ether_addr(drv->bssid) && !authorized) {
+               wpa_printf(MSG_DEBUG, "nl80211: Skip set_supp_port(unauthorized) while not associated");
                return 0;
        }
 
-       if (os_strncmp(drv->first_bss.ifname, "p2p-", 4) == 0) {
-               /*
-                * P2P interface name is of the format p2p-%s-%d. For monitor
-                * interface name corresponding to P2P GO, replace "p2p-" with
-                * "mon-" to retain the same interface name length and to
-                * indicate that it is a monitor interface.
-                */
-               snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss.ifname + 4);
-       } else {
-               /* Non-P2P interface with AP functionality. */
-               snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss.ifname);
-       }
-
-       buf[IFNAMSIZ - 1] = '\0';
-
-       drv->monitor_ifidx =
-               nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL,
-                                    0, NULL, NULL);
-
-       if (drv->monitor_ifidx == -EOPNOTSUPP) {
-               /*
-                * This is backward compatibility for a few versions of
-                * the kernel only that didn't advertise the right
-                * attributes for the only driver that then supported
-                * AP mode w/o monitor -- ath6kl.
-                */
-               wpa_printf(MSG_DEBUG, "nl80211: Driver does not support "
-                          "monitor interface type - try to run without it");
-               drv->device_ap_sme = 1;
-       }
-
-       if (drv->monitor_ifidx < 0)
-               return -1;
+       wpa_printf(MSG_DEBUG, "nl80211: Set supplicant port %sauthorized for "
+                  MACSTR, authorized ? "" : "un", MAC2STR(drv->bssid));
 
-       if (linux_set_iface_flags(drv->global->ioctl_sock, buf, 1))
-               goto error;
+       os_memset(&upd, 0, sizeof(upd));
+       upd.mask = BIT(NL80211_STA_FLAG_AUTHORIZED);
+       if (authorized)
+               upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED);
 
-       memset(&ll, 0, sizeof(ll));
-       ll.sll_family = AF_PACKET;
-       ll.sll_ifindex = drv->monitor_ifidx;
-       drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
-       if (drv->monitor_sock < 0) {
-               perror("socket[PF_PACKET,SOCK_RAW]");
-               goto error;
+       if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid) ||
+           nla_put(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd)) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
        }
 
-       if (add_monitor_filter(drv->monitor_sock)) {
-               wpa_printf(MSG_INFO, "Failed to set socket filter for monitor "
-                          "interface; do filtering in user space");
-               /* This works, but will cost in performance. */
-       }
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (!ret)
+               return 0;
+       wpa_printf(MSG_DEBUG, "nl80211: Failed to set STA flag: %d (%s)",
+                  ret, strerror(-ret));
+       return ret;
+}
 
-       if (bind(drv->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
-               perror("monitor socket bind");
-               goto error;
-       }
 
-       optlen = sizeof(optval);
-       optval = 20;
-       if (setsockopt
-           (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) {
-               perror("Failed to set socket priority");
-               goto error;
-       }
+/* Set kernel driver on given frequency (MHz) */
+static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
+{
+       struct i802_bss *bss = priv;
+       return nl80211_set_channel(bss, freq, 0);
+}
 
-       if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read,
-                                    drv, NULL)) {
-               printf("Could not register monitor read socket\n");
-               goto error;
-       }
 
-       return 0;
- error:
-       nl80211_remove_monitor_interface(drv);
-       return -1;
+static inline int min_int(int a, int b)
+{
+       if (a < b)
+               return a;
+       return b;
 }
 
 
-static int nl80211_setup_ap(struct i802_bss *bss)
+static int get_key_handler(struct nl_msg *msg, void *arg)
 {
-       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
 
-       wpa_printf(MSG_DEBUG, "nl80211: Setup AP - device_ap_sme=%d "
-                  "use_monitor=%d", drv->device_ap_sme, drv->use_monitor);
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
 
        /*
-        * Disable Probe Request reporting unless we need it in this way for
-        * devices that include the AP SME, in the other case (unless using
-        * monitor iface) we'll get it through the nl_mgmt socket instead.
+        * TODO: validate the key index and mac address!
+        * Otherwise, there's a race condition as soon as
+        * the kernel starts sending key notifications.
         */
-       if (!drv->device_ap_sme)
-               wpa_driver_nl80211_probe_req_report(bss, 0);
-
-       if (!drv->device_ap_sme && !drv->use_monitor)
-               if (nl80211_mgmt_subscribe_ap(bss))
-                       return -1;
-
-       if (drv->device_ap_sme && !drv->use_monitor)
-               if (nl80211_mgmt_subscribe_ap_dev_sme(bss))
-                       return -1;
-
-       if (!drv->device_ap_sme && drv->use_monitor &&
-           nl80211_create_monitor_interface(drv) &&
-           !drv->device_ap_sme)
-               return -1;
-
-       if (drv->device_ap_sme &&
-           wpa_driver_nl80211_probe_req_report(bss, 1) < 0) {
-               wpa_printf(MSG_DEBUG, "nl80211: Failed to enable "
-                          "Probe Request frame reporting in AP mode");
-               /* Try to survive without this */
-       }
 
-       return 0;
+       if (tb[NL80211_ATTR_KEY_SEQ])
+               memcpy(arg, nla_data(tb[NL80211_ATTR_KEY_SEQ]),
+                      min_int(nla_len(tb[NL80211_ATTR_KEY_SEQ]), 6));
+       return NL_SKIP;
 }
 
 
-static void nl80211_teardown_ap(struct i802_bss *bss)
+static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr,
+                          int idx, u8 *seq)
 {
+       struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
 
-       if (drv->device_ap_sme) {
-               wpa_driver_nl80211_probe_req_report(bss, 0);
-               if (!drv->use_monitor)
-                       nl80211_mgmt_unsubscribe(bss, "AP teardown (dev SME)");
-       } else if (drv->use_monitor)
-               nl80211_remove_monitor_interface(drv);
-       else
-               nl80211_mgmt_unsubscribe(bss, "AP teardown");
-
-       bss->beacon_set = 0;
-}
-
-
-static int nl80211_send_eapol_data(struct i802_bss *bss,
-                                  const u8 *addr, const u8 *data,
-                                  size_t data_len)
-{
-       struct sockaddr_ll ll;
-       int ret;
-
-       if (bss->drv->eapol_tx_sock < 0) {
-               wpa_printf(MSG_DEBUG, "nl80211: No socket to send EAPOL");
-               return -1;
+       msg = nl80211_ifindex_msg(drv, if_nametoindex(iface), 0,
+                                 NL80211_CMD_GET_KEY);
+       if (!msg ||
+           (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) ||
+           nla_put_u8(msg, NL80211_ATTR_KEY_IDX, idx)) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
        }
 
-       os_memset(&ll, 0, sizeof(ll));
-       ll.sll_family = AF_PACKET;
-       ll.sll_ifindex = bss->ifindex;
-       ll.sll_protocol = htons(ETH_P_PAE);
-       ll.sll_halen = ETH_ALEN;
-       os_memcpy(ll.sll_addr, addr, ETH_ALEN);
-       ret = sendto(bss->drv->eapol_tx_sock, data, data_len, 0,
-                    (struct sockaddr *) &ll, sizeof(ll));
-       if (ret < 0)
-               wpa_printf(MSG_ERROR, "nl80211: EAPOL TX: %s",
-                          strerror(errno));
+       memset(seq, 0, 6);
 
-       return ret;
+       return send_and_recv_msgs(drv, msg, get_key_handler, seq);
 }
 
 
-static const u8 rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 };
-
-static int wpa_driver_nl80211_hapd_send_eapol(
-       void *priv, const u8 *addr, const u8 *data,
-       size_t data_len, int encrypt, const u8 *own_addr, u32 flags)
+static int i802_set_rts(void *priv, int rts)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct ieee80211_hdr *hdr;
-       size_t len;
-       u8 *pos;
-       int res;
-       int qos = flags & WPA_STA_WMM;
-
-       if (drv->device_ap_sme || !drv->use_monitor)
-               return nl80211_send_eapol_data(bss, addr, data, data_len);
-
-       len = sizeof(*hdr) + (qos ? 2 : 0) + sizeof(rfc1042_header) + 2 +
-               data_len;
-       hdr = os_zalloc(len);
-       if (hdr == NULL) {
-               printf("malloc() failed for i802_send_data(len=%lu)\n",
-                      (unsigned long) len);
-               return -1;
-       }
-
-       hdr->frame_control =
-               IEEE80211_FC(WLAN_FC_TYPE_DATA, WLAN_FC_STYPE_DATA);
-       hdr->frame_control |= host_to_le16(WLAN_FC_FROMDS);
-       if (encrypt)
-               hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
-       if (qos) {
-               hdr->frame_control |=
-                       host_to_le16(WLAN_FC_STYPE_QOS_DATA << 4);
-       }
-
-       memcpy(hdr->IEEE80211_DA_FROMDS, addr, ETH_ALEN);
-       memcpy(hdr->IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
-       memcpy(hdr->IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
-       pos = (u8 *) (hdr + 1);
-
-       if (qos) {
-               /* Set highest priority in QoS header */
-               pos[0] = 7;
-               pos[1] = 0;
-               pos += 2;
-       }
+       struct nl_msg *msg;
+       int ret;
+       u32 val;
 
-       memcpy(pos, rfc1042_header, sizeof(rfc1042_header));
-       pos += sizeof(rfc1042_header);
-       WPA_PUT_BE16(pos, ETH_P_PAE);
-       pos += 2;
-       memcpy(pos, data, data_len);
+       if (rts >= 2347)
+               val = (u32) -1;
+       else
+               val = rts;
 
-       res = wpa_driver_nl80211_send_frame(bss, (u8 *) hdr, len, encrypt, 0,
-                                           0, 0, 0, 0);
-       if (res < 0) {
-               wpa_printf(MSG_ERROR, "i802_send_eapol - packet len: %lu - "
-                          "failed: %d (%s)",
-                          (unsigned long) len, errno, strerror(errno));
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) ||
+           nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val)) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
        }
-       os_free(hdr);
 
-       return res;
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (!ret)
+               return 0;
+       wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: "
+                  "%d (%s)", rts, ret, strerror(-ret));
+       return ret;
 }
 
 
-static int wpa_driver_nl80211_sta_set_flags(void *priv, const u8 *addr,
-                                           int total_flags,
-                                           int flags_or, int flags_and)
+static int i802_set_frag(void *priv, int frag)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
-       struct nlattr *flags;
-       struct nl80211_sta_flag_update upd;
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
-
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION);
-
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
-                   if_nametoindex(bss->ifname));
-       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
-
-       /*
-        * Backwards compatibility version using NL80211_ATTR_STA_FLAGS. This
-        * can be removed eventually.
-        */
-       flags = nla_nest_start(msg, NL80211_ATTR_STA_FLAGS);
-       if (!flags)
-               goto nla_put_failure;
-       if (total_flags & WPA_STA_AUTHORIZED)
-               NLA_PUT_FLAG(msg, NL80211_STA_FLAG_AUTHORIZED);
+       int ret;
+       u32 val;
 
-       if (total_flags & WPA_STA_WMM)
-               NLA_PUT_FLAG(msg, NL80211_STA_FLAG_WME);
+       if (frag >= 2346)
+               val = (u32) -1;
+       else
+               val = frag;
 
-       if (total_flags & WPA_STA_SHORT_PREAMBLE)
-               NLA_PUT_FLAG(msg, NL80211_STA_FLAG_SHORT_PREAMBLE);
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WIPHY)) ||
+           nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val)) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
+       }
 
-       if (total_flags & WPA_STA_MFP)
-               NLA_PUT_FLAG(msg, NL80211_STA_FLAG_MFP);
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (!ret)
+               return 0;
+       wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold "
+                  "%d: %d (%s)", frag, ret, strerror(-ret));
+       return ret;
+}
 
-       if (total_flags & WPA_STA_TDLS_PEER)
-               NLA_PUT_FLAG(msg, NL80211_STA_FLAG_TDLS_PEER);
 
-       nla_nest_end(msg, flags);
+static int i802_flush(void *priv)
+{
+       struct i802_bss *bss = priv;
+       struct nl_msg *msg;
+       int res;
 
-       os_memset(&upd, 0, sizeof(upd));
-       upd.mask = sta_flags_nl80211(flags_or | ~flags_and);
-       upd.set = sta_flags_nl80211(flags_or);
-       NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd);
+       wpa_printf(MSG_DEBUG, "nl80211: flush -> DEL_STATION %s (all)",
+                  bss->ifname);
 
-       return send_and_recv_msgs(drv, msg, NULL, NULL);
- nla_put_failure:
-       nlmsg_free(msg);
-       return -ENOBUFS;
+       /*
+        * XXX: FIX! this needs to flush all VLANs too
+        */
+       msg = nl80211_bss_msg(bss, 0, NL80211_CMD_DEL_STATION);
+       res = send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+       if (res) {
+               wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d "
+                          "(%s)", res, strerror(-res));
+       }
+       return res;
 }
 
 
-static int wpa_driver_nl80211_ap(struct wpa_driver_nl80211_data *drv,
-                                struct wpa_driver_associate_params *params)
+static int get_sta_handler(struct nl_msg *msg, void *arg)
 {
-       enum nl80211_iftype nlmode, old_mode;
-       struct hostapd_freq_params freq = {
-               .freq = params->freq,
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct hostap_sta_driver_data *data = arg;
+       struct nlattr *stats[NL80211_STA_INFO_MAX + 1];
+       static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
+               [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
+               [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
+               [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
+               [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
+               [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
+               [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
        };
 
-#ifdef TIZEN_EXT
-       nlmode = NL80211_IFTYPE_AP;
-#else
-       if (params->p2p) {
-               wpa_printf(MSG_DEBUG, "nl80211: Setup AP operations for P2P "
-                          "group (GO)");
-               nlmode = NL80211_IFTYPE_P2P_GO;
-       } else
-               nlmode = NL80211_IFTYPE_AP;
-#endif
-       old_mode = drv->nlmode;
-       if (wpa_driver_nl80211_set_mode(&drv->first_bss, nlmode)) {
-               nl80211_remove_monitor_interface(drv);
-               return -1;
-       }
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
 
-       if (wpa_driver_nl80211_set_freq(&drv->first_bss, &freq)) {
-               if (old_mode != nlmode)
-                       wpa_driver_nl80211_set_mode(&drv->first_bss, old_mode);
-               nl80211_remove_monitor_interface(drv);
-               return -1;
+       /*
+        * TODO: validate the interface and mac address!
+        * Otherwise, there's a race condition as soon as
+        * the kernel starts sending station notifications.
+        */
+
+       if (!tb[NL80211_ATTR_STA_INFO]) {
+               wpa_printf(MSG_DEBUG, "sta stats missing!");
+               return NL_SKIP;
+       }
+       if (nla_parse_nested(stats, NL80211_STA_INFO_MAX,
+                            tb[NL80211_ATTR_STA_INFO],
+                            stats_policy)) {
+               wpa_printf(MSG_DEBUG, "failed to parse nested attributes!");
+               return NL_SKIP;
        }
 
-       return 0;
-}
+       if (stats[NL80211_STA_INFO_INACTIVE_TIME])
+               data->inactive_msec =
+                       nla_get_u32(stats[NL80211_STA_INFO_INACTIVE_TIME]);
+       if (stats[NL80211_STA_INFO_RX_BYTES])
+               data->rx_bytes = nla_get_u32(stats[NL80211_STA_INFO_RX_BYTES]);
+       if (stats[NL80211_STA_INFO_TX_BYTES])
+               data->tx_bytes = nla_get_u32(stats[NL80211_STA_INFO_TX_BYTES]);
+       if (stats[NL80211_STA_INFO_RX_PACKETS])
+               data->rx_packets =
+                       nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]);
+       if (stats[NL80211_STA_INFO_TX_PACKETS])
+               data->tx_packets =
+                       nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]);
+       if (stats[NL80211_STA_INFO_TX_FAILED])
+               data->tx_retry_failed =
+                       nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]);
 
+       return NL_SKIP;
+}
 
-static int nl80211_leave_ibss(struct wpa_driver_nl80211_data *drv)
+static int i802_read_sta_data(struct i802_bss *bss,
+                             struct hostap_sta_driver_data *data,
+                             const u8 *addr)
 {
        struct nl_msg *msg;
-       int ret = -1;
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -1;
+       os_memset(data, 0, sizeof(*data));
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_LEAVE_IBSS);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       msg = NULL;
-       if (ret) {
-               wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS failed: ret=%d "
-                          "(%s)", ret, strerror(-ret));
-               goto nla_put_failure;
+       if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_GET_STATION)) ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
        }
 
-       ret = 0;
-       wpa_printf(MSG_DEBUG, "nl80211: Leave IBSS request sent successfully");
-
-nla_put_failure:
-       nlmsg_free(msg);
-       return ret;
+       return send_and_recv_msgs(bss->drv, msg, get_sta_handler, data);
 }
 
 
-static int wpa_driver_nl80211_ibss(struct wpa_driver_nl80211_data *drv,
-                                  struct wpa_driver_associate_params *params)
+static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
+                                   int cw_min, int cw_max, int burst_time)
 {
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
-       int ret = -1;
-       int count = 0;
-
-       wpa_printf(MSG_DEBUG, "nl80211: Join IBSS (ifindex=%d)", drv->ifindex);
-
-       if (wpa_driver_nl80211_set_mode(&drv->first_bss,
-                                       NL80211_IFTYPE_ADHOC)) {
-               wpa_printf(MSG_INFO, "nl80211: Failed to set interface into "
-                          "IBSS mode");
-               return -1;
-       }
+       struct nlattr *txq, *params;
 
-retry:
-       msg = nlmsg_alloc();
+       msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_WIPHY);
        if (!msg)
                return -1;
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_JOIN_IBSS);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       txq = nla_nest_start(msg, NL80211_ATTR_WIPHY_TXQ_PARAMS);
+       if (!txq)
+               goto fail;
 
-       if (params->ssid == NULL || params->ssid_len > sizeof(drv->ssid))
-               goto nla_put_failure;
+       /* We are only sending parameters for a single TXQ at a time */
+       params = nla_nest_start(msg, 1);
+       if (!params)
+               goto fail;
 
-       wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
-                         params->ssid, params->ssid_len);
-       NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
-               params->ssid);
-       os_memcpy(drv->ssid, params->ssid, params->ssid_len);
-       drv->ssid_len = params->ssid_len;
+       switch (queue) {
+       case 0:
+               if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VO))
+                       goto fail;
+               break;
+       case 1:
+               if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VI))
+                       goto fail;
+               break;
+       case 2:
+               if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BE))
+                       goto fail;
+               break;
+       case 3:
+               if (nla_put_u8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BK))
+                       goto fail;
+               break;
+       }
+       /* Burst time is configured in units of 0.1 msec and TXOP parameter in
+        * 32 usec, so need to convert the value here. */
+       if (nla_put_u16(msg, NL80211_TXQ_ATTR_TXOP,
+                       (burst_time * 100 + 16) / 32) ||
+           nla_put_u16(msg, NL80211_TXQ_ATTR_CWMIN, cw_min) ||
+           nla_put_u16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max) ||
+           nla_put_u8(msg, NL80211_TXQ_ATTR_AIFS, aifs))
+               goto fail;
 
-       wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
-       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
+       nla_nest_end(msg, params);
 
-       ret = nl80211_set_conn_keys(params, msg);
-       if (ret)
-               goto nla_put_failure;
+       nla_nest_end(msg, txq);
 
-       if (params->bssid && params->fixed_bssid) {
-               wpa_printf(MSG_DEBUG, "  * BSSID=" MACSTR,
-                          MAC2STR(params->bssid));
-               NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
-       }
+       if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
+               return 0;
+       msg = NULL;
+fail:
+       nlmsg_free(msg);
+       return -1;
+}
 
-       if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
-           params->key_mgmt_suite == KEY_MGMT_PSK ||
-           params->key_mgmt_suite == KEY_MGMT_802_1X_SHA256 ||
-           params->key_mgmt_suite == KEY_MGMT_PSK_SHA256) {
-               wpa_printf(MSG_DEBUG, "  * control port");
-               NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT);
-       }
 
-       if (params->wpa_ie) {
-               wpa_hexdump(MSG_DEBUG,
-                           "  * Extra IEs for Beacon/Probe Response frames",
-                           params->wpa_ie, params->wpa_ie_len);
-               NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
-                       params->wpa_ie);
+static int i802_set_sta_vlan(struct i802_bss *bss, const u8 *addr,
+                            const char *ifname, int vlan_id)
+{
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret;
+
+       wpa_printf(MSG_DEBUG, "nl80211: %s[%d]: set_sta_vlan(" MACSTR
+                  ", ifname=%s[%d], vlan_id=%d)",
+                  bss->ifname, if_nametoindex(bss->ifname),
+                  MAC2STR(addr), ifname, if_nametoindex(ifname), vlan_id);
+       if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_STATION)) ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+           nla_put_u32(msg, NL80211_ATTR_STA_VLAN, if_nametoindex(ifname))) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
        }
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       msg = NULL;
-       if (ret) {
-               wpa_printf(MSG_DEBUG, "nl80211: Join IBSS failed: ret=%d (%s)",
-                          ret, strerror(-ret));
-               count++;
-               if (ret == -EALREADY && count == 1) {
-                       wpa_printf(MSG_DEBUG, "nl80211: Retry IBSS join after "
-                                  "forced leave");
-                       nl80211_leave_ibss(drv);
-                       nlmsg_free(msg);
-                       goto retry;
-               }
-
-               goto nla_put_failure;
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr="
+                          MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)",
+                          MAC2STR(addr), ifname, vlan_id, ret,
+                          strerror(-ret));
        }
-       ret = 0;
-       wpa_printf(MSG_DEBUG, "nl80211: Join IBSS request sent successfully");
-
-nla_put_failure:
-       nlmsg_free(msg);
        return ret;
 }
 
 
-static int wpa_driver_nl80211_try_connect(
-       struct wpa_driver_nl80211_data *drv,
-       struct wpa_driver_associate_params *params)
+static int i802_get_inact_sec(void *priv, const u8 *addr)
 {
-       struct nl_msg *msg;
-       enum nl80211_auth_type type;
-       int ret = 0;
-       int algs;
+       struct hostap_sta_driver_data data;
+       int ret;
 
-       msg = nlmsg_alloc();
-       if (!msg)
+       data.inactive_msec = (unsigned long) -1;
+       ret = i802_read_sta_data(priv, &data, addr);
+       if (ret == -ENOENT)
+               return -ENOENT;
+       if (ret || data.inactive_msec == (unsigned long) -1)
                return -1;
+       return data.inactive_msec / 1000;
+}
 
-       wpa_printf(MSG_DEBUG, "nl80211: Connect (ifindex=%d)", drv->ifindex);
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_CONNECT);
-
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-       if (params->bssid) {
-               wpa_printf(MSG_DEBUG, "  * bssid=" MACSTR,
-                          MAC2STR(params->bssid));
-               NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
-       }
-       if (params->freq) {
-               wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
-               NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
-       }
-       if (params->bg_scan_period >= 0) {
-               wpa_printf(MSG_DEBUG, "  * bg scan period=%d",
-                          params->bg_scan_period);
-               NLA_PUT_U16(msg, NL80211_ATTR_BG_SCAN_PERIOD,
-                           params->bg_scan_period);
-       }
-       if (params->ssid) {
-               wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
-                                 params->ssid, params->ssid_len);
-               NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
-                       params->ssid);
-               if (params->ssid_len > sizeof(drv->ssid))
-                       goto nla_put_failure;
-               os_memcpy(drv->ssid, params->ssid, params->ssid_len);
-               drv->ssid_len = params->ssid_len;
-       }
-       wpa_hexdump(MSG_DEBUG, "  * IEs", params->wpa_ie, params->wpa_ie_len);
-       if (params->wpa_ie)
-               NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
-                       params->wpa_ie);
 
-       algs = 0;
-       if (params->auth_alg & WPA_AUTH_ALG_OPEN)
-               algs++;
-       if (params->auth_alg & WPA_AUTH_ALG_SHARED)
-               algs++;
-       if (params->auth_alg & WPA_AUTH_ALG_LEAP)
-               algs++;
-       if (algs > 1) {
-               wpa_printf(MSG_DEBUG, "  * Leave out Auth Type for automatic "
-                          "selection");
-               goto skip_auth_type;
-       }
+static int i802_sta_clear_stats(void *priv, const u8 *addr)
+{
+#if 0
+       /* TODO */
+#endif
+       return 0;
+}
 
-       if (params->auth_alg & WPA_AUTH_ALG_OPEN)
-               type = NL80211_AUTHTYPE_OPEN_SYSTEM;
-       else if (params->auth_alg & WPA_AUTH_ALG_SHARED)
-               type = NL80211_AUTHTYPE_SHARED_KEY;
-       else if (params->auth_alg & WPA_AUTH_ALG_LEAP)
-               type = NL80211_AUTHTYPE_NETWORK_EAP;
-       else if (params->auth_alg & WPA_AUTH_ALG_FT)
-               type = NL80211_AUTHTYPE_FT;
-       else
-               goto nla_put_failure;
 
-       wpa_printf(MSG_DEBUG, "  * Auth Type %d", type);
-       NLA_PUT_U32(msg, NL80211_ATTR_AUTH_TYPE, type);
+static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
+                          int reason)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct ieee80211_mgmt mgmt;
 
-skip_auth_type:
-#ifdef TIZEN_EXT
-       if (params->wpa_ie && params->wpa_ie_len) {
-               enum nl80211_wpa_versions ver;
+       if (is_mesh_interface(drv->nlmode))
+               return -1;
 
-               if (params->wpa_ie[0] == WLAN_EID_RSN)
-                       ver = NL80211_WPA_VERSION_2;
-               else
-                       ver = NL80211_WPA_VERSION_1;
-#else
-       if (params->wpa_proto) {
-               enum nl80211_wpa_versions ver = 0;
+       if (drv->device_ap_sme)
+               return wpa_driver_nl80211_sta_remove(bss, addr, 1, reason);
 
-               if (params->wpa_proto & WPA_PROTO_WPA)
-                       ver |= NL80211_WPA_VERSION_1;
-               if (params->wpa_proto & WPA_PROTO_RSN)
-                       ver |= NL80211_WPA_VERSION_2;
-#endif
-               wpa_printf(MSG_DEBUG, "  * WPA Versions 0x%x", ver);
-               NLA_PUT_U32(msg, NL80211_ATTR_WPA_VERSIONS, ver);
-       }
+       memset(&mgmt, 0, sizeof(mgmt));
+       mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                         WLAN_FC_STYPE_DEAUTH);
+       memcpy(mgmt.da, addr, ETH_ALEN);
+       memcpy(mgmt.sa, own_addr, ETH_ALEN);
+       memcpy(mgmt.bssid, own_addr, ETH_ALEN);
+       mgmt.u.deauth.reason_code = host_to_le16(reason);
+       return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
+                                           IEEE80211_HDRLEN +
+                                           sizeof(mgmt.u.deauth), 0, 0, 0, 0,
+                                           0);
+}
 
-       if (params->pairwise_suite != CIPHER_NONE) {
-               int cipher;
 
-               switch (params->pairwise_suite) {
-               case CIPHER_SMS4:
-                       cipher = WLAN_CIPHER_SUITE_SMS4;
-                       break;
-               case CIPHER_WEP40:
-                       cipher = WLAN_CIPHER_SUITE_WEP40;
-                       break;
-               case CIPHER_WEP104:
-                       cipher = WLAN_CIPHER_SUITE_WEP104;
-                       break;
-               case CIPHER_CCMP:
-                       cipher = WLAN_CIPHER_SUITE_CCMP;
-                       break;
-               case CIPHER_GCMP:
-                       cipher = WLAN_CIPHER_SUITE_GCMP;
-                       break;
-               case CIPHER_TKIP:
-               default:
-                       cipher = WLAN_CIPHER_SUITE_TKIP;
-                       break;
-               }
-               NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher);
-       }
+static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
+                            int reason)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct ieee80211_mgmt mgmt;
 
-       if (params->group_suite != CIPHER_NONE) {
-               int cipher;
+       if (is_mesh_interface(drv->nlmode))
+               return -1;
 
-               switch (params->group_suite) {
-               case CIPHER_SMS4:
-                       cipher = WLAN_CIPHER_SUITE_SMS4;
-                       break;
-               case CIPHER_WEP40:
-                       cipher = WLAN_CIPHER_SUITE_WEP40;
-                       break;
-               case CIPHER_WEP104:
-                       cipher = WLAN_CIPHER_SUITE_WEP104;
-                       break;
-               case CIPHER_CCMP:
-                       cipher = WLAN_CIPHER_SUITE_CCMP;
-                       break;
-               case CIPHER_GCMP:
-                       cipher = WLAN_CIPHER_SUITE_GCMP;
-                       break;
-               case CIPHER_TKIP:
-               default:
-                       cipher = WLAN_CIPHER_SUITE_TKIP;
-                       break;
-               }
-               NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher);
-       }
+       if (drv->device_ap_sme)
+               return wpa_driver_nl80211_sta_remove(bss, addr, 0, reason);
 
-       if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
-           params->key_mgmt_suite == KEY_MGMT_PSK ||
-           params->key_mgmt_suite == KEY_MGMT_FT_802_1X ||
-           params->key_mgmt_suite == KEY_MGMT_FT_PSK ||
-           params->key_mgmt_suite == KEY_MGMT_CCKM) {
-               int mgmt = WLAN_AKM_SUITE_PSK;
+       memset(&mgmt, 0, sizeof(mgmt));
+       mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                         WLAN_FC_STYPE_DISASSOC);
+       memcpy(mgmt.da, addr, ETH_ALEN);
+       memcpy(mgmt.sa, own_addr, ETH_ALEN);
+       memcpy(mgmt.bssid, own_addr, ETH_ALEN);
+       mgmt.u.disassoc.reason_code = host_to_le16(reason);
+       return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
+                                           IEEE80211_HDRLEN +
+                                           sizeof(mgmt.u.disassoc), 0, 0, 0, 0,
+                                           0);
+}
 
-               switch (params->key_mgmt_suite) {
-               case KEY_MGMT_CCKM:
-                       mgmt = WLAN_AKM_SUITE_CCKM;
-                       break;
-               case KEY_MGMT_802_1X:
-                       mgmt = WLAN_AKM_SUITE_8021X;
-                       break;
-               case KEY_MGMT_FT_802_1X:
-                       mgmt = WLAN_AKM_SUITE_FT_8021X;
-                       break;
-               case KEY_MGMT_FT_PSK:
-                       mgmt = WLAN_AKM_SUITE_FT_PSK;
-                       break;
-               case KEY_MGMT_PSK:
-               default:
-                       mgmt = WLAN_AKM_SUITE_PSK;
-                       break;
-               }
-               NLA_PUT_U32(msg, NL80211_ATTR_AKM_SUITES, mgmt);
-       }
 
-#ifdef CONFIG_IEEE80211W
-       if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED)
-               NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED);
-#endif /* CONFIG_IEEE80211W */
+static void dump_ifidx(struct wpa_driver_nl80211_data *drv)
+{
+       char buf[200], *pos, *end;
+       int i, res;
 
-       if (params->disable_ht)
-               NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT);
+       pos = buf;
+       end = pos + sizeof(buf);
 
-       if (params->htcaps && params->htcaps_mask) {
-               int sz = sizeof(struct ieee80211_ht_capabilities);
-               NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps);
-               NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
-                       params->htcaps_mask);
+       for (i = 0; i < drv->num_if_indices; i++) {
+               if (!drv->if_indices[i])
+                       continue;
+               res = os_snprintf(pos, end - pos, " %d", drv->if_indices[i]);
+               if (os_snprintf_error(end - pos, res))
+                       break;
+               pos += res;
        }
+       *pos = '\0';
 
-#ifdef CONFIG_VHT_OVERRIDES
-       if (params->disable_vht) {
-               wpa_printf(MSG_DEBUG, "  * VHT disabled");
-               NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_VHT);
-       }
+       wpa_printf(MSG_DEBUG, "nl80211: if_indices[%d]:%s",
+                  drv->num_if_indices, buf);
+}
 
-       if (params->vhtcaps && params->vhtcaps_mask) {
-               int sz = sizeof(struct ieee80211_vht_capabilities);
-               NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY, sz, params->vhtcaps);
-               NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz,
-                       params->vhtcaps_mask);
-       }
-#endif /* CONFIG_VHT_OVERRIDES */
 
-       ret = nl80211_set_conn_keys(params, msg);
-       if (ret)
-               goto nla_put_failure;
+static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+       int i;
+       int *old;
 
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       msg = NULL;
-       if (ret) {
-               wpa_printf(MSG_DEBUG, "nl80211: MLME connect failed: ret=%d "
-                          "(%s)", ret, strerror(-ret));
-               goto nla_put_failure;
+       wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d",
+                  ifidx);
+       if (have_ifidx(drv, ifidx)) {
+               wpa_printf(MSG_DEBUG, "nl80211: ifindex %d already in the list",
+                          ifidx);
+               return;
+       }
+       for (i = 0; i < drv->num_if_indices; i++) {
+               if (drv->if_indices[i] == 0) {
+                       drv->if_indices[i] = ifidx;
+                       dump_ifidx(drv);
+                       return;
+               }
        }
-       ret = 0;
-       wpa_printf(MSG_DEBUG, "nl80211: Connect request send successfully");
 
-nla_put_failure:
-       nlmsg_free(msg);
-       return ret;
+       if (drv->if_indices != drv->default_if_indices)
+               old = drv->if_indices;
+       else
+               old = NULL;
 
+       drv->if_indices = os_realloc_array(old, drv->num_if_indices + 1,
+                                          sizeof(int));
+       if (!drv->if_indices) {
+               if (!old)
+                       drv->if_indices = drv->default_if_indices;
+               else
+                       drv->if_indices = old;
+               wpa_printf(MSG_ERROR, "Failed to reallocate memory for "
+                          "interfaces");
+               wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx);
+               return;
+       } else if (!old)
+               os_memcpy(drv->if_indices, drv->default_if_indices,
+                         sizeof(drv->default_if_indices));
+       drv->if_indices[drv->num_if_indices] = ifidx;
+       drv->num_if_indices++;
+       dump_ifidx(drv);
 }
 
 
-static int wpa_driver_nl80211_connect(
-       struct wpa_driver_nl80211_data *drv,
-       struct wpa_driver_associate_params *params)
+static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
 {
-       int ret = wpa_driver_nl80211_try_connect(drv, params);
-       if (ret == -EALREADY) {
-               /*
-                * cfg80211 does not currently accept new connections if
-                * we are already connected. As a workaround, force
-                * disconnection and try again.
-                */
-               wpa_printf(MSG_DEBUG, "nl80211: Explicitly "
-                          "disconnecting before reassociation "
-                          "attempt");
-               if (wpa_driver_nl80211_disconnect(
-                           drv, WLAN_REASON_PREV_AUTH_NOT_VALID))
-                       return -1;
-               /* Ignore the next local disconnect message. */
-               drv->ignore_next_local_disconnect = 1;
-               ret = wpa_driver_nl80211_try_connect(drv, params);
+       int i;
+
+       for (i = 0; i < drv->num_if_indices; i++) {
+               if (drv->if_indices[i] == ifidx) {
+                       drv->if_indices[i] = 0;
+                       break;
+               }
        }
-       return ret;
+       dump_ifidx(drv);
 }
 
 
-static int wpa_driver_nl80211_associate(
-       void *priv, struct wpa_driver_associate_params *params)
+static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+{
+       int i;
+
+       for (i = 0; i < drv->num_if_indices; i++)
+               if (drv->if_indices[i] == ifidx)
+                       return 1;
+
+       return 0;
+}
+
+
+static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
+                           const char *bridge_ifname, char *ifname_wds)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       int ret = -1;
-       struct nl_msg *msg;
+       char name[IFNAMSIZ + 1];
 
-       if (params->mode == IEEE80211_MODE_AP)
-               return wpa_driver_nl80211_ap(drv, params);
+       os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
+       if (ifname_wds)
+               os_strlcpy(ifname_wds, name, IFNAMSIZ + 1);
 
-       if (params->mode == IEEE80211_MODE_IBSS)
-               return wpa_driver_nl80211_ibss(drv, params);
+       wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR
+                  " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name);
+       if (val) {
+               if (!if_nametoindex(name)) {
+                       if (nl80211_create_iface(drv, name,
+                                                NL80211_IFTYPE_AP_VLAN,
+                                                bss->addr, 1, NULL, NULL, 0) <
+                           0)
+                               return -1;
+                       if (bridge_ifname &&
+                           linux_br_add_if(drv->global->ioctl_sock,
+                                           bridge_ifname, name) < 0)
+                               return -1;
+               }
+               if (linux_set_iface_flags(drv->global->ioctl_sock, name, 1)) {
+                       wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA "
+                                  "interface %s up", name);
+               }
+               return i802_set_sta_vlan(priv, addr, name, 0);
+       } else {
+               if (bridge_ifname)
+                       linux_br_del_if(drv->global->ioctl_sock, bridge_ifname,
+                                       name);
 
-       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME)) {
-               enum nl80211_iftype nlmode = params->p2p ?
-                       NL80211_IFTYPE_P2P_CLIENT : NL80211_IFTYPE_STATION;
+               i802_set_sta_vlan(priv, addr, bss->ifname, 0);
+               nl80211_remove_iface(drv, if_nametoindex(name));
+               return 0;
+       }
+}
+
+
+static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct wpa_driver_nl80211_data *drv = eloop_ctx;
+       struct sockaddr_ll lladdr;
+       unsigned char buf[3000];
+       int len;
+       socklen_t fromlen = sizeof(lladdr);
 
-               if (wpa_driver_nl80211_set_mode(priv, nlmode) < 0)
-                       return -1;
-               return wpa_driver_nl80211_connect(drv, params);
+       len = recvfrom(sock, buf, sizeof(buf), 0,
+                      (struct sockaddr *)&lladdr, &fromlen);
+       if (len < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: EAPOL recv failed: %s",
+                          strerror(errno));
+               return;
        }
 
-       nl80211_mark_disconnected(drv);
+       if (have_ifidx(drv, lladdr.sll_ifindex))
+               drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
+}
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -1;
 
-       wpa_printf(MSG_DEBUG, "nl80211: Associate (ifindex=%d)",
-                  drv->ifindex);
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_ASSOCIATE);
+static int i802_check_bridge(struct wpa_driver_nl80211_data *drv,
+                            struct i802_bss *bss,
+                            const char *brname, const char *ifname)
+{
+       int br_ifindex;
+       char in_br[IFNAMSIZ];
 
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-       if (params->bssid) {
-               wpa_printf(MSG_DEBUG, "  * bssid=" MACSTR,
-                          MAC2STR(params->bssid));
-               NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, params->bssid);
-       }
-       if (params->freq) {
-               wpa_printf(MSG_DEBUG, "  * freq=%d", params->freq);
-               NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, params->freq);
-               drv->assoc_freq = params->freq;
-       } else
-               drv->assoc_freq = 0;
-       if (params->bg_scan_period >= 0) {
-               wpa_printf(MSG_DEBUG, "  * bg scan period=%d",
-                          params->bg_scan_period);
-               NLA_PUT_U16(msg, NL80211_ATTR_BG_SCAN_PERIOD,
-                           params->bg_scan_period);
-       }
-       if (params->ssid) {
-               wpa_hexdump_ascii(MSG_DEBUG, "  * SSID",
-                                 params->ssid, params->ssid_len);
-               NLA_PUT(msg, NL80211_ATTR_SSID, params->ssid_len,
-                       params->ssid);
-               if (params->ssid_len > sizeof(drv->ssid))
-                       goto nla_put_failure;
-               os_memcpy(drv->ssid, params->ssid, params->ssid_len);
-               drv->ssid_len = params->ssid_len;
+       os_strlcpy(bss->brname, brname, IFNAMSIZ);
+       br_ifindex = if_nametoindex(brname);
+       if (br_ifindex == 0) {
+               /*
+                * Bridge was configured, but the bridge device does
+                * not exist. Try to add it now.
+                */
+               if (linux_br_add(drv->global->ioctl_sock, brname) < 0) {
+                       wpa_printf(MSG_ERROR, "nl80211: Failed to add the "
+                                  "bridge interface %s: %s",
+                                  brname, strerror(errno));
+                       return -1;
+               }
+               bss->added_bridge = 1;
+               br_ifindex = if_nametoindex(brname);
+               add_ifidx(drv, br_ifindex);
        }
-       wpa_hexdump(MSG_DEBUG, "  * IEs", params->wpa_ie, params->wpa_ie_len);
-       if (params->wpa_ie)
-               NLA_PUT(msg, NL80211_ATTR_IE, params->wpa_ie_len,
-                       params->wpa_ie);
+       bss->br_ifindex = br_ifindex;
 
-       if (params->pairwise_suite != CIPHER_NONE) {
-               int cipher;
+       if (linux_br_get(in_br, ifname) == 0) {
+               if (os_strcmp(in_br, brname) == 0)
+                       return 0; /* already in the bridge */
 
-               switch (params->pairwise_suite) {
-               case CIPHER_WEP40:
-                       cipher = WLAN_CIPHER_SUITE_WEP40;
-                       break;
-               case CIPHER_WEP104:
-                       cipher = WLAN_CIPHER_SUITE_WEP104;
-                       break;
-               case CIPHER_CCMP:
-                       cipher = WLAN_CIPHER_SUITE_CCMP;
-                       break;
-               case CIPHER_GCMP:
-                       cipher = WLAN_CIPHER_SUITE_GCMP;
-                       break;
-               case CIPHER_TKIP:
-               default:
-                       cipher = WLAN_CIPHER_SUITE_TKIP;
-                       break;
+               wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from "
+                          "bridge %s", ifname, in_br);
+               if (linux_br_del_if(drv->global->ioctl_sock, in_br, ifname) <
+                   0) {
+                       wpa_printf(MSG_ERROR, "nl80211: Failed to "
+                                  "remove interface %s from bridge "
+                                  "%s: %s",
+                                  ifname, brname, strerror(errno));
+                       return -1;
                }
-               wpa_printf(MSG_DEBUG, "  * pairwise=0x%x", cipher);
-               NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITES_PAIRWISE, cipher);
        }
 
-       if (params->group_suite != CIPHER_NONE) {
-               int cipher;
-
-               switch (params->group_suite) {
-               case CIPHER_WEP40:
-                       cipher = WLAN_CIPHER_SUITE_WEP40;
-                       break;
-               case CIPHER_WEP104:
-                       cipher = WLAN_CIPHER_SUITE_WEP104;
-                       break;
-               case CIPHER_CCMP:
-                       cipher = WLAN_CIPHER_SUITE_CCMP;
-                       break;
-               case CIPHER_GCMP:
-                       cipher = WLAN_CIPHER_SUITE_GCMP;
-                       break;
-               case CIPHER_TKIP:
-               default:
-                       cipher = WLAN_CIPHER_SUITE_TKIP;
-                       break;
-               }
-               wpa_printf(MSG_DEBUG, "  * group=0x%x", cipher);
-               NLA_PUT_U32(msg, NL80211_ATTR_CIPHER_SUITE_GROUP, cipher);
+       wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s",
+                  ifname, brname);
+       if (linux_br_add_if(drv->global->ioctl_sock, brname, ifname) < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to add interface %s "
+                          "into bridge %s: %s",
+                          ifname, brname, strerror(errno));
+               return -1;
        }
+       bss->added_if_into_bridge = 1;
 
-#ifdef CONFIG_IEEE80211W
-       if (params->mgmt_frame_protection == MGMT_FRAME_PROTECTION_REQUIRED)
-               NLA_PUT_U32(msg, NL80211_ATTR_USE_MFP, NL80211_MFP_REQUIRED);
-#endif /* CONFIG_IEEE80211W */
+       return 0;
+}
 
-       NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT);
 
-       if (params->prev_bssid) {
-               wpa_printf(MSG_DEBUG, "  * prev_bssid=" MACSTR,
-                          MAC2STR(params->prev_bssid));
-               NLA_PUT(msg, NL80211_ATTR_PREV_BSSID, ETH_ALEN,
-                       params->prev_bssid);
-       }
+static void *i802_init(struct hostapd_data *hapd,
+                      struct wpa_init_params *params)
+{
+       struct wpa_driver_nl80211_data *drv;
+       struct i802_bss *bss;
+       size_t i;
+       char brname[IFNAMSIZ];
+       int ifindex, br_ifindex;
+       int br_added = 0;
 
-       if (params->disable_ht)
-               NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_HT);
+       bss = wpa_driver_nl80211_drv_init(hapd, params->ifname,
+                                         params->global_priv, 1,
+                                         params->bssid, params->driver_params);
+       if (bss == NULL)
+               return NULL;
 
-       if (params->htcaps && params->htcaps_mask) {
-               int sz = sizeof(struct ieee80211_ht_capabilities);
-               NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY, sz, params->htcaps);
-               NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK, sz,
-                       params->htcaps_mask);
-       }
+       drv = bss->drv;
 
-#ifdef CONFIG_VHT_OVERRIDES
-       if (params->disable_vht) {
-               wpa_printf(MSG_DEBUG, "  * VHT disabled");
-               NLA_PUT_FLAG(msg, NL80211_ATTR_DISABLE_VHT);
+       if (linux_br_get(brname, params->ifname) == 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s",
+                          params->ifname, brname);
+               br_ifindex = if_nametoindex(brname);
+               os_strlcpy(bss->brname, brname, IFNAMSIZ);
+       } else {
+               brname[0] = '\0';
+               br_ifindex = 0;
        }
+       bss->br_ifindex = br_ifindex;
 
-       if (params->vhtcaps && params->vhtcaps_mask) {
-               int sz = sizeof(struct ieee80211_vht_capabilities);
-               NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY, sz, params->vhtcaps);
-               NLA_PUT(msg, NL80211_ATTR_VHT_CAPABILITY_MASK, sz,
-                       params->vhtcaps_mask);
+       for (i = 0; i < params->num_bridge; i++) {
+               if (params->bridge[i]) {
+                       ifindex = if_nametoindex(params->bridge[i]);
+                       if (ifindex)
+                               add_ifidx(drv, ifindex);
+                       if (ifindex == br_ifindex)
+                               br_added = 1;
+               }
        }
-#endif /* CONFIG_VHT_OVERRIDES */
 
-       if (params->p2p)
-               wpa_printf(MSG_DEBUG, "  * P2P group");
+       /* start listening for EAPOL on the default AP interface */
+       add_ifidx(drv, drv->ifindex);
 
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       msg = NULL;
-       if (ret) {
-               wpa_dbg(drv->ctx, MSG_DEBUG,
-                       "nl80211: MLME command failed (assoc): ret=%d (%s)",
-                       ret, strerror(-ret));
-               nl80211_dump_scan(drv);
-               goto nla_put_failure;
+       if (params->num_bridge && params->bridge[0]) {
+               if (i802_check_bridge(drv, bss, params->bridge[0],
+                                     params->ifname) < 0)
+                       goto failed;
+               if (os_strcmp(params->bridge[0], brname) != 0)
+                       br_added = 1;
        }
-       ret = 0;
-       wpa_printf(MSG_DEBUG, "nl80211: Association request send "
-                  "successfully");
-
-nla_put_failure:
-       nlmsg_free(msg);
-       return ret;
-}
-
 
-static int nl80211_set_mode(struct wpa_driver_nl80211_data *drv,
-                           int ifindex, enum nl80211_iftype mode)
-{
-       struct nl_msg *msg;
-       int ret = -ENOBUFS;
+       if (!br_added && br_ifindex &&
+           (params->num_bridge == 0 || !params->bridge[0]))
+               add_ifidx(drv, br_ifindex);
 
-       wpa_printf(MSG_DEBUG, "nl80211: Set mode ifindex %d iftype %d (%s)",
-                  ifindex, mode, nl80211_iftype_str(mode));
+#ifdef CONFIG_LIBNL3_ROUTE
+       if (bss->added_if_into_bridge) {
+               drv->rtnl_sk = nl_socket_alloc();
+               if (drv->rtnl_sk == NULL) {
+                       wpa_printf(MSG_ERROR, "nl80211: Failed to allocate nl_sock");
+                       goto failed;
+               }
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
+               if (nl_connect(drv->rtnl_sk, NETLINK_ROUTE)) {
+                       wpa_printf(MSG_ERROR, "nl80211: Failed to connect nl_sock to NETLINK_ROUTE: %s",
+                                  strerror(errno));
+                       goto failed;
+               }
+       }
+#endif /* CONFIG_LIBNL3_ROUTE */
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_INTERFACE);
-       if (nl80211_set_iface_id(msg, &drv->first_bss) < 0)
-               goto nla_put_failure;
-       NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, mode);
+       drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
+       if (drv->eapol_sock < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE) failed: %s",
+                          strerror(errno));
+               goto failed;
+       }
 
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       msg = NULL;
-       if (!ret)
-               return 0;
-nla_put_failure:
-       nlmsg_free(msg);
-       wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface %d to mode %d:"
-                  " %d (%s)", ifindex, mode, ret, strerror(-ret));
-       return ret;
-}
+       if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL))
+       {
+               wpa_printf(MSG_INFO, "nl80211: Could not register read socket for eapol");
+               goto failed;
+       }
 
+       if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
+                              params->own_addr))
+               goto failed;
+       os_memcpy(drv->perm_addr, params->own_addr, ETH_ALEN);
 
-static int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
-                                      enum nl80211_iftype nlmode)
-{
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       int ret = -1;
-       int i;
-       int was_ap = is_ap_interface(drv->nlmode);
-       int res;
+       memcpy(bss->addr, params->own_addr, ETH_ALEN);
 
-       res = nl80211_set_mode(drv, drv->ifindex, nlmode);
-       if (res && nlmode == nl80211_get_ifmode(bss))
-               res = 0;
+       return bss;
 
-       if (res == 0) {
-               drv->nlmode = nlmode;
-               ret = 0;
-               goto done;
-       }
+failed:
+       wpa_driver_nl80211_deinit(bss);
+       return NULL;
+}
 
-       if (res == -ENODEV)
-               return -1;
 
-       if (nlmode == drv->nlmode) {
-               wpa_printf(MSG_DEBUG, "nl80211: Interface already in "
-                          "requested mode - ignore error");
-               ret = 0;
-               goto done; /* Already in the requested mode */
-       }
+static void i802_deinit(void *priv)
+{
+       struct i802_bss *bss = priv;
+       wpa_driver_nl80211_deinit(bss);
+}
 
-       /* mac80211 doesn't allow mode changes while the device is up, so
-        * take the device down, try to set the mode again, and bring the
-        * device back up.
-        */
-       wpa_printf(MSG_DEBUG, "nl80211: Try mode change after setting "
-                  "interface down");
-       for (i = 0; i < 10; i++) {
-               res = i802_set_iface_flags(bss, 0);
-               if (res == -EACCES || res == -ENODEV)
-                       break;
-               if (res == 0) {
-                       /* Try to set the mode again while the interface is
-                        * down */
-                       ret = nl80211_set_mode(drv, drv->ifindex, nlmode);
-                       if (ret == -EACCES)
-                               break;
-                       res = i802_set_iface_flags(bss, 1);
-                       if (res && !ret)
-                               ret = -1;
-                       else if (ret != -EBUSY)
-                               break;
-               } else
-                       wpa_printf(MSG_DEBUG, "nl80211: Failed to set "
-                                  "interface down");
-               os_sleep(0, 100000);
-       }
 
-       if (!ret) {
-               wpa_printf(MSG_DEBUG, "nl80211: Mode change succeeded while "
-                          "interface is down");
-               drv->nlmode = nlmode;
-               drv->ignore_if_down_event = 1;
+static enum nl80211_iftype wpa_driver_nl80211_if_type(
+       enum wpa_driver_if_type type)
+{
+       switch (type) {
+       case WPA_IF_STATION:
+               return NL80211_IFTYPE_STATION;
+       case WPA_IF_P2P_CLIENT:
+       case WPA_IF_P2P_GROUP:
+#ifdef BCM_DRIVER_V115
+               return NL80211_IFTYPE_STATION;
+#else
+               return NL80211_IFTYPE_P2P_CLIENT;
+#endif
+       case WPA_IF_AP_VLAN:
+               return NL80211_IFTYPE_AP_VLAN;
+       case WPA_IF_AP_BSS:
+               return NL80211_IFTYPE_AP;
+       case WPA_IF_P2P_GO:
+#ifdef BCM_DRIVER_V115
+               return NL80211_IFTYPE_AP;
+#else
+               return NL80211_IFTYPE_P2P_GO;
+#endif
+       case WPA_IF_P2P_DEVICE:
+               return NL80211_IFTYPE_P2P_DEVICE;
+       case WPA_IF_MESH:
+               return NL80211_IFTYPE_MESH_POINT;
        }
+       return -1;
+}
 
-done:
-       if (ret) {
-               wpa_printf(MSG_DEBUG, "nl80211: Interface mode change to %d "
-                          "from %d failed", nlmode, drv->nlmode);
-               return ret;
-       }
 
-       if (is_p2p_net_interface(nlmode))
-               nl80211_disable_11b_rates(drv, drv->ifindex, 1);
-       else if (drv->disabled_11b_rates)
-               nl80211_disable_11b_rates(drv, drv->ifindex, 0);
+#if defined(CONFIG_P2P) || defined(CONFIG_MESH)
 
-       if (is_ap_interface(nlmode)) {
-               nl80211_mgmt_unsubscribe(bss, "start AP");
-               /* Setup additional AP mode functionality if needed */
-               if (nl80211_setup_ap(bss))
-                       return -1;
-       } else if (was_ap) {
-               /* Remove additional AP mode functionality */
-               nl80211_teardown_ap(bss);
-       } else {
-               nl80211_mgmt_unsubscribe(bss, "mode change");
+static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr)
+{
+       struct wpa_driver_nl80211_data *drv;
+       dl_list_for_each(drv, &global->interfaces,
+                        struct wpa_driver_nl80211_data, list) {
+               if (os_memcmp(addr, drv->first_bss->addr, ETH_ALEN) == 0)
+                       return 1;
        }
-
-       if (!bss->in_deinit && !is_ap_interface(nlmode) &&
-           nl80211_mgmt_subscribe_non_ap(bss) < 0)
-               wpa_printf(MSG_DEBUG, "nl80211: Failed to register Action "
-                          "frame processing - ignore for now");
-
        return 0;
 }
 
 
-static int wpa_driver_nl80211_get_capa(void *priv,
-                                      struct wpa_driver_capa *capa)
+static int nl80211_vif_addr(struct wpa_driver_nl80211_data *drv, u8 *new_addr)
 {
-       struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       if (!drv->has_capability)
+       unsigned int idx;
+
+       if (!drv->global)
                return -1;
-       os_memcpy(capa, &drv->capa, sizeof(*capa));
-       if (drv->extended_capa && drv->extended_capa_mask) {
-               capa->extended_capa = drv->extended_capa;
-               capa->extended_capa_mask = drv->extended_capa_mask;
-               capa->extended_capa_len = drv->extended_capa_len;
-       }
 
-       if ((capa->flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) &&
-           !drv->allow_p2p_device) {
-               wpa_printf(MSG_DEBUG, "nl80211: Do not indicate P2P_DEVICE support (p2p_device=1 driver param not specified)");
-               capa->flags &= ~WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE;
+       os_memcpy(new_addr, drv->first_bss->addr, ETH_ALEN);
+       for (idx = 0; idx < 64; idx++) {
+               new_addr[0] = drv->first_bss->addr[0] | 0x02;
+               new_addr[0] ^= idx << 2;
+               if (!nl80211_addr_in_use(drv->global, new_addr))
+                       break;
        }
+       if (idx == 64)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Assigned new virtual interface address "
+                  MACSTR, MAC2STR(new_addr));
 
        return 0;
 }
 
+#endif /* CONFIG_P2P || CONFIG_MESH */
 
-static int wpa_driver_nl80211_set_operstate(void *priv, int state)
+
+struct wdev_info {
+       u64 wdev_id;
+       int wdev_id_set;
+       u8 macaddr[ETH_ALEN];
+};
+
+static int nl80211_wdev_handler(struct nl_msg *msg, void *arg)
 {
-       struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct wdev_info *wi = arg;
 
-       wpa_printf(MSG_DEBUG, "%s: operstate %d->%d (%s)",
-                  __func__, drv->operstate, state, state ? "UP" : "DORMANT");
-       drv->operstate = state;
-       return netlink_send_oper_ifla(drv->global->netlink, drv->ifindex, -1,
-                                     state ? IF_OPER_UP : IF_OPER_DORMANT);
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+       if (tb[NL80211_ATTR_WDEV]) {
+               wi->wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
+               wi->wdev_id_set = 1;
+       }
+
+       if (tb[NL80211_ATTR_MAC])
+               os_memcpy(wi->macaddr, nla_data(tb[NL80211_ATTR_MAC]),
+                         ETH_ALEN);
+
+       return NL_SKIP;
 }
 
 
-static int wpa_driver_nl80211_set_supp_port(void *priv, int authorized)
+static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
+                                    const char *ifname, const u8 *addr,
+                                    void *bss_ctx, void **drv_priv,
+                                    char *force_ifname, u8 *if_addr,
+                                    const char *bridge, int use_existing)
 {
+       enum nl80211_iftype nlmode;
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct nl_msg *msg;
-       struct nl80211_sta_flag_update upd;
+       int ifidx;
+       int added = 1;
 
-       wpa_printf(MSG_DEBUG, "nl80211: Set supplicant port %sauthorized for "
-                  MACSTR, authorized ? "" : "un", MAC2STR(drv->bssid));
+       if (addr)
+               os_memcpy(if_addr, addr, ETH_ALEN);
+       nlmode = wpa_driver_nl80211_if_type(type);
+       if (nlmode == NL80211_IFTYPE_P2P_DEVICE) {
+               struct wdev_info p2pdev_info;
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
+               os_memset(&p2pdev_info, 0, sizeof(p2pdev_info));
+               ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
+                                            0, nl80211_wdev_handler,
+                                            &p2pdev_info, use_existing);
+               if (!p2pdev_info.wdev_id_set || ifidx != 0) {
+                       wpa_printf(MSG_ERROR, "nl80211: Failed to create a P2P Device interface %s",
+                                  ifname);
+                       return -1;
+               }
+
+               drv->global->if_add_wdevid = p2pdev_info.wdev_id;
+               drv->global->if_add_wdevid_set = p2pdev_info.wdev_id_set;
+               if (!is_zero_ether_addr(p2pdev_info.macaddr))
+                       os_memcpy(if_addr, p2pdev_info.macaddr, ETH_ALEN);
+               wpa_printf(MSG_DEBUG, "nl80211: New P2P Device interface %s (0x%llx) created",
+                          ifname,
+                          (long long unsigned int) p2pdev_info.wdev_id);
+       } else {
+               ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
+                                            0, NULL, NULL, use_existing);
+               if (use_existing && ifidx == -ENFILE) {
+                       added = 0;
+                       ifidx = if_nametoindex(ifname);
+               } else if (ifidx < 0) {
+                       return -1;
+               }
+       }
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION);
+       if (!addr) {
+               if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
+                       os_memcpy(if_addr, bss->addr, ETH_ALEN);
+               else if (linux_get_ifhwaddr(drv->global->ioctl_sock,
+                                           bss->ifname, if_addr) < 0) {
+                       if (added)
+                               nl80211_remove_iface(drv, ifidx);
+                       return -1;
+               }
+       }
 
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
-                   if_nametoindex(bss->ifname));
-       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, drv->bssid);
+#if defined(CONFIG_P2P) || defined(CONFIG_MESH)
+       if (!addr &&
+           (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP ||
+            type == WPA_IF_P2P_GO || type == WPA_IF_MESH)) {
+               /* Enforce unique P2P Interface Address */
+               u8 new_addr[ETH_ALEN];
 
-       os_memset(&upd, 0, sizeof(upd));
-       upd.mask = BIT(NL80211_STA_FLAG_AUTHORIZED);
-       if (authorized)
-               upd.set = BIT(NL80211_STA_FLAG_AUTHORIZED);
-       NLA_PUT(msg, NL80211_ATTR_STA_FLAGS2, sizeof(upd), &upd);
+               if (linux_get_ifhwaddr(drv->global->ioctl_sock, ifname,
+                                      new_addr) < 0) {
+                       if (added)
+                               nl80211_remove_iface(drv, ifidx);
+                       return -1;
+               }
+               if (nl80211_addr_in_use(drv->global, new_addr)) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Allocate new address "
+                                  "for %s interface", type == WPA_IF_MESH ?
+                                  "mesh" : "P2P group");
+                       if (nl80211_vif_addr(drv, new_addr) < 0) {
+                               if (added)
+                                       nl80211_remove_iface(drv, ifidx);
+                               return -1;
+                       }
+                       if (linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
+                                              new_addr) < 0) {
+                               if (added)
+                                       nl80211_remove_iface(drv, ifidx);
+                               return -1;
+                       }
+               }
+               os_memcpy(if_addr, new_addr, ETH_ALEN);
+       }
+#endif /* CONFIG_P2P || CONFIG_MESH */
 
-       return send_and_recv_msgs(drv, msg, NULL, NULL);
- nla_put_failure:
-       nlmsg_free(msg);
-       return -ENOBUFS;
+       if (type == WPA_IF_AP_BSS) {
+               struct i802_bss *new_bss = os_zalloc(sizeof(*new_bss));
+               if (new_bss == NULL) {
+                       if (added)
+                               nl80211_remove_iface(drv, ifidx);
+                       return -1;
+               }
+
+               if (bridge &&
+                   i802_check_bridge(drv, new_bss, bridge, ifname) < 0) {
+                       wpa_printf(MSG_ERROR, "nl80211: Failed to add the new "
+                                  "interface %s to a bridge %s",
+                                  ifname, bridge);
+                       if (added)
+                               nl80211_remove_iface(drv, ifidx);
+                       os_free(new_bss);
+                       return -1;
+               }
+
+               if (linux_set_iface_flags(drv->global->ioctl_sock, ifname, 1))
+               {
+                       if (added)
+                               nl80211_remove_iface(drv, ifidx);
+                       os_free(new_bss);
+                       return -1;
+               }
+               os_strlcpy(new_bss->ifname, ifname, IFNAMSIZ);
+               os_memcpy(new_bss->addr, if_addr, ETH_ALEN);
+               new_bss->ifindex = ifidx;
+               new_bss->drv = drv;
+               new_bss->next = drv->first_bss->next;
+               new_bss->freq = drv->first_bss->freq;
+               new_bss->ctx = bss_ctx;
+               new_bss->added_if = added;
+               drv->first_bss->next = new_bss;
+               if (drv_priv)
+                       *drv_priv = new_bss;
+               nl80211_init_bss(new_bss);
+
+               /* Subscribe management frames for this WPA_IF_AP_BSS */
+               if (nl80211_setup_ap(new_bss))
+                       return -1;
+       }
+
+       if (drv->global)
+               drv->global->if_add_ifindex = ifidx;
+
+       /*
+        * Some virtual interfaces need to process EAPOL packets and events on
+        * the parent interface. This is used mainly with hostapd.
+        */
+       if (ifidx > 0 &&
+           (drv->hostapd ||
+            nlmode == NL80211_IFTYPE_AP_VLAN ||
+            nlmode == NL80211_IFTYPE_WDS ||
+            nlmode == NL80211_IFTYPE_MONITOR))
+               add_ifidx(drv, ifidx);
+
+       return 0;
 }
 
 
-/* Set kernel driver on given frequency (MHz) */
-static int i802_set_freq(void *priv, struct hostapd_freq_params *freq)
+static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
+                                       enum wpa_driver_if_type type,
+                                       const char *ifname)
 {
-       struct i802_bss *bss = priv;
-       return wpa_driver_nl80211_set_freq(bss, freq);
-}
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ifindex = if_nametoindex(ifname);
+
+       wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d added_if=%d",
+                  __func__, type, ifname, ifindex, bss->added_if);
+       if (ifindex > 0 && (bss->added_if || bss->ifindex != ifindex))
+               nl80211_remove_iface(drv, ifindex);
+       else if (ifindex > 0 && !bss->added_if) {
+               struct wpa_driver_nl80211_data *drv2;
+               dl_list_for_each(drv2, &drv->global->interfaces,
+                                struct wpa_driver_nl80211_data, list)
+                       del_ifidx(drv2, ifindex);
+       }
+
+       if (type != WPA_IF_AP_BSS)
+               return 0;
 
+       if (bss->added_if_into_bridge) {
+               if (linux_br_del_if(drv->global->ioctl_sock, bss->brname,
+                                   bss->ifname) < 0)
+                       wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+                                  "interface %s from bridge %s: %s",
+                                  bss->ifname, bss->brname, strerror(errno));
+       }
+       if (bss->added_bridge) {
+               if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0)
+                       wpa_printf(MSG_INFO, "nl80211: Failed to remove "
+                                  "bridge %s: %s",
+                                  bss->brname, strerror(errno));
+       }
+
+       if (bss != drv->first_bss) {
+               struct i802_bss *tbss;
 
-#if defined(HOSTAPD) || defined(CONFIG_AP)
+               wpa_printf(MSG_DEBUG, "nl80211: Not the first BSS - remove it");
+               for (tbss = drv->first_bss; tbss; tbss = tbss->next) {
+                       if (tbss->next == bss) {
+                               tbss->next = bss->next;
+                               /* Unsubscribe management frames */
+                               nl80211_teardown_ap(bss);
+                               nl80211_destroy_bss(bss);
+                               if (!bss->added_if)
+                                       i802_set_iface_flags(bss, 0);
+                               os_free(bss);
+                               bss = NULL;
+                               break;
+                       }
+               }
+               if (bss)
+                       wpa_printf(MSG_INFO, "nl80211: %s - could not find "
+                                  "BSS %p in the list", __func__, bss);
+       } else {
+               wpa_printf(MSG_DEBUG, "nl80211: First BSS - reassign context");
+               nl80211_teardown_ap(bss);
+               if (!bss->added_if && !drv->first_bss->next)
+                       wpa_driver_nl80211_del_beacon(drv);
+               nl80211_destroy_bss(bss);
+               if (!bss->added_if)
+                       i802_set_iface_flags(bss, 0);
+               if (drv->first_bss->next) {
+                       drv->first_bss = drv->first_bss->next;
+                       drv->ctx = drv->first_bss->ctx;
+                       os_free(bss);
+               } else {
+                       wpa_printf(MSG_DEBUG, "nl80211: No second BSS to reassign context to");
+               }
+       }
 
-static inline int min_int(int a, int b)
-{
-       if (a < b)
-               return a;
-       return b;
+       return 0;
 }
 
 
-static int get_key_handler(struct nl_msg *msg, void *arg)
+static int cookie_handler(struct nl_msg *msg, void *arg)
 {
        struct nlattr *tb[NL80211_ATTR_MAX + 1];
        struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-
+       u64 *cookie = arg;
        nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
                  genlmsg_attrlen(gnlh, 0), NULL);
-
-       /*
-        * TODO: validate the key index and mac address!
-        * Otherwise, there's a race condition as soon as
-        * the kernel starts sending key notifications.
-        */
-
-       if (tb[NL80211_ATTR_KEY_SEQ])
-               memcpy(arg, nla_data(tb[NL80211_ATTR_KEY_SEQ]),
-                      min_int(nla_len(tb[NL80211_ATTR_KEY_SEQ]), 6));
+       if (tb[NL80211_ATTR_COOKIE])
+               *cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
        return NL_SKIP;
 }
 
 
-static int i802_get_seqnum(const char *iface, void *priv, const u8 *addr,
-                          int idx, u8 *seq)
+static int nl80211_send_frame_cmd(struct i802_bss *bss,
+                                 unsigned int freq, unsigned int wait,
+                                 const u8 *buf, size_t buf_len,
+                                 u64 *cookie_out, int no_cck, int no_ack,
+                                 int offchanok)
 {
-       struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
+       u64 cookie;
+       int ret = -1;
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
+       wpa_printf(MSG_MSGDUMP, "nl80211: CMD_FRAME freq=%u wait=%u no_cck=%d "
+                  "no_ack=%d offchanok=%d",
+                  freq, wait, no_cck, no_ack, offchanok);
+       wpa_hexdump(MSG_MSGDUMP, "CMD_FRAME", buf, buf_len);
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_KEY);
+       if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME)) ||
+           (freq && nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
+#ifndef BCM_DRIVER_V115
+           (wait && nla_put_u32(msg, NL80211_ATTR_DURATION, wait)) ||
+#endif
+           (offchanok && ((drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
+                          drv->test_use_roc_tx) &&
+            nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK)) ||
+           (no_cck && nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE)) ||
+           (no_ack && nla_put_flag(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK)) ||
+           nla_put(msg, NL80211_ATTR_FRAME, buf_len, buf))
+               goto fail;
 
-       if (addr)
-               NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
-       NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, idx);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(iface));
+       cookie = 0;
+       ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d "
+                          "(%s) (freq=%u wait=%u)", ret, strerror(-ret),
+                          freq, wait);
+       } else {
+               wpa_printf(MSG_MSGDUMP, "nl80211: Frame TX command accepted%s; "
+                          "cookie 0x%llx", no_ack ? " (no ACK)" : "",
+                          (long long unsigned int) cookie);
 
-       memset(seq, 0, 6);
+               if (cookie_out)
+                       *cookie_out = no_ack ? (u64) -1 : cookie;
+       }
 
-       return send_and_recv_msgs(drv, msg, get_key_handler, seq);
- nla_put_failure:
+fail:
        nlmsg_free(msg);
-       return -ENOBUFS;
+       return ret;
 }
 
 
-static int i802_set_rts(void *priv, int rts)
+static int wpa_driver_nl80211_send_action(struct i802_bss *bss,
+                                         unsigned int freq,
+                                         unsigned int wait_time,
+                                         const u8 *dst, const u8 *src,
+                                         const u8 *bssid,
+                                         const u8 *data, size_t data_len,
+                                         int no_cck)
 {
-       struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct nl_msg *msg;
-       int ret = -ENOBUFS;
-       u32 val;
+       int ret = -1;
+       u8 *buf;
+       struct ieee80211_hdr *hdr;
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
+       wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, "
+                  "freq=%u MHz wait=%d ms no_cck=%d)",
+                  drv->ifindex, freq, wait_time, no_cck);
 
-       if (rts >= 2347)
-               val = (u32) -1;
-       else
-               val = rts;
+       buf = os_zalloc(24 + data_len);
+       if (buf == NULL)
+               return ret;
+       os_memcpy(buf + 24, data, data_len);
+       hdr = (struct ieee80211_hdr *) buf;
+       hdr->frame_control =
+               IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION);
+       os_memcpy(hdr->addr1, dst, ETH_ALEN);
+       os_memcpy(hdr->addr2, src, ETH_ALEN);
+       os_memcpy(hdr->addr3, bssid, ETH_ALEN);
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD, val);
+       if (is_ap_interface(drv->nlmode) &&
+           (!(drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) ||
+            (int) freq == bss->freq || drv->device_ap_sme ||
+            !drv->use_monitor))
+               ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len,
+                                                  0, freq, no_cck, 1,
+                                                  wait_time);
+       else
+               ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf,
+                                            24 + data_len,
+                                            &drv->send_action_cookie,
+                                            no_cck, 0, 1);
 
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       msg = NULL;
-       if (!ret)
-               return 0;
-nla_put_failure:
-       nlmsg_free(msg);
-       wpa_printf(MSG_DEBUG, "nl80211: Failed to set RTS threshold %d: "
-                  "%d (%s)", rts, ret, strerror(-ret));
+       os_free(buf);
        return ret;
 }
 
 
-static int i802_set_frag(void *priv, int frag)
+static void wpa_driver_nl80211_send_action_cancel_wait(void *priv)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
-       int ret = -ENOBUFS;
-       u32 val;
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
-
-       if (frag >= 2346)
-               val = (u32) -1;
-       else
-               val = frag;
+       int ret;
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD, val);
+       wpa_printf(MSG_DEBUG, "nl80211: Cancel TX frame wait: cookie=0x%llx",
+                  (long long unsigned int) drv->send_action_cookie);
+       if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_FRAME_WAIT_CANCEL)) ||
+           nla_put_u64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie)) {
+               nlmsg_free(msg);
+               return;
+       }
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       msg = NULL;
-       if (!ret)
-               return 0;
-nla_put_failure:
-       nlmsg_free(msg);
-       wpa_printf(MSG_DEBUG, "nl80211: Failed to set fragmentation threshold "
-                  "%d: %d (%s)", frag, ret, strerror(-ret));
-       return ret;
+       if (ret)
+               wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d "
+                          "(%s)", ret, strerror(-ret));
 }
 
 
-static int i802_flush(void *priv)
+static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
+                                               unsigned int duration)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
-       int res;
+       int ret;
+       u64 cookie;
 
-       msg = nlmsg_alloc();
-       if (!msg)
+       if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_REMAIN_ON_CHANNEL)) ||
+           nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
+           nla_put_u32(msg, NL80211_ATTR_DURATION, duration)) {
+               nlmsg_free(msg);
                return -1;
-
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_DEL_STATION);
-
-       /*
-        * XXX: FIX! this needs to flush all VLANs too
-        */
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
-                   if_nametoindex(bss->ifname));
-
-       res = send_and_recv_msgs(drv, msg, NULL, NULL);
-       if (res) {
-               wpa_printf(MSG_DEBUG, "nl80211: Station flush failed: ret=%d "
-                          "(%s)", res, strerror(-res));
        }
-       return res;
- nla_put_failure:
-       nlmsg_free(msg);
-       return -ENOBUFS;
-}
-
-#endif /* HOSTAPD || CONFIG_AP */
 
-
-static int get_sta_handler(struct nl_msg *msg, void *arg)
-{
-       struct nlattr *tb[NL80211_ATTR_MAX + 1];
-       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-       struct hostap_sta_driver_data *data = arg;
-       struct nlattr *stats[NL80211_STA_INFO_MAX + 1];
-       static struct nla_policy stats_policy[NL80211_STA_INFO_MAX + 1] = {
-               [NL80211_STA_INFO_INACTIVE_TIME] = { .type = NLA_U32 },
-               [NL80211_STA_INFO_RX_BYTES] = { .type = NLA_U32 },
-               [NL80211_STA_INFO_TX_BYTES] = { .type = NLA_U32 },
-               [NL80211_STA_INFO_RX_PACKETS] = { .type = NLA_U32 },
-               [NL80211_STA_INFO_TX_PACKETS] = { .type = NLA_U32 },
-               [NL80211_STA_INFO_TX_FAILED] = { .type = NLA_U32 },
-       };
-
-       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-                 genlmsg_attrlen(gnlh, 0), NULL);
-
-       /*
-        * TODO: validate the interface and mac address!
-        * Otherwise, there's a race condition as soon as
-        * the kernel starts sending station notifications.
-        */
-
-       if (!tb[NL80211_ATTR_STA_INFO]) {
-               wpa_printf(MSG_DEBUG, "sta stats missing!");
-               return NL_SKIP;
-       }
-       if (nla_parse_nested(stats, NL80211_STA_INFO_MAX,
-                            tb[NL80211_ATTR_STA_INFO],
-                            stats_policy)) {
-               wpa_printf(MSG_DEBUG, "failed to parse nested attributes!");
-               return NL_SKIP;
+       cookie = 0;
+       ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
+       if (ret == 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie "
+                          "0x%llx for freq=%u MHz duration=%u",
+                          (long long unsigned int) cookie, freq, duration);
+               drv->remain_on_chan_cookie = cookie;
+               drv->pending_remain_on_chan = 1;
+               return 0;
        }
-
-       if (stats[NL80211_STA_INFO_INACTIVE_TIME])
-               data->inactive_msec =
-                       nla_get_u32(stats[NL80211_STA_INFO_INACTIVE_TIME]);
-       if (stats[NL80211_STA_INFO_RX_BYTES])
-               data->rx_bytes = nla_get_u32(stats[NL80211_STA_INFO_RX_BYTES]);
-       if (stats[NL80211_STA_INFO_TX_BYTES])
-               data->tx_bytes = nla_get_u32(stats[NL80211_STA_INFO_TX_BYTES]);
-       if (stats[NL80211_STA_INFO_RX_PACKETS])
-               data->rx_packets =
-                       nla_get_u32(stats[NL80211_STA_INFO_RX_PACKETS]);
-       if (stats[NL80211_STA_INFO_TX_PACKETS])
-               data->tx_packets =
-                       nla_get_u32(stats[NL80211_STA_INFO_TX_PACKETS]);
-       if (stats[NL80211_STA_INFO_TX_FAILED])
-               data->tx_retry_failed =
-                       nla_get_u32(stats[NL80211_STA_INFO_TX_FAILED]);
-
-       return NL_SKIP;
+       wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel "
+                  "(freq=%d duration=%u): %d (%s)",
+                  freq, duration, ret, strerror(-ret));
+       return -1;
 }
 
-static int i802_read_sta_data(struct i802_bss *bss,
-                             struct hostap_sta_driver_data *data,
-                             const u8 *addr)
+
+static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv)
 {
+       struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
+       int ret;
 
-       os_memset(data, 0, sizeof(*data));
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
+       if (!drv->pending_remain_on_chan) {
+               wpa_printf(MSG_DEBUG, "nl80211: No pending remain-on-channel "
+                          "to cancel");
+               return -1;
+       }
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_STATION);
+       wpa_printf(MSG_DEBUG, "nl80211: Cancel remain-on-channel with cookie "
+                  "0x%llx",
+                  (long long unsigned int) drv->remain_on_chan_cookie);
 
-       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+       msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL);
+       if (!msg ||
+           nla_put_u64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie)) {
+               nlmsg_free(msg);
+               return -1;
+       }
 
-       return send_and_recv_msgs(drv, msg, get_sta_handler, data);
- nla_put_failure:
-       nlmsg_free(msg);
-       return -ENOBUFS;
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret == 0)
+               return 0;
+       wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: "
+                  "%d (%s)", ret, strerror(-ret));
+       return -1;
 }
 
 
-#if defined(HOSTAPD) || defined(CONFIG_AP)
-
-static int i802_set_tx_queue_params(void *priv, int queue, int aifs,
-                                   int cw_min, int cw_max, int burst_time)
+static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report)
 {
-       struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct nl_msg *msg;
-       struct nlattr *txq, *params;
 
-       msg = nlmsg_alloc();
-       if (!msg)
+       if (!report) {
+               if (bss->nl_preq && drv->device_ap_sme &&
+                   is_ap_interface(drv->nlmode) && !bss->in_deinit &&
+                   !bss->static_ap) {
+                       /*
+                        * Do not disable Probe Request reporting that was
+                        * enabled in nl80211_setup_ap().
+                        */
+                       wpa_printf(MSG_DEBUG, "nl80211: Skip disabling of "
+                                  "Probe Request reporting nl_preq=%p while "
+                                  "in AP mode", bss->nl_preq);
+               } else if (bss->nl_preq) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request "
+                                  "reporting nl_preq=%p", bss->nl_preq);
+                       nl80211_destroy_eloop_handle(&bss->nl_preq);
+               }
+               return 0;
+       }
+
+       if (bss->nl_preq) {
+               wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting "
+                          "already on! nl_preq=%p", bss->nl_preq);
+               return 0;
+       }
+
+       bss->nl_preq = nl_create_handle(drv->global->nl_cb, "preq");
+       if (bss->nl_preq == NULL)
                return -1;
+       wpa_printf(MSG_DEBUG, "nl80211: Enable Probe Request "
+                  "reporting nl_preq=%p", bss->nl_preq);
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_WIPHY);
+       if (nl80211_register_frame(bss, bss->nl_preq,
+                                  (WLAN_FC_TYPE_MGMT << 2) |
+                                  (WLAN_FC_STYPE_PROBE_REQ << 4),
+                                  NULL, 0) < 0)
+               goto out_err;
 
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
+#ifdef BCM_DRIVER_V115
+       if (drv->nlmode != NL80211_IFTYPE_AP &&
+               drv->nlmode != NL80211_IFTYPE_P2P_GO) {
+               wpa_printf(MSG_DEBUG, "nl80211: probe_req_report control only "
+                          "allowed in AP or P2P GO mode (iftype=%d)",
+                          drv->nlmode);
+               goto done;
+       }
 
-       txq = nla_nest_start(msg, NL80211_ATTR_WIPHY_TXQ_PARAMS);
-       if (!txq)
-               goto nla_put_failure;
+       if (nl80211_register_frame(bss, bss->nl_preq,
+                          (WLAN_FC_TYPE_MGMT << 2) |
+                          (WLAN_FC_STYPE_ASSOC_REQ << 4),
+                          NULL, 0) < 0) {
+               goto out_err;
+       }
 
-       /* We are only sending parameters for a single TXQ at a time */
-       params = nla_nest_start(msg, 1);
-       if (!params)
-               goto nla_put_failure;
+       if (nl80211_register_frame(bss, bss->nl_preq,
+                          (WLAN_FC_TYPE_MGMT << 2) |
+                          (WLAN_FC_STYPE_REASSOC_REQ << 4),
+                          NULL, 0) < 0) {
+               goto out_err;
+       }
 
-       switch (queue) {
-       case 0:
-               NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VO);
-               break;
-       case 1:
-               NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_VI);
-               break;
-       case 2:
-               NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BE);
-               break;
-       case 3:
-               NLA_PUT_U8(msg, NL80211_TXQ_ATTR_QUEUE, NL80211_TXQ_Q_BK);
-               break;
+       if (nl80211_register_frame(bss, bss->nl_preq,
+                          (WLAN_FC_TYPE_MGMT << 2) |
+                          (WLAN_FC_STYPE_DISASSOC << 4),
+                          NULL, 0) < 0) {
+               goto out_err;
        }
-       /* Burst time is configured in units of 0.1 msec and TXOP parameter in
-        * 32 usec, so need to convert the value here. */
-       NLA_PUT_U16(msg, NL80211_TXQ_ATTR_TXOP, (burst_time * 100 + 16) / 32);
-       NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMIN, cw_min);
-       NLA_PUT_U16(msg, NL80211_TXQ_ATTR_CWMAX, cw_max);
-       NLA_PUT_U8(msg, NL80211_TXQ_ATTR_AIFS, aifs);
 
-       nla_nest_end(msg, params);
+       if (nl80211_register_frame(bss, bss->nl_preq,
+                                          (WLAN_FC_TYPE_MGMT << 2) |
+                                          (WLAN_FC_STYPE_DEAUTH << 4),
+                                          NULL, 0) < 0) {
+               goto out_err;
+       }
 
-       nla_nest_end(msg, txq);
+done:
+#endif /* BCM_DRIVER_V115 */
 
-       if (send_and_recv_msgs(drv, msg, NULL, NULL) == 0)
-               return 0;
-       msg = NULL;
- nla_put_failure:
-       nlmsg_free(msg);
+       nl80211_register_eloop_read(&bss->nl_preq,
+                                   wpa_driver_nl80211_event_receive,
+                                   bss->nl_cb);
+
+       return 0;
+
+ out_err:
+       nl_destroy_handles(&bss->nl_preq);
        return -1;
 }
 
 
-static int i802_set_sta_vlan(struct i802_bss *bss, const u8 *addr,
-                            const char *ifname, int vlan_id)
+static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
+                                    int ifindex, int disabled)
 {
-       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
-       int ret = -ENOBUFS;
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
+       struct nlattr *bands, *band;
+       int ret;
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_STATION);
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: NL80211_CMD_SET_TX_BITRATE_MASK (ifindex=%d %s)",
+                  ifindex, disabled ? "NL80211_TXRATE_LEGACY=OFDM-only" :
+                  "no NL80211_TXRATE_LEGACY constraint");
 
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX,
-                   if_nametoindex(bss->ifname));
-       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
-       NLA_PUT_U32(msg, NL80211_ATTR_STA_VLAN,
-                   if_nametoindex(ifname));
+       msg = nl80211_ifindex_msg(drv, ifindex, 0,
+                                 NL80211_CMD_SET_TX_BITRATE_MASK);
+       if (!msg)
+               return -1;
 
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       msg = NULL;
-       if (ret < 0) {
-               wpa_printf(MSG_ERROR, "nl80211: NL80211_ATTR_STA_VLAN (addr="
-                          MACSTR " ifname=%s vlan_id=%d) failed: %d (%s)",
-                          MAC2STR(addr), ifname, vlan_id, ret,
-                          strerror(-ret));
-       }
- nla_put_failure:
-       nlmsg_free(msg);
-       return ret;
-}
+       bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
+       if (!bands)
+               goto fail;
 
+       /*
+        * Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything
+        * else apart from 6, 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS
+        * rates. All 5 GHz rates are left enabled.
+        */
+       band = nla_nest_start(msg, NL80211_BAND_2GHZ);
+       if (!band ||
+           (disabled && nla_put(msg, NL80211_TXRATE_LEGACY, 8,
+                                "\x0c\x12\x18\x24\x30\x48\x60\x6c")))
+               goto fail;
+       nla_nest_end(msg, band);
 
-static int i802_get_inact_sec(void *priv, const u8 *addr)
-{
-       struct hostap_sta_driver_data data;
-       int ret;
+       nla_nest_end(msg, bands);
 
-       data.inactive_msec = (unsigned long) -1;
-       ret = i802_read_sta_data(priv, &data, addr);
-       if (ret || data.inactive_msec == (unsigned long) -1)
-               return -1;
-       return data.inactive_msec / 1000;
-}
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d "
+                          "(%s)", ret, strerror(-ret));
+       } else
+               drv->disabled_11b_rates = disabled;
 
+       return ret;
 
-static int i802_sta_clear_stats(void *priv, const u8 *addr)
-{
-#if 0
-       /* TODO */
-#endif
-       return 0;
+fail:
+       nlmsg_free(msg);
+       return -1;
 }
 
 
-static int i802_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr,
-                          int reason)
+static int wpa_driver_nl80211_deinit_ap(void *priv)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct ieee80211_mgmt mgmt;
+       if (!is_ap_interface(drv->nlmode))
+               return -1;
+       wpa_driver_nl80211_del_beacon(drv);
+       bss->beacon_set = 0;
 
-       if (drv->device_ap_sme)
-               return wpa_driver_nl80211_sta_remove(bss, addr);
+       /*
+        * If the P2P GO interface was dynamically added, then it is
+        * possible that the interface change to station is not possible.
+        */
+       if (drv->nlmode == NL80211_IFTYPE_P2P_GO && bss->if_dynamic)
+               return 0;
 
-       memset(&mgmt, 0, sizeof(mgmt));
-       mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-                                         WLAN_FC_STYPE_DEAUTH);
-       memcpy(mgmt.da, addr, ETH_ALEN);
-       memcpy(mgmt.sa, own_addr, ETH_ALEN);
-       memcpy(mgmt.bssid, own_addr, ETH_ALEN);
-       mgmt.u.deauth.reason_code = host_to_le16(reason);
-       return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
-                                           IEEE80211_HDRLEN +
-                                           sizeof(mgmt.u.deauth), 0, 0, 0, 0,
-                                           0);
+       return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
 }
 
 
-static int i802_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr,
-                            int reason)
+static int wpa_driver_nl80211_stop_ap(void *priv)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct ieee80211_mgmt mgmt;
-
-       if (drv->device_ap_sme)
-               return wpa_driver_nl80211_sta_remove(bss, addr);
-
-       memset(&mgmt, 0, sizeof(mgmt));
-       mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
-                                         WLAN_FC_STYPE_DISASSOC);
-       memcpy(mgmt.da, addr, ETH_ALEN);
-       memcpy(mgmt.sa, own_addr, ETH_ALEN);
-       memcpy(mgmt.bssid, own_addr, ETH_ALEN);
-       mgmt.u.disassoc.reason_code = host_to_le16(reason);
-       return wpa_driver_nl80211_send_mlme(bss, (u8 *) &mgmt,
-                                           IEEE80211_HDRLEN +
-                                           sizeof(mgmt.u.disassoc), 0, 0, 0, 0,
-                                           0);
+       if (!is_ap_interface(drv->nlmode))
+               return -1;
+       wpa_driver_nl80211_del_beacon(drv);
+       bss->beacon_set = 0;
+       return 0;
 }
 
-#endif /* HOSTAPD || CONFIG_AP */
 
-#ifdef HOSTAPD
-
-static void add_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+static int wpa_driver_nl80211_deinit_p2p_cli(void *priv)
 {
-       int i;
-       int *old;
-
-       wpa_printf(MSG_DEBUG, "nl80211: Add own interface ifindex %d",
-                  ifidx);
-       for (i = 0; i < drv->num_if_indices; i++) {
-               if (drv->if_indices[i] == 0) {
-                       drv->if_indices[i] = ifidx;
-                       return;
-               }
-       }
-
-       if (drv->if_indices != drv->default_if_indices)
-               old = drv->if_indices;
-       else
-               old = NULL;
-
-       drv->if_indices = os_realloc_array(old, drv->num_if_indices + 1,
-                                          sizeof(int));
-       if (!drv->if_indices) {
-               if (!old)
-                       drv->if_indices = drv->default_if_indices;
-               else
-                       drv->if_indices = old;
-               wpa_printf(MSG_ERROR, "Failed to reallocate memory for "
-                          "interfaces");
-               wpa_printf(MSG_ERROR, "Ignoring EAPOL on interface %d", ifidx);
-               return;
-       } else if (!old)
-               os_memcpy(drv->if_indices, drv->default_if_indices,
-                         sizeof(drv->default_if_indices));
-       drv->if_indices[drv->num_if_indices] = ifidx;
-       drv->num_if_indices++;
-}
-
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT)
+               return -1;
 
-static void del_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
-{
-       int i;
+       /*
+        * If the P2P Client interface was dynamically added, then it is
+        * possible that the interface change to station is not possible.
+        */
+       if (bss->if_dynamic)
+               return 0;
 
-       for (i = 0; i < drv->num_if_indices; i++) {
-               if (drv->if_indices[i] == ifidx) {
-                       drv->if_indices[i] = 0;
-                       break;
-               }
-       }
+       return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
 }
 
 
-static int have_ifidx(struct wpa_driver_nl80211_data *drv, int ifidx)
+static void wpa_driver_nl80211_resume(void *priv)
 {
-       int i;
-
-       for (i = 0; i < drv->num_if_indices; i++)
-               if (drv->if_indices[i] == ifidx)
-                       return 1;
+       struct i802_bss *bss = priv;
 
-       return 0;
+       if (i802_set_iface_flags(bss, 1))
+               wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on resume event");
 }
 
 
-static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
-                            const char *bridge_ifname)
+static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       char name[IFNAMSIZ + 1];
+       struct nl_msg *msg;
+       struct nlattr *cqm;
 
-       os_snprintf(name, sizeof(name), "%s.sta%d", bss->ifname, aid);
-       wpa_printf(MSG_DEBUG, "nl80211: Set WDS STA addr=" MACSTR
-                  " aid=%d val=%d name=%s", MAC2STR(addr), aid, val, name);
-       if (val) {
-               if (!if_nametoindex(name)) {
-                       if (nl80211_create_iface(drv, name,
-                                                NL80211_IFTYPE_AP_VLAN,
-                                                bss->addr, 1, NULL, NULL) < 0)
-                               return -1;
-                       if (bridge_ifname &&
-                           linux_br_add_if(drv->global->ioctl_sock,
-                                           bridge_ifname, name) < 0)
-                               return -1;
-               }
-               if (linux_set_iface_flags(drv->global->ioctl_sock, name, 1)) {
-                       wpa_printf(MSG_ERROR, "nl80211: Failed to set WDS STA "
-                                  "interface %s up", name);
-               }
-               return i802_set_sta_vlan(priv, addr, name, 0);
-       } else {
-               if (bridge_ifname)
-                       linux_br_del_if(drv->global->ioctl_sock, bridge_ifname,
-                                       name);
+       wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d "
+                  "hysteresis=%d", threshold, hysteresis);
 
-               i802_set_sta_vlan(priv, addr, bss->ifname, 0);
-               return wpa_driver_nl80211_if_remove(priv, WPA_IF_AP_VLAN,
-                                                   name);
+       if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_CQM)) ||
+           !(cqm = nla_nest_start(msg, NL80211_ATTR_CQM)) ||
+           nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THOLD, threshold) ||
+           nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_HYST, hysteresis)) {
+               nlmsg_free(msg);
+               return -1;
        }
+       nla_nest_end(msg, cqm);
+
+       return send_and_recv_msgs(drv, msg, NULL, NULL);
 }
 
 
-static void handle_eapol(int sock, void *eloop_ctx, void *sock_ctx)
+static int get_channel_width(struct nl_msg *msg, void *arg)
 {
-       struct wpa_driver_nl80211_data *drv = eloop_ctx;
-       struct sockaddr_ll lladdr;
-       unsigned char buf[3000];
-       int len;
-       socklen_t fromlen = sizeof(lladdr);
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct wpa_signal_info *sig_change = arg;
 
-       len = recvfrom(sock, buf, sizeof(buf), 0,
-                      (struct sockaddr *)&lladdr, &fromlen);
-       if (len < 0) {
-               perror("recv");
-               return;
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       sig_change->center_frq1 = -1;
+       sig_change->center_frq2 = -1;
+       sig_change->chanwidth = CHAN_WIDTH_UNKNOWN;
+
+       if (tb[NL80211_ATTR_CHANNEL_WIDTH]) {
+               sig_change->chanwidth = convert2width(
+                       nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH]));
+               if (tb[NL80211_ATTR_CENTER_FREQ1])
+                       sig_change->center_frq1 =
+                               nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
+               if (tb[NL80211_ATTR_CENTER_FREQ2])
+                       sig_change->center_frq2 =
+                               nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
        }
 
-       if (have_ifidx(drv, lladdr.sll_ifindex))
-               drv_event_eapol_rx(drv->ctx, lladdr.sll_addr, buf, len);
+       return NL_SKIP;
 }
 
 
-static int i802_check_bridge(struct wpa_driver_nl80211_data *drv,
-                            struct i802_bss *bss,
-                            const char *brname, const char *ifname)
+static int nl80211_get_channel_width(struct wpa_driver_nl80211_data *drv,
+                                    struct wpa_signal_info *sig)
 {
-       int ifindex;
-       char in_br[IFNAMSIZ];
+       struct nl_msg *msg;
 
-       os_strlcpy(bss->brname, brname, IFNAMSIZ);
-       ifindex = if_nametoindex(brname);
-       if (ifindex == 0) {
-               /*
-                * Bridge was configured, but the bridge device does
-                * not exist. Try to add it now.
-                */
-               if (linux_br_add(drv->global->ioctl_sock, brname) < 0) {
-                       wpa_printf(MSG_ERROR, "nl80211: Failed to add the "
-                                  "bridge interface %s: %s",
-                                  brname, strerror(errno));
-                       return -1;
-               }
-               bss->added_bridge = 1;
-               add_ifidx(drv, if_nametoindex(brname));
-       }
+       msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+       return send_and_recv_msgs(drv, msg, get_channel_width, sig);
+}
 
-       if (linux_br_get(in_br, ifname) == 0) {
-               if (os_strcmp(in_br, brname) == 0)
-                       return 0; /* already in the bridge */
 
-               wpa_printf(MSG_DEBUG, "nl80211: Removing interface %s from "
-                          "bridge %s", ifname, in_br);
-               if (linux_br_del_if(drv->global->ioctl_sock, in_br, ifname) <
-                   0) {
-                       wpa_printf(MSG_ERROR, "nl80211: Failed to "
-                                  "remove interface %s from bridge "
-                                  "%s: %s",
-                                  ifname, brname, strerror(errno));
-                       return -1;
-               }
-       }
+static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int res;
 
-       wpa_printf(MSG_DEBUG, "nl80211: Adding interface %s into bridge %s",
-                  ifname, brname);
-       if (linux_br_add_if(drv->global->ioctl_sock, brname, ifname) < 0) {
-               wpa_printf(MSG_ERROR, "nl80211: Failed to add interface %s "
-                          "into bridge %s: %s",
-                          ifname, brname, strerror(errno));
-               return -1;
-       }
-       bss->added_if_into_bridge = 1;
+       os_memset(si, 0, sizeof(*si));
+       res = nl80211_get_link_signal(drv, si);
+       if (res != 0)
+               return res;
 
-       return 0;
+       res = nl80211_get_channel_width(drv, si);
+       if (res != 0)
+               return res;
+
+       return nl80211_get_link_noise(drv, si);
 }
 
 
-static void *i802_init(struct hostapd_data *hapd,
-                      struct wpa_init_params *params)
+static int wpa_driver_nl80211_shared_freq(void *priv)
 {
-       struct wpa_driver_nl80211_data *drv;
-       struct i802_bss *bss;
-       size_t i;
-       char brname[IFNAMSIZ];
-       int ifindex, br_ifindex;
-       int br_added = 0;
-
-       bss = wpa_driver_nl80211_init(hapd, params->ifname,
-                                     params->global_priv);
-       if (bss == NULL)
-               return NULL;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct wpa_driver_nl80211_data *driver;
+       int freq = 0;
 
-       drv = bss->drv;
-       drv->nlmode = NL80211_IFTYPE_AP;
-       drv->eapol_sock = -1;
+       /*
+        * If the same PHY is in connected state with some other interface,
+        * then retrieve the assoc freq.
+        */
+       wpa_printf(MSG_DEBUG, "nl80211: Get shared freq for PHY %s",
+                  drv->phyname);
 
-       if (linux_br_get(brname, params->ifname) == 0) {
-               wpa_printf(MSG_DEBUG, "nl80211: Interface %s is in bridge %s",
-                          params->ifname, brname);
-               br_ifindex = if_nametoindex(brname);
-       } else {
-               brname[0] = '\0';
-               br_ifindex = 0;
-       }
+       dl_list_for_each(driver, &drv->global->interfaces,
+                        struct wpa_driver_nl80211_data, list) {
+               if (drv == driver ||
+                   os_strcmp(drv->phyname, driver->phyname) != 0 ||
+                   !driver->associated)
+                       continue;
 
-       drv->num_if_indices = sizeof(drv->default_if_indices) / sizeof(int);
-       drv->if_indices = drv->default_if_indices;
-       for (i = 0; i < params->num_bridge; i++) {
-               if (params->bridge[i]) {
-                       ifindex = if_nametoindex(params->bridge[i]);
-                       if (ifindex)
-                               add_ifidx(drv, ifindex);
-                       if (ifindex == br_ifindex)
-                               br_added = 1;
-               }
+               wpa_printf(MSG_DEBUG, "nl80211: Found a match for PHY %s - %s "
+                          MACSTR,
+                          driver->phyname, driver->first_bss->ifname,
+                          MAC2STR(driver->first_bss->addr));
+               if (is_ap_interface(driver->nlmode))
+                       freq = driver->first_bss->freq;
+               else
+                       freq = nl80211_get_assoc_freq(driver);
+               wpa_printf(MSG_DEBUG, "nl80211: Shared freq for PHY %s: %d",
+                          drv->phyname, freq);
        }
-       if (!br_added && br_ifindex &&
-           (params->num_bridge == 0 || !params->bridge[0]))
-               add_ifidx(drv, br_ifindex);
 
-       /* start listening for EAPOL on the default AP interface */
-       add_ifidx(drv, drv->ifindex);
+       if (!freq)
+               wpa_printf(MSG_DEBUG, "nl80211: No shared interface for "
+                          "PHY (%s) in associated state", drv->phyname);
 
-       if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0))
-               goto failed;
+       return freq;
+}
 
-       if (params->bssid) {
-               if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
-                                      params->bssid))
-                       goto failed;
-       }
 
-       if (wpa_driver_nl80211_set_mode(bss, drv->nlmode)) {
-               wpa_printf(MSG_ERROR, "nl80211: Failed to set interface %s "
-                          "into AP mode", bss->ifname);
-               goto failed;
-       }
+static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len,
+                             int encrypt)
+{
+       struct i802_bss *bss = priv;
+       return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, 0,
+                                            0, 0, 0, 0);
+}
 
-       if (params->num_bridge && params->bridge[0] &&
-           i802_check_bridge(drv, bss, params->bridge[0], params->ifname) < 0)
-               goto failed;
 
-       if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1))
-               goto failed;
+static int nl80211_set_param(void *priv, const char *param)
+{
+       wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param);
+       if (param == NULL)
+               return 0;
 
-       drv->eapol_sock = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_PAE));
-       if (drv->eapol_sock < 0) {
-               perror("socket(PF_PACKET, SOCK_DGRAM, ETH_P_PAE)");
-               goto failed;
-       }
+#ifdef CONFIG_P2P
+       if (os_strstr(param, "use_p2p_group_interface=1")) {
+               struct i802_bss *bss = priv;
+               struct wpa_driver_nl80211_data *drv = bss->drv;
 
-       if (eloop_register_read_sock(drv->eapol_sock, handle_eapol, drv, NULL))
-       {
-               printf("Could not register read socket for eapol\n");
-               goto failed;
+               wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
+                          "interface");
+               drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
+               drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
        }
+#endif /* CONFIG_P2P */
 
-       if (linux_get_ifhwaddr(drv->global->ioctl_sock, bss->ifname,
-                              params->own_addr))
-               goto failed;
+       if (os_strstr(param, "use_monitor=1")) {
+               struct i802_bss *bss = priv;
+               struct wpa_driver_nl80211_data *drv = bss->drv;
+               drv->use_monitor = 1;
+       }
 
-       memcpy(bss->addr, params->own_addr, ETH_ALEN);
+       if (os_strstr(param, "force_connect_cmd=1")) {
+               struct i802_bss *bss = priv;
+               struct wpa_driver_nl80211_data *drv = bss->drv;
+               drv->capa.flags &= ~WPA_DRIVER_FLAGS_SME;
+               drv->force_connect_cmd = 1;
+       }
 
-       return bss;
+       if (os_strstr(param, "no_offchannel_tx=1")) {
+               struct i802_bss *bss = priv;
+               struct wpa_driver_nl80211_data *drv = bss->drv;
+               drv->capa.flags &= ~WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
+               drv->test_use_roc_tx = 1;
+       }
 
-failed:
-       wpa_driver_nl80211_deinit(bss);
-       return NULL;
+       return 0;
 }
 
 
-static void i802_deinit(void *priv)
+static void * nl80211_global_init(void)
 {
-       struct i802_bss *bss = priv;
-       wpa_driver_nl80211_deinit(bss);
-}
+       struct nl80211_global *global;
+       struct netlink_config *cfg;
 
-#endif /* HOSTAPD */
+       global = os_zalloc(sizeof(*global));
+       if (global == NULL)
+               return NULL;
+       global->ioctl_sock = -1;
+       dl_list_init(&global->interfaces);
+       global->if_add_ifindex = -1;
 
+       cfg = os_zalloc(sizeof(*cfg));
+       if (cfg == NULL)
+               goto err;
 
-static enum nl80211_iftype wpa_driver_nl80211_if_type(
-       enum wpa_driver_if_type type)
-{
-       switch (type) {
-       case WPA_IF_STATION:
-               return NL80211_IFTYPE_STATION;
-       case WPA_IF_P2P_CLIENT:
-       case WPA_IF_P2P_GROUP:
-#ifdef TIZEN_EXT
-               return NL80211_IFTYPE_STATION;
-#else
-               return NL80211_IFTYPE_P2P_CLIENT;
-#endif
-       case WPA_IF_AP_VLAN:
-               return NL80211_IFTYPE_AP_VLAN;
-       case WPA_IF_AP_BSS:
-               return NL80211_IFTYPE_AP;
-       case WPA_IF_P2P_GO:
-#ifdef TIZEN_EXT
-               return NL80211_IFTYPE_AP;
-#else
-               return NL80211_IFTYPE_P2P_GO;
-#endif
-       case WPA_IF_P2P_DEVICE:
-               return NL80211_IFTYPE_P2P_DEVICE;
+       cfg->ctx = global;
+       cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;
+       cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;
+       global->netlink = netlink_init(cfg);
+       if (global->netlink == NULL) {
+               os_free(cfg);
+               goto err;
        }
-       return -1;
-}
-
 
-#ifdef CONFIG_P2P
+       if (wpa_driver_nl80211_init_nl_global(global) < 0)
+               goto err;
 
-static int nl80211_addr_in_use(struct nl80211_global *global, const u8 *addr)
-{
-       struct wpa_driver_nl80211_data *drv;
-       dl_list_for_each(drv, &global->interfaces,
-                        struct wpa_driver_nl80211_data, list) {
-               if (os_memcmp(addr, drv->first_bss.addr, ETH_ALEN) == 0)
-                       return 1;
+       global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
+       if (global->ioctl_sock < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: socket(PF_INET,SOCK_DGRAM) failed: %s",
+                          strerror(errno));
+               goto err;
        }
-       return 0;
+
+       return global;
+
+err:
+       nl80211_global_deinit(global);
+       return NULL;
 }
 
 
-static int nl80211_p2p_interface_addr(struct wpa_driver_nl80211_data *drv,
-                                     u8 *new_addr)
+static void nl80211_global_deinit(void *priv)
 {
-       unsigned int idx;
+       struct nl80211_global *global = priv;
+       if (global == NULL)
+               return;
+       if (!dl_list_empty(&global->interfaces)) {
+               wpa_printf(MSG_ERROR, "nl80211: %u interface(s) remain at "
+                          "nl80211_global_deinit",
+                          dl_list_len(&global->interfaces));
+       }
 
-       if (!drv->global)
-               return -1;
+       if (global->netlink)
+               netlink_deinit(global->netlink);
 
-       os_memcpy(new_addr, drv->first_bss.addr, ETH_ALEN);
-       for (idx = 0; idx < 64; idx++) {
-               new_addr[0] = drv->first_bss.addr[0] | 0x02;
-               new_addr[0] ^= idx << 2;
-               if (!nl80211_addr_in_use(drv->global, new_addr))
-                       break;
-       }
-       if (idx == 64)
-               return -1;
+       nl_destroy_handles(&global->nl);
 
-       wpa_printf(MSG_DEBUG, "nl80211: Assigned new P2P Interface Address "
-                  MACSTR, MAC2STR(new_addr));
+       if (global->nl_event)
+               nl80211_destroy_eloop_handle(&global->nl_event);
 
-       return 0;
+       nl_cb_put(global->nl_cb);
+
+       if (global->ioctl_sock >= 0)
+               close(global->ioctl_sock);
+
+       os_free(global);
 }
 
-#endif /* CONFIG_P2P */
 
+static const char * nl80211_get_radio_name(void *priv)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       return drv->phyname;
+}
 
-struct wdev_info {
-       u64 wdev_id;
-       int wdev_id_set;
-       u8 macaddr[ETH_ALEN];
-};
 
-static int nl80211_wdev_handler(struct nl_msg *msg, void *arg)
+static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid,
+                        const u8 *pmkid)
 {
-       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-       struct nlattr *tb[NL80211_ATTR_MAX + 1];
-       struct wdev_info *wi = arg;
+       struct nl_msg *msg;
 
-       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-                 genlmsg_attrlen(gnlh, 0), NULL);
-       if (tb[NL80211_ATTR_WDEV]) {
-               wi->wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
-               wi->wdev_id_set = 1;
+       if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
+           (pmkid && nla_put(msg, NL80211_ATTR_PMKID, 16, pmkid)) ||
+           (bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
        }
 
-       if (tb[NL80211_ATTR_MAC])
-               os_memcpy(wi->macaddr, nla_data(tb[NL80211_ATTR_MAC]),
-                         ETH_ALEN);
-
-       return NL_SKIP;
+       return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
 }
 
 
-static int wpa_driver_nl80211_if_add(void *priv, enum wpa_driver_if_type type,
-                                    const char *ifname, const u8 *addr,
-                                    void *bss_ctx, void **drv_priv,
-                                    char *force_ifname, u8 *if_addr,
-                                    const char *bridge)
+static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
 {
-       enum nl80211_iftype nlmode;
        struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       int ifidx;
-#ifdef HOSTAPD
-       struct i802_bss *new_bss = NULL;
-
-       if (type == WPA_IF_AP_BSS) {
-               new_bss = os_zalloc(sizeof(*new_bss));
-               if (new_bss == NULL)
-                       return -1;
-       }
-#endif /* HOSTAPD */
-
-       if (addr)
-               os_memcpy(if_addr, addr, ETH_ALEN);
-       nlmode = wpa_driver_nl80211_if_type(type);
-       if (nlmode == NL80211_IFTYPE_P2P_DEVICE) {
-               struct wdev_info p2pdev_info;
+       wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, MAC2STR(bssid));
+       return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, bssid, pmkid);
+}
 
-               os_memset(&p2pdev_info, 0, sizeof(p2pdev_info));
-               ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
-                                            0, nl80211_wdev_handler,
-                                            &p2pdev_info);
-               if (!p2pdev_info.wdev_id_set || ifidx != 0) {
-                       wpa_printf(MSG_ERROR, "nl80211: Failed to create a P2P Device interface %s",
-                                  ifname);
-                       return -1;
-               }
 
-               drv->global->if_add_wdevid = p2pdev_info.wdev_id;
-               drv->global->if_add_wdevid_set = p2pdev_info.wdev_id_set;
-               if (!is_zero_ether_addr(p2pdev_info.macaddr))
-                       os_memcpy(if_addr, p2pdev_info.macaddr, ETH_ALEN);
-               wpa_printf(MSG_DEBUG, "nl80211: New P2P Device interface %s (0x%llx) created",
-                          ifname,
-                          (long long unsigned int) p2pdev_info.wdev_id);
-       } else {
-               ifidx = nl80211_create_iface(drv, ifname, nlmode, addr,
-                                            0, NULL, NULL);
-               if (ifidx < 0) {
-#ifdef HOSTAPD
-                       os_free(new_bss);
-#endif /* HOSTAPD */
-                       return -1;
-               }
-       }
+static int nl80211_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
+{
+       struct i802_bss *bss = priv;
+       wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR,
+                  MAC2STR(bssid));
+       return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, bssid, pmkid);
+}
 
-       if (!addr) {
-               if (drv->nlmode == NL80211_IFTYPE_P2P_DEVICE)
-                       os_memcpy(if_addr, bss->addr, ETH_ALEN);
-               else if (linux_get_ifhwaddr(drv->global->ioctl_sock,
-                                           bss->ifname, if_addr) < 0) {
-                       nl80211_remove_iface(drv, ifidx);
-                       return -1;
-               }
-       }
 
-#ifdef CONFIG_P2P
-       if (!addr &&
-           (type == WPA_IF_P2P_CLIENT || type == WPA_IF_P2P_GROUP ||
-            type == WPA_IF_P2P_GO)) {
-               /* Enforce unique P2P Interface Address */
-               u8 new_addr[ETH_ALEN];
+static int nl80211_flush_pmkid(void *priv)
+{
+       struct i802_bss *bss = priv;
+       wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs");
+       return nl80211_pmkid(bss, NL80211_CMD_FLUSH_PMKSA, NULL, NULL);
+}
 
-               if (linux_get_ifhwaddr(drv->global->ioctl_sock, ifname,
-                                      new_addr) < 0) {
-                       nl80211_remove_iface(drv, ifidx);
-                       return -1;
-               }
-               if (nl80211_addr_in_use(drv->global, new_addr)) {
-                       wpa_printf(MSG_DEBUG, "nl80211: Allocate new address "
-                                  "for P2P group interface");
-                       if (nl80211_p2p_interface_addr(drv, new_addr) < 0) {
-                               nl80211_remove_iface(drv, ifidx);
-                               return -1;
-                       }
-                       if (linux_set_ifhwaddr(drv->global->ioctl_sock, ifname,
-                                              new_addr) < 0) {
-                               nl80211_remove_iface(drv, ifidx);
-                               return -1;
-                       }
-               }
-               os_memcpy(if_addr, new_addr, ETH_ALEN);
-       }
-#endif /* CONFIG_P2P */
 
-#ifdef HOSTAPD
-       if (bridge &&
-           i802_check_bridge(drv, new_bss, bridge, ifname) < 0) {
-               wpa_printf(MSG_ERROR, "nl80211: Failed to add the new "
-                          "interface %s to a bridge %s", ifname, bridge);
-               nl80211_remove_iface(drv, ifidx);
-               os_free(new_bss);
-               return -1;
-       }
+static void clean_survey_results(struct survey_results *survey_results)
+{
+       struct freq_survey *survey, *tmp;
 
-       if (type == WPA_IF_AP_BSS) {
-               if (linux_set_iface_flags(drv->global->ioctl_sock, ifname, 1))
-               {
-                       nl80211_remove_iface(drv, ifidx);
-                       os_free(new_bss);
-                       return -1;
-               }
-               os_strlcpy(new_bss->ifname, ifname, IFNAMSIZ);
-               os_memcpy(new_bss->addr, if_addr, ETH_ALEN);
-               new_bss->ifindex = ifidx;
-               new_bss->drv = drv;
-               new_bss->next = drv->first_bss.next;
-               new_bss->freq = drv->first_bss.freq;
-               new_bss->ctx = bss_ctx;
-               drv->first_bss.next = new_bss;
-               if (drv_priv)
-                       *drv_priv = new_bss;
-               nl80211_init_bss(new_bss);
+       if (dl_list_empty(&survey_results->survey_list))
+               return;
 
-               /* Subscribe management frames for this WPA_IF_AP_BSS */
-               if (nl80211_setup_ap(new_bss))
-                       return -1;
+       dl_list_for_each_safe(survey, tmp, &survey_results->survey_list,
+                             struct freq_survey, list) {
+               dl_list_del(&survey->list);
+               os_free(survey);
        }
-#endif /* HOSTAPD */
-
-       if (drv->global)
-               drv->global->if_add_ifindex = ifidx;
-
-       return 0;
 }
 
 
-static int wpa_driver_nl80211_if_remove(struct i802_bss *bss,
-                                       enum wpa_driver_if_type type,
-                                       const char *ifname)
+static void add_survey(struct nlattr **sinfo, u32 ifidx,
+                      struct dl_list *survey_list)
 {
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       int ifindex = if_nametoindex(ifname);
+       struct freq_survey *survey;
 
-       wpa_printf(MSG_DEBUG, "nl80211: %s(type=%d ifname=%s) ifindex=%d",
-                  __func__, type, ifname, ifindex);
-       if (ifindex <= 0)
-               return -1;
+       survey = os_zalloc(sizeof(struct freq_survey));
+       if  (!survey)
+               return;
 
-       nl80211_remove_iface(drv, ifindex);
+       survey->ifidx = ifidx;
+       survey->freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]);
+       survey->filled = 0;
 
-#ifdef HOSTAPD
-       if (type != WPA_IF_AP_BSS)
-               return 0;
+       if (sinfo[NL80211_SURVEY_INFO_NOISE]) {
+               survey->nf = (int8_t)
+                       nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+               survey->filled |= SURVEY_HAS_NF;
+       }
 
-       if (bss->added_if_into_bridge) {
-               if (linux_br_del_if(drv->global->ioctl_sock, bss->brname,
-                                   bss->ifname) < 0)
-                       wpa_printf(MSG_INFO, "nl80211: Failed to remove "
-                                  "interface %s from bridge %s: %s",
-                                  bss->ifname, bss->brname, strerror(errno));
+       if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]) {
+               survey->channel_time =
+                       nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME]);
+               survey->filled |= SURVEY_HAS_CHAN_TIME;
        }
-       if (bss->added_bridge) {
-               if (linux_br_del(drv->global->ioctl_sock, bss->brname) < 0)
-                       wpa_printf(MSG_INFO, "nl80211: Failed to remove "
-                                  "bridge %s: %s",
-                                  bss->brname, strerror(errno));
+
+       if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]) {
+               survey->channel_time_busy =
+                       nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY]);
+               survey->filled |= SURVEY_HAS_CHAN_TIME_BUSY;
        }
 
-       if (bss != &drv->first_bss) {
-               struct i802_bss *tbss;
+       if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]) {
+               survey->channel_time_rx =
+                       nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_RX]);
+               survey->filled |= SURVEY_HAS_CHAN_TIME_RX;
+       }
 
-               for (tbss = &drv->first_bss; tbss; tbss = tbss->next) {
-                       if (tbss->next == bss) {
-                               tbss->next = bss->next;
-                               /* Unsubscribe management frames */
-                               nl80211_teardown_ap(bss);
-                               nl80211_destroy_bss(bss);
-                               os_free(bss);
-                               bss = NULL;
-                               break;
-                       }
-               }
-               if (bss)
-                       wpa_printf(MSG_INFO, "nl80211: %s - could not find "
-                                  "BSS %p in the list", __func__, bss);
+       if (sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]) {
+               survey->channel_time_tx =
+                       nla_get_u64(sinfo[NL80211_SURVEY_INFO_CHANNEL_TIME_TX]);
+               survey->filled |= SURVEY_HAS_CHAN_TIME_TX;
        }
-#endif /* HOSTAPD */
 
-       return 0;
+       wpa_printf(MSG_DEBUG, "nl80211: Freq survey dump event (freq=%d MHz noise=%d channel_time=%ld busy_time=%ld tx_time=%ld rx_time=%ld filled=%04x)",
+                  survey->freq,
+                  survey->nf,
+                  (unsigned long int) survey->channel_time,
+                  (unsigned long int) survey->channel_time_busy,
+                  (unsigned long int) survey->channel_time_tx,
+                  (unsigned long int) survey->channel_time_rx,
+                  survey->filled);
+
+       dl_list_add_tail(survey_list, &survey->list);
 }
 
 
-static int cookie_handler(struct nl_msg *msg, void *arg)
+static int check_survey_ok(struct nlattr **sinfo, u32 surveyed_freq,
+                          unsigned int freq_filter)
+{
+       if (!freq_filter)
+               return 1;
+
+       return freq_filter == surveyed_freq;
+}
+
+
+static int survey_handler(struct nl_msg *msg, void *arg)
 {
        struct nlattr *tb[NL80211_ATTR_MAX + 1];
        struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-       u64 *cookie = arg;
+       struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
+       struct survey_results *survey_results;
+       u32 surveyed_freq = 0;
+       u32 ifidx;
+
+       static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
+               [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
+               [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
+       };
+
+       survey_results = (struct survey_results *) arg;
+
        nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
                  genlmsg_attrlen(gnlh, 0), NULL);
-       if (tb[NL80211_ATTR_COOKIE])
-               *cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+
+       if (!tb[NL80211_ATTR_IFINDEX])
+               return NL_SKIP;
+
+       ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+
+       if (!tb[NL80211_ATTR_SURVEY_INFO])
+               return NL_SKIP;
+
+       if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
+                            tb[NL80211_ATTR_SURVEY_INFO],
+                            survey_policy))
+               return NL_SKIP;
+
+       if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY]) {
+               wpa_printf(MSG_ERROR, "nl80211: Invalid survey data");
+               return NL_SKIP;
+       }
+
+       surveyed_freq = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]);
+
+       if (!check_survey_ok(sinfo, surveyed_freq,
+                            survey_results->freq_filter))
+               return NL_SKIP;
+
+       if (survey_results->freq_filter &&
+           survey_results->freq_filter != surveyed_freq) {
+               wpa_printf(MSG_EXCESSIVE, "nl80211: Ignoring survey data for freq %d MHz",
+                          surveyed_freq);
+               return NL_SKIP;
+       }
+
+       add_survey(sinfo, ifidx, &survey_results->survey_list);
+
        return NL_SKIP;
 }
 
 
-static int nl80211_send_frame_cmd(struct i802_bss *bss,
-                                 unsigned int freq, unsigned int wait,
-                                 const u8 *buf, size_t buf_len,
-                                 u64 *cookie_out, int no_cck, int no_ack,
-                                 int offchanok)
+static int wpa_driver_nl80211_get_survey(void *priv, unsigned int freq)
 {
+       struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
-       u64 cookie;
-       int ret = -1;
+       int err;
+       union wpa_event_data data;
+       struct survey_results *survey_results;
 
-       msg = nlmsg_alloc();
+       os_memset(&data, 0, sizeof(data));
+       survey_results = &data.survey_results;
+
+       dl_list_init(&survey_results->survey_list);
+
+       msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
        if (!msg)
-               return -1;
+               return -ENOBUFS;
 
-       wpa_printf(MSG_MSGDUMP, "nl80211: CMD_FRAME freq=%u wait=%u no_cck=%d "
-                  "no_ack=%d offchanok=%d",
-                  freq, wait, no_cck, no_ack, offchanok);
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME);
+       if (freq)
+               data.survey_results.freq_filter = freq;
 
-       if (nl80211_set_iface_id(msg, bss) < 0)
-               goto nla_put_failure;
+       do {
+               wpa_printf(MSG_DEBUG, "nl80211: Fetch survey data");
+               err = send_and_recv_msgs(drv, msg, survey_handler,
+                                        survey_results);
+       } while (err > 0);
 
-       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
-#ifndef BCM_DRIVER_V115
-       if (wait)
-               NLA_PUT_U32(msg, NL80211_ATTR_DURATION, wait);
-#endif
-       if (offchanok && (drv->capa.flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX))
-               NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
-       if (no_cck)
-               NLA_PUT_FLAG(msg, NL80211_ATTR_TX_NO_CCK_RATE);
-       if (no_ack)
-               NLA_PUT_FLAG(msg, NL80211_ATTR_DONT_WAIT_FOR_ACK);
+       if (err)
+               wpa_printf(MSG_ERROR, "nl80211: Failed to process survey data");
+       else
+               wpa_supplicant_event(drv->ctx, EVENT_SURVEY, &data);
 
-       NLA_PUT(msg, NL80211_ATTR_FRAME, buf_len, buf);
+       clean_survey_results(survey_results);
+       return err;
+}
 
-       cookie = 0;
-       ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
-       msg = NULL;
-       if (ret) {
-               wpa_printf(MSG_DEBUG, "nl80211: Frame command failed: ret=%d "
-                          "(%s) (freq=%u wait=%u)", ret, strerror(-ret),
-                          freq, wait);
-               goto nla_put_failure;
+
+static void nl80211_set_rekey_info(void *priv, const u8 *kek, size_t kek_len,
+                                  const u8 *kck, size_t kck_len,
+                                  const u8 *replay_ctr)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nlattr *replay_nested;
+       struct nl_msg *msg;
+       int ret;
+
+       if (!drv->set_rekey_offload)
+               return;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Set rekey offload");
+       if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_REKEY_OFFLOAD)) ||
+           !(replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA)) ||
+           nla_put(msg, NL80211_REKEY_DATA_KEK, kek_len, kek) ||
+           nla_put(msg, NL80211_REKEY_DATA_KCK, kck_len, kck) ||
+           nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN,
+                   replay_ctr)) {
+               nl80211_nlmsg_clear(msg);
+               nlmsg_free(msg);
+               return;
        }
-       wpa_printf(MSG_MSGDUMP, "nl80211: Frame TX command accepted%s; "
-                  "cookie 0x%llx", no_ack ? " (no ACK)" : "",
-                  (long long unsigned int) cookie);
 
-       if (cookie_out)
-               *cookie_out = no_ack ? (u64) -1 : cookie;
+       nla_nest_end(msg, replay_nested);
 
-nla_put_failure:
-       nlmsg_free(msg);
-       return ret;
+       ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
+       if (ret == -EOPNOTSUPP) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Driver does not support rekey offload");
+               drv->set_rekey_offload = 0;
+       }
 }
 
 
-static int wpa_driver_nl80211_send_action(struct i802_bss *bss,
-                                         unsigned int freq,
-                                         unsigned int wait_time,
-                                         const u8 *dst, const u8 *src,
-                                         const u8 *bssid,
-                                         const u8 *data, size_t data_len,
-                                         int no_cck)
+static void nl80211_send_null_frame(struct i802_bss *bss, const u8 *own_addr,
+                                   const u8 *addr, int qos)
+{
+       /* send data frame to poll STA and check whether
+        * this frame is ACKed */
+       struct {
+               struct ieee80211_hdr hdr;
+               u16 qos_ctl;
+       } STRUCT_PACKED nulldata;
+       size_t size;
+
+       /* Send data frame to poll STA and check whether this frame is ACKed */
+
+       os_memset(&nulldata, 0, sizeof(nulldata));
+
+       if (qos) {
+               nulldata.hdr.frame_control =
+                       IEEE80211_FC(WLAN_FC_TYPE_DATA,
+                                    WLAN_FC_STYPE_QOS_NULL);
+               size = sizeof(nulldata);
+       } else {
+               nulldata.hdr.frame_control =
+                       IEEE80211_FC(WLAN_FC_TYPE_DATA,
+                                    WLAN_FC_STYPE_NULLFUNC);
+               size = sizeof(struct ieee80211_hdr);
+       }
+
+       nulldata.hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS);
+       os_memcpy(nulldata.hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN);
+       os_memcpy(nulldata.hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
+       os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
+
+       if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size, 0, 0, 0,
+                                        0, 0) < 0)
+               wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to "
+                          "send poll frame");
+}
+
+static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr,
+                               int qos)
 {
+       struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       int ret = -1;
-       u8 *buf;
-       struct ieee80211_hdr *hdr;
+       struct nl_msg *msg;
+       int ret;
 
-       wpa_printf(MSG_DEBUG, "nl80211: Send Action frame (ifindex=%d, "
-                  "freq=%u MHz wait=%d ms no_cck=%d)",
-                  drv->ifindex, freq, wait_time, no_cck);
+       if (!drv->poll_command_supported) {
+               nl80211_send_null_frame(bss, own_addr, addr, qos);
+               return;
+       }
 
-       buf = os_zalloc(24 + data_len);
-       if (buf == NULL)
-               return ret;
-       os_memcpy(buf + 24, data, data_len);
-       hdr = (struct ieee80211_hdr *) buf;
-       hdr->frame_control =
-               IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_ACTION);
-       os_memcpy(hdr->addr1, dst, ETH_ALEN);
-       os_memcpy(hdr->addr2, src, ETH_ALEN);
-       os_memcpy(hdr->addr3, bssid, ETH_ALEN);
+       if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_PROBE_CLIENT)) ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+               nlmsg_free(msg);
+               return;
+       }
 
-       if (is_ap_interface(drv->nlmode))
-               ret = wpa_driver_nl80211_send_mlme(bss, buf, 24 + data_len,
-                                                  0, freq, no_cck, 1,
-                                                  wait_time);
-       else
-               ret = nl80211_send_frame_cmd(bss, freq, wait_time, buf,
-                                            24 + data_len,
-                                            &drv->send_action_cookie,
-                                            no_cck, 0, 1);
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Client probe request for "
+                          MACSTR " failed: ret=%d (%s)",
+                          MAC2STR(addr), ret, strerror(-ret));
+       }
+}
 
-       os_free(buf);
-       return ret;
+
+static int nl80211_set_power_save(struct i802_bss *bss, int enabled)
+{
+       struct nl_msg *msg;
+
+       if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_SET_POWER_SAVE)) ||
+           nla_put_u32(msg, NL80211_ATTR_PS_STATE,
+                       enabled ? NL80211_PS_ENABLED : NL80211_PS_DISABLED)) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
+       }
+       return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
 }
 
 
-static void wpa_driver_nl80211_send_action_cancel_wait(void *priv)
+static int nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps,
+                                    int ctwindow)
+{
+       struct i802_bss *bss = priv;
+
+       wpa_printf(MSG_DEBUG, "nl80211: set_p2p_powersave (legacy_ps=%d "
+                  "opp_ps=%d ctwindow=%d)", legacy_ps, opp_ps, ctwindow);
+
+       if (opp_ps != -1 || ctwindow != -1) {
+#ifdef ANDROID_P2P
+               wpa_driver_set_p2p_ps(priv, legacy_ps, opp_ps, ctwindow);
+#else /* ANDROID_P2P */
+               return -1; /* Not yet supported */
+#endif /* ANDROID_P2P */
+       }
+
+       if (legacy_ps == -1)
+               return 0;
+       if (legacy_ps != 0 && legacy_ps != 1)
+               return -1; /* Not yet supported */
+
+       return nl80211_set_power_save(bss, legacy_ps);
+}
+
+
+static int nl80211_start_radar_detection(void *priv,
+                                        struct hostapd_freq_params *freq)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
        int ret;
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return;
+       wpa_printf(MSG_DEBUG, "nl80211: Start radar detection (CAC) %d MHz (ht_enabled=%d, vht_enabled=%d, bandwidth=%d MHz, cf1=%d MHz, cf2=%d MHz)",
+                  freq->freq, freq->ht_enabled, freq->vht_enabled,
+                  freq->bandwidth, freq->center_freq1, freq->center_freq2);
 
-       wpa_printf(MSG_DEBUG, "nl80211: Cancel TX frame wait: cookie=0x%llx",
-                  (long long unsigned int) drv->send_action_cookie);
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_FRAME_WAIT_CANCEL);
+       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_RADAR)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Driver does not support radar "
+                          "detection");
+               return -1;
+       }
 
-       if (nl80211_set_iface_id(msg, bss) < 0)
-               goto nla_put_failure;
-       NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->send_action_cookie);
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_RADAR_DETECT)) ||
+           nl80211_put_freq_params(msg, freq) < 0) {
+               nlmsg_free(msg);
+               return -1;
+       }
 
        ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       msg = NULL;
-       if (ret)
-               wpa_printf(MSG_DEBUG, "nl80211: wait cancel failed: ret=%d "
-                          "(%s)", ret, strerror(-ret));
-
- nla_put_failure:
-       nlmsg_free(msg);
+       if (ret == 0)
+               return 0;
+       wpa_printf(MSG_DEBUG, "nl80211: Failed to start radar detection: "
+                  "%d (%s)", ret, strerror(-ret));
+       return -1;
 }
 
+#ifdef CONFIG_TDLS
 
-static int wpa_driver_nl80211_remain_on_channel(void *priv, unsigned int freq,
-                                               unsigned int duration)
+static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code,
+                                 u8 dialog_token, u16 status_code,
+                                 u32 peer_capab, int initiator, const u8 *buf,
+                                 size_t len)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
-       int ret;
-       u64 cookie;
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -1;
+       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+               return -EOPNOTSUPP;
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_REMAIN_ON_CHANNEL);
+       if (!dst)
+               return -EINVAL;
 
-       if (nl80211_set_iface_id(msg, bss) < 0)
-               goto nla_put_failure;
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_MGMT)) ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dst) ||
+           nla_put_u8(msg, NL80211_ATTR_TDLS_ACTION, action_code) ||
+           nla_put_u8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token) ||
+           nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, status_code))
+               goto fail;
+       if (peer_capab) {
+               /*
+                * The internal enum tdls_peer_capability definition is
+                * currently identical with the nl80211 enum
+                * nl80211_tdls_peer_capability, so no conversion is needed
+                * here.
+                */
+               if (nla_put_u32(msg, NL80211_ATTR_TDLS_PEER_CAPABILITY,
+                               peer_capab))
+                       goto fail;
+       }
+       if ((initiator &&
+            nla_put_flag(msg, NL80211_ATTR_TDLS_INITIATOR)) ||
+           nla_put(msg, NL80211_ATTR_IE, len, buf))
+               goto fail;
 
-       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
-       NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
+       return send_and_recv_msgs(drv, msg, NULL, NULL);
 
-       cookie = 0;
-       ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
-       msg = NULL;
-       if (ret == 0) {
-               wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel cookie "
-                          "0x%llx for freq=%u MHz duration=%u",
-                          (long long unsigned int) cookie, freq, duration);
-               drv->remain_on_chan_cookie = cookie;
-               drv->pending_remain_on_chan = 1;
-               return 0;
-       }
-       wpa_printf(MSG_DEBUG, "nl80211: Failed to request remain-on-channel "
-                  "(freq=%d duration=%u): %d (%s)",
-                  freq, duration, ret, strerror(-ret));
-nla_put_failure:
+fail:
        nlmsg_free(msg);
-       return -1;
+       return -ENOBUFS;
 }
 
 
-static int wpa_driver_nl80211_cancel_remain_on_channel(void *priv)
+static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
-       int ret;
-
-       if (!drv->pending_remain_on_chan) {
-               wpa_printf(MSG_DEBUG, "nl80211: No pending remain-on-channel "
-                          "to cancel");
-               return -1;
-       }
-
-       wpa_printf(MSG_DEBUG, "nl80211: Cancel remain-on-channel with cookie "
-                  "0x%llx",
-                  (long long unsigned int) drv->remain_on_chan_cookie);
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -1;
+       enum nl80211_tdls_operation nl80211_oper;
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL);
+       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
+               return -EOPNOTSUPP;
 
-       if (nl80211_set_iface_id(msg, bss) < 0)
-               goto nla_put_failure;
+       switch (oper) {
+       case TDLS_DISCOVERY_REQ:
+               nl80211_oper = NL80211_TDLS_DISCOVERY_REQ;
+               break;
+       case TDLS_SETUP:
+               nl80211_oper = NL80211_TDLS_SETUP;
+               break;
+       case TDLS_TEARDOWN:
+               nl80211_oper = NL80211_TDLS_TEARDOWN;
+               break;
+       case TDLS_ENABLE_LINK:
+               nl80211_oper = NL80211_TDLS_ENABLE_LINK;
+               break;
+       case TDLS_DISABLE_LINK:
+               nl80211_oper = NL80211_TDLS_DISABLE_LINK;
+               break;
+       case TDLS_ENABLE:
+               return 0;
+       case TDLS_DISABLE:
+               return 0;
+       default:
+               return -EINVAL;
+       }
 
-       NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, drv->remain_on_chan_cookie);
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_TDLS_OPER)) ||
+           nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper) ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer)) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
+       }
 
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       msg = NULL;
-       if (ret == 0)
-               return 0;
-       wpa_printf(MSG_DEBUG, "nl80211: Failed to cancel remain-on-channel: "
-                  "%d (%s)", ret, strerror(-ret));
-nla_put_failure:
-       nlmsg_free(msg);
-       return -1;
+       return send_and_recv_msgs(drv, msg, NULL, NULL);
 }
 
 
-static int wpa_driver_nl80211_probe_req_report(struct i802_bss *bss, int report)
+static int
+nl80211_tdls_enable_channel_switch(void *priv, const u8 *addr, u8 oper_class,
+                                  const struct hostapd_freq_params *params)
 {
+       struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret = -ENOBUFS;
 
-       if (!report) {
-               if (bss->nl_preq && drv->device_ap_sme &&
-                   is_ap_interface(drv->nlmode)) {
-                       /*
-                        * Do not disable Probe Request reporting that was
-                        * enabled in nl80211_setup_ap().
-                        */
-                       wpa_printf(MSG_DEBUG, "nl80211: Skip disabling of "
-                                  "Probe Request reporting nl_preq=%p while "
-                                  "in AP mode", bss->nl_preq);
-               } else if (bss->nl_preq) {
-                       wpa_printf(MSG_DEBUG, "nl80211: Disable Probe Request "
-                                  "reporting nl_preq=%p", bss->nl_preq);
-                       eloop_unregister_read_sock(
-                               nl_socket_get_fd(bss->nl_preq));
-                       nl_destroy_handles(&bss->nl_preq);
-               }
-               return 0;
-       }
+       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT) ||
+           !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH))
+               return -EOPNOTSUPP;
 
-       if (bss->nl_preq) {
-               wpa_printf(MSG_DEBUG, "nl80211: Probe Request reporting "
-                          "already on! nl_preq=%p", bss->nl_preq);
-               return 0;
+       wpa_printf(MSG_DEBUG, "nl80211: Enable TDLS channel switch " MACSTR
+                  " oper_class=%u freq=%u",
+                  MAC2STR(addr), oper_class, params->freq);
+       msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_TDLS_CHANNEL_SWITCH);
+       if (!msg ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+           nla_put_u8(msg, NL80211_ATTR_OPER_CLASS, oper_class) ||
+           (ret = nl80211_put_freq_params(msg, params))) {
+               nlmsg_free(msg);
+               wpa_printf(MSG_DEBUG, "nl80211: Could not build TDLS chan switch");
+               return ret;
        }
 
-       bss->nl_preq = nl_create_handle(drv->global->nl_cb, "preq");
-       if (bss->nl_preq == NULL)
-               return -1;
-       wpa_printf(MSG_DEBUG, "nl80211: Enable Probe Request "
-                  "reporting nl_preq=%p", bss->nl_preq);
+       return send_and_recv_msgs(drv, msg, NULL, NULL);
+}
 
-       if (nl80211_register_frame(bss, bss->nl_preq,
-                                  (WLAN_FC_TYPE_MGMT << 2) |
-                                  (WLAN_FC_STYPE_PROBE_REQ << 4),
-                                  NULL, 0) < 0)
-               goto out_err;
 
-#ifdef TIZEN_EXT
-       if (drv->nlmode != NL80211_IFTYPE_AP &&
-               drv->nlmode != NL80211_IFTYPE_P2P_GO) {
-               wpa_printf(MSG_DEBUG, "nl80211: probe_req_report control only "
-                          "allowed in AP or P2P GO mode (iftype=%d)",
-                          drv->nlmode);
-               goto done;
-       }
+static int
+nl80211_tdls_disable_channel_switch(void *priv, const u8 *addr)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
 
-       if (nl80211_register_frame(bss, bss->nl_preq,
-                          (WLAN_FC_TYPE_MGMT << 2) |
-                          (WLAN_FC_STYPE_ASSOC_REQ << 4),
-                          NULL, 0) < 0) {
-               goto out_err;
-       }
+       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT) ||
+           !(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH))
+               return -EOPNOTSUPP;
 
-       if (nl80211_register_frame(bss, bss->nl_preq,
-                          (WLAN_FC_TYPE_MGMT << 2) |
-                          (WLAN_FC_STYPE_REASSOC_REQ << 4),
-                          NULL, 0) < 0) {
-               goto out_err;
+       wpa_printf(MSG_DEBUG, "nl80211: Disable TDLS channel switch " MACSTR,
+                  MAC2STR(addr));
+       msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH);
+       if (!msg ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+               nlmsg_free(msg);
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Could not build TDLS cancel chan switch");
+               return -ENOBUFS;
        }
 
-       if (nl80211_register_frame(bss, bss->nl_preq,
-                          (WLAN_FC_TYPE_MGMT << 2) |
-                          (WLAN_FC_STYPE_DISASSOC << 4),
-                          NULL, 0) < 0) {
-               goto out_err;
-       }
+       return send_and_recv_msgs(drv, msg, NULL, NULL);
+}
 
-       if (nl80211_register_frame(bss, bss->nl_preq,
-                                          (WLAN_FC_TYPE_MGMT << 2) |
-                                          (WLAN_FC_STYPE_DEAUTH << 4),
-                                          NULL, 0) < 0) {
-               goto out_err;
-       }
+#endif /* CONFIG TDLS */
 
-done:
-#endif
-       eloop_register_read_sock(nl_socket_get_fd(bss->nl_preq),
-                                wpa_driver_nl80211_event_receive, bss->nl_cb,
-                                bss->nl_preq);
 
-       return 0;
+static int driver_nl80211_set_key(const char *ifname, void *priv,
+                                 enum wpa_alg alg, const u8 *addr,
+                                 int key_idx, int set_tx,
+                                 const u8 *seq, size_t seq_len,
+                                 const u8 *key, size_t key_len)
+{
+       struct i802_bss *bss = priv;
+       return wpa_driver_nl80211_set_key(ifname, bss, alg, addr, key_idx,
+                                         set_tx, seq, seq_len, key, key_len);
+}
 
- out_err:
-       nl_destroy_handles(&bss->nl_preq);
-       return -1;
+
+static int driver_nl80211_scan2(void *priv,
+                               struct wpa_driver_scan_params *params)
+{
+       struct i802_bss *bss = priv;
+       return wpa_driver_nl80211_scan(bss, params);
 }
 
 
-static int nl80211_disable_11b_rates(struct wpa_driver_nl80211_data *drv,
-                                    int ifindex, int disabled)
+static int driver_nl80211_deauthenticate(void *priv, const u8 *addr,
+                                        int reason_code)
 {
-       struct nl_msg *msg;
-       struct nlattr *bands, *band;
-       int ret;
+       struct i802_bss *bss = priv;
+       return wpa_driver_nl80211_deauthenticate(bss, addr, reason_code);
+}
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -1;
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_TX_BITRATE_MASK);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, ifindex);
+static int driver_nl80211_authenticate(void *priv,
+                                      struct wpa_driver_auth_params *params)
+{
+       struct i802_bss *bss = priv;
+       return wpa_driver_nl80211_authenticate(bss, params);
+}
 
-       bands = nla_nest_start(msg, NL80211_ATTR_TX_RATES);
-       if (!bands)
-               goto nla_put_failure;
 
-       /*
-        * Disable 2 GHz rates 1, 2, 5.5, 11 Mbps by masking out everything
-        * else apart from 6, 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS
-        * rates. All 5 GHz rates are left enabled.
-        */
-       band = nla_nest_start(msg, NL80211_BAND_2GHZ);
-       if (!band)
-               goto nla_put_failure;
-       if (disabled) {
-               NLA_PUT(msg, NL80211_TXRATE_LEGACY, 8,
-                       "\x0c\x12\x18\x24\x30\x48\x60\x6c");
-       }
-       nla_nest_end(msg, band);
+static void driver_nl80211_deinit(void *priv)
+{
+       struct i802_bss *bss = priv;
+       wpa_driver_nl80211_deinit(bss);
+}
 
-       nla_nest_end(msg, bands);
 
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       msg = NULL;
-       if (ret) {
-               wpa_printf(MSG_DEBUG, "nl80211: Set TX rates failed: ret=%d "
-                          "(%s)", ret, strerror(-ret));
-       } else
-               drv->disabled_11b_rates = disabled;
+static int driver_nl80211_if_remove(void *priv, enum wpa_driver_if_type type,
+                                   const char *ifname)
+{
+       struct i802_bss *bss = priv;
+       return wpa_driver_nl80211_if_remove(bss, type, ifname);
+}
 
-       return ret;
 
-nla_put_failure:
-       nlmsg_free(msg);
-       return -1;
+static int driver_nl80211_send_mlme(void *priv, const u8 *data,
+                                   size_t data_len, int noack)
+{
+       struct i802_bss *bss = priv;
+       return wpa_driver_nl80211_send_mlme(bss, data, data_len, noack,
+                                           0, 0, 0, 0);
 }
 
 
-static int wpa_driver_nl80211_deinit_ap(void *priv)
+static int driver_nl80211_sta_remove(void *priv, const u8 *addr)
 {
        struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       if (!is_ap_interface(drv->nlmode))
-               return -1;
-       wpa_driver_nl80211_del_beacon(drv);
-       return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
+       return wpa_driver_nl80211_sta_remove(bss, addr, -1, 0);
 }
 
 
-static int wpa_driver_nl80211_stop_ap(void *priv)
+static int driver_nl80211_set_sta_vlan(void *priv, const u8 *addr,
+                                      const char *ifname, int vlan_id)
 {
        struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       if (!is_ap_interface(drv->nlmode))
-               return -1;
-       wpa_driver_nl80211_del_beacon(drv);
-       bss->beacon_set = 0;
-       return 0;
+       return i802_set_sta_vlan(bss, addr, ifname, vlan_id);
 }
 
 
-static int wpa_driver_nl80211_deinit_p2p_cli(void *priv)
+static int driver_nl80211_read_sta_data(void *priv,
+                                       struct hostap_sta_driver_data *data,
+                                       const u8 *addr)
 {
        struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       if (drv->nlmode != NL80211_IFTYPE_P2P_CLIENT)
-               return -1;
-       return wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_STATION);
+       return i802_read_sta_data(bss, data, addr);
 }
 
 
-static void wpa_driver_nl80211_resume(void *priv)
+static int driver_nl80211_send_action(void *priv, unsigned int freq,
+                                     unsigned int wait_time,
+                                     const u8 *dst, const u8 *src,
+                                     const u8 *bssid,
+                                     const u8 *data, size_t data_len,
+                                     int no_cck)
 {
        struct i802_bss *bss = priv;
+       return wpa_driver_nl80211_send_action(bss, freq, wait_time, dst, src,
+                                             bssid, data, data_len, no_cck);
+}
 
-       if (i802_set_iface_flags(bss, 1))
-               wpa_printf(MSG_DEBUG, "nl80211: Failed to set interface up on resume event");
+
+static int driver_nl80211_probe_req_report(void *priv, int report)
+{
+       struct i802_bss *bss = priv;
+       return wpa_driver_nl80211_probe_req_report(bss, report);
 }
 
 
-static int nl80211_send_ft_action(void *priv, u8 action, const u8 *target_ap,
-                                 const u8 *ies, size_t ies_len)
+static int wpa_driver_nl80211_update_ft_ies(void *priv, const u8 *md,
+                                           const u8 *ies, size_t ies_len)
 {
+       int ret;
+       struct nl_msg *msg;
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       int ret;
-       u8 *data, *pos;
-       size_t data_len;
-       const u8 *own_addr = bss->addr;
+       u16 mdid = WPA_GET_LE16(md);
 
-       if (action != 1) {
-               wpa_printf(MSG_ERROR, "nl80211: Unsupported send_ft_action "
-                          "action %d", action);
-               return -1;
+       wpa_printf(MSG_DEBUG, "nl80211: Updating FT IEs");
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_UPDATE_FT_IES)) ||
+           nla_put(msg, NL80211_ATTR_IE, ies_len, ies) ||
+           nla_put_u16(msg, NL80211_ATTR_MDID, mdid)) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
        }
 
-       /*
-        * Action frame payload:
-        * Category[1] = 6 (Fast BSS Transition)
-        * Action[1] = 1 (Fast BSS Transition Request)
-        * STA Address
-        * Target AP Address
-        * FT IEs
-        */
-
-       data_len = 2 + 2 * ETH_ALEN + ies_len;
-       data = os_malloc(data_len);
-       if (data == NULL)
-               return -1;
-       pos = data;
-       *pos++ = 0x06; /* FT Action category */
-       *pos++ = action;
-       os_memcpy(pos, own_addr, ETH_ALEN);
-       pos += ETH_ALEN;
-       os_memcpy(pos, target_ap, ETH_ALEN);
-       pos += ETH_ALEN;
-       os_memcpy(pos, ies, ies_len);
-
-       ret = wpa_driver_nl80211_send_action(bss, drv->assoc_freq, 0,
-                                            drv->bssid, own_addr, drv->bssid,
-                                            data, data_len, 0);
-       os_free(data);
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: update_ft_ies failed "
+                          "err=%d (%s)", ret, strerror(-ret));
+       }
 
        return ret;
 }
 
 
-static int nl80211_signal_monitor(void *priv, int threshold, int hysteresis)
+const u8 * wpa_driver_nl80211_get_macaddr(void *priv)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct nl_msg *msg;
-       struct nlattr *cqm;
-       int ret = -1;
-
-       wpa_printf(MSG_DEBUG, "nl80211: Signal monitor threshold=%d "
-                  "hysteresis=%d", threshold, hysteresis);
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -1;
-
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_CQM);
 
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
-
-       cqm = nla_nest_start(msg, NL80211_ATTR_CQM);
-       if (cqm == NULL)
-               goto nla_put_failure;
+       if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE)
+               return NULL;
 
-       NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THOLD, threshold);
-       NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_HYST, hysteresis);
-       nla_nest_end(msg, cqm);
+       return bss->addr;
+}
 
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       msg = NULL;
+#if defined(TIZEN_WLAN_BOARD_SPRD)
 
-nla_put_failure:
-       nlmsg_free(msg);
-       return ret;
-}
+typedef struct {
+       char *buf;
+       int used_len;
+       int total_len;
+} tizen_wifi_priv_cmd;
 
+static int drv_errors = 0;
 
-/* Converts nl80211_chan_width to a common format */
-static enum chan_width convert2width(int width)
+static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv)
 {
-       switch (width) {
-       case NL80211_CHAN_WIDTH_20_NOHT:
-               return CHAN_WIDTH_20_NOHT;
-       case NL80211_CHAN_WIDTH_20:
-               return CHAN_WIDTH_20;
-       case NL80211_CHAN_WIDTH_40:
-               return CHAN_WIDTH_40;
-       case NL80211_CHAN_WIDTH_80:
-               return CHAN_WIDTH_80;
-       case NL80211_CHAN_WIDTH_80P80:
-               return CHAN_WIDTH_80P80;
-       case NL80211_CHAN_WIDTH_160:
-               return CHAN_WIDTH_160;
+       drv_errors++;
+       if (drv_errors > 4) {
+               drv_errors = 0;
+               wpa_msg(drv->ctx, MSG_INFO, "Driver State : HANGED");
        }
-       return CHAN_WIDTH_UNKNOWN;
 }
 
-
-static int get_channel_width(struct nl_msg *msg, void *arg)
+static int wpa_driver_nl80211_priv_cmd(struct i802_bss *bss, const char *cmd)
 {
-       struct nlattr *tb[NL80211_ATTR_MAX + 1];
-       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
-       struct wpa_signal_info *sig_change = arg;
-
-       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
-                 genlmsg_attrlen(gnlh, 0), NULL);
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct ifreq ifr;
+       tizen_wifi_priv_cmd priv_cmd;
+       char buf[MAX_DRV_CMD_SIZE];
+       int ret = 0;
 
-       sig_change->center_frq1 = -1;
-       sig_change->center_frq2 = -1;
-       sig_change->chanwidth = CHAN_WIDTH_UNKNOWN;
+       if (cmd)
+               wpa_printf(MSG_DEBUG, "%s = %s", __func__, cmd);
+       else
+               return -1;
 
-       if (tb[NL80211_ATTR_CHANNEL_WIDTH]) {
-               sig_change->chanwidth = convert2width(
-                       nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH]));
-               if (tb[NL80211_ATTR_CENTER_FREQ1])
-                       sig_change->center_frq1 =
-                               nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
-               if (tb[NL80211_ATTR_CENTER_FREQ2])
-                       sig_change->center_frq2 =
-                               nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
-       }
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_memset(&priv_cmd, 0, sizeof(priv_cmd));
+       os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
 
-       return NL_SKIP;
-}
+       os_memset(buf, 0, sizeof(buf));
+       os_strlcpy(buf, cmd, sizeof(buf));
 
+       priv_cmd.buf = buf;
+       priv_cmd.used_len = sizeof(buf);
+       priv_cmd.total_len = sizeof(buf);
+       ifr.ifr_data = &priv_cmd;
 
-static int nl80211_get_channel_width(struct wpa_driver_nl80211_data *drv,
-                                    struct wpa_signal_info *sig)
-{
-       struct nl_msg *msg;
+       wpa_printf(MSG_DEBUG, "[KGB_DEBUG] %s: ioctl socket [%d], ifname [%s]",
+                       __func__, drv->global->ioctl_sock, bss->ifname);
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
+       ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "%s: failed to issue private commands",
+                          __func__);
+               wpa_driver_send_hang_msg(drv);
+               return ret;
+       }
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_INTERFACE);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
+       drv_errors = 0;
+       return 0;
+}
 
-       return send_and_recv_msgs(drv, msg, get_channel_width, sig);
+int hostapd_send_conf_to_driver(struct hostapd_data *hapd)
+{
+       char buf[MAX_DRV_CMD_SIZE];
+       char *ssid = hapd->conf->ssid.ssid;
+       unsigned int ssid_len = hapd->conf->ssid.ssid_len;
+       unsigned int ignore_broadcast_ssid = hapd->conf->ignore_broadcast_ssid;
 
-nla_put_failure:
-       nlmsg_free(msg);
-       return -ENOBUFS;
+       memset(buf, 0, sizeof(buf));
+       snprintf(buf, sizeof(buf), "HIDDEN_SSID 0x%02x 0x%02x %s",
+                       ignore_broadcast_ssid, ssid_len, ssid);
+       return wpa_driver_nl80211_priv_cmd(hapd->drv_priv, buf);
 }
+#endif
 
+#ifdef BCM_DRIVER_V115
 
-static int nl80211_signal_poll(void *priv, struct wpa_signal_info *si)
-{
-       struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       int res;
+#define MAX_WPSP2PIE_CMD_SIZE          384
 
-       os_memset(si, 0, sizeof(*si));
-       res = nl80211_get_link_signal(drv, si);
-       if (res != 0)
-               return res;
+typedef struct tizen_wifi_priv_cmd {
+       char *buf;
+       int used_len;
+       int total_len;
+} bcm_driver_priv_cmd;
 
-       res = nl80211_get_channel_width(drv, si);
-       if (res != 0)
-               return res;
+static int drv_errors = 0;
 
-       return nl80211_get_link_noise(drv, si);
+static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv)
+{
+       drv_errors++;
+       if (drv_errors > 4) {
+               drv_errors = 0;
+               wpa_msg(drv->ctx, MSG_INFO, "BCM Driver State : HANGED");
+       }
 }
 
-
-static int wpa_driver_nl80211_shared_freq(void *priv)
+int wpa_driver_nl80211_priv_cmd_bcm(void *priv, char *cmd, char *buf,
+                                 size_t buf_len )
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct wpa_driver_nl80211_data *driver;
-       int freq = 0;
+       struct ifreq ifr;
+       bcm_driver_priv_cmd priv_cmd_data;
+       int ret = 0;
 
-       /*
-        * If the same PHY is in connected state with some other interface,
-        * then retrieve the assoc freq.
-        */
-       wpa_printf(MSG_DEBUG, "nl80211: Get shared freq for PHY %s",
-                  drv->phyname);
+       if (cmd)
+               wpa_printf(MSG_DEBUG, "%s = %s", __func__, cmd);
+       else
+               return -1;
 
-       dl_list_for_each(driver, &drv->global->interfaces,
-                        struct wpa_driver_nl80211_data, list) {
-               if (drv == driver ||
-                   os_strcmp(drv->phyname, driver->phyname) != 0 ||
-                   !driver->associated)
-                       continue;
+       os_memcpy(buf, cmd, strlen(cmd) + 1);
+
+       memset(&ifr, 0, sizeof(ifr));
+       memset(&priv_cmd_data, 0, sizeof(priv_cmd_data));
+       os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
+
+       priv_cmd_data.buf = buf;
+       priv_cmd_data.used_len = buf_len;
+       priv_cmd_data.total_len = buf_len;
+       ifr.ifr_data = &priv_cmd_data;
+
+       wpa_printf(MSG_DEBUG, "[KGB_DEBUG] %s: ioctl socket [%d], ifname [%s]", __func__, drv->global->ioctl_sock, bss->ifname);
+       errno = 0;
+       if ((ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr)) < 0) {
+               wpa_printf(MSG_ERROR, "%s: failed to issue private commands. Error [%s]", __func__, strerror(errno));
+               wpa_driver_send_hang_msg(drv);
+       } else {
+               drv_errors = 0;
+               ret = 0;
+               if ((os_strcasecmp(cmd, "LINKSPEED") == 0) ||
+                   (os_strcasecmp(cmd, "RSSI") == 0) ||
+                   (os_strcasecmp(cmd, "GETBAND") == 0) ||
+                   (os_strcasecmp(cmd, "P2P_GET_NOA") == 0))
+                       ret = strlen(buf);
 
-               wpa_printf(MSG_DEBUG, "nl80211: Found a match for PHY %s - %s "
-                          MACSTR,
-                          driver->phyname, driver->first_bss.ifname,
-                          MAC2STR(driver->first_bss.addr));
-               if (is_ap_interface(driver->nlmode))
-                       freq = driver->first_bss.freq;
-               else
-                       freq = nl80211_get_assoc_freq(driver);
-               wpa_printf(MSG_DEBUG, "nl80211: Shared freq for PHY %s: %d",
-                          drv->phyname, freq);
+               wpa_printf(MSG_DEBUG, "%s %s len = %d, %d", __func__, buf, ret, strlen(buf));
        }
-
-       if (!freq)
-               wpa_printf(MSG_DEBUG, "nl80211: No shared interface for "
-                          "PHY (%s) in associated state", drv->phyname);
-
-       return freq;
+       return ret;
 }
 
-
-static int nl80211_send_frame(void *priv, const u8 *data, size_t data_len,
-                             int encrypt)
+int wpa_driver_nl80211_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon,
+                                const struct wpabuf *proberesp,
+                                const struct wpabuf *assocresp)
 {
-       struct i802_bss *bss = priv;
-       return wpa_driver_nl80211_send_frame(bss, data, data_len, encrypt, 0,
-                                            0, 0, 0, 0);
+       char buf[MAX_WPSP2PIE_CMD_SIZE+768];
+       struct wpabuf *ap_wps_p2p_ie = NULL;
+       char *_cmd = "SET_AP_WPS_P2P_IE";
+       char *pbuf;
+       int ret = 0;
+       int i;
+       struct cmd_desc {
+               int cmd;
+               const struct wpabuf *src;
+       } cmd_arr[] = {
+               {0x1, beacon},
+               {0x2, proberesp},
+               {0x4, assocresp},
+               {-1, NULL}
+       };
+
+       wpa_printf(MSG_DEBUG, "%s: Entry", __func__);
+       for (i = 0; cmd_arr[i].cmd != -1; i++) {
+               os_memset(buf, 0, sizeof(buf));
+               pbuf = buf;
+               pbuf += sprintf(pbuf, "%s %d", _cmd, cmd_arr[i].cmd);
+               *pbuf++ = '\0';
+               ap_wps_p2p_ie = cmd_arr[i].src ?
+                       wpabuf_dup(cmd_arr[i].src) : NULL;
+               if (ap_wps_p2p_ie) {
+                       os_memcpy(pbuf, wpabuf_head(ap_wps_p2p_ie), wpabuf_len(ap_wps_p2p_ie));
+                       ret = wpa_driver_nl80211_priv_cmd_bcm(priv, buf, buf,
+                               strlen(_cmd) + 3 + wpabuf_len(ap_wps_p2p_ie));
+                       wpabuf_free(ap_wps_p2p_ie);
+                       if (ret < 0)
+                               break;
+               }
+       }
+
+       return ret;
 }
+#endif /* BCM_DRIVER_V115 */
 
 
-static int nl80211_set_param(void *priv, const char *param)
+static const char * scan_state_str(enum scan_states scan_state)
 {
-       wpa_printf(MSG_DEBUG, "nl80211: driver param='%s'", param);
-       if (param == NULL)
-               return 0;
-
-#ifdef CONFIG_P2P
-       if (os_strstr(param, "use_p2p_group_interface=1")) {
-               struct i802_bss *bss = priv;
-               struct wpa_driver_nl80211_data *drv = bss->drv;
+       switch (scan_state) {
+       case NO_SCAN:
+               return "NO_SCAN";
+       case SCAN_REQUESTED:
+               return "SCAN_REQUESTED";
+       case SCAN_STARTED:
+               return "SCAN_STARTED";
+       case SCAN_COMPLETED:
+               return "SCAN_COMPLETED";
+       case SCAN_ABORTED:
+               return "SCAN_ABORTED";
+       case SCHED_SCAN_STARTED:
+               return "SCHED_SCAN_STARTED";
+       case SCHED_SCAN_STOPPED:
+               return "SCHED_SCAN_STOPPED";
+       case SCHED_SCAN_RESULTS:
+               return "SCHED_SCAN_RESULTS";
+       }
 
-               wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
-                          "interface");
-               drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
-               drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
+       return "??";
+}
 
-#if defined(TIZEN_EXT_P2P) && defined(BCM_DRIVER_V115)
-               wpa_printf(MSG_ERROR, "nl80211: Use Multi channel concurrency");
-               drv->capa.flags |= WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT;
-#endif /* TIZEN_EXT_P2P && BCM_DRIVER_V115 */
-       }
 
-       if (os_strstr(param, "p2p_device=1")) {
-               struct i802_bss *bss = priv;
-               struct wpa_driver_nl80211_data *drv = bss->drv;
-               drv->allow_p2p_device = 1;
-       }
-#endif /* CONFIG_P2P */
+static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int res;
+       char *pos, *end;
+
+       pos = buf;
+       end = buf + buflen;
+
+       res = os_snprintf(pos, end - pos,
+                         "ifindex=%d\n"
+                         "ifname=%s\n"
+                         "brname=%s\n"
+                         "addr=" MACSTR "\n"
+                         "freq=%d\n"
+                         "%s%s%s%s%s",
+                         bss->ifindex,
+                         bss->ifname,
+                         bss->brname,
+                         MAC2STR(bss->addr),
+                         bss->freq,
+                         bss->beacon_set ? "beacon_set=1\n" : "",
+                         bss->added_if_into_bridge ?
+                         "added_if_into_bridge=1\n" : "",
+                         bss->added_bridge ? "added_bridge=1\n" : "",
+                         bss->in_deinit ? "in_deinit=1\n" : "",
+                         bss->if_dynamic ? "if_dynamic=1\n" : "");
+       if (os_snprintf_error(end - pos, res))
+               return pos - buf;
+       pos += res;
+
+       if (bss->wdev_id_set) {
+               res = os_snprintf(pos, end - pos, "wdev_id=%llu\n",
+                                 (unsigned long long) bss->wdev_id);
+               if (os_snprintf_error(end - pos, res))
+                       return pos - buf;
+               pos += res;
+       }
+
+       res = os_snprintf(pos, end - pos,
+                         "phyname=%s\n"
+                         "perm_addr=" MACSTR "\n"
+                         "drv_ifindex=%d\n"
+                         "operstate=%d\n"
+                         "scan_state=%s\n"
+                         "auth_bssid=" MACSTR "\n"
+                         "auth_attempt_bssid=" MACSTR "\n"
+                         "bssid=" MACSTR "\n"
+                         "prev_bssid=" MACSTR "\n"
+                         "associated=%d\n"
+                         "assoc_freq=%u\n"
+                         "monitor_sock=%d\n"
+                         "monitor_ifidx=%d\n"
+                         "monitor_refcount=%d\n"
+                         "last_mgmt_freq=%u\n"
+                         "eapol_tx_sock=%d\n"
+                         "%s%s%s%s%s%s%s%s%s%s%s%s%s",
+                         drv->phyname,
+                         MAC2STR(drv->perm_addr),
+                         drv->ifindex,
+                         drv->operstate,
+                         scan_state_str(drv->scan_state),
+                         MAC2STR(drv->auth_bssid),
+                         MAC2STR(drv->auth_attempt_bssid),
+                         MAC2STR(drv->bssid),
+                         MAC2STR(drv->prev_bssid),
+                         drv->associated,
+                         drv->assoc_freq,
+                         drv->monitor_sock,
+                         drv->monitor_ifidx,
+                         drv->monitor_refcount,
+                         drv->last_mgmt_freq,
+                         drv->eapol_tx_sock,
+                         drv->ignore_if_down_event ?
+                         "ignore_if_down_event=1\n" : "",
+                         drv->scan_complete_events ?
+                         "scan_complete_events=1\n" : "",
+                         drv->disabled_11b_rates ?
+                         "disabled_11b_rates=1\n" : "",
+                         drv->pending_remain_on_chan ?
+                         "pending_remain_on_chan=1\n" : "",
+                         drv->in_interface_list ? "in_interface_list=1\n" : "",
+                         drv->device_ap_sme ? "device_ap_sme=1\n" : "",
+                         drv->poll_command_supported ?
+                         "poll_command_supported=1\n" : "",
+                         drv->data_tx_status ? "data_tx_status=1\n" : "",
+                         drv->scan_for_auth ? "scan_for_auth=1\n" : "",
+                         drv->retry_auth ? "retry_auth=1\n" : "",
+                         drv->use_monitor ? "use_monitor=1\n" : "",
+                         drv->ignore_next_local_disconnect ?
+                         "ignore_next_local_disconnect=1\n" : "",
+                         drv->ignore_next_local_deauth ?
+                         "ignore_next_local_deauth=1\n" : "");
+       if (os_snprintf_error(end - pos, res))
+               return pos - buf;
+       pos += res;
+
+       if (drv->has_capability) {
+               res = os_snprintf(pos, end - pos,
+                                 "capa.key_mgmt=0x%x\n"
+                                 "capa.enc=0x%x\n"
+                                 "capa.auth=0x%x\n"
+                                 "capa.flags=0x%llx\n"
+                                 "capa.rrm_flags=0x%x\n"
+                                 "capa.max_scan_ssids=%d\n"
+                                 "capa.max_sched_scan_ssids=%d\n"
+                                 "capa.sched_scan_supported=%d\n"
+                                 "capa.max_match_sets=%d\n"
+                                 "capa.max_remain_on_chan=%u\n"
+                                 "capa.max_stations=%u\n"
+                                 "capa.probe_resp_offloads=0x%x\n"
+                                 "capa.max_acl_mac_addrs=%u\n"
+                                 "capa.num_multichan_concurrent=%u\n"
+                                 "capa.mac_addr_rand_sched_scan_supported=%d\n"
+                                 "capa.mac_addr_rand_scan_supported=%d\n",
+                                 drv->capa.key_mgmt,
+                                 drv->capa.enc,
+                                 drv->capa.auth,
+                                 (unsigned long long) drv->capa.flags,
+                                 drv->capa.rrm_flags,
+                                 drv->capa.max_scan_ssids,
+                                 drv->capa.max_sched_scan_ssids,
+                                 drv->capa.sched_scan_supported,
+                                 drv->capa.max_match_sets,
+                                 drv->capa.max_remain_on_chan,
+                                 drv->capa.max_stations,
+                                 drv->capa.probe_resp_offloads,
+                                 drv->capa.max_acl_mac_addrs,
+                                 drv->capa.num_multichan_concurrent,
+                                 drv->capa.mac_addr_rand_sched_scan_supported,
+                                 drv->capa.mac_addr_rand_scan_supported);
+               if (os_snprintf_error(end - pos, res))
+                       return pos - buf;
+               pos += res;
+       }
+
+       return pos - buf;
+}
+
+
+static int set_beacon_data(struct nl_msg *msg, struct beacon_data *settings)
+{
+       if ((settings->head &&
+            nla_put(msg, NL80211_ATTR_BEACON_HEAD,
+                    settings->head_len, settings->head)) ||
+           (settings->tail &&
+            nla_put(msg, NL80211_ATTR_BEACON_TAIL,
+                    settings->tail_len, settings->tail)) ||
+           (settings->beacon_ies &&
+            nla_put(msg, NL80211_ATTR_IE,
+                    settings->beacon_ies_len, settings->beacon_ies)) ||
+           (settings->proberesp_ies &&
+            nla_put(msg, NL80211_ATTR_IE_PROBE_RESP,
+                    settings->proberesp_ies_len, settings->proberesp_ies)) ||
+           (settings->assocresp_ies &&
+            nla_put(msg, NL80211_ATTR_IE_ASSOC_RESP,
+                    settings->assocresp_ies_len, settings->assocresp_ies)) ||
+           (settings->probe_resp &&
+            nla_put(msg, NL80211_ATTR_PROBE_RESP,
+                    settings->probe_resp_len, settings->probe_resp)))
+               return -ENOBUFS;
 
        return 0;
 }
 
 
-static void * nl80211_global_init(void)
+static int nl80211_switch_channel(void *priv, struct csa_settings *settings)
 {
-       struct nl80211_global *global;
-       struct netlink_config *cfg;
-
-       global = os_zalloc(sizeof(*global));
-       if (global == NULL)
-               return NULL;
-       global->ioctl_sock = -1;
-       dl_list_init(&global->interfaces);
-       global->if_add_ifindex = -1;
+       struct nl_msg *msg;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nlattr *beacon_csa;
+       int ret = -ENOBUFS;
 
-       cfg = os_zalloc(sizeof(*cfg));
-       if (cfg == NULL)
-               goto err;
+       wpa_printf(MSG_DEBUG, "nl80211: Channel switch request (cs_count=%u block_tx=%u freq=%d width=%d cf1=%d cf2=%d)",
+                  settings->cs_count, settings->block_tx,
+                  settings->freq_params.freq, settings->freq_params.bandwidth,
+                  settings->freq_params.center_freq1,
+                  settings->freq_params.center_freq2);
 
-       cfg->ctx = global;
-       cfg->newlink_cb = wpa_driver_nl80211_event_rtm_newlink;
-       cfg->dellink_cb = wpa_driver_nl80211_event_rtm_dellink;
-       global->netlink = netlink_init(cfg);
-       if (global->netlink == NULL) {
-               os_free(cfg);
-               goto err;
+       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_AP_CSA)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Driver does not support channel switch command");
+               return -EOPNOTSUPP;
        }
 
-       if (wpa_driver_nl80211_init_nl_global(global) < 0)
-               goto err;
+       if ((drv->nlmode != NL80211_IFTYPE_AP) &&
+           (drv->nlmode != NL80211_IFTYPE_P2P_GO))
+               return -EOPNOTSUPP;
 
-       global->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
-       if (global->ioctl_sock < 0) {
-               perror("socket(PF_INET,SOCK_DGRAM)");
-               goto err;
-       }
+       /* check settings validity */
+       if (!settings->beacon_csa.tail ||
+           ((settings->beacon_csa.tail_len <=
+             settings->counter_offset_beacon) ||
+            (settings->beacon_csa.tail[settings->counter_offset_beacon] !=
+             settings->cs_count)))
+               return -EINVAL;
 
-       return global;
+       if (settings->beacon_csa.probe_resp &&
+           ((settings->beacon_csa.probe_resp_len <=
+             settings->counter_offset_presp) ||
+            (settings->beacon_csa.probe_resp[settings->counter_offset_presp] !=
+             settings->cs_count)))
+               return -EINVAL;
 
-err:
-       nl80211_global_deinit(global);
-       return NULL;
-}
+       if (!(msg = nl80211_bss_msg(bss, 0, NL80211_CMD_CHANNEL_SWITCH)) ||
+           nla_put_u32(msg, NL80211_ATTR_CH_SWITCH_COUNT,
+                       settings->cs_count) ||
+           (ret = nl80211_put_freq_params(msg, &settings->freq_params)) ||
+           (settings->block_tx &&
+            nla_put_flag(msg, NL80211_ATTR_CH_SWITCH_BLOCK_TX)))
+               goto error;
 
+       /* beacon_after params */
+       ret = set_beacon_data(msg, &settings->beacon_after);
+       if (ret)
+               goto error;
 
-static void nl80211_global_deinit(void *priv)
-{
-       struct nl80211_global *global = priv;
-       if (global == NULL)
-               return;
-       if (!dl_list_empty(&global->interfaces)) {
-               wpa_printf(MSG_ERROR, "nl80211: %u interface(s) remain at "
-                          "nl80211_global_deinit",
-                          dl_list_len(&global->interfaces));
-       }
+       /* beacon_csa params */
+       beacon_csa = nla_nest_start(msg, NL80211_ATTR_CSA_IES);
+       if (!beacon_csa)
+               goto fail;
 
-       if (global->netlink)
-               netlink_deinit(global->netlink);
+       ret = set_beacon_data(msg, &settings->beacon_csa);
+       if (ret)
+               goto error;
 
-       nl_destroy_handles(&global->nl);
+       if (nla_put_u16(msg, NL80211_ATTR_CSA_C_OFF_BEACON,
+                       settings->counter_offset_beacon) ||
+           (settings->beacon_csa.probe_resp &&
+            nla_put_u16(msg, NL80211_ATTR_CSA_C_OFF_PRESP,
+                        settings->counter_offset_presp)))
+               goto fail;
 
-       if (global->nl_event) {
-               eloop_unregister_read_sock(
-                       nl_socket_get_fd(global->nl_event));
-               nl_destroy_handles(&global->nl_event);
+       nla_nest_end(msg, beacon_csa);
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: switch_channel failed err=%d (%s)",
+                          ret, strerror(-ret));
        }
+       return ret;
 
-       nl_cb_put(global->nl_cb);
-
-       if (global->ioctl_sock >= 0)
-               close(global->ioctl_sock);
-
-       os_free(global);
+fail:
+       ret = -ENOBUFS;
+error:
+       nlmsg_free(msg);
+       wpa_printf(MSG_DEBUG, "nl80211: Could not build channel switch request");
+       return ret;
 }
 
 
-static const char * nl80211_get_radio_name(void *priv)
+static int nl80211_add_ts(void *priv, u8 tsid, const u8 *addr,
+                         u8 user_priority, u16 admitted_time)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       return drv->phyname;
-}
-
-
-static int nl80211_pmkid(struct i802_bss *bss, int cmd, const u8 *bssid,
-                        const u8 *pmkid)
-{
        struct nl_msg *msg;
+       int ret;
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: add_ts request: tsid=%u admitted_time=%u up=%d",
+                  tsid, admitted_time, user_priority);
 
-       nl80211_cmd(bss->drv, msg, 0, cmd);
+       if (!is_sta_interface(drv->nlmode))
+               return -ENOTSUP;
 
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, if_nametoindex(bss->ifname));
-       if (pmkid)
-               NLA_PUT(msg, NL80211_ATTR_PMKID, 16, pmkid);
-       if (bssid)
-               NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
+       msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_ADD_TX_TS);
+       if (!msg ||
+           nla_put_u8(msg, NL80211_ATTR_TSID, tsid) ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
+           nla_put_u8(msg, NL80211_ATTR_USER_PRIO, user_priority) ||
+           nla_put_u16(msg, NL80211_ATTR_ADMITTED_TIME, admitted_time)) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
+       }
 
-       return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
- nla_put_failure:
-       nlmsg_free(msg);
-       return -ENOBUFS;
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret)
+               wpa_printf(MSG_DEBUG, "nl80211: add_ts failed err=%d (%s)",
+                          ret, strerror(-ret));
+       return ret;
 }
 
 
-static int nl80211_add_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
+static int nl80211_del_ts(void *priv, u8 tsid, const u8 *addr)
 {
        struct i802_bss *bss = priv;
-       wpa_printf(MSG_DEBUG, "nl80211: Add PMKID for " MACSTR, MAC2STR(bssid));
-       return nl80211_pmkid(bss, NL80211_CMD_SET_PMKSA, bssid, pmkid);
-}
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       int ret;
 
+       wpa_printf(MSG_DEBUG, "nl80211: del_ts request: tsid=%u", tsid);
 
-static int nl80211_remove_pmkid(void *priv, const u8 *bssid, const u8 *pmkid)
-{
-       struct i802_bss *bss = priv;
-       wpa_printf(MSG_DEBUG, "nl80211: Delete PMKID for " MACSTR,
-                  MAC2STR(bssid));
-       return nl80211_pmkid(bss, NL80211_CMD_DEL_PMKSA, bssid, pmkid);
-}
+       if (!is_sta_interface(drv->nlmode))
+               return -ENOTSUP;
 
+       if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_DEL_TX_TS)) ||
+           nla_put_u8(msg, NL80211_ATTR_TSID, tsid) ||
+           nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
+       }
 
-static int nl80211_flush_pmkid(void *priv)
-{
-       struct i802_bss *bss = priv;
-       wpa_printf(MSG_DEBUG, "nl80211: Flush PMKIDs");
-       return nl80211_pmkid(bss, NL80211_CMD_FLUSH_PMKSA, NULL, NULL);
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret)
+               wpa_printf(MSG_DEBUG, "nl80211: del_ts failed err=%d (%s)",
+                          ret, strerror(-ret));
+       return ret;
 }
 
 
-static void nl80211_set_rekey_info(void *priv, const u8 *kek, const u8 *kck,
-                                  const u8 *replay_ctr)
+#ifdef CONFIG_TESTING_OPTIONS
+static int cmd_reply_handler(struct nl_msg *msg, void *arg)
 {
-       struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct nlattr *replay_nested;
-       struct nl_msg *msg;
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return;
-
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_SET_REKEY_OFFLOAD);
-
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct wpabuf *buf = arg;
 
-       replay_nested = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA);
-       if (!replay_nested)
-               goto nla_put_failure;
+       if (!buf)
+               return NL_SKIP;
 
-       NLA_PUT(msg, NL80211_REKEY_DATA_KEK, NL80211_KEK_LEN, kek);
-       NLA_PUT(msg, NL80211_REKEY_DATA_KCK, NL80211_KCK_LEN, kck);
-       NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR, NL80211_REPLAY_CTR_LEN,
-               replay_ctr);
+       if ((size_t) genlmsg_attrlen(gnlh, 0) > wpabuf_tailroom(buf)) {
+               wpa_printf(MSG_INFO, "nl80211: insufficient buffer space for reply");
+               return NL_SKIP;
+       }
 
-       nla_nest_end(msg, replay_nested);
+       wpabuf_put_data(buf, genlmsg_attrdata(gnlh, 0),
+                       genlmsg_attrlen(gnlh, 0));
 
-       send_and_recv_msgs(drv, msg, NULL, NULL);
-       return;
- nla_put_failure:
-       nlmsg_free(msg);
+       return NL_SKIP;
 }
+#endif /* CONFIG_TESTING_OPTIONS */
 
 
-static void nl80211_send_null_frame(struct i802_bss *bss, const u8 *own_addr,
-                                   const u8 *addr, int qos)
+static int vendor_reply_handler(struct nl_msg *msg, void *arg)
 {
-       /* send data frame to poll STA and check whether
-        * this frame is ACKed */
-       struct {
-               struct ieee80211_hdr hdr;
-               u16 qos_ctl;
-       } STRUCT_PACKED nulldata;
-       size_t size;
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct nlattr *nl_vendor_reply, *nl;
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct wpabuf *buf = arg;
+       int rem;
 
-       /* Send data frame to poll STA and check whether this frame is ACKed */
+       if (!buf)
+               return NL_SKIP;
 
-       os_memset(&nulldata, 0, sizeof(nulldata));
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+       nl_vendor_reply = tb[NL80211_ATTR_VENDOR_DATA];
 
-       if (qos) {
-               nulldata.hdr.frame_control =
-                       IEEE80211_FC(WLAN_FC_TYPE_DATA,
-                                    WLAN_FC_STYPE_QOS_NULL);
-               size = sizeof(nulldata);
-       } else {
-               nulldata.hdr.frame_control =
-                       IEEE80211_FC(WLAN_FC_TYPE_DATA,
-                                    WLAN_FC_STYPE_NULLFUNC);
-               size = sizeof(struct ieee80211_hdr);
-       }
+       if (!nl_vendor_reply)
+               return NL_SKIP;
 
-       nulldata.hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS);
-       os_memcpy(nulldata.hdr.IEEE80211_DA_FROMDS, addr, ETH_ALEN);
-       os_memcpy(nulldata.hdr.IEEE80211_BSSID_FROMDS, own_addr, ETH_ALEN);
-       os_memcpy(nulldata.hdr.IEEE80211_SA_FROMDS, own_addr, ETH_ALEN);
+       if ((size_t) nla_len(nl_vendor_reply) > wpabuf_tailroom(buf)) {
+               wpa_printf(MSG_INFO, "nl80211: Vendor command: insufficient buffer space for reply");
+               return NL_SKIP;
+       }
 
-       if (wpa_driver_nl80211_send_mlme(bss, (u8 *) &nulldata, size, 0, 0, 0,
-                                        0, 0) < 0)
-               wpa_printf(MSG_DEBUG, "nl80211_send_null_frame: Failed to "
-                          "send poll frame");
+       nla_for_each_nested(nl, nl_vendor_reply, rem) {
+               wpabuf_put_data(buf, nla_data(nl), nla_len(nl));
+       }
+
+       return NL_SKIP;
 }
 
-static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr,
-                               int qos)
+
+static int nl80211_vendor_cmd(void *priv, unsigned int vendor_id,
+                             unsigned int subcmd, const u8 *data,
+                             size_t data_len, struct wpabuf *buf)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
+       int ret;
 
-       if (!drv->poll_command_supported) {
-               nl80211_send_null_frame(bss, own_addr, addr, qos);
-               return;
-       }
+#ifdef CONFIG_TESTING_OPTIONS
+       if (vendor_id == 0xffffffff) {
+               msg = nlmsg_alloc();
+               if (!msg)
+                       return -ENOMEM;
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return;
+               nl80211_cmd(drv, msg, 0, subcmd);
+               if (nlmsg_append(msg, (void *) data, data_len, NLMSG_ALIGNTO) <
+                   0)
+                       goto fail;
+               ret = send_and_recv_msgs(drv, msg, cmd_reply_handler, buf);
+               if (ret)
+                       wpa_printf(MSG_DEBUG, "nl80211: command failed err=%d",
+                                  ret);
+               return ret;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_PROBE_CLIENT);
+       if (!(msg = nl80211_cmd_msg(bss, 0, NL80211_CMD_VENDOR)) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, vendor_id) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD, subcmd) ||
+           (data &&
+            nla_put(msg, NL80211_ATTR_VENDOR_DATA, data_len, data)))
+               goto fail;
 
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
-       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
+       ret = send_and_recv_msgs(drv, msg, vendor_reply_handler, buf);
+       if (ret)
+               wpa_printf(MSG_DEBUG, "nl80211: vendor command failed err=%d",
+                          ret);
+       return ret;
 
-       send_and_recv_msgs(drv, msg, NULL, NULL);
-       return;
- nla_put_failure:
+fail:
        nlmsg_free(msg);
+       return -ENOBUFS;
 }
 
 
-static int nl80211_set_power_save(struct i802_bss *bss, int enabled)
+static int nl80211_set_qos_map(void *priv, const u8 *qos_map_set,
+                              u8 qos_map_set_len)
 {
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
+       int ret;
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
+       wpa_hexdump(MSG_DEBUG, "nl80211: Setting QoS Map",
+                   qos_map_set, qos_map_set_len);
 
-       nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_SET_POWER_SAVE);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, bss->ifindex);
-       NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE,
-                   enabled ? NL80211_PS_ENABLED : NL80211_PS_DISABLED);
-       return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
-nla_put_failure:
-       nlmsg_free(msg);
-       return -ENOBUFS;
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_QOS_MAP)) ||
+           nla_put(msg, NL80211_ATTR_QOS_MAP, qos_map_set_len, qos_map_set)) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
+       }
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret)
+               wpa_printf(MSG_DEBUG, "nl80211: Setting QoS Map failed");
+
+       return ret;
 }
 
 
-static int nl80211_set_p2p_powersave(void *priv, int legacy_ps, int opp_ps,
-                                    int ctwindow)
+static int nl80211_set_wowlan(void *priv,
+                             const struct wowlan_triggers *triggers)
 {
        struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       struct nlattr *wowlan_triggers;
+       int ret;
 
-       wpa_printf(MSG_DEBUG, "nl80211: set_p2p_powersave (legacy_ps=%d "
-                  "opp_ps=%d ctwindow=%d)", legacy_ps, opp_ps, ctwindow);
+       wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan");
+
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_SET_WOWLAN)) ||
+           !(wowlan_triggers = nla_nest_start(msg,
+                                              NL80211_ATTR_WOWLAN_TRIGGERS)) ||
+           (triggers->any &&
+            nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
+           (triggers->disconnect &&
+            nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
+           (triggers->magic_pkt &&
+            nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
+           (triggers->gtk_rekey_failure &&
+            nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
+           (triggers->eap_identity_req &&
+            nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
+           (triggers->four_way_handshake &&
+            nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
+           (triggers->rfkill_release &&
+            nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE))) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
+       }
 
-       if (opp_ps != -1 || ctwindow != -1)
-               return -1; /* Not yet supported */
+       nla_nest_end(msg, wowlan_triggers);
 
-       if (legacy_ps == -1)
-               return 0;
-       if (legacy_ps != 0 && legacy_ps != 1)
-               return -1; /* Not yet supported */
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret)
+               wpa_printf(MSG_DEBUG, "nl80211: Setting wowlan failed");
 
-       return nl80211_set_power_save(bss, legacy_ps);
+       return ret;
 }
 
 
-static int nl80211_start_radar_detection(void *priv, int freq)
+static int nl80211_roaming(void *priv, int allowed, const u8 *bssid)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
-       int ret;
+       struct nlattr *params;
 
-       wpa_printf(MSG_DEBUG, "nl80211: Start radar detection (CAC)");
-       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_RADAR)) {
-               wpa_printf(MSG_DEBUG, "nl80211: Driver does not support radar "
-                          "detection");
+       wpa_printf(MSG_DEBUG, "nl80211: Roaming policy: allowed=%d", allowed);
+
+       if (!drv->roaming_vendor_cmd_avail) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Ignore roaming policy change since driver does not provide command for setting it");
                return -1;
        }
 
-       msg = nlmsg_alloc();
-       if (!msg)
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+                       QCA_NL80211_VENDOR_SUBCMD_ROAMING) ||
+           !(params = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+           nla_put_u32(msg, QCA_WLAN_VENDOR_ATTR_ROAMING_POLICY,
+                       allowed ? QCA_ROAMING_ALLOWED_WITHIN_ESS :
+                       QCA_ROAMING_NOT_ALLOWED) ||
+           (bssid &&
+            nla_put(msg, QCA_WLAN_VENDOR_ATTR_MAC_ADDR, ETH_ALEN, bssid))) {
+               nlmsg_free(msg);
                return -1;
+       }
+       nla_nest_end(msg, params);
 
-       nl80211_cmd(bss->drv, msg, 0, NL80211_CMD_RADAR_DETECT);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
-
-       /* only HT20 is supported at this point */
-       NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, NL80211_CHAN_HT20);
-
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       if (ret == 0)
-               return 0;
-       wpa_printf(MSG_DEBUG, "nl80211: Failed to start radar detection: "
-                  "%d (%s)", ret, strerror(-ret));
-nla_put_failure:
-       return -1;
+       return send_and_recv_msgs(drv, msg, NULL, NULL);
 }
 
-#ifdef CONFIG_TDLS
 
-static int nl80211_send_tdls_mgmt(void *priv, const u8 *dst, u8 action_code,
-                                 u8 dialog_token, u16 status_code,
-                                 const u8 *buf, size_t len)
+static int nl80211_set_mac_addr(void *priv, const u8 *addr)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct nl_msg *msg;
+       int new_addr = addr != NULL;
 
-       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
-               return -EOPNOTSUPP;
+       if (!addr)
+               addr = drv->perm_addr;
 
-       if (!dst)
-               return -EINVAL;
+       if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 0) < 0)
+               return -1;
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
+       if (linux_set_ifhwaddr(drv->global->ioctl_sock, bss->ifname, addr) < 0)
+       {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: failed to set_mac_addr for %s to " MACSTR,
+                          bss->ifname, MAC2STR(addr));
+               if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname,
+                                         1) < 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "nl80211: Could not restore interface UP after failed set_mac_addr");
+               }
+               return -1;
+       }
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_MGMT);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
-       NLA_PUT_U8(msg, NL80211_ATTR_TDLS_ACTION, action_code);
-       NLA_PUT_U8(msg, NL80211_ATTR_TDLS_DIALOG_TOKEN, dialog_token);
-       NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status_code);
-       NLA_PUT(msg, NL80211_ATTR_IE, len, buf);
+       wpa_printf(MSG_DEBUG, "nl80211: set_mac_addr for %s to " MACSTR,
+                  bss->ifname, MAC2STR(addr));
+       drv->addr_changed = new_addr;
+       os_memcpy(bss->addr, addr, ETH_ALEN);
 
-       return send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (linux_set_iface_flags(drv->global->ioctl_sock, bss->ifname, 1) < 0)
+       {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Could not restore interface UP after set_mac_addr");
+       }
 
-nla_put_failure:
-       nlmsg_free(msg);
-       return -ENOBUFS;
+       return 0;
 }
 
 
-static int nl80211_tdls_oper(void *priv, enum tdls_oper oper, const u8 *peer)
+#ifdef CONFIG_MESH
+
+static int wpa_driver_nl80211_init_mesh(void *priv)
+{
+       if (wpa_driver_nl80211_set_mode(priv, NL80211_IFTYPE_MESH_POINT)) {
+               wpa_printf(MSG_INFO,
+                          "nl80211: Failed to set interface into mesh mode");
+               return -1;
+       }
+       return 0;
+}
+
+
+static int nl80211_put_mesh_id(struct nl_msg *msg, const u8 *mesh_id,
+                              size_t mesh_id_len)
+{
+       if (mesh_id) {
+               wpa_hexdump_ascii(MSG_DEBUG, "  * Mesh ID (SSID)",
+                                 mesh_id, mesh_id_len);
+               return nla_put(msg, NL80211_ATTR_MESH_ID, mesh_id_len, mesh_id);
+       }
+
+       return 0;
+}
+
+
+static int nl80211_join_mesh(struct i802_bss *bss,
+                            struct wpa_driver_mesh_join_params *params)
 {
-       struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
        struct nl_msg *msg;
-       enum nl80211_tdls_operation nl80211_oper;
+       struct nlattr *container;
+       int ret = -1;
 
-       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT))
-               return -EOPNOTSUPP;
+       wpa_printf(MSG_DEBUG, "nl80211: mesh join (ifindex=%d)", drv->ifindex);
+       msg = nl80211_drv_msg(drv, 0, NL80211_CMD_JOIN_MESH);
+       if (!msg ||
+           nl80211_put_freq_params(msg, &params->freq) ||
+           nl80211_put_basic_rates(msg, params->basic_rates) ||
+           nl80211_put_mesh_id(msg, params->meshid, params->meshid_len) ||
+           nl80211_put_beacon_int(msg, params->beacon_int))
+               goto fail;
 
-       switch (oper) {
-       case TDLS_DISCOVERY_REQ:
-               nl80211_oper = NL80211_TDLS_DISCOVERY_REQ;
-               break;
-       case TDLS_SETUP:
-               nl80211_oper = NL80211_TDLS_SETUP;
-               break;
-       case TDLS_TEARDOWN:
-               nl80211_oper = NL80211_TDLS_TEARDOWN;
-               break;
-       case TDLS_ENABLE_LINK:
-               nl80211_oper = NL80211_TDLS_ENABLE_LINK;
-               break;
-       case TDLS_DISABLE_LINK:
-               nl80211_oper = NL80211_TDLS_DISABLE_LINK;
-               break;
-       case TDLS_ENABLE:
-               return 0;
-       case TDLS_DISABLE:
-               return 0;
-       default:
-               return -EINVAL;
+       wpa_printf(MSG_DEBUG, "  * flags=%08X", params->flags);
+
+       container = nla_nest_start(msg, NL80211_ATTR_MESH_SETUP);
+       if (!container)
+               goto fail;
+
+       if (params->ies) {
+               wpa_hexdump(MSG_DEBUG, "  * IEs", params->ies, params->ie_len);
+               if (nla_put(msg, NL80211_MESH_SETUP_IE, params->ie_len,
+                           params->ies))
+                       goto fail;
+       }
+       /* WPA_DRIVER_MESH_FLAG_OPEN_AUTH is treated as default by nl80211 */
+       if (params->flags & WPA_DRIVER_MESH_FLAG_SAE_AUTH) {
+               if (nla_put_u8(msg, NL80211_MESH_SETUP_AUTH_PROTOCOL, 0x1) ||
+                   nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_AUTH))
+                       goto fail;
        }
+       if ((params->flags & WPA_DRIVER_MESH_FLAG_AMPE) &&
+           nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_AMPE))
+               goto fail;
+       if ((params->flags & WPA_DRIVER_MESH_FLAG_USER_MPM) &&
+           nla_put_flag(msg, NL80211_MESH_SETUP_USERSPACE_MPM))
+               goto fail;
+       nla_nest_end(msg, container);
 
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
+       container = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
+       if (!container)
+               goto fail;
 
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_TDLS_OPER);
-       NLA_PUT_U8(msg, NL80211_ATTR_TDLS_OPERATION, nl80211_oper);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-       NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer);
+       if (!(params->conf.flags & WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS) &&
+           nla_put_u32(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, 0))
+               goto fail;
+       if ((params->conf.flags & WPA_DRIVER_MESH_FLAG_DRIVER_MPM) &&
+           nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
+                       params->max_peer_links))
+               goto fail;
 
-       return send_and_recv_msgs(drv, msg, NULL, NULL);
+       /*
+        * Set NL80211_MESHCONF_PLINK_TIMEOUT even if user mpm is used because
+        * the timer could disconnect stations even in that case.
+        */
+       if (nla_put_u32(msg, NL80211_MESHCONF_PLINK_TIMEOUT,
+                       params->conf.peer_link_timeout)) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to set PLINK_TIMEOUT");
+               goto fail;
+       }
+
+       nla_nest_end(msg, container);
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: mesh join failed: ret=%d (%s)",
+                          ret, strerror(-ret));
+               goto fail;
+       }
+       ret = 0;
+       bss->freq = params->freq.freq;
+       wpa_printf(MSG_DEBUG, "nl80211: mesh join request send successfully");
 
-nla_put_failure:
+fail:
        nlmsg_free(msg);
-       return -ENOBUFS;
+       return ret;
 }
 
-#endif /* CONFIG TDLS */
-
 
-#ifdef ANDROID
+static int
+wpa_driver_nl80211_join_mesh(void *priv,
+                            struct wpa_driver_mesh_join_params *params)
+{
+       struct i802_bss *bss = priv;
+       int ret, timeout;
 
-typedef struct android_wifi_priv_cmd {
-       char *buf;
-       int used_len;
-       int total_len;
-} android_wifi_priv_cmd;
+       timeout = params->conf.peer_link_timeout;
 
-static int drv_errors = 0;
+       /* Disable kernel inactivity timer */
+       if (params->flags & WPA_DRIVER_MESH_FLAG_USER_MPM)
+               params->conf.peer_link_timeout = 0;
 
-static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv)
-{
-       drv_errors++;
-       if (drv_errors > DRV_NUMBER_SEQUENTIAL_ERRORS) {
-               drv_errors = 0;
-               wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
+       ret = nl80211_join_mesh(bss, params);
+       if (ret == -EINVAL && params->conf.peer_link_timeout == 0) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Mesh join retry for peer_link_timeout");
+               /*
+                * Old kernel does not support setting
+                * NL80211_MESHCONF_PLINK_TIMEOUT to zero, so set 60 seconds
+                * into future from peer_link_timeout.
+                */
+               params->conf.peer_link_timeout = timeout + 60;
+               ret = nl80211_join_mesh(priv, params);
        }
+
+       params->conf.peer_link_timeout = timeout;
+       return ret;
 }
 
 
-static int android_priv_cmd(struct i802_bss *bss, const char *cmd)
+static int wpa_driver_nl80211_leave_mesh(void *priv)
 {
+       struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct ifreq ifr;
-       android_wifi_priv_cmd priv_cmd;
-       char buf[MAX_DRV_CMD_SIZE];
+       struct nl_msg *msg;
        int ret;
 
-       os_memset(&ifr, 0, sizeof(ifr));
-       os_memset(&priv_cmd, 0, sizeof(priv_cmd));
-       os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
-
-       os_memset(buf, 0, sizeof(buf));
-       os_strlcpy(buf, cmd, sizeof(buf));
-
-       priv_cmd.buf = buf;
-       priv_cmd.used_len = sizeof(buf);
-       priv_cmd.total_len = sizeof(buf);
-       ifr.ifr_data = &priv_cmd;
-
-       ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr);
-       if (ret < 0) {
-               wpa_printf(MSG_ERROR, "%s: failed to issue private commands",
-                          __func__);
-               wpa_driver_send_hang_msg(drv);
-               return ret;
+       wpa_printf(MSG_DEBUG, "nl80211: mesh leave (ifindex=%d)", drv->ifindex);
+       msg = nl80211_drv_msg(drv, 0, NL80211_CMD_LEAVE_MESH);
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: mesh leave failed: ret=%d (%s)",
+                          ret, strerror(-ret));
+       } else {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: mesh leave request send successfully");
        }
 
-       drv_errors = 0;
-       return 0;
+       if (wpa_driver_nl80211_set_mode(drv->first_bss,
+                                       NL80211_IFTYPE_STATION)) {
+               wpa_printf(MSG_INFO,
+                          "nl80211: Failed to set interface into station mode");
+       }
+       return ret;
 }
 
+#endif /* CONFIG_MESH */
 
-static int android_pno_start(struct i802_bss *bss,
-                            struct wpa_driver_scan_params *params)
+
+static int wpa_driver_br_add_ip_neigh(void *priv, u8 version,
+                                     const u8 *ipaddr, int prefixlen,
+                                     const u8 *addr)
 {
+#ifdef CONFIG_LIBNL3_ROUTE
+       struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct ifreq ifr;
-       android_wifi_priv_cmd priv_cmd;
-       int ret = 0, i = 0, bp;
-       char buf[WEXT_PNO_MAX_COMMAND_SIZE];
-
-       bp = WEXT_PNOSETUP_HEADER_SIZE;
-       os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp);
-       buf[bp++] = WEXT_PNO_TLV_PREFIX;
-       buf[bp++] = WEXT_PNO_TLV_VERSION;
-       buf[bp++] = WEXT_PNO_TLV_SUBVERSION;
-       buf[bp++] = WEXT_PNO_TLV_RESERVED;
-
-       while (i < WEXT_PNO_AMOUNT && (size_t) i < params->num_ssids) {
-               /* Check that there is enough space needed for 1 more SSID, the
-                * other sections and null termination */
-               if ((bp + WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN +
-                    WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int) sizeof(buf))
-                       break;
-               wpa_hexdump_ascii(MSG_DEBUG, "For PNO Scan",
-                                 params->ssids[i].ssid,
-                                 params->ssids[i].ssid_len);
-               buf[bp++] = WEXT_PNO_SSID_SECTION;
-               buf[bp++] = params->ssids[i].ssid_len;
-               os_memcpy(&buf[bp], params->ssids[i].ssid,
-                         params->ssids[i].ssid_len);
-               bp += params->ssids[i].ssid_len;
-               i++;
-       }
-
-       buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION;
-       os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x",
-                   WEXT_PNO_SCAN_INTERVAL);
-       bp += WEXT_PNO_SCAN_INTERVAL_LENGTH;
-
-       buf[bp++] = WEXT_PNO_REPEAT_SECTION;
-       os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x",
-                   WEXT_PNO_REPEAT);
-       bp += WEXT_PNO_REPEAT_LENGTH;
-
-       buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION;
-       os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x",
-                   WEXT_PNO_MAX_REPEAT);
-       bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1;
+       struct rtnl_neigh *rn;
+       struct nl_addr *nl_ipaddr = NULL;
+       struct nl_addr *nl_lladdr = NULL;
+       int family, addrsize;
+       int res;
 
-       memset(&ifr, 0, sizeof(ifr));
-       memset(&priv_cmd, 0, sizeof(priv_cmd));
-       os_strncpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
+       if (!ipaddr || prefixlen == 0 || !addr)
+               return -EINVAL;
 
-       priv_cmd.buf = buf;
-       priv_cmd.used_len = bp;
-       priv_cmd.total_len = bp;
-       ifr.ifr_data = &priv_cmd;
+       if (bss->br_ifindex == 0) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: bridge must be set before adding an ip neigh to it");
+               return -1;
+       }
 
-       ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr);
+       if (!drv->rtnl_sk) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: nl_sock for NETLINK_ROUTE is not initialized");
+               return -1;
+       }
 
-       if (ret < 0) {
-               wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d",
-                          ret);
-               wpa_driver_send_hang_msg(drv);
-               return ret;
+       if (version == 4) {
+               family = AF_INET;
+               addrsize = 4;
+       } else if (version == 6) {
+               family = AF_INET6;
+               addrsize = 16;
+       } else {
+               return -EINVAL;
        }
 
-       drv_errors = 0;
+       rn = rtnl_neigh_alloc();
+       if (rn == NULL)
+               return -ENOMEM;
 
-       return android_priv_cmd(bss, "PNOFORCE 1");
-}
+       /* set the destination ip address for neigh */
+       nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
+       if (nl_ipaddr == NULL) {
+               wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
+               res = -ENOMEM;
+               goto errout;
+       }
+       nl_addr_set_prefixlen(nl_ipaddr, prefixlen);
+       res = rtnl_neigh_set_dst(rn, nl_ipaddr);
+       if (res) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: neigh set destination addr failed");
+               goto errout;
+       }
 
+       /* set the corresponding lladdr for neigh */
+       nl_lladdr = nl_addr_build(AF_BRIDGE, (u8 *) addr, ETH_ALEN);
+       if (nl_lladdr == NULL) {
+               wpa_printf(MSG_DEBUG, "nl80211: neigh set lladdr failed");
+               res = -ENOMEM;
+               goto errout;
+       }
+       rtnl_neigh_set_lladdr(rn, nl_lladdr);
 
-static int android_pno_stop(struct i802_bss *bss)
-{
-       return android_priv_cmd(bss, "PNOFORCE 0");
-}
+       rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
+       rtnl_neigh_set_state(rn, NUD_PERMANENT);
 
-#endif /* ANDROID */
+       res = rtnl_neigh_add(drv->rtnl_sk, rn, NLM_F_CREATE);
+       if (res) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Adding bridge ip neigh failed: %s",
+                          strerror(errno));
+       }
+errout:
+       if (nl_lladdr)
+               nl_addr_put(nl_lladdr);
+       if (nl_ipaddr)
+               nl_addr_put(nl_ipaddr);
+       if (rn)
+               rtnl_neigh_put(rn);
+       return res;
+#else /* CONFIG_LIBNL3_ROUTE */
+       return -1;
+#endif /* CONFIG_LIBNL3_ROUTE */
+}
 
 
-static int driver_nl80211_set_key(const char *ifname, void *priv,
-                                 enum wpa_alg alg, const u8 *addr,
-                                 int key_idx, int set_tx,
-                                 const u8 *seq, size_t seq_len,
-                                 const u8 *key, size_t key_len)
+static int wpa_driver_br_delete_ip_neigh(void *priv, u8 version,
+                                        const u8 *ipaddr)
 {
+#ifdef CONFIG_LIBNL3_ROUTE
        struct i802_bss *bss = priv;
-       return wpa_driver_nl80211_set_key(ifname, bss, alg, addr, key_idx,
-                                         set_tx, seq, seq_len, key, key_len);
-}
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct rtnl_neigh *rn;
+       struct nl_addr *nl_ipaddr;
+       int family, addrsize;
+       int res;
 
+       if (!ipaddr)
+               return -EINVAL;
 
-static int driver_nl80211_scan2(void *priv,
-                               struct wpa_driver_scan_params *params)
-{
-       struct i802_bss *bss = priv;
-       return wpa_driver_nl80211_scan(bss, params);
-}
+       if (version == 4) {
+               family = AF_INET;
+               addrsize = 4;
+       } else if (version == 6) {
+               family = AF_INET6;
+               addrsize = 16;
+       } else {
+               return -EINVAL;
+       }
 
+       if (bss->br_ifindex == 0) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: bridge must be set to delete an ip neigh");
+               return -1;
+       }
 
-static int driver_nl80211_deauthenticate(void *priv, const u8 *addr,
-                                        int reason_code)
-{
-       struct i802_bss *bss = priv;
-       return wpa_driver_nl80211_deauthenticate(bss, addr, reason_code);
-}
+       if (!drv->rtnl_sk) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: nl_sock for NETLINK_ROUTE is not initialized");
+               return -1;
+       }
 
+       rn = rtnl_neigh_alloc();
+       if (rn == NULL)
+               return -ENOMEM;
 
-static int driver_nl80211_authenticate(void *priv,
-                                      struct wpa_driver_auth_params *params)
-{
-       struct i802_bss *bss = priv;
-       return wpa_driver_nl80211_authenticate(bss, params);
-}
+       /* set the destination ip address for neigh */
+       nl_ipaddr = nl_addr_build(family, (void *) ipaddr, addrsize);
+       if (nl_ipaddr == NULL) {
+               wpa_printf(MSG_DEBUG, "nl80211: nl_ipaddr build failed");
+               res = -ENOMEM;
+               goto errout;
+       }
+       res = rtnl_neigh_set_dst(rn, nl_ipaddr);
+       if (res) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: neigh set destination addr failed");
+               goto errout;
+       }
 
+       rtnl_neigh_set_ifindex(rn, bss->br_ifindex);
 
-static void driver_nl80211_deinit(void *priv)
-{
-       struct i802_bss *bss = priv;
-       wpa_driver_nl80211_deinit(bss);
+       res = rtnl_neigh_delete(drv->rtnl_sk, rn, 0);
+       if (res) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Deleting bridge ip neigh failed: %s",
+                          strerror(errno));
+       }
+errout:
+       if (nl_ipaddr)
+               nl_addr_put(nl_ipaddr);
+       if (rn)
+               rtnl_neigh_put(rn);
+       return res;
+#else /* CONFIG_LIBNL3_ROUTE */
+       return -1;
+#endif /* CONFIG_LIBNL3_ROUTE */
 }
 
 
-static int driver_nl80211_if_remove(void *priv, enum wpa_driver_if_type type,
-                                   const char *ifname)
+static int linux_write_system_file(const char *path, unsigned int val)
 {
-       struct i802_bss *bss = priv;
-       return wpa_driver_nl80211_if_remove(bss, type, ifname);
-}
+       char buf[50];
+       int fd, len;
 
+       len = os_snprintf(buf, sizeof(buf), "%u\n", val);
+       if (os_snprintf_error(sizeof(buf), len))
+               return -1;
 
-static int driver_nl80211_send_mlme(void *priv, const u8 *data,
-                                   size_t data_len, int noack)
-{
-       struct i802_bss *bss = priv;
-       return wpa_driver_nl80211_send_mlme(bss, data, data_len, noack,
-                                           0, 0, 0, 0);
-}
+       fd = open(path, O_WRONLY);
+       if (fd < 0)
+               return -1;
 
+       if (write(fd, buf, len) < 0) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Failed to write Linux system file: %s with the value of %d",
+                          path, val);
+               close(fd);
+               return -1;
+       }
+       close(fd);
 
-static int driver_nl80211_sta_remove(void *priv, const u8 *addr)
-{
-       struct i802_bss *bss = priv;
-       return wpa_driver_nl80211_sta_remove(bss, addr);
+       return 0;
 }
 
 
-#if defined(HOSTAPD) || defined(CONFIG_AP)
-static int driver_nl80211_set_sta_vlan(void *priv, const u8 *addr,
-                                      const char *ifname, int vlan_id)
+static const char * drv_br_port_attr_str(enum drv_br_port_attr attr)
 {
-       struct i802_bss *bss = priv;
-       return i802_set_sta_vlan(bss, addr, ifname, vlan_id);
+       switch (attr) {
+       case DRV_BR_PORT_ATTR_PROXYARP:
+               return "proxyarp_wifi";
+       case DRV_BR_PORT_ATTR_HAIRPIN_MODE:
+               return "hairpin_mode";
+       }
+
+       return NULL;
 }
-#endif /* HOSTAPD || CONFIG_AP */
 
 
-static int driver_nl80211_read_sta_data(void *priv,
-                                       struct hostap_sta_driver_data *data,
-                                       const u8 *addr)
+static int wpa_driver_br_port_set_attr(void *priv, enum drv_br_port_attr attr,
+                                      unsigned int val)
 {
        struct i802_bss *bss = priv;
-       return i802_read_sta_data(bss, data, addr);
-}
+       char path[128];
+       const char *attr_txt;
 
+       attr_txt = drv_br_port_attr_str(attr);
+       if (attr_txt == NULL)
+               return -EINVAL;
 
-static int driver_nl80211_send_action(void *priv, unsigned int freq,
-                                     unsigned int wait_time,
-                                     const u8 *dst, const u8 *src,
-                                     const u8 *bssid,
-                                     const u8 *data, size_t data_len,
-                                     int no_cck)
-{
-       struct i802_bss *bss = priv;
-       return wpa_driver_nl80211_send_action(bss, freq, wait_time, dst, src,
-                                             bssid, data, data_len, no_cck);
-}
+       os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/%s",
+                   bss->ifname, attr_txt);
 
+       if (linux_write_system_file(path, val))
+               return -1;
 
-static int driver_nl80211_probe_req_report(void *priv, int report)
-{
-       struct i802_bss *bss = priv;
-       return wpa_driver_nl80211_probe_req_report(bss, report);
+       return 0;
 }
 
 
-static int wpa_driver_nl80211_update_ft_ies(void *priv, const u8 *md,
-                                           const u8 *ies, size_t ies_len)
+static const char * drv_br_net_param_str(enum drv_br_net_param param)
 {
-       int ret;
-       struct nl_msg *msg;
-       struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
-       u16 mdid = WPA_GET_LE16(md);
-
-       msg = nlmsg_alloc();
-       if (!msg)
-               return -ENOMEM;
-
-       wpa_printf(MSG_DEBUG, "nl80211: Updating FT IEs");
-       nl80211_cmd(drv, msg, 0, NL80211_CMD_UPDATE_FT_IES);
-       NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, drv->ifindex);
-       NLA_PUT(msg, NL80211_ATTR_IE, ies_len, ies);
-       NLA_PUT_U16(msg, NL80211_ATTR_MDID, mdid);
-
-       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
-       if (ret) {
-               wpa_printf(MSG_DEBUG, "nl80211: update_ft_ies failed "
-                          "err=%d (%s)", ret, strerror(-ret));
+       switch (param) {
+       case DRV_BR_NET_PARAM_GARP_ACCEPT:
+               return "arp_accept";
        }
 
-       return ret;
-
-nla_put_failure:
-       nlmsg_free(msg);
-       return -ENOBUFS;
+       return NULL;
 }
 
 
-const u8 * wpa_driver_nl80211_get_macaddr(void *priv)
+static int wpa_driver_br_set_net_param(void *priv, enum drv_br_net_param param,
+                                      unsigned int val)
 {
        struct i802_bss *bss = priv;
-       struct wpa_driver_nl80211_data *drv = bss->drv;
+       char path[128];
+       const char *param_txt;
+       int ip_version = 4;
 
-       if (drv->nlmode != NL80211_IFTYPE_P2P_DEVICE)
-               return NULL;
+       param_txt = drv_br_net_param_str(param);
+       if (param_txt == NULL)
+               return -EINVAL;
 
-       return bss->addr;
-}
+       switch (param) {
+               case DRV_BR_NET_PARAM_GARP_ACCEPT:
+                       ip_version = 4;
+                       break;
+               default:
+                       return -EINVAL;
+       }
 
-#if defined(TIZEN_EXT_P2P) && defined(BCM_DRIVER_V115)
+       os_snprintf(path, sizeof(path), "/proc/sys/net/ipv%d/conf/%s/%s",
+                   ip_version, bss->brname, param_txt);
 
-#define MAX_WPSP2PIE_CMD_SIZE          384
+       if (linux_write_system_file(path, val))
+               return -1;
 
-typedef struct tizen_wifi_priv_cmd {
-       char *buf;
-       int used_len;
-       int total_len;
-} bcm_driver_priv_cmd;
+       return 0;
+}
 
-static int drv_errors = 0;
 
-static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv)
+static int hw_mode_to_qca_acs(enum hostapd_hw_mode hw_mode)
 {
-       drv_errors++;
-       if (drv_errors > 4) {
-               drv_errors = 0;
-               wpa_msg(drv->ctx, MSG_INFO, "BCM Driver State : HANGED");
+       switch (hw_mode) {
+       case HOSTAPD_MODE_IEEE80211B:
+               return QCA_ACS_MODE_IEEE80211B;
+       case HOSTAPD_MODE_IEEE80211G:
+               return QCA_ACS_MODE_IEEE80211G;
+       case HOSTAPD_MODE_IEEE80211A:
+               return QCA_ACS_MODE_IEEE80211A;
+       case HOSTAPD_MODE_IEEE80211AD:
+               return QCA_ACS_MODE_IEEE80211AD;
+       default:
+               return -1;
        }
 }
 
-int wpa_driver_nl80211_priv_cmd_bcm(void *priv, char *cmd, char *buf,
-                                 size_t buf_len )
+
+static int wpa_driver_do_acs(void *priv, struct drv_acs_params *params)
 {
        struct i802_bss *bss = priv;
        struct wpa_driver_nl80211_data *drv = bss->drv;
-       struct ifreq ifr;
-       bcm_driver_priv_cmd priv_cmd_data;
-       int ret = 0;
-
-       if (cmd)
-               wpa_printf(MSG_DEBUG, "%s = %s", __func__, cmd);
-
-       os_memcpy(buf, cmd, strlen(cmd) + 1);
-
-       memset(&ifr, 0, sizeof(ifr));
-       memset(&priv_cmd_data, 0, sizeof(priv_cmd_data));
-       os_strncpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
-
-       priv_cmd_data.buf = buf;
-       priv_cmd_data.used_len = buf_len;
-       priv_cmd_data.total_len = buf_len;
-       ifr.ifr_data = &priv_cmd_data;
+       struct nl_msg *msg;
+       struct nlattr *data;
+       int ret;
+       int mode;
 
-       wpa_printf(MSG_DEBUG, "[KGB_DEBUG] %s: ioctl socket [%d], ifname [%s]", __func__, drv->global->ioctl_sock, bss->ifname);
-       errno = 0;
-       if ((ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr)) < 0) {
-               wpa_printf(MSG_ERROR, "%s: failed to issue private commands. Error [%s]", __func__, strerror(errno));
-               wpa_driver_send_hang_msg(drv);
-       } else {
-               drv_errors = 0;
-               ret = 0;
-               if ((os_strcasecmp(cmd, "LINKSPEED") == 0) ||
-                   (os_strcasecmp(cmd, "RSSI") == 0) ||
-                   (os_strcasecmp(cmd, "GETBAND") == 0) ||
-                   (os_strcasecmp(cmd, "P2P_GET_NOA") == 0))
-                       ret = strlen(buf);
+       mode = hw_mode_to_qca_acs(params->hw_mode);
+       if (mode < 0)
+               return -1;
 
-               wpa_printf(MSG_DEBUG, "%s %s len = %d, %d", __func__, buf, ret, strlen(buf));
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+                       QCA_NL80211_VENDOR_SUBCMD_DO_ACS) ||
+           !(data = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA)) ||
+           nla_put_u8(msg, QCA_WLAN_VENDOR_ATTR_ACS_HW_MODE, mode) ||
+           (params->ht_enabled &&
+            nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT_ENABLED)) ||
+           (params->ht40_enabled &&
+            nla_put_flag(msg, QCA_WLAN_VENDOR_ATTR_ACS_HT40_ENABLED))) {
+               nlmsg_free(msg);
+               return -ENOBUFS;
        }
-       return ret;
-}
-
-int wpa_driver_nl80211_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon,
-                                const struct wpabuf *proberesp,
-                                const struct wpabuf *assocresp)
-{
-       char buf[MAX_WPSP2PIE_CMD_SIZE+768];
-       struct wpabuf *ap_wps_p2p_ie = NULL;
-       char *_cmd = "SET_AP_WPS_P2P_IE";
-       char *pbuf;
-       int ret = 0;
-       int i;
-       struct cmd_desc {
-               int cmd;
-               const struct wpabuf *src;
-       } cmd_arr[] = {
-               {0x1, beacon},
-               {0x2, proberesp},
-               {0x4, assocresp},
-               {-1, NULL}
-       };
+       nla_nest_end(msg, data);
 
-       wpa_printf(MSG_DEBUG, "%s: Entry", __func__);
-       for (i = 0; cmd_arr[i].cmd != -1; i++) {
-               os_memset(buf, 0, sizeof(buf));
-               pbuf = buf;
-               pbuf += sprintf(pbuf, "%s %d", _cmd, cmd_arr[i].cmd);
-               *pbuf++ = '\0';
-               ap_wps_p2p_ie = cmd_arr[i].src ?
-                       wpabuf_dup(cmd_arr[i].src) : NULL;
-               if (ap_wps_p2p_ie) {
-                       os_memcpy(pbuf, wpabuf_head(ap_wps_p2p_ie), wpabuf_len(ap_wps_p2p_ie));
-                       ret = wpa_driver_nl80211_priv_cmd_bcm(priv, buf, buf,
-                               strlen(_cmd) + 3 + wpabuf_len(ap_wps_p2p_ie));
-                       wpabuf_free(ap_wps_p2p_ie);
-                       if (ret < 0)
-                               break;
-               }
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Failed to invoke driver ACS function: %s",
+                          strerror(errno));
        }
-
        return ret;
 }
-#endif /* TIZEN_EXT_P2P && BCM_DRIVER_V115*/
 
 
 const struct wpa_driver_ops wpa_driver_nl80211_ops = {
@@ -10733,22 +8711,20 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .set_operstate = wpa_driver_nl80211_set_operstate,
        .set_supp_port = wpa_driver_nl80211_set_supp_port,
        .set_country = wpa_driver_nl80211_set_country,
+       .get_country = wpa_driver_nl80211_get_country,
        .set_ap = wpa_driver_nl80211_set_ap,
        .set_acl = wpa_driver_nl80211_set_acl,
        .if_add = wpa_driver_nl80211_if_add,
        .if_remove = driver_nl80211_if_remove,
        .send_mlme = driver_nl80211_send_mlme,
-       .get_hw_feature_data = wpa_driver_nl80211_get_hw_feature_data,
+       .get_hw_feature_data = nl80211_get_hw_feature_data,
        .sta_add = wpa_driver_nl80211_sta_add,
        .sta_remove = driver_nl80211_sta_remove,
        .hapd_send_eapol = wpa_driver_nl80211_hapd_send_eapol,
        .sta_set_flags = wpa_driver_nl80211_sta_set_flags,
-#ifdef HOSTAPD
        .hapd_init = i802_init,
        .hapd_deinit = i802_deinit,
        .set_wds_sta = i802_set_wds_sta,
-#endif /* HOSTAPD */
-#if defined(HOSTAPD) || defined(CONFIG_AP)
        .get_seqnum = i802_get_seqnum,
        .flush = i802_flush,
        .get_inact_sec = i802_get_inact_sec,
@@ -10759,7 +8735,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .set_sta_vlan = driver_nl80211_set_sta_vlan,
        .sta_deauth = i802_sta_deauth,
        .sta_disassoc = i802_sta_disassoc,
-#endif /* HOSTAPD || CONFIG_AP */
        .read_sta_data = driver_nl80211_read_sta_data,
        .set_freq = i802_set_freq,
        .send_action = driver_nl80211_send_action,
@@ -10771,7 +8746,6 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
        .deinit_ap = wpa_driver_nl80211_deinit_ap,
        .deinit_p2p_cli = wpa_driver_nl80211_deinit_p2p_cli,
        .resume = wpa_driver_nl80211_resume,
-       .send_ft_action = nl80211_send_ft_action,
        .signal_monitor = nl80211_signal_monitor,
        .signal_poll = nl80211_signal_poll,
        .send_frame = nl80211_send_frame,
@@ -10789,11 +8763,41 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
 #ifdef CONFIG_TDLS
        .send_tdls_mgmt = nl80211_send_tdls_mgmt,
        .tdls_oper = nl80211_tdls_oper,
+       .tdls_enable_channel_switch = nl80211_tdls_enable_channel_switch,
+       .tdls_disable_channel_switch = nl80211_tdls_disable_channel_switch,
 #endif /* CONFIG_TDLS */
        .update_ft_ies = wpa_driver_nl80211_update_ft_ies,
        .get_mac_addr = wpa_driver_nl80211_get_macaddr,
-#if defined(TIZEN_EXT_P2P) && defined(BCM_DRIVER_V115)
+#ifdef BCM_DRIVER_V115
        .priv_cmd = wpa_driver_nl80211_priv_cmd_bcm,
        .set_ap_wps_ie = wpa_driver_nl80211_set_ap_wps_p2p_ie,
-#endif /* TIZEN_EXT_P2P && BCM_DRIVER_V115 */
+#endif /* BCM_DRIVER_V115 */
+       .get_survey = wpa_driver_nl80211_get_survey,
+       .status = wpa_driver_nl80211_status,
+       .switch_channel = nl80211_switch_channel,
+#ifdef ANDROID_P2P
+       .set_noa = wpa_driver_set_p2p_noa,
+       .get_noa = wpa_driver_get_p2p_noa,
+       .set_ap_wps_ie = wpa_driver_set_ap_wps_p2p_ie,
+#endif /* ANDROID_P2P */
+#ifdef ANDROID
+       .driver_cmd = wpa_driver_nl80211_driver_cmd,
+#endif /* ANDROID */
+       .vendor_cmd = nl80211_vendor_cmd,
+       .set_qos_map = nl80211_set_qos_map,
+       .set_wowlan = nl80211_set_wowlan,
+       .roaming = nl80211_roaming,
+       .set_mac_addr = nl80211_set_mac_addr,
+#ifdef CONFIG_MESH
+       .init_mesh = wpa_driver_nl80211_init_mesh,
+       .join_mesh = wpa_driver_nl80211_join_mesh,
+       .leave_mesh = wpa_driver_nl80211_leave_mesh,
+#endif /* CONFIG_MESH */
+       .br_add_ip_neigh = wpa_driver_br_add_ip_neigh,
+       .br_delete_ip_neigh = wpa_driver_br_delete_ip_neigh,
+       .br_port_set_attr = wpa_driver_br_port_set_attr,
+       .br_set_net_param = wpa_driver_br_set_net_param,
+       .add_tx_ts = nl80211_add_ts,
+       .del_tx_ts = nl80211_del_ts,
+       .do_acs = wpa_driver_do_acs,
 };
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
new file mode 100755 (executable)
index 0000000..a680c55
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - definitions
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef DRIVER_NL80211_H
+#define DRIVER_NL80211_H
+
+#include "nl80211_copy.h"
+#include "utils/list.h"
+#include "driver.h"
+
+#ifdef CONFIG_LIBNL20
+/* libnl 2.0 compatibility code */
+#define nl_handle nl_sock
+#define nl80211_handle_alloc nl_socket_alloc_cb
+#define nl80211_handle_destroy nl_socket_free
+#endif /* CONFIG_LIBNL20 */
+
+struct nl80211_global {
+       struct dl_list interfaces;
+       int if_add_ifindex;
+       u64 if_add_wdevid;
+       int if_add_wdevid_set;
+       struct netlink_data *netlink;
+       struct nl_cb *nl_cb;
+       struct nl_handle *nl;
+       int nl80211_id;
+       int ioctl_sock; /* socket for ioctl() use */
+
+       struct nl_handle *nl_event;
+};
+
+struct nl80211_wiphy_data {
+       struct dl_list list;
+       struct dl_list bsss;
+       struct dl_list drvs;
+
+       struct nl_handle *nl_beacons;
+       struct nl_cb *nl_cb;
+
+       int wiphy_idx;
+};
+
+struct i802_bss {
+       struct wpa_driver_nl80211_data *drv;
+       struct i802_bss *next;
+       int ifindex;
+       int br_ifindex;
+       u64 wdev_id;
+       char ifname[IFNAMSIZ + 1];
+       char brname[IFNAMSIZ];
+       unsigned int beacon_set:1;
+       unsigned int added_if_into_bridge:1;
+       unsigned int added_bridge:1;
+       unsigned int in_deinit:1;
+       unsigned int wdev_id_set:1;
+       unsigned int added_if:1;
+       unsigned int static_ap:1;
+
+       u8 addr[ETH_ALEN];
+#ifdef BCM_DRIVER_V115
+       u8 dev_addr[ETH_ALEN];
+#endif /* BCM_DRIVER_V115 */
+
+       int freq;
+       int bandwidth;
+       int if_dynamic;
+
+       void *ctx;
+       struct nl_handle *nl_preq, *nl_mgmt;
+       struct nl_cb *nl_cb;
+
+       struct nl80211_wiphy_data *wiphy_data;
+       struct dl_list wiphy_list;
+};
+
+struct wpa_driver_nl80211_data {
+       struct nl80211_global *global;
+       struct dl_list list;
+       struct dl_list wiphy_list;
+       char phyname[32];
+       u8 perm_addr[ETH_ALEN];
+       void *ctx;
+       int ifindex;
+       int if_removed;
+       int if_disabled;
+       int ignore_if_down_event;
+       struct rfkill_data *rfkill;
+       struct wpa_driver_capa capa;
+       u8 *extended_capa, *extended_capa_mask;
+       unsigned int extended_capa_len;
+       int has_capability;
+
+       int operstate;
+
+       int scan_complete_events;
+       enum scan_states {
+               NO_SCAN, SCAN_REQUESTED, SCAN_STARTED, SCAN_COMPLETED,
+               SCAN_ABORTED, SCHED_SCAN_STARTED, SCHED_SCAN_STOPPED,
+               SCHED_SCAN_RESULTS
+       } scan_state;
+
+       u8 auth_bssid[ETH_ALEN];
+       u8 auth_attempt_bssid[ETH_ALEN];
+       u8 bssid[ETH_ALEN];
+       u8 prev_bssid[ETH_ALEN];
+       int associated;
+       u8 ssid[32];
+       size_t ssid_len;
+       enum nl80211_iftype nlmode;
+       enum nl80211_iftype ap_scan_as_station;
+       unsigned int assoc_freq;
+
+       int monitor_sock;
+       int monitor_ifidx;
+       int monitor_refcount;
+
+       unsigned int disabled_11b_rates:1;
+       unsigned int pending_remain_on_chan:1;
+       unsigned int in_interface_list:1;
+       unsigned int device_ap_sme:1;
+       unsigned int poll_command_supported:1;
+       unsigned int data_tx_status:1;
+       unsigned int scan_for_auth:1;
+       unsigned int retry_auth:1;
+       unsigned int use_monitor:1;
+       unsigned int ignore_next_local_disconnect:1;
+       unsigned int ignore_next_local_deauth:1;
+       unsigned int hostapd:1;
+       unsigned int start_mode_ap:1;
+       unsigned int start_iface_up:1;
+       unsigned int test_use_roc_tx:1;
+       unsigned int ignore_deauth_event:1;
+       unsigned int vendor_cmd_test_avail:1;
+       unsigned int roaming_vendor_cmd_avail:1;
+       unsigned int dfs_vendor_cmd_avail:1;
+       unsigned int have_low_prio_scan:1;
+       unsigned int force_connect_cmd:1;
+       unsigned int addr_changed:1;
+       unsigned int get_features_vendor_cmd_avail:1;
+       unsigned int set_rekey_offload:1;
+       unsigned int p2p_go_ctwindow_supported:1;
+
+       u64 remain_on_chan_cookie;
+       u64 send_action_cookie;
+
+       unsigned int last_mgmt_freq;
+
+       struct wpa_driver_scan_filter *filter_ssids;
+       size_t num_filter_ssids;
+
+       struct i802_bss *first_bss;
+
+       int eapol_tx_sock;
+
+       int eapol_sock; /* socket for EAPOL frames */
+
+       struct nl_handle *rtnl_sk; /* nl_sock for NETLINK_ROUTE */
+
+       int default_if_indices[16];
+       int *if_indices;
+       int num_if_indices;
+
+       /* From failed authentication command */
+       int auth_freq;
+       u8 auth_bssid_[ETH_ALEN];
+       u8 auth_ssid[32];
+       size_t auth_ssid_len;
+       int auth_alg;
+       u8 *auth_ie;
+       size_t auth_ie_len;
+       u8 auth_wep_key[4][16];
+       size_t auth_wep_key_len[4];
+       int auth_wep_tx_keyidx;
+       int auth_local_state_change;
+       int auth_p2p;
+};
+
+struct nl_msg;
+
+void * nl80211_cmd(struct wpa_driver_nl80211_data *drv,
+                  struct nl_msg *msg, int flags, uint8_t cmd);
+struct nl_msg * nl80211_cmd_msg(struct i802_bss *bss, int flags, uint8_t cmd);
+struct nl_msg * nl80211_drv_msg(struct wpa_driver_nl80211_data *drv, int flags,
+                               uint8_t cmd);
+struct nl_msg * nl80211_bss_msg(struct i802_bss *bss, int flags, uint8_t cmd);
+int send_and_recv_msgs(struct wpa_driver_nl80211_data *drv, struct nl_msg *msg,
+                      int (*valid_handler)(struct nl_msg *, void *),
+                      void *valid_data);
+int nl80211_create_iface(struct wpa_driver_nl80211_data *drv,
+                        const char *ifname, enum nl80211_iftype iftype,
+                        const u8 *addr, int wds,
+                        int (*handler)(struct nl_msg *, void *),
+                        void *arg, int use_existing);
+void nl80211_remove_iface(struct wpa_driver_nl80211_data *drv, int ifidx);
+unsigned int nl80211_get_assoc_freq(struct wpa_driver_nl80211_data *drv);
+enum chan_width convert2width(int width);
+void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv);
+struct i802_bss * get_bss_ifindex(struct wpa_driver_nl80211_data *drv,
+                                 int ifindex);
+int is_ap_interface(enum nl80211_iftype nlmode);
+int is_sta_interface(enum nl80211_iftype nlmode);
+int wpa_driver_nl80211_authenticate_retry(struct wpa_driver_nl80211_data *drv);
+int nl80211_get_link_signal(struct wpa_driver_nl80211_data *drv,
+                           struct wpa_signal_info *sig);
+int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
+                          struct wpa_signal_info *sig_change);
+int nl80211_get_wiphy_index(struct i802_bss *bss);
+int wpa_driver_nl80211_set_mode(struct i802_bss *bss,
+                               enum nl80211_iftype nlmode);
+int wpa_driver_nl80211_mlme(struct wpa_driver_nl80211_data *drv,
+                           const u8 *addr, int cmd, u16 reason_code,
+                           int local_state_change);
+
+int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv);
+void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv);
+int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv,
+                        const void *data, size_t len,
+                        int encrypt, int noack);
+
+int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv);
+struct hostapd_hw_modes *
+nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags);
+
+int process_global_event(struct nl_msg *msg, void *arg);
+int process_bss_event(struct nl_msg *msg, void *arg);
+
+#ifdef ANDROID
+int android_nl_socket_set_nonblocking(struct nl_handle *handle);
+int android_genl_ctrl_resolve(struct nl_handle *handle, const char *name);
+int android_pno_start(struct i802_bss *bss,
+                     struct wpa_driver_scan_params *params);
+int android_pno_stop(struct i802_bss *bss);
+extern int wpa_driver_nl80211_driver_cmd(void *priv, char *cmd, char *buf,
+                                        size_t buf_len);
+
+#ifdef ANDROID_P2P
+int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration);
+int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len);
+int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow);
+int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon,
+                                const struct wpabuf *proberesp,
+                                const struct wpabuf *assocresp);
+#endif /* ANDROID_P2P */
+#endif /* ANDROID */
+
+
+/* driver_nl80211_scan.c */
+
+struct nl80211_bss_info_arg {
+       struct wpa_driver_nl80211_data *drv;
+       struct wpa_scan_results *res;
+       unsigned int assoc_freq;
+       unsigned int ibss_freq;
+       u8 assoc_bssid[ETH_ALEN];
+};
+
+int bss_info_handler(struct nl_msg *msg, void *arg);
+void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx);
+int wpa_driver_nl80211_scan(struct i802_bss *bss,
+                           struct wpa_driver_scan_params *params);
+int wpa_driver_nl80211_sched_scan(void *priv,
+                                 struct wpa_driver_scan_params *params,
+                                 u32 interval);
+int wpa_driver_nl80211_stop_sched_scan(void *priv);
+struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv);
+void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv);
+
+#if defined(TIZEN_WLAN_BOARD_SPRD)
+#define MAX_DRV_CMD_SIZE               248
+int hostapd_send_conf_to_driver(struct hostapd_data *hapd);
+#endif
+
+#endif /* DRIVER_NL80211_H */
diff --git a/src/drivers/driver_nl80211_android.c b/src/drivers/driver_nl80211_android.c
new file mode 100755 (executable)
index 0000000..3cc9a65
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - Android specific
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <netlink/genl/genl.h>
+#include <netlink/genl/family.h>
+#include <netlink/genl/ctrl.h>
+#include <fcntl.h>
+
+#include "utils/common.h"
+#include "driver_nl80211.h"
+#include "android_drv.h"
+
+
+typedef struct android_wifi_priv_cmd {
+       char *buf;
+       int used_len;
+       int total_len;
+} android_wifi_priv_cmd;
+
+static int drv_errors = 0;
+
+static void wpa_driver_send_hang_msg(struct wpa_driver_nl80211_data *drv)
+{
+       drv_errors++;
+       if (drv_errors > DRV_NUMBER_SEQUENTIAL_ERRORS) {
+               drv_errors = 0;
+               wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
+       }
+}
+
+
+static int android_priv_cmd(struct i802_bss *bss, const char *cmd)
+{
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct ifreq ifr;
+       android_wifi_priv_cmd priv_cmd;
+       char buf[MAX_DRV_CMD_SIZE];
+       int ret;
+
+       os_memset(&ifr, 0, sizeof(ifr));
+       os_memset(&priv_cmd, 0, sizeof(priv_cmd));
+       os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
+
+       os_memset(buf, 0, sizeof(buf));
+       os_strlcpy(buf, cmd, sizeof(buf));
+
+       priv_cmd.buf = buf;
+       priv_cmd.used_len = sizeof(buf);
+       priv_cmd.total_len = sizeof(buf);
+       ifr.ifr_data = &priv_cmd;
+
+       ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "%s: failed to issue private commands",
+                          __func__);
+               wpa_driver_send_hang_msg(drv);
+               return ret;
+       }
+
+       drv_errors = 0;
+       return 0;
+}
+
+
+int android_pno_start(struct i802_bss *bss,
+                     struct wpa_driver_scan_params *params)
+{
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct ifreq ifr;
+       android_wifi_priv_cmd priv_cmd;
+       int ret = 0, i = 0, bp;
+       char buf[WEXT_PNO_MAX_COMMAND_SIZE];
+
+       bp = WEXT_PNOSETUP_HEADER_SIZE;
+       os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp);
+       buf[bp++] = WEXT_PNO_TLV_PREFIX;
+       buf[bp++] = WEXT_PNO_TLV_VERSION;
+       buf[bp++] = WEXT_PNO_TLV_SUBVERSION;
+       buf[bp++] = WEXT_PNO_TLV_RESERVED;
+
+       while (i < WEXT_PNO_AMOUNT && (size_t) i < params->num_ssids) {
+               /* Check that there is enough space needed for 1 more SSID, the
+                * other sections and null termination */
+               if ((bp + WEXT_PNO_SSID_HEADER_SIZE + MAX_SSID_LEN +
+                    WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int) sizeof(buf))
+                       break;
+               wpa_hexdump_ascii(MSG_DEBUG, "For PNO Scan",
+                                 params->ssids[i].ssid,
+                                 params->ssids[i].ssid_len);
+               buf[bp++] = WEXT_PNO_SSID_SECTION;
+               buf[bp++] = params->ssids[i].ssid_len;
+               os_memcpy(&buf[bp], params->ssids[i].ssid,
+                         params->ssids[i].ssid_len);
+               bp += params->ssids[i].ssid_len;
+               i++;
+       }
+
+       buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION;
+       os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x",
+                   WEXT_PNO_SCAN_INTERVAL);
+       bp += WEXT_PNO_SCAN_INTERVAL_LENGTH;
+
+       buf[bp++] = WEXT_PNO_REPEAT_SECTION;
+       os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x",
+                   WEXT_PNO_REPEAT);
+       bp += WEXT_PNO_REPEAT_LENGTH;
+
+       buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION;
+       os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x",
+                   WEXT_PNO_MAX_REPEAT);
+       bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1;
+
+       memset(&ifr, 0, sizeof(ifr));
+       memset(&priv_cmd, 0, sizeof(priv_cmd));
+       os_strlcpy(ifr.ifr_name, bss->ifname, IFNAMSIZ);
+
+       priv_cmd.buf = buf;
+       priv_cmd.used_len = bp;
+       priv_cmd.total_len = bp;
+       ifr.ifr_data = &priv_cmd;
+
+       ret = ioctl(drv->global->ioctl_sock, SIOCDEVPRIVATE + 1, &ifr);
+
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d",
+                          ret);
+               wpa_driver_send_hang_msg(drv);
+               return ret;
+       }
+
+       drv_errors = 0;
+
+       return android_priv_cmd(bss, "PNOFORCE 1");
+}
+
+
+int android_pno_stop(struct i802_bss *bss)
+{
+       return android_priv_cmd(bss, "PNOFORCE 0");
+}
+
+
+#ifdef ANDROID_P2P
+#ifdef ANDROID_P2P_STUB
+
+int wpa_driver_set_p2p_noa(void *priv, u8 count, int start, int duration)
+{
+       return 0;
+}
+
+
+int wpa_driver_get_p2p_noa(void *priv, u8 *buf, size_t len)
+{
+       return 0;
+}
+
+
+int wpa_driver_set_p2p_ps(void *priv, int legacy_ps, int opp_ps, int ctwindow)
+{
+       return -1;
+}
+
+
+int wpa_driver_set_ap_wps_p2p_ie(void *priv, const struct wpabuf *beacon,
+                                const struct wpabuf *proberesp,
+                                const struct wpabuf *assocresp)
+{
+       return 0;
+}
+
+#endif /* ANDROID_P2P_STUB */
+#endif /* ANDROID_P2P */
+
+
+int android_nl_socket_set_nonblocking(struct nl_handle *handle)
+{
+       return fcntl(nl_socket_get_fd(handle), F_SETFL, O_NONBLOCK);
+}
+
+
+int android_genl_ctrl_resolve(struct nl_handle *handle, const char *name)
+{
+       /*
+        * Android ICS has very minimal genl_ctrl_resolve() implementation, so
+        * need to work around that.
+        */
+       struct nl_cache *cache = NULL;
+       struct genl_family *nl80211 = NULL;
+       int id = -1;
+
+       if (genl_ctrl_alloc_cache(handle, &cache) < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to allocate generic "
+                          "netlink cache");
+               goto fail;
+       }
+
+       nl80211 = genl_ctrl_search_by_name(cache, name);
+       if (nl80211 == NULL)
+               goto fail;
+
+       id = genl_family_get_id(nl80211);
+
+fail:
+       if (nl80211)
+               genl_family_put(nl80211);
+       if (cache)
+               nl_cache_free(cache);
+
+       return id;
+}
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
new file mode 100755 (executable)
index 0000000..301e6b6
--- /dev/null
@@ -0,0 +1,1538 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - Capabilities
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <netlink/genl/genl.h>
+
+#include "utils/common.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "common/qca-vendor.h"
+#include "common/qca-vendor-attr.h"
+#include "driver_nl80211.h"
+
+
+static int protocol_feature_handler(struct nl_msg *msg, void *arg)
+{
+       u32 *feat = arg;
+       struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+
+       nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (tb_msg[NL80211_ATTR_PROTOCOL_FEATURES])
+               *feat = nla_get_u32(tb_msg[NL80211_ATTR_PROTOCOL_FEATURES]);
+
+       return NL_SKIP;
+}
+
+
+static u32 get_nl80211_protocol_features(struct wpa_driver_nl80211_data *drv)
+{
+       u32 feat = 0;
+       struct nl_msg *msg;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return 0;
+
+       if (!nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_PROTOCOL_FEATURES)) {
+               nlmsg_free(msg);
+               return 0;
+       }
+
+       if (send_and_recv_msgs(drv, msg, protocol_feature_handler, &feat) == 0)
+               return feat;
+
+       return 0;
+}
+
+
+struct wiphy_info_data {
+       struct wpa_driver_nl80211_data *drv;
+       struct wpa_driver_capa *capa;
+
+       unsigned int num_multichan_concurrent;
+
+       unsigned int error:1;
+       unsigned int device_ap_sme:1;
+       unsigned int poll_command_supported:1;
+       unsigned int data_tx_status:1;
+       unsigned int monitor_supported:1;
+       unsigned int auth_supported:1;
+       unsigned int connect_supported:1;
+       unsigned int p2p_go_supported:1;
+       unsigned int p2p_client_supported:1;
+       unsigned int p2p_go_ctwindow_supported:1;
+       unsigned int p2p_concurrent:1;
+       unsigned int channel_switch_supported:1;
+       unsigned int set_qos_map_supported:1;
+       unsigned int have_low_prio_scan:1;
+       unsigned int wmm_ac_supported:1;
+       unsigned int mac_addr_rand_scan_supported:1;
+       unsigned int mac_addr_rand_sched_scan_supported:1;
+};
+
+
+static unsigned int probe_resp_offload_support(int supp_protocols)
+{
+       unsigned int prot = 0;
+
+       if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS)
+               prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS;
+       if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_WPS2)
+               prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_WPS2;
+       if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P)
+               prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_P2P;
+       if (supp_protocols & NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U)
+               prot |= WPA_DRIVER_PROBE_RESP_OFFLOAD_INTERWORKING;
+
+       return prot;
+}
+
+
+static void wiphy_info_supported_iftypes(struct wiphy_info_data *info,
+                                        struct nlattr *tb)
+{
+       struct nlattr *nl_mode;
+       int i;
+
+       if (tb == NULL)
+               return;
+
+       nla_for_each_nested(nl_mode, tb, i) {
+               switch (nla_type(nl_mode)) {
+               case NL80211_IFTYPE_AP:
+                       info->capa->flags |= WPA_DRIVER_FLAGS_AP;
+                       break;
+               case NL80211_IFTYPE_MESH_POINT:
+                       info->capa->flags |= WPA_DRIVER_FLAGS_MESH;
+                       break;
+               case NL80211_IFTYPE_ADHOC:
+                       info->capa->flags |= WPA_DRIVER_FLAGS_IBSS;
+                       break;
+               case NL80211_IFTYPE_P2P_DEVICE:
+                       info->capa->flags |=
+                               WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE;
+                       break;
+               case NL80211_IFTYPE_P2P_GO:
+                       info->p2p_go_supported = 1;
+                       break;
+               case NL80211_IFTYPE_P2P_CLIENT:
+                       info->p2p_client_supported = 1;
+                       break;
+               case NL80211_IFTYPE_MONITOR:
+                       info->monitor_supported = 1;
+                       break;
+               }
+       }
+}
+
+
+static int wiphy_info_iface_comb_process(struct wiphy_info_data *info,
+                                        struct nlattr *nl_combi)
+{
+       struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB];
+       struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT];
+       struct nlattr *nl_limit, *nl_mode;
+       int err, rem_limit, rem_mode;
+       int combination_has_p2p = 0, combination_has_mgd = 0;
+       static struct nla_policy
+       iface_combination_policy[NUM_NL80211_IFACE_COMB] = {
+               [NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED },
+               [NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 },
+               [NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG },
+               [NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 },
+               [NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS] = { .type = NLA_U32 },
+       },
+       iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = {
+               [NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED },
+               [NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 },
+       };
+
+       err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB,
+                              nl_combi, iface_combination_policy);
+       if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] ||
+           !tb_comb[NL80211_IFACE_COMB_MAXNUM] ||
+           !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS])
+               return 0; /* broken combination */
+
+       if (tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS])
+               info->capa->flags |= WPA_DRIVER_FLAGS_RADAR;
+
+       nla_for_each_nested(nl_limit, tb_comb[NL80211_IFACE_COMB_LIMITS],
+                           rem_limit) {
+               err = nla_parse_nested(tb_limit, MAX_NL80211_IFACE_LIMIT,
+                                      nl_limit, iface_limit_policy);
+               if (err || !tb_limit[NL80211_IFACE_LIMIT_TYPES])
+                       return 0; /* broken combination */
+
+               nla_for_each_nested(nl_mode,
+                                   tb_limit[NL80211_IFACE_LIMIT_TYPES],
+                                   rem_mode) {
+                       int ift = nla_type(nl_mode);
+                       if (ift == NL80211_IFTYPE_P2P_GO ||
+                           ift == NL80211_IFTYPE_P2P_CLIENT)
+                               combination_has_p2p = 1;
+                       if (ift == NL80211_IFTYPE_STATION)
+                               combination_has_mgd = 1;
+               }
+               if (combination_has_p2p && combination_has_mgd)
+                       break;
+       }
+
+       if (combination_has_p2p && combination_has_mgd) {
+               unsigned int num_channels =
+                       nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]);
+
+               info->p2p_concurrent = 1;
+               if (info->num_multichan_concurrent < num_channels)
+                       info->num_multichan_concurrent = num_channels;
+       }
+
+       return 0;
+}
+
+
+static void wiphy_info_iface_comb(struct wiphy_info_data *info,
+                                 struct nlattr *tb)
+{
+       struct nlattr *nl_combi;
+       int rem_combi;
+
+       if (tb == NULL)
+               return;
+
+       nla_for_each_nested(nl_combi, tb, rem_combi) {
+               if (wiphy_info_iface_comb_process(info, nl_combi) > 0)
+                       break;
+       }
+}
+
+
+static void wiphy_info_supp_cmds(struct wiphy_info_data *info,
+                                struct nlattr *tb)
+{
+       struct nlattr *nl_cmd;
+       int i;
+
+       if (tb == NULL)
+               return;
+
+       nla_for_each_nested(nl_cmd, tb, i) {
+               switch (nla_get_u32(nl_cmd)) {
+               case NL80211_CMD_AUTHENTICATE:
+                       info->auth_supported = 1;
+                       break;
+               case NL80211_CMD_CONNECT:
+                       info->connect_supported = 1;
+                       break;
+               case NL80211_CMD_START_SCHED_SCAN:
+                       info->capa->sched_scan_supported = 1;
+                       break;
+               case NL80211_CMD_PROBE_CLIENT:
+                       info->poll_command_supported = 1;
+                       break;
+               case NL80211_CMD_CHANNEL_SWITCH:
+                       info->channel_switch_supported = 1;
+                       break;
+               case NL80211_CMD_SET_QOS_MAP:
+                       info->set_qos_map_supported = 1;
+                       break;
+               }
+       }
+}
+
+
+static void wiphy_info_cipher_suites(struct wiphy_info_data *info,
+                                    struct nlattr *tb)
+{
+       int i, num;
+       u32 *ciphers;
+
+       if (tb == NULL)
+               return;
+
+       num = nla_len(tb) / sizeof(u32);
+       ciphers = nla_data(tb);
+       for (i = 0; i < num; i++) {
+               u32 c = ciphers[i];
+
+               wpa_printf(MSG_DEBUG, "nl80211: Supported cipher %02x-%02x-%02x:%d",
+                          c >> 24, (c >> 16) & 0xff,
+                          (c >> 8) & 0xff, c & 0xff);
+               switch (c) {
+               case WLAN_CIPHER_SUITE_CCMP_256:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP_256;
+                       break;
+               case WLAN_CIPHER_SUITE_GCMP_256:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP_256;
+                       break;
+               case WLAN_CIPHER_SUITE_CCMP:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_CCMP;
+                       break;
+               case WLAN_CIPHER_SUITE_GCMP:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_GCMP;
+                       break;
+               case WLAN_CIPHER_SUITE_TKIP:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_TKIP;
+                       break;
+               case WLAN_CIPHER_SUITE_WEP104:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP104;
+                       break;
+               case WLAN_CIPHER_SUITE_WEP40:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_WEP40;
+                       break;
+               case WLAN_CIPHER_SUITE_AES_CMAC:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP;
+                       break;
+               case WLAN_CIPHER_SUITE_BIP_GMAC_128:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_128;
+                       break;
+               case WLAN_CIPHER_SUITE_BIP_GMAC_256:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_GMAC_256;
+                       break;
+               case WLAN_CIPHER_SUITE_BIP_CMAC_256:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_BIP_CMAC_256;
+                       break;
+               case WLAN_CIPHER_SUITE_NO_GROUP_ADDR:
+                       info->capa->enc |= WPA_DRIVER_CAPA_ENC_GTK_NOT_USED;
+                       break;
+               }
+       }
+}
+
+
+static void wiphy_info_max_roc(struct wpa_driver_capa *capa,
+                              struct nlattr *tb)
+{
+       if (tb)
+               capa->max_remain_on_chan = nla_get_u32(tb);
+}
+
+
+static void wiphy_info_tdls(struct wpa_driver_capa *capa, struct nlattr *tdls,
+                           struct nlattr *ext_setup)
+{
+       if (tdls == NULL)
+               return;
+
+       wpa_printf(MSG_DEBUG, "nl80211: TDLS supported");
+       capa->flags |= WPA_DRIVER_FLAGS_TDLS_SUPPORT;
+
+       if (ext_setup) {
+               wpa_printf(MSG_DEBUG, "nl80211: TDLS external setup");
+               capa->flags |= WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP;
+       }
+}
+
+
+static void wiphy_info_feature_flags(struct wiphy_info_data *info,
+                                    struct nlattr *tb)
+{
+       u32 flags;
+       struct wpa_driver_capa *capa = info->capa;
+
+       if (tb == NULL)
+               return;
+
+       flags = nla_get_u32(tb);
+
+       if (flags & NL80211_FEATURE_SK_TX_STATUS)
+               info->data_tx_status = 1;
+
+       if (flags & NL80211_FEATURE_INACTIVITY_TIMER)
+               capa->flags |= WPA_DRIVER_FLAGS_INACTIVITY_TIMER;
+
+       if (flags & NL80211_FEATURE_SAE)
+               capa->flags |= WPA_DRIVER_FLAGS_SAE;
+
+       if (flags & NL80211_FEATURE_NEED_OBSS_SCAN)
+               capa->flags |= WPA_DRIVER_FLAGS_OBSS_SCAN;
+
+       if (flags & NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE)
+               capa->flags |= WPA_DRIVER_FLAGS_HT_2040_COEX;
+
+       if (flags & NL80211_FEATURE_TDLS_CHANNEL_SWITCH) {
+               wpa_printf(MSG_DEBUG, "nl80211: TDLS channel switch");
+               capa->flags |= WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH;
+       }
+
+       if (flags & NL80211_FEATURE_P2P_GO_CTWIN)
+               info->p2p_go_ctwindow_supported = 1;
+
+       if (flags & NL80211_FEATURE_LOW_PRIORITY_SCAN)
+               info->have_low_prio_scan = 1;
+
+       if (flags & NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR)
+               info->mac_addr_rand_scan_supported = 1;
+
+       if (flags & NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR)
+               info->mac_addr_rand_sched_scan_supported = 1;
+
+       if (flags & NL80211_FEATURE_STATIC_SMPS)
+               capa->smps_modes |= WPA_DRIVER_SMPS_MODE_STATIC;
+
+       if (flags & NL80211_FEATURE_DYNAMIC_SMPS)
+               capa->smps_modes |= WPA_DRIVER_SMPS_MODE_DYNAMIC;
+
+       if (flags & NL80211_FEATURE_SUPPORTS_WMM_ADMISSION)
+               info->wmm_ac_supported = 1;
+
+       if (flags & NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES)
+               capa->rrm_flags |= WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES;
+
+       if (flags & NL80211_FEATURE_WFA_TPC_IE_IN_PROBES)
+               capa->rrm_flags |= WPA_DRIVER_FLAGS_WFA_TPC_IE_IN_PROBES;
+
+       if (flags & NL80211_FEATURE_QUIET)
+               capa->rrm_flags |= WPA_DRIVER_FLAGS_QUIET;
+
+       if (flags & NL80211_FEATURE_TX_POWER_INSERTION)
+               capa->rrm_flags |= WPA_DRIVER_FLAGS_TX_POWER_INSERTION;
+
+       if (flags & NL80211_FEATURE_HT_IBSS)
+               capa->flags |= WPA_DRIVER_FLAGS_HT_IBSS;
+}
+
+
+static void wiphy_info_probe_resp_offload(struct wpa_driver_capa *capa,
+                                         struct nlattr *tb)
+{
+       u32 protocols;
+
+       if (tb == NULL)
+               return;
+
+       protocols = nla_get_u32(tb);
+       wpa_printf(MSG_DEBUG, "nl80211: Supports Probe Response offload in AP "
+                  "mode");
+       capa->flags |= WPA_DRIVER_FLAGS_PROBE_RESP_OFFLOAD;
+       capa->probe_resp_offloads = probe_resp_offload_support(protocols);
+}
+
+
+static void wiphy_info_wowlan_triggers(struct wpa_driver_capa *capa,
+                                      struct nlattr *tb)
+{
+       struct nlattr *triggers[MAX_NL80211_WOWLAN_TRIG + 1];
+
+       if (tb == NULL)
+               return;
+
+       if (nla_parse_nested(triggers, MAX_NL80211_WOWLAN_TRIG,
+                            tb, NULL))
+               return;
+
+       if (triggers[NL80211_WOWLAN_TRIG_ANY])
+               capa->wowlan_triggers.any = 1;
+       if (triggers[NL80211_WOWLAN_TRIG_DISCONNECT])
+               capa->wowlan_triggers.disconnect = 1;
+       if (triggers[NL80211_WOWLAN_TRIG_MAGIC_PKT])
+               capa->wowlan_triggers.magic_pkt = 1;
+       if (triggers[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE])
+               capa->wowlan_triggers.gtk_rekey_failure = 1;
+       if (triggers[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST])
+               capa->wowlan_triggers.eap_identity_req = 1;
+       if (triggers[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE])
+               capa->wowlan_triggers.four_way_handshake = 1;
+       if (triggers[NL80211_WOWLAN_TRIG_RFKILL_RELEASE])
+               capa->wowlan_triggers.rfkill_release = 1;
+}
+
+
+static int wiphy_info_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct wiphy_info_data *info = arg;
+       struct wpa_driver_capa *capa = info->capa;
+       struct wpa_driver_nl80211_data *drv = info->drv;
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (tb[NL80211_ATTR_WIPHY_NAME])
+               os_strlcpy(drv->phyname,
+                          nla_get_string(tb[NL80211_ATTR_WIPHY_NAME]),
+                          sizeof(drv->phyname));
+       if (tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS])
+               capa->max_scan_ssids =
+                       nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCAN_SSIDS]);
+
+       if (tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS])
+               capa->max_sched_scan_ssids =
+                       nla_get_u8(tb[NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS]);
+
+       if (tb[NL80211_ATTR_MAX_MATCH_SETS])
+               capa->max_match_sets =
+                       nla_get_u8(tb[NL80211_ATTR_MAX_MATCH_SETS]);
+
+       if (tb[NL80211_ATTR_MAC_ACL_MAX])
+               capa->max_acl_mac_addrs =
+                       nla_get_u8(tb[NL80211_ATTR_MAC_ACL_MAX]);
+
+       wiphy_info_supported_iftypes(info, tb[NL80211_ATTR_SUPPORTED_IFTYPES]);
+       wiphy_info_iface_comb(info, tb[NL80211_ATTR_INTERFACE_COMBINATIONS]);
+       wiphy_info_supp_cmds(info, tb[NL80211_ATTR_SUPPORTED_COMMANDS]);
+       wiphy_info_cipher_suites(info, tb[NL80211_ATTR_CIPHER_SUITES]);
+
+       if (tb[NL80211_ATTR_OFFCHANNEL_TX_OK]) {
+               wpa_printf(MSG_DEBUG, "nl80211: Using driver-based "
+                          "off-channel TX");
+               capa->flags |= WPA_DRIVER_FLAGS_OFFCHANNEL_TX;
+       }
+
+       if (tb[NL80211_ATTR_ROAM_SUPPORT]) {
+               wpa_printf(MSG_DEBUG, "nl80211: Using driver-based roaming");
+               capa->flags |= WPA_DRIVER_FLAGS_BSS_SELECTION;
+       }
+
+       wiphy_info_max_roc(capa,
+                          tb[NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION]);
+
+       if (tb[NL80211_ATTR_SUPPORT_AP_UAPSD])
+               capa->flags |= WPA_DRIVER_FLAGS_AP_UAPSD;
+
+       wiphy_info_tdls(capa, tb[NL80211_ATTR_TDLS_SUPPORT],
+                       tb[NL80211_ATTR_TDLS_EXTERNAL_SETUP]);
+
+       if (tb[NL80211_ATTR_DEVICE_AP_SME])
+               info->device_ap_sme = 1;
+
+       wiphy_info_feature_flags(info, tb[NL80211_ATTR_FEATURE_FLAGS]);
+       wiphy_info_probe_resp_offload(capa,
+                                     tb[NL80211_ATTR_PROBE_RESP_OFFLOAD]);
+
+       if (tb[NL80211_ATTR_EXT_CAPA] && tb[NL80211_ATTR_EXT_CAPA_MASK] &&
+           drv->extended_capa == NULL) {
+               drv->extended_capa =
+                       os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA]));
+               if (drv->extended_capa) {
+                       os_memcpy(drv->extended_capa,
+                                 nla_data(tb[NL80211_ATTR_EXT_CAPA]),
+                                 nla_len(tb[NL80211_ATTR_EXT_CAPA]));
+                       drv->extended_capa_len =
+                               nla_len(tb[NL80211_ATTR_EXT_CAPA]);
+               }
+               drv->extended_capa_mask =
+                       os_malloc(nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
+               if (drv->extended_capa_mask) {
+                       os_memcpy(drv->extended_capa_mask,
+                                 nla_data(tb[NL80211_ATTR_EXT_CAPA_MASK]),
+                                 nla_len(tb[NL80211_ATTR_EXT_CAPA_MASK]));
+               } else {
+                       os_free(drv->extended_capa);
+                       drv->extended_capa = NULL;
+                       drv->extended_capa_len = 0;
+               }
+       }
+
+       if (tb[NL80211_ATTR_VENDOR_DATA]) {
+               struct nlattr *nl;
+               int rem;
+
+               nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_DATA], rem) {
+                       struct nl80211_vendor_cmd_info *vinfo;
+                       if (nla_len(nl) != sizeof(*vinfo)) {
+                               wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info");
+                               continue;
+                       }
+                       vinfo = nla_data(nl);
+                       switch (vinfo->subcmd) {
+                       case QCA_NL80211_VENDOR_SUBCMD_TEST:
+                               drv->vendor_cmd_test_avail = 1;
+                               break;
+                       case QCA_NL80211_VENDOR_SUBCMD_ROAMING:
+                               drv->roaming_vendor_cmd_avail = 1;
+                               break;
+                       case QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY:
+                               drv->dfs_vendor_cmd_avail = 1;
+                               break;
+                       case QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES:
+                               drv->get_features_vendor_cmd_avail = 1;
+                               break;
+                       case QCA_NL80211_VENDOR_SUBCMD_DO_ACS:
+                               drv->capa.flags |= WPA_DRIVER_FLAGS_ACS_OFFLOAD;
+                               break;
+                       }
+
+                       wpa_printf(MSG_DEBUG, "nl80211: Supported vendor command: vendor_id=0x%x subcmd=%u",
+                                  vinfo->vendor_id, vinfo->subcmd);
+               }
+       }
+
+       if (tb[NL80211_ATTR_VENDOR_EVENTS]) {
+               struct nlattr *nl;
+               int rem;
+
+               nla_for_each_nested(nl, tb[NL80211_ATTR_VENDOR_EVENTS], rem) {
+                       struct nl80211_vendor_cmd_info *vinfo;
+                       if (nla_len(nl) != sizeof(*vinfo)) {
+                               wpa_printf(MSG_DEBUG, "nl80211: Unexpected vendor data info");
+                               continue;
+                       }
+                       vinfo = nla_data(nl);
+                       wpa_printf(MSG_DEBUG, "nl80211: Supported vendor event: vendor_id=0x%x subcmd=%u",
+                                  vinfo->vendor_id, vinfo->subcmd);
+               }
+       }
+
+       wiphy_info_wowlan_triggers(capa,
+                                  tb[NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED]);
+
+       if (tb[NL80211_ATTR_MAX_AP_ASSOC_STA])
+               capa->max_stations =
+                       nla_get_u32(tb[NL80211_ATTR_MAX_AP_ASSOC_STA]);
+
+       return NL_SKIP;
+}
+
+
+static int wpa_driver_nl80211_get_info(struct wpa_driver_nl80211_data *drv,
+                                      struct wiphy_info_data *info)
+{
+       u32 feat;
+       struct nl_msg *msg;
+       int flags = 0;
+
+       os_memset(info, 0, sizeof(*info));
+       info->capa = &drv->capa;
+       info->drv = drv;
+
+       feat = get_nl80211_protocol_features(drv);
+       if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
+               flags = NLM_F_DUMP;
+       msg = nl80211_cmd_msg(drv->first_bss, flags, NL80211_CMD_GET_WIPHY);
+       if (!msg || nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) {
+               nlmsg_free(msg);
+               return -1;
+       }
+
+       if (send_and_recv_msgs(drv, msg, wiphy_info_handler, info))
+               return -1;
+
+       if (info->auth_supported)
+               drv->capa.flags |= WPA_DRIVER_FLAGS_SME;
+       else if (!info->connect_supported) {
+               wpa_printf(MSG_INFO, "nl80211: Driver does not support "
+                          "authentication/association or connect commands");
+               info->error = 1;
+       }
+
+       if (info->p2p_go_supported && info->p2p_client_supported)
+               drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CAPABLE;
+       if (info->p2p_concurrent) {
+               wpa_printf(MSG_DEBUG, "nl80211: Use separate P2P group "
+                          "interface (driver advertised support)");
+               drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
+               drv->capa.flags |= WPA_DRIVER_FLAGS_P2P_MGMT_AND_NON_P2P;
+       }
+       if (info->num_multichan_concurrent > 1) {
+               wpa_printf(MSG_DEBUG, "nl80211: Enable multi-channel "
+                          "concurrent (driver advertised support)");
+               drv->capa.num_multichan_concurrent =
+                       info->num_multichan_concurrent;
+       }
+       if (drv->capa.flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)
+               wpa_printf(MSG_DEBUG, "nl80211: use P2P_DEVICE support");
+
+       /* default to 5000 since early versions of mac80211 don't set it */
+       if (!drv->capa.max_remain_on_chan)
+               drv->capa.max_remain_on_chan = 5000;
+#ifdef BCM_DRIVER_V115
+       info->device_ap_sme = 1;
+#endif
+       if (info->channel_switch_supported)
+               drv->capa.flags |= WPA_DRIVER_FLAGS_AP_CSA;
+       drv->capa.wmm_ac_supported = info->wmm_ac_supported;
+
+       drv->capa.mac_addr_rand_sched_scan_supported =
+               info->mac_addr_rand_sched_scan_supported;
+       drv->capa.mac_addr_rand_scan_supported =
+               info->mac_addr_rand_scan_supported;
+
+       return 0;
+}
+
+
+static int dfs_info_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       int *dfs_capability_ptr = arg;
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (tb[NL80211_ATTR_VENDOR_DATA]) {
+               struct nlattr *nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
+               struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+
+               nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+                         nla_data(nl_vend), nla_len(nl_vend), NULL);
+
+               if (tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]) {
+                       u32 val;
+                       val = nla_get_u32(tb_vendor[QCA_WLAN_VENDOR_ATTR_DFS]);
+                       wpa_printf(MSG_DEBUG, "nl80211: DFS offload capability: %u",
+                                  val);
+                       *dfs_capability_ptr = val;
+               }
+       }
+
+       return NL_SKIP;
+}
+
+
+static void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv)
+{
+       struct nl_msg *msg;
+       int dfs_capability = 0;
+       int ret;
+
+       if (!drv->dfs_vendor_cmd_avail)
+               return;
+
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+                       QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY)) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       ret = send_and_recv_msgs(drv, msg, dfs_info_handler, &dfs_capability);
+       if (!ret && dfs_capability)
+               drv->capa.flags |= WPA_DRIVER_FLAGS_DFS_OFFLOAD;
+}
+
+
+struct features_info {
+       u8 *flags;
+       size_t flags_len;
+};
+
+
+static int features_info_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct features_info *info = arg;
+       struct nlattr *nl_vend, *attr;
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
+       if (nl_vend) {
+               struct nlattr *tb_vendor[QCA_WLAN_VENDOR_ATTR_MAX + 1];
+
+               nla_parse(tb_vendor, QCA_WLAN_VENDOR_ATTR_MAX,
+                         nla_data(nl_vend), nla_len(nl_vend), NULL);
+
+               attr = tb_vendor[QCA_WLAN_VENDOR_ATTR_FEATURE_FLAGS];
+               if (attr) {
+                       info->flags = nla_data(attr);
+                       info->flags_len = nla_len(attr);
+               }
+       }
+
+       return NL_SKIP;
+}
+
+
+static int check_feature(enum qca_wlan_vendor_features feature,
+                        struct features_info *info)
+{
+       size_t idx = feature / 8;
+
+       return (idx < info->flags_len) &&
+               (info->flags[idx] & BIT(feature % 8));
+}
+
+
+static void qca_nl80211_get_features(struct wpa_driver_nl80211_data *drv)
+{
+       struct nl_msg *msg;
+       struct features_info info;
+       int ret;
+
+       if (!drv->get_features_vendor_cmd_avail)
+               return;
+
+       if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+           nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+                       QCA_NL80211_VENDOR_SUBCMD_GET_FEATURES)) {
+               nlmsg_free(msg);
+               return;
+       }
+
+       os_memset(&info, 0, sizeof(info));
+       ret = send_and_recv_msgs(drv, msg, features_info_handler, &info);
+       if (ret || !info.flags)
+               return;
+
+       if (check_feature(QCA_WLAN_VENDOR_FEATURE_KEY_MGMT_OFFLOAD, &info))
+               drv->capa.flags |= WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD;
+}
+
+
+int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
+{
+       struct wiphy_info_data info;
+       if (wpa_driver_nl80211_get_info(drv, &info))
+               return -1;
+
+       if (info.error)
+               return -1;
+
+       drv->has_capability = 1;
+       drv->capa.key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+               WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+               WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+               WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK |
+               WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B |
+               WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
+       drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
+               WPA_DRIVER_AUTH_SHARED |
+               WPA_DRIVER_AUTH_LEAP;
+
+       drv->capa.flags |= WPA_DRIVER_FLAGS_SANE_ERROR_CODES;
+       drv->capa.flags |= WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE;
+       drv->capa.flags |= WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+
+       /*
+        * As all cfg80211 drivers must support cases where the AP interface is
+        * removed without the knowledge of wpa_supplicant/hostapd, e.g., in
+        * case that the user space daemon has crashed, they must be able to
+        * cleanup all stations and key entries in the AP tear down flow. Thus,
+        * this flag can/should always be set for cfg80211 drivers.
+        */
+       drv->capa.flags |= WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT;
+
+       if (!info.device_ap_sme) {
+               drv->capa.flags |= WPA_DRIVER_FLAGS_DEAUTH_TX_STATUS;
+
+               /*
+                * No AP SME is currently assumed to also indicate no AP MLME
+                * in the driver/firmware.
+                */
+               drv->capa.flags |= WPA_DRIVER_FLAGS_AP_MLME;
+       }
+
+       drv->device_ap_sme = info.device_ap_sme;
+       drv->poll_command_supported = info.poll_command_supported;
+       drv->data_tx_status = info.data_tx_status;
+       drv->p2p_go_ctwindow_supported = info.p2p_go_ctwindow_supported;
+       if (info.set_qos_map_supported)
+               drv->capa.flags |= WPA_DRIVER_FLAGS_QOS_MAPPING;
+       drv->have_low_prio_scan = info.have_low_prio_scan;
+
+       /*
+        * If poll command and tx status are supported, mac80211 is new enough
+        * to have everything we need to not need monitor interfaces.
+        */
+#ifdef BCM_DRIVER_V115
+       drv->use_monitor = 0;
+#else
+       drv->use_monitor = !info.poll_command_supported || !info.data_tx_status;
+#endif
+
+       if (drv->device_ap_sme && drv->use_monitor) {
+               /*
+                * Non-mac80211 drivers may not support monitor interface.
+                * Make sure we do not get stuck with incorrect capability here
+                * by explicitly testing this.
+                */
+               if (!info.monitor_supported) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Disable use_monitor "
+                                  "with device_ap_sme since no monitor mode "
+                                  "support detected");
+                       drv->use_monitor = 0;
+               }
+       }
+
+       /*
+        * If we aren't going to use monitor interfaces, but the
+        * driver doesn't support data TX status, we won't get TX
+        * status for EAPOL frames.
+        */
+       if (!drv->use_monitor && !info.data_tx_status)
+               drv->capa.flags &= ~WPA_DRIVER_FLAGS_EAPOL_TX_STATUS;
+
+       qca_nl80211_check_dfs_capa(drv);
+       qca_nl80211_get_features(drv);
+
+       return 0;
+}
+
+
+struct phy_info_arg {
+       u16 *num_modes;
+       struct hostapd_hw_modes *modes;
+       int last_mode, last_chan_idx;
+};
+
+static void phy_info_ht_capa(struct hostapd_hw_modes *mode, struct nlattr *capa,
+                            struct nlattr *ampdu_factor,
+                            struct nlattr *ampdu_density,
+                            struct nlattr *mcs_set)
+{
+       if (capa)
+               mode->ht_capab = nla_get_u16(capa);
+
+       if (ampdu_factor)
+               mode->a_mpdu_params |= nla_get_u8(ampdu_factor) & 0x03;
+
+       if (ampdu_density)
+               mode->a_mpdu_params |= nla_get_u8(ampdu_density) << 2;
+
+       if (mcs_set && nla_len(mcs_set) >= 16) {
+               u8 *mcs;
+               mcs = nla_data(mcs_set);
+               os_memcpy(mode->mcs_set, mcs, 16);
+       }
+}
+
+
+static void phy_info_vht_capa(struct hostapd_hw_modes *mode,
+                             struct nlattr *capa,
+                             struct nlattr *mcs_set)
+{
+       if (capa)
+               mode->vht_capab = nla_get_u32(capa);
+
+       if (mcs_set && nla_len(mcs_set) >= 8) {
+               u8 *mcs;
+               mcs = nla_data(mcs_set);
+               os_memcpy(mode->vht_mcs_set, mcs, 8);
+       }
+}
+
+
+static void phy_info_freq(struct hostapd_hw_modes *mode,
+                         struct hostapd_channel_data *chan,
+                         struct nlattr *tb_freq[])
+{
+       u8 channel;
+       chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
+       chan->flag = 0;
+       chan->dfs_cac_ms = 0;
+       if (ieee80211_freq_to_chan(chan->freq, &channel) != NUM_HOSTAPD_MODES)
+               chan->chan = channel;
+
+       if (tb_freq[NL80211_FREQUENCY_ATTR_DISABLED])
+               chan->flag |= HOSTAPD_CHAN_DISABLED;
+       if (tb_freq[NL80211_FREQUENCY_ATTR_NO_IR])
+               chan->flag |= HOSTAPD_CHAN_NO_IR;
+       if (tb_freq[NL80211_FREQUENCY_ATTR_RADAR])
+               chan->flag |= HOSTAPD_CHAN_RADAR;
+       if (tb_freq[NL80211_FREQUENCY_ATTR_INDOOR_ONLY])
+               chan->flag |= HOSTAPD_CHAN_INDOOR_ONLY;
+       if (tb_freq[NL80211_FREQUENCY_ATTR_GO_CONCURRENT])
+               chan->flag |= HOSTAPD_CHAN_GO_CONCURRENT;
+
+       if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
+               enum nl80211_dfs_state state =
+                       nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]);
+
+               switch (state) {
+               case NL80211_DFS_USABLE:
+                       chan->flag |= HOSTAPD_CHAN_DFS_USABLE;
+                       break;
+               case NL80211_DFS_AVAILABLE:
+                       chan->flag |= HOSTAPD_CHAN_DFS_AVAILABLE;
+                       break;
+               case NL80211_DFS_UNAVAILABLE:
+                       chan->flag |= HOSTAPD_CHAN_DFS_UNAVAILABLE;
+                       break;
+               }
+       }
+
+       if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]) {
+               chan->dfs_cac_ms = nla_get_u32(
+                       tb_freq[NL80211_FREQUENCY_ATTR_DFS_CAC_TIME]);
+       }
+}
+
+
+static int phy_info_freqs(struct phy_info_arg *phy_info,
+                         struct hostapd_hw_modes *mode, struct nlattr *tb)
+{
+       static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
+               [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
+               [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
+               [NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG },
+               [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
+               [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
+               [NL80211_FREQUENCY_ATTR_DFS_STATE] = { .type = NLA_U32 },
+       };
+       int new_channels = 0;
+       struct hostapd_channel_data *channel;
+       struct nlattr *tb_freq[NL80211_FREQUENCY_ATTR_MAX + 1];
+       struct nlattr *nl_freq;
+       int rem_freq, idx;
+
+       if (tb == NULL)
+               return NL_OK;
+
+       nla_for_each_nested(nl_freq, tb, rem_freq) {
+               nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
+                         nla_data(nl_freq), nla_len(nl_freq), freq_policy);
+               if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
+                       continue;
+               new_channels++;
+       }
+
+       channel = os_realloc_array(mode->channels,
+                                  mode->num_channels + new_channels,
+                                  sizeof(struct hostapd_channel_data));
+       if (!channel)
+               return NL_SKIP;
+
+       mode->channels = channel;
+       mode->num_channels += new_channels;
+
+       idx = phy_info->last_chan_idx;
+
+       nla_for_each_nested(nl_freq, tb, rem_freq) {
+               nla_parse(tb_freq, NL80211_FREQUENCY_ATTR_MAX,
+                         nla_data(nl_freq), nla_len(nl_freq), freq_policy);
+               if (!tb_freq[NL80211_FREQUENCY_ATTR_FREQ])
+                       continue;
+               phy_info_freq(mode, &mode->channels[idx], tb_freq);
+               idx++;
+       }
+       phy_info->last_chan_idx = idx;
+
+       return NL_OK;
+}
+
+
+static int phy_info_rates(struct hostapd_hw_modes *mode, struct nlattr *tb)
+{
+       static struct nla_policy rate_policy[NL80211_BITRATE_ATTR_MAX + 1] = {
+               [NL80211_BITRATE_ATTR_RATE] = { .type = NLA_U32 },
+               [NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE] =
+               { .type = NLA_FLAG },
+       };
+       struct nlattr *tb_rate[NL80211_BITRATE_ATTR_MAX + 1];
+       struct nlattr *nl_rate;
+       int rem_rate, idx;
+
+       if (tb == NULL)
+               return NL_OK;
+
+       nla_for_each_nested(nl_rate, tb, rem_rate) {
+               nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX,
+                         nla_data(nl_rate), nla_len(nl_rate),
+                         rate_policy);
+               if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
+                       continue;
+               mode->num_rates++;
+       }
+
+       mode->rates = os_calloc(mode->num_rates, sizeof(int));
+       if (!mode->rates)
+               return NL_SKIP;
+
+       idx = 0;
+
+       nla_for_each_nested(nl_rate, tb, rem_rate) {
+               nla_parse(tb_rate, NL80211_BITRATE_ATTR_MAX,
+                         nla_data(nl_rate), nla_len(nl_rate),
+                         rate_policy);
+               if (!tb_rate[NL80211_BITRATE_ATTR_RATE])
+                       continue;
+               mode->rates[idx] = nla_get_u32(
+                       tb_rate[NL80211_BITRATE_ATTR_RATE]);
+               idx++;
+       }
+
+       return NL_OK;
+}
+
+
+static int phy_info_band(struct phy_info_arg *phy_info, struct nlattr *nl_band)
+{
+       struct nlattr *tb_band[NL80211_BAND_ATTR_MAX + 1];
+       struct hostapd_hw_modes *mode;
+       int ret;
+
+       if (phy_info->last_mode != nl_band->nla_type) {
+               mode = os_realloc_array(phy_info->modes,
+                                       *phy_info->num_modes + 1,
+                                       sizeof(*mode));
+               if (!mode)
+                       return NL_SKIP;
+               phy_info->modes = mode;
+
+               mode = &phy_info->modes[*(phy_info->num_modes)];
+               os_memset(mode, 0, sizeof(*mode));
+               mode->mode = NUM_HOSTAPD_MODES;
+               mode->flags = HOSTAPD_MODE_FLAG_HT_INFO_KNOWN |
+                       HOSTAPD_MODE_FLAG_VHT_INFO_KNOWN;
+
+               /*
+                * Unsupported VHT MCS stream is defined as value 3, so the VHT
+                * MCS RX/TX map must be initialized with 0xffff to mark all 8
+                * possible streams as unsupported. This will be overridden if
+                * driver advertises VHT support.
+                */
+               mode->vht_mcs_set[0] = 0xff;
+               mode->vht_mcs_set[1] = 0xff;
+               mode->vht_mcs_set[4] = 0xff;
+               mode->vht_mcs_set[5] = 0xff;
+
+               *(phy_info->num_modes) += 1;
+               phy_info->last_mode = nl_band->nla_type;
+               phy_info->last_chan_idx = 0;
+       } else
+               mode = &phy_info->modes[*(phy_info->num_modes) - 1];
+
+       nla_parse(tb_band, NL80211_BAND_ATTR_MAX, nla_data(nl_band),
+                 nla_len(nl_band), NULL);
+
+       phy_info_ht_capa(mode, tb_band[NL80211_BAND_ATTR_HT_CAPA],
+                        tb_band[NL80211_BAND_ATTR_HT_AMPDU_FACTOR],
+                        tb_band[NL80211_BAND_ATTR_HT_AMPDU_DENSITY],
+                        tb_band[NL80211_BAND_ATTR_HT_MCS_SET]);
+       phy_info_vht_capa(mode, tb_band[NL80211_BAND_ATTR_VHT_CAPA],
+                         tb_band[NL80211_BAND_ATTR_VHT_MCS_SET]);
+       ret = phy_info_freqs(phy_info, mode, tb_band[NL80211_BAND_ATTR_FREQS]);
+       if (ret != NL_OK)
+               return ret;
+       ret = phy_info_rates(mode, tb_band[NL80211_BAND_ATTR_RATES]);
+       if (ret != NL_OK)
+               return ret;
+
+       return NL_OK;
+}
+
+
+static int phy_info_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct phy_info_arg *phy_info = arg;
+       struct nlattr *nl_band;
+       int rem_band;
+
+       nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (!tb_msg[NL80211_ATTR_WIPHY_BANDS])
+               return NL_SKIP;
+
+       nla_for_each_nested(nl_band, tb_msg[NL80211_ATTR_WIPHY_BANDS], rem_band)
+       {
+               int res = phy_info_band(phy_info, nl_band);
+               if (res != NL_OK)
+                       return res;
+       }
+
+       return NL_SKIP;
+}
+
+
+static struct hostapd_hw_modes *
+wpa_driver_nl80211_postprocess_modes(struct hostapd_hw_modes *modes,
+                                    u16 *num_modes)
+{
+       u16 m;
+       struct hostapd_hw_modes *mode11g = NULL, *nmodes, *mode;
+       int i, mode11g_idx = -1;
+
+       /* heuristic to set up modes */
+       for (m = 0; m < *num_modes; m++) {
+               if (!modes[m].num_channels)
+                       continue;
+               if (modes[m].channels[0].freq < 4000) {
+                       modes[m].mode = HOSTAPD_MODE_IEEE80211B;
+                       for (i = 0; i < modes[m].num_rates; i++) {
+                               if (modes[m].rates[i] > 200) {
+                                       modes[m].mode = HOSTAPD_MODE_IEEE80211G;
+                                       break;
+                               }
+                       }
+               } else if (modes[m].channels[0].freq > 50000)
+                       modes[m].mode = HOSTAPD_MODE_IEEE80211AD;
+               else
+                       modes[m].mode = HOSTAPD_MODE_IEEE80211A;
+       }
+
+       /* If only 802.11g mode is included, use it to construct matching
+        * 802.11b mode data. */
+
+       for (m = 0; m < *num_modes; m++) {
+               if (modes[m].mode == HOSTAPD_MODE_IEEE80211B)
+                       return modes; /* 802.11b already included */
+               if (modes[m].mode == HOSTAPD_MODE_IEEE80211G)
+                       mode11g_idx = m;
+       }
+
+       if (mode11g_idx < 0)
+               return modes; /* 2.4 GHz band not supported at all */
+
+       nmodes = os_realloc_array(modes, *num_modes + 1, sizeof(*nmodes));
+       if (nmodes == NULL)
+               return modes; /* Could not add 802.11b mode */
+
+       mode = &nmodes[*num_modes];
+       os_memset(mode, 0, sizeof(*mode));
+       (*num_modes)++;
+       modes = nmodes;
+
+       mode->mode = HOSTAPD_MODE_IEEE80211B;
+
+       mode11g = &modes[mode11g_idx];
+       mode->num_channels = mode11g->num_channels;
+       mode->channels = os_malloc(mode11g->num_channels *
+                                  sizeof(struct hostapd_channel_data));
+       if (mode->channels == NULL) {
+               (*num_modes)--;
+               return modes; /* Could not add 802.11b mode */
+       }
+       os_memcpy(mode->channels, mode11g->channels,
+                 mode11g->num_channels * sizeof(struct hostapd_channel_data));
+
+       mode->num_rates = 0;
+       mode->rates = os_malloc(4 * sizeof(int));
+       if (mode->rates == NULL) {
+               os_free(mode->channels);
+               (*num_modes)--;
+               return modes; /* Could not add 802.11b mode */
+       }
+
+       for (i = 0; i < mode11g->num_rates; i++) {
+               if (mode11g->rates[i] != 10 && mode11g->rates[i] != 20 &&
+                   mode11g->rates[i] != 55 && mode11g->rates[i] != 110)
+                       continue;
+               mode->rates[mode->num_rates] = mode11g->rates[i];
+               mode->num_rates++;
+               if (mode->num_rates == 4)
+                       break;
+       }
+
+       if (mode->num_rates == 0) {
+               os_free(mode->channels);
+               os_free(mode->rates);
+               (*num_modes)--;
+               return modes; /* No 802.11b rates */
+       }
+
+       wpa_printf(MSG_DEBUG, "nl80211: Added 802.11b mode based on 802.11g "
+                  "information");
+
+       return modes;
+}
+
+
+static void nl80211_set_ht40_mode(struct hostapd_hw_modes *mode, int start,
+                                 int end)
+{
+       int c;
+
+       for (c = 0; c < mode->num_channels; c++) {
+               struct hostapd_channel_data *chan = &mode->channels[c];
+               if (chan->freq - 10 >= start && chan->freq + 10 <= end)
+                       chan->flag |= HOSTAPD_CHAN_HT40;
+       }
+}
+
+
+static void nl80211_set_ht40_mode_sec(struct hostapd_hw_modes *mode, int start,
+                                     int end)
+{
+       int c;
+
+       for (c = 0; c < mode->num_channels; c++) {
+               struct hostapd_channel_data *chan = &mode->channels[c];
+               if (!(chan->flag & HOSTAPD_CHAN_HT40))
+                       continue;
+               if (chan->freq - 30 >= start && chan->freq - 10 <= end)
+                       chan->flag |= HOSTAPD_CHAN_HT40MINUS;
+               if (chan->freq + 10 >= start && chan->freq + 30 <= end)
+                       chan->flag |= HOSTAPD_CHAN_HT40PLUS;
+       }
+}
+
+
+static void nl80211_reg_rule_max_eirp(u32 start, u32 end, u32 max_eirp,
+                                     struct phy_info_arg *results)
+{
+       u16 m;
+
+       for (m = 0; m < *results->num_modes; m++) {
+               int c;
+               struct hostapd_hw_modes *mode = &results->modes[m];
+
+               for (c = 0; c < mode->num_channels; c++) {
+                       struct hostapd_channel_data *chan = &mode->channels[c];
+                       if ((u32) chan->freq - 10 >= start &&
+                           (u32) chan->freq + 10 <= end)
+                               chan->max_tx_power = max_eirp;
+               }
+       }
+}
+
+
+static void nl80211_reg_rule_ht40(u32 start, u32 end,
+                                 struct phy_info_arg *results)
+{
+       u16 m;
+
+       for (m = 0; m < *results->num_modes; m++) {
+               if (!(results->modes[m].ht_capab &
+                     HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+                       continue;
+               nl80211_set_ht40_mode(&results->modes[m], start, end);
+       }
+}
+
+
+static void nl80211_reg_rule_sec(struct nlattr *tb[],
+                                struct phy_info_arg *results)
+{
+       u32 start, end, max_bw;
+       u16 m;
+
+       if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
+           tb[NL80211_ATTR_FREQ_RANGE_END] == NULL ||
+           tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL)
+               return;
+
+       start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
+       end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
+       max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
+
+       if (max_bw < 20)
+               return;
+
+       for (m = 0; m < *results->num_modes; m++) {
+               if (!(results->modes[m].ht_capab &
+                     HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+                       continue;
+               nl80211_set_ht40_mode_sec(&results->modes[m], start, end);
+       }
+}
+
+
+static void nl80211_set_vht_mode(struct hostapd_hw_modes *mode, int start,
+                                int end)
+{
+       int c;
+
+       for (c = 0; c < mode->num_channels; c++) {
+               struct hostapd_channel_data *chan = &mode->channels[c];
+               if (chan->freq - 10 >= start && chan->freq + 70 <= end)
+                       chan->flag |= HOSTAPD_CHAN_VHT_10_70;
+
+               if (chan->freq - 30 >= start && chan->freq + 50 <= end)
+                       chan->flag |= HOSTAPD_CHAN_VHT_30_50;
+
+               if (chan->freq - 50 >= start && chan->freq + 30 <= end)
+                       chan->flag |= HOSTAPD_CHAN_VHT_50_30;
+
+               if (chan->freq - 70 >= start && chan->freq + 10 <= end)
+                       chan->flag |= HOSTAPD_CHAN_VHT_70_10;
+       }
+}
+
+
+static void nl80211_reg_rule_vht(struct nlattr *tb[],
+                                struct phy_info_arg *results)
+{
+       u32 start, end, max_bw;
+       u16 m;
+
+       if (tb[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
+           tb[NL80211_ATTR_FREQ_RANGE_END] == NULL ||
+           tb[NL80211_ATTR_FREQ_RANGE_MAX_BW] == NULL)
+               return;
+
+       start = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
+       end = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
+       max_bw = nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
+
+       if (max_bw < 80)
+               return;
+
+       for (m = 0; m < *results->num_modes; m++) {
+               if (!(results->modes[m].ht_capab &
+                     HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET))
+                       continue;
+               /* TODO: use a real VHT support indication */
+               if (!results->modes[m].vht_capab)
+                       continue;
+
+               nl80211_set_vht_mode(&results->modes[m], start, end);
+       }
+}
+
+
+static const char * dfs_domain_name(enum nl80211_dfs_regions region)
+{
+       switch (region) {
+       case NL80211_DFS_UNSET:
+               return "DFS-UNSET";
+       case NL80211_DFS_FCC:
+               return "DFS-FCC";
+       case NL80211_DFS_ETSI:
+               return "DFS-ETSI";
+       case NL80211_DFS_JP:
+               return "DFS-JP";
+       default:
+               return "DFS-invalid";
+       }
+}
+
+
+static int nl80211_get_reg(struct nl_msg *msg, void *arg)
+{
+       struct phy_info_arg *results = arg;
+       struct nlattr *tb_msg[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *nl_rule;
+       struct nlattr *tb_rule[NL80211_FREQUENCY_ATTR_MAX + 1];
+       int rem_rule;
+       static struct nla_policy reg_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
+               [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
+               [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
+               [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
+               [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
+               [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
+               [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
+       };
+
+       nla_parse(tb_msg, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+       if (!tb_msg[NL80211_ATTR_REG_ALPHA2] ||
+           !tb_msg[NL80211_ATTR_REG_RULES]) {
+               wpa_printf(MSG_DEBUG, "nl80211: No regulatory information "
+                          "available");
+               return NL_SKIP;
+       }
+
+       if (tb_msg[NL80211_ATTR_DFS_REGION]) {
+               enum nl80211_dfs_regions dfs_domain;
+               dfs_domain = nla_get_u8(tb_msg[NL80211_ATTR_DFS_REGION]);
+               wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s (%s)",
+                          (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]),
+                          dfs_domain_name(dfs_domain));
+       } else {
+               wpa_printf(MSG_DEBUG, "nl80211: Regulatory information - country=%s",
+                          (char *) nla_data(tb_msg[NL80211_ATTR_REG_ALPHA2]));
+       }
+
+       nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
+       {
+               u32 start, end, max_eirp = 0, max_bw = 0, flags = 0;
+               nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
+                         nla_data(nl_rule), nla_len(nl_rule), reg_policy);
+               if (tb_rule[NL80211_ATTR_FREQ_RANGE_START] == NULL ||
+                   tb_rule[NL80211_ATTR_FREQ_RANGE_END] == NULL)
+                       continue;
+               start = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_START]) / 1000;
+               end = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_END]) / 1000;
+               if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP])
+                       max_eirp = nla_get_u32(tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP]) / 100;
+               if (tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW])
+                       max_bw = nla_get_u32(tb_rule[NL80211_ATTR_FREQ_RANGE_MAX_BW]) / 1000;
+               if (tb_rule[NL80211_ATTR_REG_RULE_FLAGS])
+                       flags = nla_get_u32(tb_rule[NL80211_ATTR_REG_RULE_FLAGS]);
+
+               wpa_printf(MSG_DEBUG, "nl80211: %u-%u @ %u MHz %u mBm%s%s%s%s%s%s%s%s",
+                          start, end, max_bw, max_eirp,
+                          flags & NL80211_RRF_NO_OFDM ? " (no OFDM)" : "",
+                          flags & NL80211_RRF_NO_CCK ? " (no CCK)" : "",
+                          flags & NL80211_RRF_NO_INDOOR ? " (no indoor)" : "",
+                          flags & NL80211_RRF_NO_OUTDOOR ? " (no outdoor)" :
+                          "",
+                          flags & NL80211_RRF_DFS ? " (DFS)" : "",
+                          flags & NL80211_RRF_PTP_ONLY ? " (PTP only)" : "",
+                          flags & NL80211_RRF_PTMP_ONLY ? " (PTMP only)" : "",
+                          flags & NL80211_RRF_NO_IR ? " (no IR)" : "");
+               if (max_bw >= 40)
+                       nl80211_reg_rule_ht40(start, end, results);
+               if (tb_rule[NL80211_ATTR_POWER_RULE_MAX_EIRP])
+                       nl80211_reg_rule_max_eirp(start, end, max_eirp,
+                                                 results);
+       }
+
+       nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
+       {
+               nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
+                         nla_data(nl_rule), nla_len(nl_rule), reg_policy);
+               nl80211_reg_rule_sec(tb_rule, results);
+       }
+
+       nla_for_each_nested(nl_rule, tb_msg[NL80211_ATTR_REG_RULES], rem_rule)
+       {
+               nla_parse(tb_rule, NL80211_FREQUENCY_ATTR_MAX,
+                         nla_data(nl_rule), nla_len(nl_rule), reg_policy);
+               nl80211_reg_rule_vht(tb_rule, results);
+       }
+
+       return NL_SKIP;
+}
+
+
+static int nl80211_set_regulatory_flags(struct wpa_driver_nl80211_data *drv,
+                                       struct phy_info_arg *results)
+{
+       struct nl_msg *msg;
+
+       msg = nlmsg_alloc();
+       if (!msg)
+               return -ENOMEM;
+
+       nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG);
+       return send_and_recv_msgs(drv, msg, nl80211_get_reg, results);
+}
+
+
+struct hostapd_hw_modes *
+nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags)
+{
+       u32 feat;
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int nl_flags = 0;
+       struct nl_msg *msg;
+       struct phy_info_arg result = {
+               .num_modes = num_modes,
+               .modes = NULL,
+               .last_mode = -1,
+       };
+
+       *num_modes = 0;
+       *flags = 0;
+
+       feat = get_nl80211_protocol_features(drv);
+       if (feat & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP)
+               nl_flags = NLM_F_DUMP;
+       if (!(msg = nl80211_cmd_msg(bss, nl_flags, NL80211_CMD_GET_WIPHY)) ||
+           nla_put_flag(msg, NL80211_ATTR_SPLIT_WIPHY_DUMP)) {
+               nlmsg_free(msg);
+               return NULL;
+       }
+
+       if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
+               nl80211_set_regulatory_flags(drv, &result);
+               return wpa_driver_nl80211_postprocess_modes(result.modes,
+                                                           num_modes);
+       }
+
+       return NULL;
+}
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
new file mode 100755 (executable)
index 0000000..c172b5c
--- /dev/null
@@ -0,0 +1,2095 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - Event processing
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <netlink/genl/genl.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/qca-vendor.h"
+#include "common/qca-vendor-attr.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "driver_nl80211.h"
+
+#ifdef BCM_DRIVER_V115
+#define GROUP_INTERFACE_PREFIX "p2p-wlan0"
+#endif
+
+static const char * nl80211_command_to_string(enum nl80211_commands cmd)
+{
+#define C2S(x) case x: return #x;
+       switch (cmd) {
+       C2S(NL80211_CMD_UNSPEC)
+       C2S(NL80211_CMD_GET_WIPHY)
+       C2S(NL80211_CMD_SET_WIPHY)
+       C2S(NL80211_CMD_NEW_WIPHY)
+       C2S(NL80211_CMD_DEL_WIPHY)
+       C2S(NL80211_CMD_GET_INTERFACE)
+       C2S(NL80211_CMD_SET_INTERFACE)
+       C2S(NL80211_CMD_NEW_INTERFACE)
+       C2S(NL80211_CMD_DEL_INTERFACE)
+       C2S(NL80211_CMD_GET_KEY)
+       C2S(NL80211_CMD_SET_KEY)
+       C2S(NL80211_CMD_NEW_KEY)
+       C2S(NL80211_CMD_DEL_KEY)
+       C2S(NL80211_CMD_GET_BEACON)
+       C2S(NL80211_CMD_SET_BEACON)
+       C2S(NL80211_CMD_START_AP)
+       C2S(NL80211_CMD_STOP_AP)
+       C2S(NL80211_CMD_GET_STATION)
+       C2S(NL80211_CMD_SET_STATION)
+       C2S(NL80211_CMD_NEW_STATION)
+       C2S(NL80211_CMD_DEL_STATION)
+       C2S(NL80211_CMD_GET_MPATH)
+       C2S(NL80211_CMD_SET_MPATH)
+       C2S(NL80211_CMD_NEW_MPATH)
+       C2S(NL80211_CMD_DEL_MPATH)
+       C2S(NL80211_CMD_SET_BSS)
+       C2S(NL80211_CMD_SET_REG)
+       C2S(NL80211_CMD_REQ_SET_REG)
+       C2S(NL80211_CMD_GET_MESH_CONFIG)
+       C2S(NL80211_CMD_SET_MESH_CONFIG)
+       C2S(NL80211_CMD_SET_MGMT_EXTRA_IE)
+       C2S(NL80211_CMD_GET_REG)
+       C2S(NL80211_CMD_GET_SCAN)
+       C2S(NL80211_CMD_TRIGGER_SCAN)
+       C2S(NL80211_CMD_NEW_SCAN_RESULTS)
+       C2S(NL80211_CMD_SCAN_ABORTED)
+       C2S(NL80211_CMD_REG_CHANGE)
+       C2S(NL80211_CMD_AUTHENTICATE)
+       C2S(NL80211_CMD_ASSOCIATE)
+       C2S(NL80211_CMD_DEAUTHENTICATE)
+       C2S(NL80211_CMD_DISASSOCIATE)
+       C2S(NL80211_CMD_MICHAEL_MIC_FAILURE)
+       C2S(NL80211_CMD_REG_BEACON_HINT)
+       C2S(NL80211_CMD_JOIN_IBSS)
+       C2S(NL80211_CMD_LEAVE_IBSS)
+       C2S(NL80211_CMD_TESTMODE)
+       C2S(NL80211_CMD_CONNECT)
+       C2S(NL80211_CMD_ROAM)
+       C2S(NL80211_CMD_DISCONNECT)
+       C2S(NL80211_CMD_SET_WIPHY_NETNS)
+       C2S(NL80211_CMD_GET_SURVEY)
+       C2S(NL80211_CMD_NEW_SURVEY_RESULTS)
+       C2S(NL80211_CMD_SET_PMKSA)
+       C2S(NL80211_CMD_DEL_PMKSA)
+       C2S(NL80211_CMD_FLUSH_PMKSA)
+       C2S(NL80211_CMD_REMAIN_ON_CHANNEL)
+       C2S(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL)
+       C2S(NL80211_CMD_SET_TX_BITRATE_MASK)
+       C2S(NL80211_CMD_REGISTER_FRAME)
+       C2S(NL80211_CMD_FRAME)
+       C2S(NL80211_CMD_FRAME_TX_STATUS)
+       C2S(NL80211_CMD_SET_POWER_SAVE)
+       C2S(NL80211_CMD_GET_POWER_SAVE)
+       C2S(NL80211_CMD_SET_CQM)
+       C2S(NL80211_CMD_NOTIFY_CQM)
+       C2S(NL80211_CMD_SET_CHANNEL)
+       C2S(NL80211_CMD_SET_WDS_PEER)
+       C2S(NL80211_CMD_FRAME_WAIT_CANCEL)
+       C2S(NL80211_CMD_JOIN_MESH)
+       C2S(NL80211_CMD_LEAVE_MESH)
+       C2S(NL80211_CMD_UNPROT_DEAUTHENTICATE)
+       C2S(NL80211_CMD_UNPROT_DISASSOCIATE)
+       C2S(NL80211_CMD_NEW_PEER_CANDIDATE)
+       C2S(NL80211_CMD_GET_WOWLAN)
+       C2S(NL80211_CMD_SET_WOWLAN)
+       C2S(NL80211_CMD_START_SCHED_SCAN)
+       C2S(NL80211_CMD_STOP_SCHED_SCAN)
+       C2S(NL80211_CMD_SCHED_SCAN_RESULTS)
+       C2S(NL80211_CMD_SCHED_SCAN_STOPPED)
+       C2S(NL80211_CMD_SET_REKEY_OFFLOAD)
+       C2S(NL80211_CMD_PMKSA_CANDIDATE)
+       C2S(NL80211_CMD_TDLS_OPER)
+       C2S(NL80211_CMD_TDLS_MGMT)
+       C2S(NL80211_CMD_UNEXPECTED_FRAME)
+       C2S(NL80211_CMD_PROBE_CLIENT)
+       C2S(NL80211_CMD_REGISTER_BEACONS)
+       C2S(NL80211_CMD_UNEXPECTED_4ADDR_FRAME)
+       C2S(NL80211_CMD_SET_NOACK_MAP)
+       C2S(NL80211_CMD_CH_SWITCH_NOTIFY)
+       C2S(NL80211_CMD_START_P2P_DEVICE)
+       C2S(NL80211_CMD_STOP_P2P_DEVICE)
+       C2S(NL80211_CMD_CONN_FAILED)
+       C2S(NL80211_CMD_SET_MCAST_RATE)
+       C2S(NL80211_CMD_SET_MAC_ACL)
+       C2S(NL80211_CMD_RADAR_DETECT)
+       C2S(NL80211_CMD_GET_PROTOCOL_FEATURES)
+       C2S(NL80211_CMD_UPDATE_FT_IES)
+       C2S(NL80211_CMD_FT_EVENT)
+       C2S(NL80211_CMD_CRIT_PROTOCOL_START)
+       C2S(NL80211_CMD_CRIT_PROTOCOL_STOP)
+       C2S(NL80211_CMD_GET_COALESCE)
+       C2S(NL80211_CMD_SET_COALESCE)
+       C2S(NL80211_CMD_CHANNEL_SWITCH)
+       C2S(NL80211_CMD_VENDOR)
+       C2S(NL80211_CMD_SET_QOS_MAP)
+       C2S(NL80211_CMD_ADD_TX_TS)
+       C2S(NL80211_CMD_DEL_TX_TS)
+       default:
+               return "NL80211_CMD_UNKNOWN";
+       }
+#undef C2S
+}
+
+
+static void mlme_event_auth(struct wpa_driver_nl80211_data *drv,
+                           const u8 *frame, size_t len)
+{
+       const struct ieee80211_mgmt *mgmt;
+       union wpa_event_data event;
+
+       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+           drv->force_connect_cmd) {
+               /*
+                * Avoid reporting two association events that would confuse
+                * the core code.
+                */
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Ignore auth event when using driver SME");
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "nl80211: Authenticate event");
+       mgmt = (const struct ieee80211_mgmt *) frame;
+       if (len < 24 + sizeof(mgmt->u.auth)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
+                          "frame");
+               return;
+       }
+
+       os_memcpy(drv->auth_bssid, mgmt->sa, ETH_ALEN);
+       os_memset(drv->auth_attempt_bssid, 0, ETH_ALEN);
+       os_memset(&event, 0, sizeof(event));
+       os_memcpy(event.auth.peer, mgmt->sa, ETH_ALEN);
+       event.auth.auth_type = le_to_host16(mgmt->u.auth.auth_alg);
+       event.auth.auth_transaction =
+               le_to_host16(mgmt->u.auth.auth_transaction);
+       event.auth.status_code = le_to_host16(mgmt->u.auth.status_code);
+       if (len > 24 + sizeof(mgmt->u.auth)) {
+               event.auth.ies = mgmt->u.auth.variable;
+               event.auth.ies_len = len - 24 - sizeof(mgmt->u.auth);
+       }
+
+       wpa_supplicant_event(drv->ctx, EVENT_AUTH, &event);
+}
+
+
+static void nl80211_parse_wmm_params(struct nlattr *wmm_attr,
+                                    struct wmm_params *wmm_params)
+{
+       struct nlattr *wmm_info[NL80211_STA_WME_MAX + 1];
+       static struct nla_policy wme_policy[NL80211_STA_WME_MAX + 1] = {
+               [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
+       };
+
+       if (!wmm_attr ||
+           nla_parse_nested(wmm_info, NL80211_STA_WME_MAX, wmm_attr,
+                            wme_policy) ||
+           !wmm_info[NL80211_STA_WME_UAPSD_QUEUES])
+               return;
+
+       wmm_params->uapsd_queues =
+               nla_get_u8(wmm_info[NL80211_STA_WME_UAPSD_QUEUES]);
+       wmm_params->info_bitmap |= WMM_PARAMS_UAPSD_QUEUES_INFO;
+}
+
+
+static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
+                           const u8 *frame, size_t len, struct nlattr *wmm)
+{
+       const struct ieee80211_mgmt *mgmt;
+       union wpa_event_data event;
+       u16 status;
+
+       if (!(drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+           drv->force_connect_cmd) {
+               /*
+                * Avoid reporting two association events that would confuse
+                * the core code.
+                */
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Ignore assoc event when using driver SME");
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "nl80211: Associate event");
+       mgmt = (const struct ieee80211_mgmt *) frame;
+#ifdef BCM_DRIVER_V115
+       if (drv->nlmode == NL80211_IFTYPE_AP || drv->nlmode == NL80211_IFTYPE_P2P_GO) {
+               if (len < 24 + sizeof(mgmt->u.assoc_req)) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
+                          "frame");
+                       return;
+               }
+               os_memset(&event, 0, sizeof(event));
+               event.assoc_info.freq = drv->assoc_freq;
+               event.assoc_info.req_ies = (u8 *) mgmt->u.assoc_req.variable;
+               event.assoc_info.req_ies_len = len - 24 - sizeof(mgmt->u.assoc_req);
+               event.assoc_info.addr = mgmt->sa;
+       } else {
+#endif
+       if (len < 24 + sizeof(mgmt->u.assoc_resp)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Too short association event "
+                          "frame");
+               return;
+       }
+
+       status = le_to_host16(mgmt->u.assoc_resp.status_code);
+       if (status != WLAN_STATUS_SUCCESS) {
+               os_memset(&event, 0, sizeof(event));
+               event.assoc_reject.bssid = mgmt->bssid;
+               if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
+                       event.assoc_reject.resp_ies =
+                               (u8 *) mgmt->u.assoc_resp.variable;
+                       event.assoc_reject.resp_ies_len =
+                               len - 24 - sizeof(mgmt->u.assoc_resp);
+               }
+               event.assoc_reject.status_code = status;
+
+               wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
+               return;
+       }
+
+       drv->associated = 1;
+       os_memcpy(drv->bssid, mgmt->sa, ETH_ALEN);
+       os_memcpy(drv->prev_bssid, mgmt->sa, ETH_ALEN);
+
+       os_memset(&event, 0, sizeof(event));
+       if (len > 24 + sizeof(mgmt->u.assoc_resp)) {
+               event.assoc_info.resp_ies = (u8 *) mgmt->u.assoc_resp.variable;
+               event.assoc_info.resp_ies_len =
+                       len - 24 - sizeof(mgmt->u.assoc_resp);
+       }
+
+       event.assoc_info.freq = drv->assoc_freq;
+#ifdef BCM_DRIVER_V115
+       }
+#endif
+       nl80211_parse_wmm_params(wmm, &event.assoc_info.wmm_params);
+
+       wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+}
+
+
+static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
+                              enum nl80211_commands cmd, struct nlattr *status,
+                              struct nlattr *addr, struct nlattr *req_ie,
+                              struct nlattr *resp_ie,
+                              struct nlattr *authorized,
+                              struct nlattr *key_replay_ctr,
+                              struct nlattr *ptk_kck,
+                              struct nlattr *ptk_kek)
+{
+       union wpa_event_data event;
+       u16 status_code;
+
+       if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+               /*
+                * Avoid reporting two association events that would confuse
+                * the core code.
+                */
+               wpa_printf(MSG_DEBUG, "nl80211: Ignore connect event (cmd=%d) "
+                          "when using userspace SME", cmd);
+               return;
+       }
+
+       status_code = status ? nla_get_u16(status) : WLAN_STATUS_SUCCESS;
+
+       if (cmd == NL80211_CMD_CONNECT) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Connect event (status=%u ignore_next_local_disconnect=%d)",
+                          status_code, drv->ignore_next_local_disconnect);
+       } else if (cmd == NL80211_CMD_ROAM) {
+               wpa_printf(MSG_DEBUG, "nl80211: Roam event");
+       }
+
+       os_memset(&event, 0, sizeof(event));
+       if (cmd == NL80211_CMD_CONNECT && status_code != WLAN_STATUS_SUCCESS) {
+               if (addr)
+                       event.assoc_reject.bssid = nla_data(addr);
+               if (drv->ignore_next_local_disconnect) {
+                       drv->ignore_next_local_disconnect = 0;
+                       if (!event.assoc_reject.bssid ||
+                           (os_memcmp(event.assoc_reject.bssid,
+                                      drv->auth_attempt_bssid,
+                                      ETH_ALEN) != 0)) {
+                               /*
+                                * Ignore the event that came without a BSSID or
+                                * for the old connection since this is likely
+                                * not relevant to the new Connect command.
+                                */
+                               wpa_printf(MSG_DEBUG,
+                                          "nl80211: Ignore connection failure event triggered during reassociation");
+                               return;
+                       }
+               }
+               if (resp_ie) {
+                       event.assoc_reject.resp_ies = nla_data(resp_ie);
+                       event.assoc_reject.resp_ies_len = nla_len(resp_ie);
+               }
+               event.assoc_reject.status_code = status_code;
+               wpa_supplicant_event(drv->ctx, EVENT_ASSOC_REJECT, &event);
+               return;
+       }
+
+       drv->associated = 1;
+       if (addr) {
+               os_memcpy(drv->bssid, nla_data(addr), ETH_ALEN);
+               os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
+       }
+
+       if (req_ie) {
+               event.assoc_info.req_ies = nla_data(req_ie);
+               event.assoc_info.req_ies_len = nla_len(req_ie);
+       }
+       if (resp_ie) {
+               event.assoc_info.resp_ies = nla_data(resp_ie);
+               event.assoc_info.resp_ies_len = nla_len(resp_ie);
+       }
+
+       event.assoc_info.freq = nl80211_get_assoc_freq(drv);
+
+       if (authorized && nla_get_u8(authorized)) {
+               event.assoc_info.authorized = 1;
+               wpa_printf(MSG_DEBUG, "nl80211: connection authorized");
+       }
+       if (key_replay_ctr) {
+               event.assoc_info.key_replay_ctr = nla_data(key_replay_ctr);
+               event.assoc_info.key_replay_ctr_len = nla_len(key_replay_ctr);
+       }
+       if (ptk_kck) {
+               event.assoc_info.ptk_kck = nla_data(ptk_kck);
+               event.assoc_info.ptk_kck_len = nla_len(ptk_kck);
+       }
+       if (ptk_kek) {
+               event.assoc_info.ptk_kek = nla_data(ptk_kek);
+               event.assoc_info.ptk_kek_len = nla_len(ptk_kek);
+       }
+
+       wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
+}
+
+
+static void mlme_event_disconnect(struct wpa_driver_nl80211_data *drv,
+                                 struct nlattr *reason, struct nlattr *addr,
+                                 struct nlattr *by_ap)
+{
+       union wpa_event_data data;
+       unsigned int locally_generated = by_ap == NULL;
+
+       if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+               /*
+                * Avoid reporting two disassociation events that could
+                * confuse the core code.
+                */
+               wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
+                          "event when using userspace SME");
+               return;
+       }
+
+       if (drv->ignore_next_local_disconnect) {
+               drv->ignore_next_local_disconnect = 0;
+               if (locally_generated) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Ignore disconnect "
+                                  "event triggered during reassociation");
+                       return;
+               }
+               wpa_printf(MSG_WARNING, "nl80211: Was expecting local "
+                          "disconnect but got another disconnect "
+                          "event first");
+       }
+
+       wpa_printf(MSG_DEBUG, "nl80211: Disconnect event");
+       nl80211_mark_disconnected(drv);
+       os_memset(&data, 0, sizeof(data));
+       if (reason)
+               data.deauth_info.reason_code = nla_get_u16(reason);
+       data.deauth_info.locally_generated = by_ap == NULL;
+       wpa_supplicant_event(drv->ctx, EVENT_DEAUTH, &data);
+}
+
+
+static int calculate_chan_offset(int width, int freq, int cf1, int cf2)
+{
+       int freq1 = 0;
+
+       switch (convert2width(width)) {
+       case CHAN_WIDTH_20_NOHT:
+       case CHAN_WIDTH_20:
+               return 0;
+       case CHAN_WIDTH_40:
+               freq1 = cf1 - 10;
+               break;
+       case CHAN_WIDTH_80:
+               freq1 = cf1 - 30;
+               break;
+       case CHAN_WIDTH_160:
+               freq1 = cf1 - 70;
+               break;
+       case CHAN_WIDTH_UNKNOWN:
+       case CHAN_WIDTH_80P80:
+               /* FIXME: implement this */
+               return 0;
+       }
+
+       return (abs(freq - freq1) / 20) % 2 == 0 ? 1 : -1;
+}
+
+
+static void mlme_event_ch_switch(struct wpa_driver_nl80211_data *drv,
+                                struct nlattr *ifindex, struct nlattr *freq,
+                                struct nlattr *type, struct nlattr *bw,
+                                struct nlattr *cf1, struct nlattr *cf2)
+{
+       struct i802_bss *bss;
+       union wpa_event_data data;
+       int ht_enabled = 1;
+       int chan_offset = 0;
+       int ifidx;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Channel switch event");
+
+       if (!freq)
+               return;
+
+       ifidx = nla_get_u32(ifindex);
+       bss = get_bss_ifindex(drv, ifidx);
+       if (bss == NULL) {
+               wpa_printf(MSG_WARNING, "nl80211: Unknown ifindex (%d) for channel switch, ignoring",
+                          ifidx);
+               return;
+       }
+
+       if (type) {
+               enum nl80211_channel_type ch_type = nla_get_u32(type);
+
+               wpa_printf(MSG_DEBUG, "nl80211: Channel type: %d", ch_type);
+               switch (ch_type) {
+               case NL80211_CHAN_NO_HT:
+                       ht_enabled = 0;
+                       break;
+               case NL80211_CHAN_HT20:
+                       break;
+               case NL80211_CHAN_HT40PLUS:
+                       chan_offset = 1;
+                       break;
+               case NL80211_CHAN_HT40MINUS:
+                       chan_offset = -1;
+                       break;
+               }
+       } else if (bw && cf1) {
+               /* This can happen for example with VHT80 ch switch */
+               chan_offset = calculate_chan_offset(nla_get_u32(bw),
+                                                   nla_get_u32(freq),
+                                                   nla_get_u32(cf1),
+                                                   cf2 ? nla_get_u32(cf2) : 0);
+       } else {
+               wpa_printf(MSG_WARNING, "nl80211: Unknown secondary channel information - following channel definition calculations may fail");
+       }
+
+       os_memset(&data, 0, sizeof(data));
+       data.ch_switch.freq = nla_get_u32(freq);
+       data.ch_switch.ht_enabled = ht_enabled;
+       data.ch_switch.ch_offset = chan_offset;
+       if (bw)
+               data.ch_switch.ch_width = convert2width(nla_get_u32(bw));
+       if (cf1)
+               data.ch_switch.cf1 = nla_get_u32(cf1);
+       if (cf2)
+               data.ch_switch.cf2 = nla_get_u32(cf2);
+
+       bss->freq = data.ch_switch.freq;
+
+       wpa_supplicant_event(bss->ctx, EVENT_CH_SWITCH, &data);
+}
+
+
+static void mlme_timeout_event(struct wpa_driver_nl80211_data *drv,
+                              enum nl80211_commands cmd, struct nlattr *addr)
+{
+       union wpa_event_data event;
+       enum wpa_event_type ev;
+
+       if (nla_len(addr) != ETH_ALEN)
+               return;
+
+       wpa_printf(MSG_DEBUG, "nl80211: MLME event %d; timeout with " MACSTR,
+                  cmd, MAC2STR((u8 *) nla_data(addr)));
+
+       if (cmd == NL80211_CMD_AUTHENTICATE)
+               ev = EVENT_AUTH_TIMED_OUT;
+       else if (cmd == NL80211_CMD_ASSOCIATE)
+               ev = EVENT_ASSOC_TIMED_OUT;
+       else
+               return;
+
+       os_memset(&event, 0, sizeof(event));
+       os_memcpy(event.timeout_event.addr, nla_data(addr), ETH_ALEN);
+       wpa_supplicant_event(drv->ctx, ev, &event);
+}
+
+#ifdef BCM_DRIVER_V115
+static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
+                                      enum wpa_event_type type,
+                                      const u8 *frame, size_t len);
+#endif
+
+static void mlme_event_mgmt(struct i802_bss *bss,
+                           struct nlattr *freq, struct nlattr *sig,
+                           const u8 *frame, size_t len)
+{
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       const struct ieee80211_mgmt *mgmt;
+       union wpa_event_data event;
+       u16 fc, stype;
+       int ssi_signal = 0;
+       int rx_freq = 0;
+
+       wpa_printf(MSG_MSGDUMP, "nl80211: Frame event");
+       mgmt = (const struct ieee80211_mgmt *) frame;
+       if (len < 24) {
+               wpa_printf(MSG_DEBUG, "nl80211: Too short management frame");
+               return;
+       }
+
+       fc = le_to_host16(mgmt->frame_control);
+       stype = WLAN_FC_GET_STYPE(fc);
+
+       if (sig)
+               ssi_signal = (s32) nla_get_u32(sig);
+
+       os_memset(&event, 0, sizeof(event));
+       if (freq) {
+               event.rx_mgmt.freq = nla_get_u32(freq);
+               rx_freq = drv->last_mgmt_freq = event.rx_mgmt.freq;
+       }
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: RX frame sa=" MACSTR
+                  " freq=%d ssi_signal=%d fc=0x%x seq_ctrl=0x%x stype=%u (%s) len=%u",
+                  MAC2STR(mgmt->sa), rx_freq, ssi_signal, fc,
+                  le_to_host16(mgmt->seq_ctrl), stype, fc2str(fc),
+                  (unsigned int) len);
+       event.rx_mgmt.frame = frame;
+       event.rx_mgmt.frame_len = len;
+       event.rx_mgmt.ssi_signal = ssi_signal;
+       event.rx_mgmt.drv_priv = bss;
+#ifdef BCM_DRIVER_V115
+       if (stype == WLAN_FC_STYPE_ASSOC_REQ) {
+               mlme_event_assoc(drv, frame, len, NULL);
+       } else if (stype == WLAN_FC_STYPE_DISASSOC) {
+               mlme_event_deauth_disassoc(drv, EVENT_DISASSOC, frame, len);
+       } else if (stype == WLAN_FC_STYPE_DEAUTH) {
+               mlme_event_deauth_disassoc(drv, EVENT_DEAUTH, frame, len);
+       } else 
+#endif
+       wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+}
+
+
+static void mlme_event_mgmt_tx_status(struct wpa_driver_nl80211_data *drv,
+                                     struct nlattr *cookie, const u8 *frame,
+                                     size_t len, struct nlattr *ack)
+{
+       union wpa_event_data event;
+       const struct ieee80211_hdr *hdr;
+       u16 fc;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Frame TX status event");
+       if (!is_ap_interface(drv->nlmode)) {
+               u64 cookie_val;
+
+               if (!cookie)
+                       return;
+
+               cookie_val = nla_get_u64(cookie);
+               wpa_printf(MSG_DEBUG, "nl80211: Action TX status:"
+                          " cookie=0%llx%s (ack=%d)",
+                          (long long unsigned int) cookie_val,
+                          cookie_val == drv->send_action_cookie ?
+                          " (match)" : " (unknown)", ack != NULL);
+               if (cookie_val != drv->send_action_cookie)
+                       return;
+       }
+
+       hdr = (const struct ieee80211_hdr *) frame;
+       fc = le_to_host16(hdr->frame_control);
+
+       os_memset(&event, 0, sizeof(event));
+       event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+       event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+       event.tx_status.dst = hdr->addr1;
+       event.tx_status.data = frame;
+       event.tx_status.data_len = len;
+       event.tx_status.ack = ack != NULL;
+       wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
+}
+
+
+static void mlme_event_deauth_disassoc(struct wpa_driver_nl80211_data *drv,
+                                      enum wpa_event_type type,
+                                      const u8 *frame, size_t len)
+{
+       const struct ieee80211_mgmt *mgmt;
+       union wpa_event_data event;
+       const u8 *bssid = NULL;
+       u16 reason_code = 0;
+
+       if (type == EVENT_DEAUTH)
+               wpa_printf(MSG_DEBUG, "nl80211: Deauthenticate event");
+       else
+               wpa_printf(MSG_DEBUG, "nl80211: Disassociate event");
+
+       mgmt = (const struct ieee80211_mgmt *) frame;
+       if (len >= 24) {
+               bssid = mgmt->bssid;
+
+               if ((drv->capa.flags & WPA_DRIVER_FLAGS_SME) &&
+                   !drv->associated &&
+                   os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0 &&
+                   os_memcmp(bssid, drv->auth_attempt_bssid, ETH_ALEN) != 0 &&
+                   os_memcmp(bssid, drv->prev_bssid, ETH_ALEN) == 0) {
+                       /*
+                        * Avoid issues with some roaming cases where
+                        * disconnection event for the old AP may show up after
+                        * we have started connection with the new AP.
+                        */
+                       wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth/disassoc event from old AP " MACSTR " when already authenticating with " MACSTR,
+                                  MAC2STR(bssid),
+                                  MAC2STR(drv->auth_attempt_bssid));
+                       return;
+               }
+
+               if (drv->associated != 0 &&
+                   os_memcmp(bssid, drv->bssid, ETH_ALEN) != 0 &&
+                   os_memcmp(bssid, drv->auth_bssid, ETH_ALEN) != 0) {
+                       /*
+                        * We have presumably received this deauth as a
+                        * response to a clear_state_mismatch() outgoing
+                        * deauth.  Don't let it take us offline!
+                        */
+                       wpa_printf(MSG_DEBUG, "nl80211: Deauth received "
+                                  "from Unknown BSSID " MACSTR " -- ignoring",
+                                  MAC2STR(bssid));
+                       return;
+               }
+       }
+
+       nl80211_mark_disconnected(drv);
+       os_memset(&event, 0, sizeof(event));
+
+       /* Note: Same offset for Reason Code in both frame subtypes */
+       if (len >= 24 + sizeof(mgmt->u.deauth))
+               reason_code = le_to_host16(mgmt->u.deauth.reason_code);
+
+       if (type == EVENT_DISASSOC) {
+               event.disassoc_info.locally_generated =
+                       !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
+#ifdef BCM_DRIVER_V115
+               if (drv->nlmode == NL80211_IFTYPE_AP ||
+                       drv->nlmode == NL80211_IFTYPE_P2P_GO) {
+                       event.disassoc_info.addr = mgmt->sa;
+               } else
+#endif /* BCM_DRIVER_V115 */
+               event.disassoc_info.addr = bssid;
+               event.disassoc_info.reason_code = reason_code;
+               if (frame + len > mgmt->u.disassoc.variable) {
+                       event.disassoc_info.ie = mgmt->u.disassoc.variable;
+                       event.disassoc_info.ie_len = frame + len -
+                               mgmt->u.disassoc.variable;
+               }
+       } else {
+               if (drv->ignore_deauth_event) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event due to previous forced deauth-during-auth");
+                       drv->ignore_deauth_event = 0;
+                       return;
+               }
+               event.deauth_info.locally_generated =
+                       !os_memcmp(mgmt->sa, drv->first_bss->addr, ETH_ALEN);
+               if (drv->ignore_next_local_deauth) {
+                       drv->ignore_next_local_deauth = 0;
+                       if (event.deauth_info.locally_generated) {
+                               wpa_printf(MSG_DEBUG, "nl80211: Ignore deauth event triggered due to own deauth request");
+                               return;
+                       }
+                       wpa_printf(MSG_WARNING, "nl80211: Was expecting local deauth but got another disconnect event first");
+               }
+#ifdef BCM_DRIVER_V115
+               if (drv->nlmode == NL80211_IFTYPE_AP ||
+                       drv->nlmode == NL80211_IFTYPE_P2P_GO) {
+               event.deauth_info.addr = mgmt->sa;
+               } else
+#endif /* BCM_DRIVER_V115 */
+               event.deauth_info.addr = bssid;
+               event.deauth_info.reason_code = reason_code;
+               if (frame + len > mgmt->u.deauth.variable) {
+                       event.deauth_info.ie = mgmt->u.deauth.variable;
+                       event.deauth_info.ie_len = frame + len -
+                               mgmt->u.deauth.variable;
+               }
+       }
+
+       wpa_supplicant_event(drv->ctx, type, &event);
+}
+
+
+static void mlme_event_unprot_disconnect(struct wpa_driver_nl80211_data *drv,
+                                        enum wpa_event_type type,
+                                        const u8 *frame, size_t len)
+{
+       const struct ieee80211_mgmt *mgmt;
+       union wpa_event_data event;
+       u16 reason_code = 0;
+
+       if (type == EVENT_UNPROT_DEAUTH)
+               wpa_printf(MSG_DEBUG, "nl80211: Unprot Deauthenticate event");
+       else
+               wpa_printf(MSG_DEBUG, "nl80211: Unprot Disassociate event");
+
+       if (len < 24)
+               return;
+
+       mgmt = (const struct ieee80211_mgmt *) frame;
+
+       os_memset(&event, 0, sizeof(event));
+       /* Note: Same offset for Reason Code in both frame subtypes */
+       if (len >= 24 + sizeof(mgmt->u.deauth))
+               reason_code = le_to_host16(mgmt->u.deauth.reason_code);
+
+       if (type == EVENT_UNPROT_DISASSOC) {
+               event.unprot_disassoc.sa = mgmt->sa;
+               event.unprot_disassoc.da = mgmt->da;
+               event.unprot_disassoc.reason_code = reason_code;
+       } else {
+               event.unprot_deauth.sa = mgmt->sa;
+               event.unprot_deauth.da = mgmt->da;
+               event.unprot_deauth.reason_code = reason_code;
+       }
+
+       wpa_supplicant_event(drv->ctx, type, &event);
+}
+
+
+static void mlme_event(struct i802_bss *bss,
+                      enum nl80211_commands cmd, struct nlattr *frame,
+                      struct nlattr *addr, struct nlattr *timed_out,
+                      struct nlattr *freq, struct nlattr *ack,
+                      struct nlattr *cookie, struct nlattr *sig,
+                      struct nlattr *wmm)
+{
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       const u8 *data;
+       size_t len;
+
+       if (timed_out && addr) {
+               mlme_timeout_event(drv, cmd, addr);
+               return;
+       }
+
+       if (frame == NULL) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: MLME event %d (%s) without frame data",
+                          cmd, nl80211_command_to_string(cmd));
+               return;
+       }
+
+       data = nla_data(frame);
+       len = nla_len(frame);
+       if (len < 4 + 2 * ETH_ALEN) {
+               wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s("
+                          MACSTR ") - too short",
+                          cmd, nl80211_command_to_string(cmd), bss->ifname,
+                          MAC2STR(bss->addr));
+               return;
+       }
+       wpa_printf(MSG_MSGDUMP, "nl80211: MLME event %d (%s) on %s(" MACSTR
+                  ") A1=" MACSTR " A2=" MACSTR, cmd,
+                  nl80211_command_to_string(cmd), bss->ifname,
+                  MAC2STR(bss->addr), MAC2STR(data + 4),
+                  MAC2STR(data + 4 + ETH_ALEN));
+#ifdef BCM_DRIVER_V115
+       wpa_printf(MSG_MSGDUMP, "nl80211: MLME event A1=" MACSTR, MAC2STR(bss->dev_addr));
+       if (os_strstr(bss->ifname, GROUP_INTERFACE_PREFIX )== NULL){
+               if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) &&
+                       os_memcmp(bss->dev_addr, data + 4, ETH_ALEN) != 0 &&
+                       os_memcmp(bss->dev_addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) {
+                       wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event on wlan0"
+                                  "for foreign address", bss->ifname);
+                       return;
+               }
+       }else{
+               if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) &&
+                       os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 &&
+                       os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) {
+                       wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event on p2p"
+                                  "for foreign address", bss->ifname);
+                       return;
+               }
+       }
+#else /* BCM_DRIVER_V115 */
+       if (cmd != NL80211_CMD_FRAME_TX_STATUS && !(data[4] & 0x01) &&
+           os_memcmp(bss->addr, data + 4, ETH_ALEN) != 0 &&
+           os_memcmp(bss->addr, data + 4 + ETH_ALEN, ETH_ALEN) != 0) {
+               wpa_printf(MSG_MSGDUMP, "nl80211: %s: Ignore MLME frame event "
+                          "for foreign address", bss->ifname);
+               return;
+       }
+#endif /* BCM_DRIVER_V115 */
+       wpa_hexdump(MSG_MSGDUMP, "nl80211: MLME event frame",
+                   nla_data(frame), nla_len(frame));
+
+       switch (cmd) {
+       case NL80211_CMD_AUTHENTICATE:
+               mlme_event_auth(drv, nla_data(frame), nla_len(frame));
+               break;
+       case NL80211_CMD_ASSOCIATE:
+               mlme_event_assoc(drv, nla_data(frame), nla_len(frame), wmm);
+               break;
+       case NL80211_CMD_DEAUTHENTICATE:
+               mlme_event_deauth_disassoc(drv, EVENT_DEAUTH,
+                                          nla_data(frame), nla_len(frame));
+               break;
+       case NL80211_CMD_DISASSOCIATE:
+               mlme_event_deauth_disassoc(drv, EVENT_DISASSOC,
+                                          nla_data(frame), nla_len(frame));
+               break;
+       case NL80211_CMD_FRAME:
+               mlme_event_mgmt(bss, freq, sig, nla_data(frame),
+                               nla_len(frame));
+               break;
+       case NL80211_CMD_FRAME_TX_STATUS:
+               mlme_event_mgmt_tx_status(drv, cookie, nla_data(frame),
+                                         nla_len(frame), ack);
+               break;
+       case NL80211_CMD_UNPROT_DEAUTHENTICATE:
+               mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DEAUTH,
+                                            nla_data(frame), nla_len(frame));
+               break;
+       case NL80211_CMD_UNPROT_DISASSOCIATE:
+               mlme_event_unprot_disconnect(drv, EVENT_UNPROT_DISASSOC,
+                                            nla_data(frame), nla_len(frame));
+               break;
+       default:
+               break;
+       }
+}
+
+
+static void mlme_event_michael_mic_failure(struct i802_bss *bss,
+                                          struct nlattr *tb[])
+{
+       union wpa_event_data data;
+
+       wpa_printf(MSG_DEBUG, "nl80211: MLME event Michael MIC failure");
+       os_memset(&data, 0, sizeof(data));
+       if (tb[NL80211_ATTR_MAC]) {
+               wpa_hexdump(MSG_DEBUG, "nl80211: Source MAC address",
+                           nla_data(tb[NL80211_ATTR_MAC]),
+                           nla_len(tb[NL80211_ATTR_MAC]));
+               data.michael_mic_failure.src = nla_data(tb[NL80211_ATTR_MAC]);
+       }
+       if (tb[NL80211_ATTR_KEY_SEQ]) {
+               wpa_hexdump(MSG_DEBUG, "nl80211: TSC",
+                           nla_data(tb[NL80211_ATTR_KEY_SEQ]),
+                           nla_len(tb[NL80211_ATTR_KEY_SEQ]));
+       }
+       if (tb[NL80211_ATTR_KEY_TYPE]) {
+               enum nl80211_key_type key_type =
+                       nla_get_u32(tb[NL80211_ATTR_KEY_TYPE]);
+               wpa_printf(MSG_DEBUG, "nl80211: Key Type %d", key_type);
+               if (key_type == NL80211_KEYTYPE_PAIRWISE)
+                       data.michael_mic_failure.unicast = 1;
+       } else
+               data.michael_mic_failure.unicast = 1;
+
+       if (tb[NL80211_ATTR_KEY_IDX]) {
+               u8 key_id = nla_get_u8(tb[NL80211_ATTR_KEY_IDX]);
+               wpa_printf(MSG_DEBUG, "nl80211: Key Id %d", key_id);
+       }
+
+       wpa_supplicant_event(bss->ctx, EVENT_MICHAEL_MIC_FAILURE, &data);
+}
+
+
+static void mlme_event_join_ibss(struct wpa_driver_nl80211_data *drv,
+                                struct nlattr *tb[])
+{
+       unsigned int freq;
+
+       if (tb[NL80211_ATTR_MAC] == NULL) {
+               wpa_printf(MSG_DEBUG, "nl80211: No address in IBSS joined "
+                          "event");
+               return;
+       }
+       os_memcpy(drv->bssid, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+
+       drv->associated = 1;
+       wpa_printf(MSG_DEBUG, "nl80211: IBSS " MACSTR " joined",
+                  MAC2STR(drv->bssid));
+
+       freq = nl80211_get_assoc_freq(drv);
+       if (freq) {
+               wpa_printf(MSG_DEBUG, "nl80211: IBSS on frequency %u MHz",
+                          freq);
+               drv->first_bss->freq = freq;
+       }
+
+       wpa_supplicant_event(drv->ctx, EVENT_ASSOC, NULL);
+}
+
+
+static void mlme_event_remain_on_channel(struct wpa_driver_nl80211_data *drv,
+                                        int cancel_event, struct nlattr *tb[])
+{
+       unsigned int freq, chan_type, duration;
+       union wpa_event_data data;
+       u64 cookie;
+
+       if (tb[NL80211_ATTR_WIPHY_FREQ])
+               freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+       else
+               freq = 0;
+
+       if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
+               chan_type = nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+       else
+               chan_type = 0;
+
+       if (tb[NL80211_ATTR_DURATION])
+               duration = nla_get_u32(tb[NL80211_ATTR_DURATION]);
+       else
+               duration = 0;
+
+       if (tb[NL80211_ATTR_COOKIE])
+               cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+       else
+               cookie = 0;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Remain-on-channel event (cancel=%d "
+                  "freq=%u channel_type=%u duration=%u cookie=0x%llx (%s))",
+                  cancel_event, freq, chan_type, duration,
+                  (long long unsigned int) cookie,
+                  cookie == drv->remain_on_chan_cookie ? "match" : "unknown");
+
+       if (cookie != drv->remain_on_chan_cookie)
+               return; /* not for us */
+
+       if (cancel_event)
+               drv->pending_remain_on_chan = 0;
+
+       os_memset(&data, 0, sizeof(data));
+       data.remain_on_channel.freq = freq;
+       data.remain_on_channel.duration = duration;
+       wpa_supplicant_event(drv->ctx, cancel_event ?
+                            EVENT_CANCEL_REMAIN_ON_CHANNEL :
+                            EVENT_REMAIN_ON_CHANNEL, &data);
+}
+
+
+static void mlme_event_ft_event(struct wpa_driver_nl80211_data *drv,
+                               struct nlattr *tb[])
+{
+       union wpa_event_data data;
+
+       os_memset(&data, 0, sizeof(data));
+
+       if (tb[NL80211_ATTR_IE]) {
+               data.ft_ies.ies = nla_data(tb[NL80211_ATTR_IE]);
+               data.ft_ies.ies_len = nla_len(tb[NL80211_ATTR_IE]);
+       }
+
+       if (tb[NL80211_ATTR_IE_RIC]) {
+               data.ft_ies.ric_ies = nla_data(tb[NL80211_ATTR_IE_RIC]);
+               data.ft_ies.ric_ies_len = nla_len(tb[NL80211_ATTR_IE_RIC]);
+       }
+
+       if (tb[NL80211_ATTR_MAC])
+               os_memcpy(data.ft_ies.target_ap,
+                         nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+
+       wpa_printf(MSG_DEBUG, "nl80211: FT event target_ap " MACSTR,
+                  MAC2STR(data.ft_ies.target_ap));
+
+       wpa_supplicant_event(drv->ctx, EVENT_FT_RESPONSE, &data);
+}
+
+
+static void send_scan_event(struct wpa_driver_nl80211_data *drv, int aborted,
+                           struct nlattr *tb[])
+{
+       union wpa_event_data event;
+       struct nlattr *nl;
+       int rem;
+       struct scan_info *info;
+#define MAX_REPORT_FREQS 50
+       int freqs[MAX_REPORT_FREQS];
+       int num_freqs = 0;
+
+       if (drv->scan_for_auth) {
+               drv->scan_for_auth = 0;
+               wpa_printf(MSG_DEBUG, "nl80211: Scan results for missing "
+                          "cfg80211 BSS entry");
+               wpa_driver_nl80211_authenticate_retry(drv);
+               return;
+       }
+
+       os_memset(&event, 0, sizeof(event));
+       info = &event.scan_info;
+       info->aborted = aborted;
+
+       if (tb[NL80211_ATTR_SCAN_SSIDS]) {
+               nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_SSIDS], rem) {
+                       struct wpa_driver_scan_ssid *s =
+                               &info->ssids[info->num_ssids];
+                       s->ssid = nla_data(nl);
+                       s->ssid_len = nla_len(nl);
+                       wpa_printf(MSG_DEBUG, "nl80211: Scan probed for SSID '%s'",
+                                  wpa_ssid_txt(s->ssid, s->ssid_len));
+                       info->num_ssids++;
+                       if (info->num_ssids == WPAS_MAX_SCAN_SSIDS)
+                               break;
+               }
+       }
+       if (tb[NL80211_ATTR_SCAN_FREQUENCIES]) {
+               char msg[200], *pos, *end;
+               int res;
+
+               pos = msg;
+               end = pos + sizeof(msg);
+               *pos = '\0';
+
+               nla_for_each_nested(nl, tb[NL80211_ATTR_SCAN_FREQUENCIES], rem)
+               {
+                       freqs[num_freqs] = nla_get_u32(nl);
+                       res = os_snprintf(pos, end - pos, " %d",
+                                         freqs[num_freqs]);
+                       if (!os_snprintf_error(end - pos, res))
+                               pos += res;
+                       num_freqs++;
+                       if (num_freqs == MAX_REPORT_FREQS - 1)
+                               break;
+               }
+               info->freqs = freqs;
+               info->num_freqs = num_freqs;
+               wpa_printf(MSG_DEBUG, "nl80211: Scan included frequencies:%s",
+                          msg);
+       }
+       wpa_supplicant_event(drv->ctx, EVENT_SCAN_RESULTS, &event);
+}
+
+
+static void nl80211_cqm_event(struct wpa_driver_nl80211_data *drv,
+                             struct nlattr *tb[])
+{
+       static struct nla_policy cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
+               [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
+               [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 },
+               [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
+               [NL80211_ATTR_CQM_PKT_LOSS_EVENT] = { .type = NLA_U32 },
+       };
+       struct nlattr *cqm[NL80211_ATTR_CQM_MAX + 1];
+       enum nl80211_cqm_rssi_threshold_event event;
+       union wpa_event_data ed;
+       struct wpa_signal_info sig;
+       int res;
+
+       if (tb[NL80211_ATTR_CQM] == NULL ||
+           nla_parse_nested(cqm, NL80211_ATTR_CQM_MAX, tb[NL80211_ATTR_CQM],
+                            cqm_policy)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid CQM event");
+               return;
+       }
+
+       os_memset(&ed, 0, sizeof(ed));
+
+       if (cqm[NL80211_ATTR_CQM_PKT_LOSS_EVENT]) {
+               if (!tb[NL80211_ATTR_MAC])
+                       return;
+               os_memcpy(ed.low_ack.addr, nla_data(tb[NL80211_ATTR_MAC]),
+                         ETH_ALEN);
+               wpa_supplicant_event(drv->ctx, EVENT_STATION_LOW_ACK, &ed);
+               return;
+       }
+
+       if (cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] == NULL)
+               return;
+       event = nla_get_u32(cqm[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT]);
+
+       if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH) {
+               wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
+                          "event: RSSI high");
+               ed.signal_change.above_threshold = 1;
+       } else if (event == NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW) {
+               wpa_printf(MSG_DEBUG, "nl80211: Connection quality monitor "
+                          "event: RSSI low");
+               ed.signal_change.above_threshold = 0;
+       } else
+               return;
+
+       res = nl80211_get_link_signal(drv, &sig);
+       if (res == 0) {
+               ed.signal_change.current_signal = sig.current_signal;
+               ed.signal_change.current_txrate = sig.current_txrate;
+               wpa_printf(MSG_DEBUG, "nl80211: Signal: %d dBm  txrate: %d",
+                          sig.current_signal, sig.current_txrate);
+       }
+
+       res = nl80211_get_link_noise(drv, &sig);
+       if (res == 0) {
+               ed.signal_change.current_noise = sig.current_noise;
+               wpa_printf(MSG_DEBUG, "nl80211: Noise: %d dBm",
+                          sig.current_noise);
+       }
+
+       wpa_supplicant_event(drv->ctx, EVENT_SIGNAL_CHANGE, &ed);
+}
+
+
+static void nl80211_new_peer_candidate(struct wpa_driver_nl80211_data *drv,
+                                      struct nlattr **tb)
+{
+       const u8 *addr;
+       union wpa_event_data data;
+
+       if (drv->nlmode != NL80211_IFTYPE_MESH_POINT ||
+           !tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_IE])
+               return;
+
+       addr = nla_data(tb[NL80211_ATTR_MAC]);
+       wpa_printf(MSG_DEBUG, "nl80211: New peer candidate" MACSTR,
+                  MAC2STR(addr));
+
+       os_memset(&data, 0, sizeof(data));
+       data.mesh_peer.peer = addr;
+       data.mesh_peer.ies = nla_data(tb[NL80211_ATTR_IE]);
+       data.mesh_peer.ie_len = nla_len(tb[NL80211_ATTR_IE]);
+       wpa_supplicant_event(drv->ctx, EVENT_NEW_PEER_CANDIDATE, &data);
+}
+
+
+static void nl80211_new_station_event(struct wpa_driver_nl80211_data *drv,
+                                     struct i802_bss *bss,
+                                     struct nlattr **tb)
+{
+       u8 *addr;
+       union wpa_event_data data;
+
+       if (tb[NL80211_ATTR_MAC] == NULL)
+               return;
+       addr = nla_data(tb[NL80211_ATTR_MAC]);
+       wpa_printf(MSG_DEBUG, "nl80211: New station " MACSTR, MAC2STR(addr));
+
+       if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
+               u8 *ies = NULL;
+               size_t ies_len = 0;
+               if (tb[NL80211_ATTR_IE]) {
+                       ies = nla_data(tb[NL80211_ATTR_IE]);
+                       ies_len = nla_len(tb[NL80211_ATTR_IE]);
+               }
+               wpa_hexdump(MSG_DEBUG, "nl80211: Assoc Req IEs", ies, ies_len);
+               drv_event_assoc(bss->ctx, addr, ies, ies_len, 0);
+               return;
+       }
+
+       if (drv->nlmode != NL80211_IFTYPE_ADHOC)
+               return;
+
+       os_memset(&data, 0, sizeof(data));
+       os_memcpy(data.ibss_rsn_start.peer, addr, ETH_ALEN);
+       wpa_supplicant_event(bss->ctx, EVENT_IBSS_RSN_START, &data);
+}
+
+
+static void nl80211_del_station_event(struct wpa_driver_nl80211_data *drv,
+                                     struct nlattr **tb)
+{
+       u8 *addr;
+       union wpa_event_data data;
+
+       if (tb[NL80211_ATTR_MAC] == NULL)
+               return;
+       addr = nla_data(tb[NL80211_ATTR_MAC]);
+       wpa_printf(MSG_DEBUG, "nl80211: Delete station " MACSTR,
+                  MAC2STR(addr));
+
+       if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
+               drv_event_disassoc(drv->ctx, addr);
+               return;
+       }
+
+       if (drv->nlmode != NL80211_IFTYPE_ADHOC)
+               return;
+
+       os_memset(&data, 0, sizeof(data));
+       os_memcpy(data.ibss_peer_lost.peer, addr, ETH_ALEN);
+       wpa_supplicant_event(drv->ctx, EVENT_IBSS_PEER_LOST, &data);
+}
+
+
+static void nl80211_rekey_offload_event(struct wpa_driver_nl80211_data *drv,
+                                       struct nlattr **tb)
+{
+       struct nlattr *rekey_info[NUM_NL80211_REKEY_DATA];
+       static struct nla_policy rekey_policy[NUM_NL80211_REKEY_DATA] = {
+               [NL80211_REKEY_DATA_KEK] = {
+                       .minlen = NL80211_KEK_LEN,
+                       .maxlen = NL80211_KEK_LEN,
+               },
+               [NL80211_REKEY_DATA_KCK] = {
+                       .minlen = NL80211_KCK_LEN,
+                       .maxlen = NL80211_KCK_LEN,
+               },
+               [NL80211_REKEY_DATA_REPLAY_CTR] = {
+                       .minlen = NL80211_REPLAY_CTR_LEN,
+                       .maxlen = NL80211_REPLAY_CTR_LEN,
+               },
+       };
+       union wpa_event_data data;
+
+       if (!tb[NL80211_ATTR_MAC] ||
+           !tb[NL80211_ATTR_REKEY_DATA] ||
+           nla_parse_nested(rekey_info, MAX_NL80211_REKEY_DATA,
+                            tb[NL80211_ATTR_REKEY_DATA], rekey_policy) ||
+           !rekey_info[NL80211_REKEY_DATA_REPLAY_CTR])
+               return;
+
+       os_memset(&data, 0, sizeof(data));
+       data.driver_gtk_rekey.bssid = nla_data(tb[NL80211_ATTR_MAC]);
+       wpa_printf(MSG_DEBUG, "nl80211: Rekey offload event for BSSID " MACSTR,
+                  MAC2STR(data.driver_gtk_rekey.bssid));
+       data.driver_gtk_rekey.replay_ctr =
+               nla_data(rekey_info[NL80211_REKEY_DATA_REPLAY_CTR]);
+       wpa_hexdump(MSG_DEBUG, "nl80211: Rekey offload - Replay Counter",
+                   data.driver_gtk_rekey.replay_ctr, NL80211_REPLAY_CTR_LEN);
+       wpa_supplicant_event(drv->ctx, EVENT_DRIVER_GTK_REKEY, &data);
+}
+
+
+static void nl80211_pmksa_candidate_event(struct wpa_driver_nl80211_data *drv,
+                                         struct nlattr **tb)
+{
+       struct nlattr *cand[NUM_NL80211_PMKSA_CANDIDATE];
+       static struct nla_policy cand_policy[NUM_NL80211_PMKSA_CANDIDATE] = {
+               [NL80211_PMKSA_CANDIDATE_INDEX] = { .type = NLA_U32 },
+               [NL80211_PMKSA_CANDIDATE_BSSID] = {
+                       .minlen = ETH_ALEN,
+                       .maxlen = ETH_ALEN,
+               },
+               [NL80211_PMKSA_CANDIDATE_PREAUTH] = { .type = NLA_FLAG },
+       };
+       union wpa_event_data data;
+
+       wpa_printf(MSG_DEBUG, "nl80211: PMKSA candidate event");
+
+       if (!tb[NL80211_ATTR_PMKSA_CANDIDATE] ||
+           nla_parse_nested(cand, MAX_NL80211_PMKSA_CANDIDATE,
+                            tb[NL80211_ATTR_PMKSA_CANDIDATE], cand_policy) ||
+           !cand[NL80211_PMKSA_CANDIDATE_INDEX] ||
+           !cand[NL80211_PMKSA_CANDIDATE_BSSID])
+               return;
+
+       os_memset(&data, 0, sizeof(data));
+       os_memcpy(data.pmkid_candidate.bssid,
+                 nla_data(cand[NL80211_PMKSA_CANDIDATE_BSSID]), ETH_ALEN);
+       data.pmkid_candidate.index =
+               nla_get_u32(cand[NL80211_PMKSA_CANDIDATE_INDEX]);
+       data.pmkid_candidate.preauth =
+               cand[NL80211_PMKSA_CANDIDATE_PREAUTH] != NULL;
+       wpa_supplicant_event(drv->ctx, EVENT_PMKID_CANDIDATE, &data);
+}
+
+
+static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv,
+                                      struct nlattr **tb)
+{
+       union wpa_event_data data;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Probe client event");
+
+       if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_ACK])
+               return;
+
+       os_memset(&data, 0, sizeof(data));
+       os_memcpy(data.client_poll.addr,
+                 nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+
+       wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data);
+}
+
+
+static void nl80211_tdls_oper_event(struct wpa_driver_nl80211_data *drv,
+                                   struct nlattr **tb)
+{
+       union wpa_event_data data;
+
+       wpa_printf(MSG_DEBUG, "nl80211: TDLS operation event");
+
+       if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_TDLS_OPERATION])
+               return;
+
+       os_memset(&data, 0, sizeof(data));
+       os_memcpy(data.tdls.peer, nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+       switch (nla_get_u8(tb[NL80211_ATTR_TDLS_OPERATION])) {
+       case NL80211_TDLS_SETUP:
+               wpa_printf(MSG_DEBUG, "nl80211: TDLS setup request for peer "
+                          MACSTR, MAC2STR(data.tdls.peer));
+               data.tdls.oper = TDLS_REQUEST_SETUP;
+               break;
+       case NL80211_TDLS_TEARDOWN:
+               wpa_printf(MSG_DEBUG, "nl80211: TDLS teardown request for peer "
+                          MACSTR, MAC2STR(data.tdls.peer));
+               data.tdls.oper = TDLS_REQUEST_TEARDOWN;
+               break;
+       case NL80211_TDLS_DISCOVERY_REQ:
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: TDLS discovery request for peer " MACSTR,
+                          MAC2STR(data.tdls.peer));
+               data.tdls.oper = TDLS_REQUEST_DISCOVER;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "nl80211: Unsupported TDLS operatione "
+                          "event");
+               return;
+       }
+       if (tb[NL80211_ATTR_REASON_CODE]) {
+               data.tdls.reason_code =
+                       nla_get_u16(tb[NL80211_ATTR_REASON_CODE]);
+       }
+
+       wpa_supplicant_event(drv->ctx, EVENT_TDLS, &data);
+}
+
+
+static void nl80211_stop_ap(struct wpa_driver_nl80211_data *drv,
+                           struct nlattr **tb)
+{
+       wpa_supplicant_event(drv->ctx, EVENT_INTERFACE_UNAVAILABLE, NULL);
+}
+
+
+static void nl80211_connect_failed_event(struct wpa_driver_nl80211_data *drv,
+                                        struct nlattr **tb)
+{
+       union wpa_event_data data;
+       u32 reason;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Connect failed event");
+
+       if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_CONN_FAILED_REASON])
+               return;
+
+       os_memset(&data, 0, sizeof(data));
+       os_memcpy(data.connect_failed_reason.addr,
+                 nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
+
+       reason = nla_get_u32(tb[NL80211_ATTR_CONN_FAILED_REASON]);
+       switch (reason) {
+       case NL80211_CONN_FAIL_MAX_CLIENTS:
+               wpa_printf(MSG_DEBUG, "nl80211: Max client reached");
+               data.connect_failed_reason.code = MAX_CLIENT_REACHED;
+               break;
+       case NL80211_CONN_FAIL_BLOCKED_CLIENT:
+               wpa_printf(MSG_DEBUG, "nl80211: Blocked client " MACSTR
+                          " tried to connect",
+                          MAC2STR(data.connect_failed_reason.addr));
+               data.connect_failed_reason.code = BLOCKED_CLIENT;
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "nl8021l: Unknown connect failed reason "
+                          "%u", reason);
+               return;
+       }
+
+       wpa_supplicant_event(drv->ctx, EVENT_CONNECT_FAILED_REASON, &data);
+}
+
+
+static void nl80211_radar_event(struct wpa_driver_nl80211_data *drv,
+                               struct nlattr **tb)
+{
+       union wpa_event_data data;
+       enum nl80211_radar_event event_type;
+
+       if (!tb[NL80211_ATTR_WIPHY_FREQ] || !tb[NL80211_ATTR_RADAR_EVENT])
+               return;
+
+       os_memset(&data, 0, sizeof(data));
+       data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+       event_type = nla_get_u32(tb[NL80211_ATTR_RADAR_EVENT]);
+
+       /* Check HT params */
+       if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+               data.dfs_event.ht_enabled = 1;
+               data.dfs_event.chan_offset = 0;
+
+               switch (nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) {
+               case NL80211_CHAN_NO_HT:
+                       data.dfs_event.ht_enabled = 0;
+                       break;
+               case NL80211_CHAN_HT20:
+                       break;
+               case NL80211_CHAN_HT40PLUS:
+                       data.dfs_event.chan_offset = 1;
+                       break;
+               case NL80211_CHAN_HT40MINUS:
+                       data.dfs_event.chan_offset = -1;
+                       break;
+               }
+       }
+
+       /* Get VHT params */
+       if (tb[NL80211_ATTR_CHANNEL_WIDTH])
+               data.dfs_event.chan_width =
+                       convert2width(nla_get_u32(
+                                             tb[NL80211_ATTR_CHANNEL_WIDTH]));
+       if (tb[NL80211_ATTR_CENTER_FREQ1])
+               data.dfs_event.cf1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
+       if (tb[NL80211_ATTR_CENTER_FREQ2])
+               data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
+
+       wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, ht: %d, offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz",
+                  data.dfs_event.freq, data.dfs_event.ht_enabled,
+                  data.dfs_event.chan_offset, data.dfs_event.chan_width,
+                  data.dfs_event.cf1, data.dfs_event.cf2);
+
+       switch (event_type) {
+       case NL80211_RADAR_DETECTED:
+               wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data);
+               break;
+       case NL80211_RADAR_CAC_FINISHED:
+               wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data);
+               break;
+       case NL80211_RADAR_CAC_ABORTED:
+               wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data);
+               break;
+       case NL80211_RADAR_NOP_FINISHED:
+               wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "nl80211: Unknown radar event %d "
+                          "received", event_type);
+               break;
+       }
+}
+
+
+static void nl80211_spurious_frame(struct i802_bss *bss, struct nlattr **tb,
+                                  int wds)
+{
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       union wpa_event_data event;
+
+       if (!tb[NL80211_ATTR_MAC])
+               return;
+
+       os_memset(&event, 0, sizeof(event));
+       event.rx_from_unknown.bssid = bss->addr;
+       event.rx_from_unknown.addr = nla_data(tb[NL80211_ATTR_MAC]);
+       event.rx_from_unknown.wds = wds;
+
+       wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
+}
+
+
+static void qca_nl80211_avoid_freq(struct wpa_driver_nl80211_data *drv,
+                                  const u8 *data, size_t len)
+{
+       u32 i, count;
+       union wpa_event_data event;
+       struct wpa_freq_range *range = NULL;
+       const struct qca_avoid_freq_list *freq_range;
+
+       freq_range = (const struct qca_avoid_freq_list *) data;
+       if (len < sizeof(freq_range->count))
+               return;
+
+       count = freq_range->count;
+       if (len < sizeof(freq_range->count) +
+           count * sizeof(struct qca_avoid_freq_range)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Ignored too short avoid frequency list (len=%u)",
+                          (unsigned int) len);
+               return;
+       }
+
+       if (count > 0) {
+               range = os_calloc(count, sizeof(struct wpa_freq_range));
+               if (range == NULL)
+                       return;
+       }
+
+       os_memset(&event, 0, sizeof(event));
+       for (i = 0; i < count; i++) {
+               unsigned int idx = event.freq_range.num;
+               range[idx].min = freq_range->range[i].start_freq;
+               range[idx].max = freq_range->range[i].end_freq;
+               wpa_printf(MSG_DEBUG, "nl80211: Avoid frequency range: %u-%u",
+                          range[idx].min, range[idx].max);
+               if (range[idx].min > range[idx].max) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Ignore invalid frequency range");
+                       continue;
+               }
+               event.freq_range.num++;
+       }
+       event.freq_range.range = range;
+
+       wpa_supplicant_event(drv->ctx, EVENT_AVOID_FREQUENCIES, &event);
+
+       os_free(range);
+}
+
+
+static void qca_nl80211_acs_select_ch(struct wpa_driver_nl80211_data *drv,
+                                  const u8 *data, size_t len)
+{
+       struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ACS_MAX + 1];
+       union wpa_event_data event;
+
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: ACS channel selection vendor event received");
+
+       if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ACS_MAX,
+                     (struct nlattr *) data, len, NULL) ||
+           !tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL] ||
+           !tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL])
+               return;
+
+       os_memset(&event, 0, sizeof(event));
+       event.acs_selected_channels.pri_channel =
+               nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_PRIMARY_CHANNEL]);
+       event.acs_selected_channels.sec_channel =
+               nla_get_u8(tb[QCA_WLAN_VENDOR_ATTR_ACS_SECONDARY_CHANNEL]);
+
+       wpa_supplicant_event(drv->ctx, EVENT_ACS_CHANNEL_SELECTED, &event);
+}
+
+
+static void qca_nl80211_key_mgmt_auth(struct wpa_driver_nl80211_data *drv,
+                                     const u8 *data, size_t len)
+{
+       struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX + 1];
+       u8 *bssid;
+
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: Key management roam+auth vendor event received");
+
+       if (nla_parse(tb, QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX,
+                     (struct nlattr *) data, len, NULL) ||
+           !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID] ||
+           nla_len(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]) != ETH_ALEN ||
+           !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE] ||
+           !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE] ||
+           !tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED])
+               return;
+
+       bssid = nla_data(tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID]);
+       wpa_printf(MSG_DEBUG, "  * roam BSSID " MACSTR, MAC2STR(bssid));
+
+       mlme_event_connect(drv, NL80211_CMD_ROAM, NULL,
+                          tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID],
+                          tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REQ_IE],
+                          tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_RESP_IE],
+                          tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AUTHORIZED],
+                          tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_KEY_REPLAY_CTR],
+                          tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KCK],
+                          tb[QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_PTK_KEK]);
+}
+
+
+static void qca_nl80211_dfs_offload_radar_event(
+       struct wpa_driver_nl80211_data *drv, u32 subcmd, u8 *msg, int length)
+{
+       union wpa_event_data data;
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+
+       wpa_printf(MSG_DEBUG,
+                  "nl80211: DFS offload radar vendor event received");
+
+       if (nla_parse(tb, NL80211_ATTR_MAX,
+                     (struct nlattr *) msg, length, NULL))
+               return;
+
+       if (!tb[NL80211_ATTR_WIPHY_FREQ]) {
+               wpa_printf(MSG_INFO,
+                          "nl80211: Error parsing WIPHY_FREQ in FS offload radar vendor event");
+               return;
+       }
+
+       os_memset(&data, 0, sizeof(data));
+       data.dfs_event.freq = nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+
+       wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz",
+                  data.dfs_event.freq);
+
+       /* Check HT params */
+       if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+               data.dfs_event.ht_enabled = 1;
+               data.dfs_event.chan_offset = 0;
+
+               switch (nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE])) {
+               case NL80211_CHAN_NO_HT:
+                       data.dfs_event.ht_enabled = 0;
+                       break;
+               case NL80211_CHAN_HT20:
+                       break;
+               case NL80211_CHAN_HT40PLUS:
+                       data.dfs_event.chan_offset = 1;
+                       break;
+               case NL80211_CHAN_HT40MINUS:
+                       data.dfs_event.chan_offset = -1;
+                       break;
+               }
+       }
+
+       /* Get VHT params */
+       if (tb[NL80211_ATTR_CHANNEL_WIDTH])
+               data.dfs_event.chan_width =
+                       convert2width(nla_get_u32(
+                                             tb[NL80211_ATTR_CHANNEL_WIDTH]));
+       if (tb[NL80211_ATTR_CENTER_FREQ1])
+               data.dfs_event.cf1 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
+       if (tb[NL80211_ATTR_CENTER_FREQ2])
+               data.dfs_event.cf2 = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
+
+       wpa_printf(MSG_DEBUG, "nl80211: DFS event on freq %d MHz, ht: %d, "
+                   "offset: %d, width: %d, cf1: %dMHz, cf2: %dMHz",
+                   data.dfs_event.freq, data.dfs_event.ht_enabled,
+                   data.dfs_event.chan_offset, data.dfs_event.chan_width,
+                   data.dfs_event.cf1, data.dfs_event.cf2);
+
+       switch (subcmd) {
+       case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED:
+               wpa_supplicant_event(drv->ctx, EVENT_DFS_RADAR_DETECTED, &data);
+               break;
+       case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED:
+               wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_STARTED, &data);
+               break;
+       case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED:
+               wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_FINISHED, &data);
+               break;
+       case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED:
+               wpa_supplicant_event(drv->ctx, EVENT_DFS_CAC_ABORTED, &data);
+               break;
+       case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED:
+               wpa_supplicant_event(drv->ctx, EVENT_DFS_NOP_FINISHED, &data);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Unknown DFS offload radar event %d received",
+                          subcmd);
+               break;
+       }
+}
+
+
+static void nl80211_vendor_event_qca(struct wpa_driver_nl80211_data *drv,
+                                    u32 subcmd, u8 *data, size_t len)
+{
+       switch (subcmd) {
+       case QCA_NL80211_VENDOR_SUBCMD_TEST:
+               wpa_hexdump(MSG_DEBUG, "nl80211: QCA test event", data, len);
+               break;
+       case QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY:
+               qca_nl80211_avoid_freq(drv, data, len);
+               break;
+       case QCA_NL80211_VENDOR_SUBCMD_KEY_MGMT_ROAM_AUTH:
+               qca_nl80211_key_mgmt_auth(drv, data, len);
+               break;
+       case QCA_NL80211_VENDOR_SUBCMD_DO_ACS:
+               qca_nl80211_acs_select_ch(drv, data, len);
+               break;
+       case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_STARTED:
+       case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_FINISHED:
+       case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_ABORTED:
+       case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_CAC_NOP_FINISHED:
+       case QCA_NL80211_VENDOR_SUBCMD_DFS_OFFLOAD_RADAR_DETECTED:
+               qca_nl80211_dfs_offload_radar_event(drv, subcmd, data, len);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Ignore unsupported QCA vendor event %u",
+                          subcmd);
+               break;
+       }
+}
+
+
+static void nl80211_vendor_event(struct wpa_driver_nl80211_data *drv,
+                                struct nlattr **tb)
+{
+       u32 vendor_id, subcmd, wiphy = 0;
+       int wiphy_idx;
+       u8 *data = NULL;
+       size_t len = 0;
+
+       if (!tb[NL80211_ATTR_VENDOR_ID] ||
+           !tb[NL80211_ATTR_VENDOR_SUBCMD])
+               return;
+
+       vendor_id = nla_get_u32(tb[NL80211_ATTR_VENDOR_ID]);
+       subcmd = nla_get_u32(tb[NL80211_ATTR_VENDOR_SUBCMD]);
+
+       if (tb[NL80211_ATTR_WIPHY])
+               wiphy = nla_get_u32(tb[NL80211_ATTR_WIPHY]);
+
+       wpa_printf(MSG_DEBUG, "nl80211: Vendor event: wiphy=%u vendor_id=0x%x subcmd=%u",
+                  wiphy, vendor_id, subcmd);
+
+       if (tb[NL80211_ATTR_VENDOR_DATA]) {
+               data = nla_data(tb[NL80211_ATTR_VENDOR_DATA]);
+               len = nla_len(tb[NL80211_ATTR_VENDOR_DATA]);
+               wpa_hexdump(MSG_MSGDUMP, "nl80211: Vendor data", data, len);
+       }
+
+       wiphy_idx = nl80211_get_wiphy_index(drv->first_bss);
+       if (wiphy_idx >= 0 && wiphy_idx != (int) wiphy) {
+               wpa_printf(MSG_DEBUG, "nl80211: Ignore vendor event for foreign wiphy %u (own: %d)",
+                          wiphy, wiphy_idx);
+               return;
+       }
+
+       switch (vendor_id) {
+       case OUI_QCA:
+               nl80211_vendor_event_qca(drv, subcmd, data, len);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "nl80211: Ignore unsupported vendor event");
+               break;
+       }
+}
+
+
+static void nl80211_reg_change_event(struct wpa_driver_nl80211_data *drv,
+                                    struct nlattr *tb[])
+{
+       union wpa_event_data data;
+       enum nl80211_reg_initiator init;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Regulatory domain change");
+
+       if (tb[NL80211_ATTR_REG_INITIATOR] == NULL)
+               return;
+
+       os_memset(&data, 0, sizeof(data));
+       init = nla_get_u8(tb[NL80211_ATTR_REG_INITIATOR]);
+       wpa_printf(MSG_DEBUG, " * initiator=%d", init);
+       switch (init) {
+       case NL80211_REGDOM_SET_BY_CORE:
+               data.channel_list_changed.initiator = REGDOM_SET_BY_CORE;
+               break;
+       case NL80211_REGDOM_SET_BY_USER:
+               data.channel_list_changed.initiator = REGDOM_SET_BY_USER;
+               break;
+       case NL80211_REGDOM_SET_BY_DRIVER:
+               data.channel_list_changed.initiator = REGDOM_SET_BY_DRIVER;
+               break;
+       case NL80211_REGDOM_SET_BY_COUNTRY_IE:
+               data.channel_list_changed.initiator = REGDOM_SET_BY_COUNTRY_IE;
+               break;
+       }
+
+       if (tb[NL80211_ATTR_REG_TYPE]) {
+               enum nl80211_reg_type type;
+               type = nla_get_u8(tb[NL80211_ATTR_REG_TYPE]);
+               wpa_printf(MSG_DEBUG, " * type=%d", type);
+               switch (type) {
+               case NL80211_REGDOM_TYPE_COUNTRY:
+                       data.channel_list_changed.type = REGDOM_TYPE_COUNTRY;
+                       break;
+               case NL80211_REGDOM_TYPE_WORLD:
+                       data.channel_list_changed.type = REGDOM_TYPE_WORLD;
+                       break;
+               case NL80211_REGDOM_TYPE_CUSTOM_WORLD:
+                       data.channel_list_changed.type =
+                               REGDOM_TYPE_CUSTOM_WORLD;
+                       break;
+               case NL80211_REGDOM_TYPE_INTERSECTION:
+                       data.channel_list_changed.type =
+                               REGDOM_TYPE_INTERSECTION;
+                       break;
+               }
+       }
+
+       if (tb[NL80211_ATTR_REG_ALPHA2]) {
+               os_strlcpy(data.channel_list_changed.alpha2,
+                          nla_get_string(tb[NL80211_ATTR_REG_ALPHA2]),
+                          sizeof(data.channel_list_changed.alpha2));
+               wpa_printf(MSG_DEBUG, " * alpha2=%s",
+                          data.channel_list_changed.alpha2);
+       }
+
+       wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, &data);
+}
+
+
+static void do_process_drv_event(struct i802_bss *bss, int cmd,
+                                struct nlattr **tb)
+{
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       union wpa_event_data data;
+
+       wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s",
+                  cmd, nl80211_command_to_string(cmd), bss->ifname);
+
+       if (cmd == NL80211_CMD_ROAM &&
+           (drv->capa.flags & WPA_DRIVER_FLAGS_KEY_MGMT_OFFLOAD)) {
+               /*
+                * Device will use roam+auth vendor event to indicate
+                * roaming, so ignore the regular roam event.
+                */
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Ignore roam event (cmd=%d), device will use vendor event roam+auth",
+                          cmd);
+               return;
+       }
+
+       if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED &&
+           (cmd == NL80211_CMD_NEW_SCAN_RESULTS ||
+            cmd == NL80211_CMD_SCAN_ABORTED)) {
+               wpa_driver_nl80211_set_mode(drv->first_bss,
+                                           drv->ap_scan_as_station);
+               drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
+       }
+
+       switch (cmd) {
+       case NL80211_CMD_TRIGGER_SCAN:
+               wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan trigger");
+               drv->scan_state = SCAN_STARTED;
+               if (drv->scan_for_auth) {
+                       /*
+                        * Cannot indicate EVENT_SCAN_STARTED here since we skip
+                        * EVENT_SCAN_RESULTS in scan_for_auth case and the
+                        * upper layer implementation could get confused about
+                        * scanning state.
+                        */
+                       wpa_printf(MSG_DEBUG, "nl80211: Do not indicate scan-start event due to internal scan_for_auth");
+                       break;
+               }
+               wpa_supplicant_event(drv->ctx, EVENT_SCAN_STARTED, NULL);
+               break;
+       case NL80211_CMD_START_SCHED_SCAN:
+               wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan started");
+               drv->scan_state = SCHED_SCAN_STARTED;
+               break;
+       case NL80211_CMD_SCHED_SCAN_STOPPED:
+               wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Sched scan stopped");
+               drv->scan_state = SCHED_SCAN_STOPPED;
+               wpa_supplicant_event(drv->ctx, EVENT_SCHED_SCAN_STOPPED, NULL);
+               break;
+       case NL80211_CMD_NEW_SCAN_RESULTS:
+               wpa_dbg(drv->ctx, MSG_DEBUG,
+                       "nl80211: New scan results available");
+               drv->scan_state = SCAN_COMPLETED;
+               drv->scan_complete_events = 1;
+               eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
+                                    drv->ctx);
+               send_scan_event(drv, 0, tb);
+               break;
+       case NL80211_CMD_SCHED_SCAN_RESULTS:
+               wpa_dbg(drv->ctx, MSG_DEBUG,
+                       "nl80211: New sched scan results available");
+               drv->scan_state = SCHED_SCAN_RESULTS;
+               send_scan_event(drv, 0, tb);
+               break;
+       case NL80211_CMD_SCAN_ABORTED:
+               wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Scan aborted");
+               drv->scan_state = SCAN_ABORTED;
+               /*
+                * Need to indicate that scan results are available in order
+                * not to make wpa_supplicant stop its scanning.
+                */
+               eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv,
+                                    drv->ctx);
+               send_scan_event(drv, 1, tb);
+               break;
+       case NL80211_CMD_AUTHENTICATE:
+       case NL80211_CMD_ASSOCIATE:
+       case NL80211_CMD_DEAUTHENTICATE:
+       case NL80211_CMD_DISASSOCIATE:
+       case NL80211_CMD_FRAME_TX_STATUS:
+       case NL80211_CMD_UNPROT_DEAUTHENTICATE:
+       case NL80211_CMD_UNPROT_DISASSOCIATE:
+               mlme_event(bss, cmd, tb[NL80211_ATTR_FRAME],
+                          tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
+                          tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
+                          tb[NL80211_ATTR_COOKIE],
+                          tb[NL80211_ATTR_RX_SIGNAL_DBM],
+                          tb[NL80211_ATTR_STA_WME]);
+               break;
+       case NL80211_CMD_CONNECT:
+       case NL80211_CMD_ROAM:
+               mlme_event_connect(drv, cmd,
+                                  tb[NL80211_ATTR_STATUS_CODE],
+                                  tb[NL80211_ATTR_MAC],
+                                  tb[NL80211_ATTR_REQ_IE],
+                                  tb[NL80211_ATTR_RESP_IE],
+                                  NULL, NULL, NULL, NULL);
+               break;
+       case NL80211_CMD_CH_SWITCH_NOTIFY:
+               mlme_event_ch_switch(drv,
+                                    tb[NL80211_ATTR_IFINDEX],
+                                    tb[NL80211_ATTR_WIPHY_FREQ],
+                                    tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE],
+                                    tb[NL80211_ATTR_CHANNEL_WIDTH],
+                                    tb[NL80211_ATTR_CENTER_FREQ1],
+                                    tb[NL80211_ATTR_CENTER_FREQ2]);
+               break;
+       case NL80211_CMD_DISCONNECT:
+               mlme_event_disconnect(drv, tb[NL80211_ATTR_REASON_CODE],
+                                     tb[NL80211_ATTR_MAC],
+                                     tb[NL80211_ATTR_DISCONNECTED_BY_AP]);
+               break;
+       case NL80211_CMD_MICHAEL_MIC_FAILURE:
+               mlme_event_michael_mic_failure(bss, tb);
+               break;
+       case NL80211_CMD_JOIN_IBSS:
+               mlme_event_join_ibss(drv, tb);
+               break;
+       case NL80211_CMD_REMAIN_ON_CHANNEL:
+               mlme_event_remain_on_channel(drv, 0, tb);
+               break;
+       case NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL:
+               mlme_event_remain_on_channel(drv, 1, tb);
+               break;
+       case NL80211_CMD_NOTIFY_CQM:
+               nl80211_cqm_event(drv, tb);
+               break;
+       case NL80211_CMD_REG_CHANGE:
+               nl80211_reg_change_event(drv, tb);
+               break;
+       case NL80211_CMD_REG_BEACON_HINT:
+               wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint");
+               os_memset(&data, 0, sizeof(data));
+               data.channel_list_changed.initiator = REGDOM_BEACON_HINT;
+               wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED,
+                                    &data);
+               break;
+       case NL80211_CMD_NEW_STATION:
+               nl80211_new_station_event(drv, bss, tb);
+               break;
+       case NL80211_CMD_DEL_STATION:
+               nl80211_del_station_event(drv, tb);
+               break;
+       case NL80211_CMD_SET_REKEY_OFFLOAD:
+               nl80211_rekey_offload_event(drv, tb);
+               break;
+       case NL80211_CMD_PMKSA_CANDIDATE:
+               nl80211_pmksa_candidate_event(drv, tb);
+               break;
+       case NL80211_CMD_PROBE_CLIENT:
+               nl80211_client_probe_event(drv, tb);
+               break;
+       case NL80211_CMD_TDLS_OPER:
+               nl80211_tdls_oper_event(drv, tb);
+               break;
+       case NL80211_CMD_CONN_FAILED:
+               nl80211_connect_failed_event(drv, tb);
+               break;
+       case NL80211_CMD_FT_EVENT:
+               mlme_event_ft_event(drv, tb);
+               break;
+       case NL80211_CMD_RADAR_DETECT:
+               nl80211_radar_event(drv, tb);
+               break;
+       case NL80211_CMD_STOP_AP:
+               nl80211_stop_ap(drv, tb);
+               break;
+       case NL80211_CMD_VENDOR:
+               nl80211_vendor_event(drv, tb);
+               break;
+       case NL80211_CMD_NEW_PEER_CANDIDATE:
+               nl80211_new_peer_candidate(drv, tb);
+               break;
+       default:
+               wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: Ignored unknown event "
+                       "(cmd=%d)", cmd);
+               break;
+       }
+}
+
+
+int process_global_event(struct nl_msg *msg, void *arg)
+{
+       struct nl80211_global *global = arg;
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct wpa_driver_nl80211_data *drv, *tmp;
+       int ifidx = -1;
+       struct i802_bss *bss;
+       u64 wdev_id = 0;
+       int wdev_id_set = 0;
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (tb[NL80211_ATTR_IFINDEX])
+               ifidx = nla_get_u32(tb[NL80211_ATTR_IFINDEX]);
+       else if (tb[NL80211_ATTR_WDEV]) {
+               wdev_id = nla_get_u64(tb[NL80211_ATTR_WDEV]);
+               wdev_id_set = 1;
+       }
+
+       dl_list_for_each_safe(drv, tmp, &global->interfaces,
+                             struct wpa_driver_nl80211_data, list) {
+               for (bss = drv->first_bss; bss; bss = bss->next) {
+                       if ((ifidx == -1 && !wdev_id_set) ||
+                           ifidx == bss->ifindex ||
+                           (wdev_id_set && bss->wdev_id_set &&
+                            wdev_id == bss->wdev_id)) {
+                               do_process_drv_event(bss, gnlh->cmd, tb);
+                               return NL_SKIP;
+                       }
+               }
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Ignored event (cmd=%d) for foreign interface (ifindex %d wdev 0x%llx)",
+                          gnlh->cmd, ifidx, (long long unsigned int) wdev_id);
+       }
+
+       return NL_SKIP;
+}
+
+
+int process_bss_event(struct nl_msg *msg, void *arg)
+{
+       struct i802_bss *bss = arg;
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       wpa_printf(MSG_DEBUG, "nl80211: BSS Event %d (%s) received for %s",
+                  gnlh->cmd, nl80211_command_to_string(gnlh->cmd),
+                  bss->ifname);
+
+       switch (gnlh->cmd) {
+       case NL80211_CMD_FRAME:
+       case NL80211_CMD_FRAME_TX_STATUS:
+               mlme_event(bss, gnlh->cmd, tb[NL80211_ATTR_FRAME],
+                          tb[NL80211_ATTR_MAC], tb[NL80211_ATTR_TIMED_OUT],
+                          tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
+                          tb[NL80211_ATTR_COOKIE],
+                          tb[NL80211_ATTR_RX_SIGNAL_DBM],
+                          tb[NL80211_ATTR_STA_WME]);
+               break;
+       case NL80211_CMD_UNEXPECTED_FRAME:
+               nl80211_spurious_frame(bss, tb, 0);
+               break;
+       case NL80211_CMD_UNEXPECTED_4ADDR_FRAME:
+               nl80211_spurious_frame(bss, tb, 1);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "nl80211: Ignored unknown event "
+                          "(cmd=%d)", gnlh->cmd);
+               break;
+       }
+
+       return NL_SKIP;
+}
diff --git a/src/drivers/driver_nl80211_monitor.c b/src/drivers/driver_nl80211_monitor.c
new file mode 100644 (file)
index 0000000..45385da
--- /dev/null
@@ -0,0 +1,491 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - AP monitor interface
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2004, Instant802 Networks, Inc.
+ * Copyright (c) 2005-2006, Devicescape Software, Inc.
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <netpacket/packet.h>
+#include <linux/filter.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
+#include "linux_ioctl.h"
+#include "radiotap_iter.h"
+#include "driver_nl80211.h"
+
+
+static void handle_tx_callback(void *ctx, u8 *buf, size_t len, int ok)
+{
+       struct ieee80211_hdr *hdr;
+       u16 fc;
+       union wpa_event_data event;
+
+       hdr = (struct ieee80211_hdr *) buf;
+       fc = le_to_host16(hdr->frame_control);
+
+       os_memset(&event, 0, sizeof(event));
+       event.tx_status.type = WLAN_FC_GET_TYPE(fc);
+       event.tx_status.stype = WLAN_FC_GET_STYPE(fc);
+       event.tx_status.dst = hdr->addr1;
+       event.tx_status.data = buf;
+       event.tx_status.data_len = len;
+       event.tx_status.ack = ok;
+       wpa_supplicant_event(ctx, EVENT_TX_STATUS, &event);
+}
+
+
+static void from_unknown_sta(struct wpa_driver_nl80211_data *drv,
+                            u8 *buf, size_t len)
+{
+       struct ieee80211_hdr *hdr = (void *)buf;
+       u16 fc;
+       union wpa_event_data event;
+
+       if (len < sizeof(*hdr))
+               return;
+
+       fc = le_to_host16(hdr->frame_control);
+
+       os_memset(&event, 0, sizeof(event));
+       event.rx_from_unknown.bssid = get_hdr_bssid(hdr, len);
+       event.rx_from_unknown.addr = hdr->addr2;
+       event.rx_from_unknown.wds = (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) ==
+               (WLAN_FC_FROMDS | WLAN_FC_TODS);
+       wpa_supplicant_event(drv->ctx, EVENT_RX_FROM_UNKNOWN, &event);
+}
+
+
+static void handle_frame(struct wpa_driver_nl80211_data *drv,
+                        u8 *buf, size_t len, int datarate, int ssi_signal)
+{
+       struct ieee80211_hdr *hdr;
+       u16 fc;
+       union wpa_event_data event;
+
+       hdr = (struct ieee80211_hdr *) buf;
+       fc = le_to_host16(hdr->frame_control);
+
+       switch (WLAN_FC_GET_TYPE(fc)) {
+       case WLAN_FC_TYPE_MGMT:
+               os_memset(&event, 0, sizeof(event));
+               event.rx_mgmt.frame = buf;
+               event.rx_mgmt.frame_len = len;
+               event.rx_mgmt.datarate = datarate;
+               event.rx_mgmt.ssi_signal = ssi_signal;
+               wpa_supplicant_event(drv->ctx, EVENT_RX_MGMT, &event);
+               break;
+       case WLAN_FC_TYPE_CTRL:
+               /* can only get here with PS-Poll frames */
+               wpa_printf(MSG_DEBUG, "CTRL");
+               from_unknown_sta(drv, buf, len);
+               break;
+       case WLAN_FC_TYPE_DATA:
+               from_unknown_sta(drv, buf, len);
+               break;
+       }
+}
+
+
+static void handle_monitor_read(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct wpa_driver_nl80211_data *drv = eloop_ctx;
+       int len;
+       unsigned char buf[3000];
+       struct ieee80211_radiotap_iterator iter;
+       int ret;
+       int datarate = 0, ssi_signal = 0;
+       int injected = 0, failed = 0, rxflags = 0;
+
+       len = recv(sock, buf, sizeof(buf), 0);
+       if (len < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: Monitor socket recv failed: %s",
+                          strerror(errno));
+               return;
+       }
+
+       if (ieee80211_radiotap_iterator_init(&iter, (void *) buf, len, NULL)) {
+               wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame");
+               return;
+       }
+
+       while (1) {
+               ret = ieee80211_radiotap_iterator_next(&iter);
+               if (ret == -ENOENT)
+                       break;
+               if (ret) {
+                       wpa_printf(MSG_INFO, "nl80211: received invalid radiotap frame (%d)",
+                                  ret);
+                       return;
+               }
+               switch (iter.this_arg_index) {
+               case IEEE80211_RADIOTAP_FLAGS:
+                       if (*iter.this_arg & IEEE80211_RADIOTAP_F_FCS)
+                               len -= 4;
+                       break;
+               case IEEE80211_RADIOTAP_RX_FLAGS:
+                       rxflags = 1;
+                       break;
+               case IEEE80211_RADIOTAP_TX_FLAGS:
+                       injected = 1;
+                       failed = le_to_host16((*(uint16_t *) iter.this_arg)) &
+                                       IEEE80211_RADIOTAP_F_TX_FAIL;
+                       break;
+               case IEEE80211_RADIOTAP_DATA_RETRIES:
+                       break;
+               case IEEE80211_RADIOTAP_CHANNEL:
+                       /* TODO: convert from freq/flags to channel number */
+                       break;
+               case IEEE80211_RADIOTAP_RATE:
+                       datarate = *iter.this_arg * 5;
+                       break;
+               case IEEE80211_RADIOTAP_DBM_ANTSIGNAL:
+                       ssi_signal = (s8) *iter.this_arg;
+                       break;
+               }
+       }
+
+       if (rxflags && injected)
+               return;
+
+       if (!injected)
+               handle_frame(drv, buf + iter._max_length,
+                            len - iter._max_length, datarate, ssi_signal);
+       else
+               handle_tx_callback(drv->ctx, buf + iter._max_length,
+                                  len - iter._max_length, !failed);
+}
+
+
+/*
+ * we post-process the filter code later and rewrite
+ * this to the offset to the last instruction
+ */
+#define PASS   0xFF
+#define FAIL   0xFE
+
+static struct sock_filter msock_filter_insns[] = {
+       /*
+        * do a little-endian load of the radiotap length field
+        */
+       /* load lower byte into A */
+       BPF_STMT(BPF_LD  | BPF_B | BPF_ABS, 2),
+       /* put it into X (== index register) */
+       BPF_STMT(BPF_MISC| BPF_TAX, 0),
+       /* load upper byte into A */
+       BPF_STMT(BPF_LD  | BPF_B | BPF_ABS, 3),
+       /* left-shift it by 8 */
+       BPF_STMT(BPF_ALU | BPF_LSH | BPF_K, 8),
+       /* or with X */
+       BPF_STMT(BPF_ALU | BPF_OR | BPF_X, 0),
+       /* put result into X */
+       BPF_STMT(BPF_MISC| BPF_TAX, 0),
+
+       /*
+        * Allow management frames through, this also gives us those
+        * management frames that we sent ourselves with status
+        */
+       /* load the lower byte of the IEEE 802.11 frame control field */
+       BPF_STMT(BPF_LD  | BPF_B | BPF_IND, 0),
+       /* mask off frame type and version */
+       BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0xF),
+       /* accept frame if it's both 0, fall through otherwise */
+       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0, PASS, 0),
+
+       /*
+        * TODO: add a bit to radiotap RX flags that indicates
+        * that the sending station is not associated, then
+        * add a filter here that filters on our DA and that flag
+        * to allow us to deauth frames to that bad station.
+        *
+        * For now allow all To DS data frames through.
+        */
+       /* load the IEEE 802.11 frame control field */
+       BPF_STMT(BPF_LD  | BPF_H | BPF_IND, 0),
+       /* mask off frame type, version and DS status */
+       BPF_STMT(BPF_ALU | BPF_AND | BPF_K, 0x0F03),
+       /* accept frame if version 0, type 2 and To DS, fall through otherwise
+        */
+       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0801, PASS, 0),
+
+#if 0
+       /*
+        * drop non-data frames
+        */
+       /* load the lower byte of the frame control field */
+       BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 0),
+       /* mask off QoS bit */
+       BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x0c),
+       /* drop non-data frames */
+       BPF_JUMP(BPF_JMP  | BPF_JEQ | BPF_K, 8, 0, FAIL),
+#endif
+       /* load the upper byte of the frame control field */
+       BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 1),
+       /* mask off toDS/fromDS */
+       BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x03),
+       /* accept WDS frames */
+       BPF_JUMP(BPF_JMP  | BPF_JEQ | BPF_K, 3, PASS, 0),
+
+       /*
+        * add header length to index
+        */
+       /* load the lower byte of the frame control field */
+       BPF_STMT(BPF_LD   | BPF_B | BPF_IND, 0),
+       /* mask off QoS bit */
+       BPF_STMT(BPF_ALU  | BPF_AND | BPF_K, 0x80),
+       /* right shift it by 6 to give 0 or 2 */
+       BPF_STMT(BPF_ALU  | BPF_RSH | BPF_K, 6),
+       /* add data frame header length */
+       BPF_STMT(BPF_ALU  | BPF_ADD | BPF_K, 24),
+       /* add index, was start of 802.11 header */
+       BPF_STMT(BPF_ALU  | BPF_ADD | BPF_X, 0),
+       /* move to index, now start of LL header */
+       BPF_STMT(BPF_MISC | BPF_TAX, 0),
+
+       /*
+        * Accept empty data frames, we use those for
+        * polling activity.
+        */
+       BPF_STMT(BPF_LD  | BPF_W | BPF_LEN, 0),
+       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_X, 0, PASS, 0),
+
+       /*
+        * Accept EAPOL frames
+        */
+       BPF_STMT(BPF_LD  | BPF_W | BPF_IND, 0),
+       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0xAAAA0300, 0, FAIL),
+       BPF_STMT(BPF_LD  | BPF_W | BPF_IND, 4),
+       BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, 0x0000888E, PASS, FAIL),
+
+       /* keep these last two statements or change the code below */
+       /* return 0 == "DROP" */
+       BPF_STMT(BPF_RET | BPF_K, 0),
+       /* return ~0 == "keep all" */
+       BPF_STMT(BPF_RET | BPF_K, ~0),
+};
+
+static struct sock_fprog msock_filter = {
+       .len = ARRAY_SIZE(msock_filter_insns),
+       .filter = msock_filter_insns,
+};
+
+
+static int add_monitor_filter(int s)
+{
+       int idx;
+
+       /* rewrite all PASS/FAIL jump offsets */
+       for (idx = 0; idx < msock_filter.len; idx++) {
+               struct sock_filter *insn = &msock_filter_insns[idx];
+
+               if (BPF_CLASS(insn->code) == BPF_JMP) {
+                       if (insn->code == (BPF_JMP|BPF_JA)) {
+                               if (insn->k == PASS)
+                                       insn->k = msock_filter.len - idx - 2;
+                               else if (insn->k == FAIL)
+                                       insn->k = msock_filter.len - idx - 3;
+                       }
+
+                       if (insn->jt == PASS)
+                               insn->jt = msock_filter.len - idx - 2;
+                       else if (insn->jt == FAIL)
+                               insn->jt = msock_filter.len - idx - 3;
+
+                       if (insn->jf == PASS)
+                               insn->jf = msock_filter.len - idx - 2;
+                       else if (insn->jf == FAIL)
+                               insn->jf = msock_filter.len - idx - 3;
+               }
+       }
+
+       if (setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER,
+                      &msock_filter, sizeof(msock_filter))) {
+               wpa_printf(MSG_ERROR, "nl80211: setsockopt(SO_ATTACH_FILTER) failed: %s",
+                          strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}
+
+
+void nl80211_remove_monitor_interface(struct wpa_driver_nl80211_data *drv)
+{
+       if (drv->monitor_refcount > 0)
+               drv->monitor_refcount--;
+       wpa_printf(MSG_DEBUG, "nl80211: Remove monitor interface: refcount=%d",
+                  drv->monitor_refcount);
+       if (drv->monitor_refcount > 0)
+               return;
+
+       if (drv->monitor_ifidx >= 0) {
+               nl80211_remove_iface(drv, drv->monitor_ifidx);
+               drv->monitor_ifidx = -1;
+       }
+       if (drv->monitor_sock >= 0) {
+               eloop_unregister_read_sock(drv->monitor_sock);
+               close(drv->monitor_sock);
+               drv->monitor_sock = -1;
+       }
+}
+
+
+int nl80211_create_monitor_interface(struct wpa_driver_nl80211_data *drv)
+{
+       char buf[IFNAMSIZ];
+       struct sockaddr_ll ll;
+       int optval;
+       socklen_t optlen;
+
+       if (drv->monitor_ifidx >= 0) {
+               drv->monitor_refcount++;
+               wpa_printf(MSG_DEBUG, "nl80211: Re-use existing monitor interface: refcount=%d",
+                          drv->monitor_refcount);
+               return 0;
+       }
+
+       if (os_strncmp(drv->first_bss->ifname, "p2p-", 4) == 0) {
+               /*
+                * P2P interface name is of the format p2p-%s-%d. For monitor
+                * interface name corresponding to P2P GO, replace "p2p-" with
+                * "mon-" to retain the same interface name length and to
+                * indicate that it is a monitor interface.
+                */
+               snprintf(buf, IFNAMSIZ, "mon-%s", drv->first_bss->ifname + 4);
+       } else {
+               /* Non-P2P interface with AP functionality. */
+               snprintf(buf, IFNAMSIZ, "mon.%s", drv->first_bss->ifname);
+       }
+
+       buf[IFNAMSIZ - 1] = '\0';
+
+       drv->monitor_ifidx =
+               nl80211_create_iface(drv, buf, NL80211_IFTYPE_MONITOR, NULL,
+                                    0, NULL, NULL, 0);
+
+       if (drv->monitor_ifidx == -EOPNOTSUPP) {
+               /*
+                * This is backward compatibility for a few versions of
+                * the kernel only that didn't advertise the right
+                * attributes for the only driver that then supported
+                * AP mode w/o monitor -- ath6kl.
+                */
+               wpa_printf(MSG_DEBUG, "nl80211: Driver does not support "
+                          "monitor interface type - try to run without it");
+               drv->device_ap_sme = 1;
+       }
+
+       if (drv->monitor_ifidx < 0)
+               return -1;
+
+       if (linux_set_iface_flags(drv->global->ioctl_sock, buf, 1))
+               goto error;
+
+       memset(&ll, 0, sizeof(ll));
+       ll.sll_family = AF_PACKET;
+       ll.sll_ifindex = drv->monitor_ifidx;
+       drv->monitor_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
+       if (drv->monitor_sock < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: socket[PF_PACKET,SOCK_RAW] failed: %s",
+                          strerror(errno));
+               goto error;
+       }
+
+       if (add_monitor_filter(drv->monitor_sock)) {
+               wpa_printf(MSG_INFO, "Failed to set socket filter for monitor "
+                          "interface; do filtering in user space");
+               /* This works, but will cost in performance. */
+       }
+
+       if (bind(drv->monitor_sock, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+               wpa_printf(MSG_ERROR, "nl80211: monitor socket bind failed: %s",
+                          strerror(errno));
+               goto error;
+       }
+
+       optlen = sizeof(optval);
+       optval = 20;
+       if (setsockopt
+           (drv->monitor_sock, SOL_SOCKET, SO_PRIORITY, &optval, optlen)) {
+               wpa_printf(MSG_ERROR, "nl80211: Failed to set socket priority: %s",
+                          strerror(errno));
+               goto error;
+       }
+
+       if (eloop_register_read_sock(drv->monitor_sock, handle_monitor_read,
+                                    drv, NULL)) {
+               wpa_printf(MSG_INFO, "nl80211: Could not register monitor read socket");
+               goto error;
+       }
+
+       drv->monitor_refcount++;
+       return 0;
+ error:
+       nl80211_remove_monitor_interface(drv);
+       return -1;
+}
+
+
+int nl80211_send_monitor(struct wpa_driver_nl80211_data *drv,
+                        const void *data, size_t len,
+                        int encrypt, int noack)
+{
+       __u8 rtap_hdr[] = {
+               0x00, 0x00, /* radiotap version */
+               0x0e, 0x00, /* radiotap length */
+               0x02, 0xc0, 0x00, 0x00, /* bmap: flags, tx and rx flags */
+               IEEE80211_RADIOTAP_F_FRAG, /* F_FRAG (fragment if required) */
+               0x00,       /* padding */
+               0x00, 0x00, /* RX and TX flags to indicate that */
+               0x00, 0x00, /* this is the injected frame directly */
+       };
+       struct iovec iov[2] = {
+               {
+                       .iov_base = &rtap_hdr,
+                       .iov_len = sizeof(rtap_hdr),
+               },
+               {
+                       .iov_base = (void *) data,
+                       .iov_len = len,
+               }
+       };
+       struct msghdr msg = {
+               .msg_name = NULL,
+               .msg_namelen = 0,
+               .msg_iov = iov,
+               .msg_iovlen = 2,
+               .msg_control = NULL,
+               .msg_controllen = 0,
+               .msg_flags = 0,
+       };
+       int res;
+       u16 txflags = 0;
+
+       if (encrypt)
+               rtap_hdr[8] |= IEEE80211_RADIOTAP_F_WEP;
+
+       if (drv->monitor_sock < 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: No monitor socket available "
+                          "for %s", __func__);
+               return -1;
+       }
+
+       if (noack)
+               txflags |= IEEE80211_RADIOTAP_F_TX_NOACK;
+       WPA_PUT_LE16(&rtap_hdr[12], txflags);
+
+       res = sendmsg(drv->monitor_sock, &msg, 0);
+       if (res < 0) {
+               wpa_printf(MSG_INFO, "nl80211: sendmsg: %s", strerror(errno));
+               return -1;
+       }
+       return 0;
+}
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
new file mode 100644 (file)
index 0000000..3911f48
--- /dev/null
@@ -0,0 +1,775 @@
+/*
+ * Driver interaction with Linux nl80211/cfg80211 - Scanning
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007, Johannes Berg <johannes@sipsolutions.net>
+ * Copyright (c) 2009-2010, Atheros Communications
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <netlink/genl/genl.h>
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "driver_nl80211.h"
+
+
+static int get_noise_for_scan_results(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
+       static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
+               [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
+               [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
+       };
+       struct wpa_scan_results *scan_results = arg;
+       struct wpa_scan_res *scan_res;
+       size_t i;
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+
+       if (!tb[NL80211_ATTR_SURVEY_INFO]) {
+               wpa_printf(MSG_DEBUG, "nl80211: Survey data missing");
+               return NL_SKIP;
+       }
+
+       if (nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
+                            tb[NL80211_ATTR_SURVEY_INFO],
+                            survey_policy)) {
+               wpa_printf(MSG_DEBUG, "nl80211: Failed to parse nested "
+                          "attributes");
+               return NL_SKIP;
+       }
+
+       if (!sinfo[NL80211_SURVEY_INFO_NOISE])
+               return NL_SKIP;
+
+       if (!sinfo[NL80211_SURVEY_INFO_FREQUENCY])
+               return NL_SKIP;
+
+       for (i = 0; i < scan_results->num; ++i) {
+               scan_res = scan_results->res[i];
+               if (!scan_res)
+                       continue;
+               if ((int) nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]) !=
+                   scan_res->freq)
+                       continue;
+               if (!(scan_res->flags & WPA_SCAN_NOISE_INVALID))
+                       continue;
+               scan_res->noise = (s8)
+                       nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+               scan_res->flags &= ~WPA_SCAN_NOISE_INVALID;
+       }
+
+       return NL_SKIP;
+}
+
+
+static int nl80211_get_noise_for_scan_results(
+       struct wpa_driver_nl80211_data *drv,
+       struct wpa_scan_results *scan_res)
+{
+       struct nl_msg *msg;
+
+       msg = nl80211_drv_msg(drv, NLM_F_DUMP, NL80211_CMD_GET_SURVEY);
+       return send_and_recv_msgs(drv, msg, get_noise_for_scan_results,
+                                 scan_res);
+}
+
+
+/**
+ * wpa_driver_nl80211_scan_timeout - Scan timeout to report scan completion
+ * @eloop_ctx: Driver private data
+ * @timeout_ctx: ctx argument given to wpa_driver_nl80211_init()
+ *
+ * This function can be used as registered timeout when starting a scan to
+ * generate a scan completed event if the driver does not report this.
+ */
+void wpa_driver_nl80211_scan_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_driver_nl80211_data *drv = eloop_ctx;
+       if (drv->ap_scan_as_station != NL80211_IFTYPE_UNSPECIFIED) {
+               wpa_driver_nl80211_set_mode(drv->first_bss,
+                                           drv->ap_scan_as_station);
+               drv->ap_scan_as_station = NL80211_IFTYPE_UNSPECIFIED;
+       }
+       wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
+       wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
+}
+
+
+static struct nl_msg *
+nl80211_scan_common(struct i802_bss *bss, u8 cmd,
+                   struct wpa_driver_scan_params *params)
+{
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct nl_msg *msg;
+       size_t i;
+       u32 scan_flags = 0;
+
+       msg = nl80211_cmd_msg(bss, 0, cmd);
+       if (!msg)
+               return NULL;
+
+       if (params->num_ssids) {
+               struct nlattr *ssids;
+
+               ssids = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
+               if (ssids == NULL)
+                       goto fail;
+               for (i = 0; i < params->num_ssids; i++) {
+                       wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID",
+                                         params->ssids[i].ssid,
+                                         params->ssids[i].ssid_len);
+                       if (nla_put(msg, i + 1, params->ssids[i].ssid_len,
+                                   params->ssids[i].ssid))
+                               goto fail;
+               }
+               nla_nest_end(msg, ssids);
+       }
+
+       if (params->extra_ies) {
+               wpa_hexdump(MSG_MSGDUMP, "nl80211: Scan extra IEs",
+                           params->extra_ies, params->extra_ies_len);
+               if (nla_put(msg, NL80211_ATTR_IE, params->extra_ies_len,
+                           params->extra_ies))
+                       goto fail;
+       }
+
+       if (params->freqs) {
+               struct nlattr *freqs;
+               freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
+               if (freqs == NULL)
+                       goto fail;
+               for (i = 0; params->freqs[i]; i++) {
+                       wpa_printf(MSG_MSGDUMP, "nl80211: Scan frequency %u "
+                                  "MHz", params->freqs[i]);
+                       if (nla_put_u32(msg, i + 1, params->freqs[i]))
+                               goto fail;
+               }
+               nla_nest_end(msg, freqs);
+       }
+
+       os_free(drv->filter_ssids);
+       drv->filter_ssids = params->filter_ssids;
+       params->filter_ssids = NULL;
+       drv->num_filter_ssids = params->num_filter_ssids;
+
+       if (params->only_new_results) {
+               wpa_printf(MSG_DEBUG, "nl80211: Add NL80211_SCAN_FLAG_FLUSH");
+               scan_flags |= NL80211_SCAN_FLAG_FLUSH;
+       }
+
+       if (params->low_priority && drv->have_low_prio_scan) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Add NL80211_SCAN_FLAG_LOW_PRIORITY");
+               scan_flags |= NL80211_SCAN_FLAG_LOW_PRIORITY;
+       }
+
+       if (params->mac_addr_rand) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Add NL80211_SCAN_FLAG_RANDOM_ADDR");
+               scan_flags |= NL80211_SCAN_FLAG_RANDOM_ADDR;
+
+               if (params->mac_addr) {
+                       wpa_printf(MSG_DEBUG, "nl80211: MAC address: " MACSTR,
+                                  MAC2STR(params->mac_addr));
+                       if (nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN,
+                                   params->mac_addr))
+                               goto fail;
+               }
+
+               if (params->mac_addr_mask) {
+                       wpa_printf(MSG_DEBUG, "nl80211: MAC address mask: "
+                                  MACSTR, MAC2STR(params->mac_addr_mask));
+                       if (nla_put(msg, NL80211_ATTR_MAC_MASK, ETH_ALEN,
+                                   params->mac_addr_mask))
+                               goto fail;
+               }
+       }
+
+       if (scan_flags &&
+           nla_put_u32(msg, NL80211_ATTR_SCAN_FLAGS, scan_flags))
+               goto fail;
+
+       return msg;
+
+fail:
+       nlmsg_free(msg);
+       return NULL;
+}
+
+
+/**
+ * wpa_driver_nl80211_scan - Request the driver to initiate scan
+ * @bss: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @params: Scan parameters
+ * Returns: 0 on success, -1 on failure
+ */
+int wpa_driver_nl80211_scan(struct i802_bss *bss,
+                           struct wpa_driver_scan_params *params)
+{
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ret = -1, timeout;
+       struct nl_msg *msg = NULL;
+
+       wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: scan request");
+       drv->scan_for_auth = 0;
+
+       msg = nl80211_scan_common(bss, NL80211_CMD_TRIGGER_SCAN, params);
+       if (!msg)
+               return -1;
+
+       if (params->p2p_probe) {
+               struct nlattr *rates;
+
+               wpa_printf(MSG_DEBUG, "nl80211: P2P probe - mask SuppRates");
+
+               rates = nla_nest_start(msg, NL80211_ATTR_SCAN_SUPP_RATES);
+               if (rates == NULL)
+                       goto fail;
+
+               /*
+                * Remove 2.4 GHz rates 1, 2, 5.5, 11 Mbps from supported rates
+                * by masking out everything else apart from the OFDM rates 6,
+                * 9, 12, 18, 24, 36, 48, 54 Mbps from non-MCS rates. All 5 GHz
+                * rates are left enabled.
+                */
+               if (nla_put(msg, NL80211_BAND_2GHZ, 8,
+                           "\x0c\x12\x18\x24\x30\x48\x60\x6c"))
+                       goto fail;
+               nla_nest_end(msg, rates);
+
+               if (nla_put_flag(msg, NL80211_ATTR_TX_NO_CCK_RATE))
+                       goto fail;
+       }
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Scan trigger failed: ret=%d "
+                          "(%s)", ret, strerror(-ret));
+               if (drv->hostapd && is_ap_interface(drv->nlmode)) {
+                       enum nl80211_iftype old_mode = drv->nlmode;
+
+                       /*
+                        * mac80211 does not allow scan requests in AP mode, so
+                        * try to do this in station mode.
+                        */
+                       if (wpa_driver_nl80211_set_mode(
+                                   bss, NL80211_IFTYPE_STATION))
+                               goto fail;
+
+                       if (wpa_driver_nl80211_scan(bss, params)) {
+                               wpa_driver_nl80211_set_mode(bss, old_mode);
+                               goto fail;
+                       }
+
+                       /* Restore AP mode when processing scan results */
+                       drv->ap_scan_as_station = old_mode;
+                       ret = 0;
+               } else
+                       goto fail;
+       }
+
+       drv->scan_state = SCAN_REQUESTED;
+       /* Not all drivers generate "scan completed" wireless event, so try to
+        * read results after a timeout. */
+       timeout = 10;
+       if (drv->scan_complete_events) {
+               /*
+                * The driver seems to deliver events to notify when scan is
+                * complete, so use longer timeout to avoid race conditions
+                * with scanning and following association request.
+                */
+               timeout = 30;
+       }
+       wpa_printf(MSG_DEBUG, "Scan requested (ret=%d) - scan timeout %d "
+                  "seconds", ret, timeout);
+       eloop_cancel_timeout(wpa_driver_nl80211_scan_timeout, drv, drv->ctx);
+       eloop_register_timeout(timeout, 0, wpa_driver_nl80211_scan_timeout,
+                              drv, drv->ctx);
+
+fail:
+       nlmsg_free(msg);
+       return ret;
+}
+
+
+/**
+ * wpa_driver_nl80211_sched_scan - Initiate a scheduled scan
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * @params: Scan parameters
+ * @interval: Interval between scan cycles in milliseconds
+ * Returns: 0 on success, -1 on failure or if not supported
+ */
+int wpa_driver_nl80211_sched_scan(void *priv,
+                                 struct wpa_driver_scan_params *params,
+                                 u32 interval)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ret = -1;
+       struct nl_msg *msg;
+       size_t i;
+
+       wpa_dbg(drv->ctx, MSG_DEBUG, "nl80211: sched_scan request");
+
+#ifdef ANDROID
+       if (!drv->capa.sched_scan_supported)
+               return android_pno_start(bss, params);
+#endif /* ANDROID */
+
+       msg = nl80211_scan_common(bss, NL80211_CMD_START_SCHED_SCAN, params);
+       if (!msg ||
+           nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, interval))
+               goto fail;
+
+       if ((drv->num_filter_ssids &&
+           (int) drv->num_filter_ssids <= drv->capa.max_match_sets) ||
+           params->filter_rssi) {
+               struct nlattr *match_sets;
+               match_sets = nla_nest_start(msg, NL80211_ATTR_SCHED_SCAN_MATCH);
+               if (match_sets == NULL)
+                       goto fail;
+
+               for (i = 0; i < drv->num_filter_ssids; i++) {
+                       struct nlattr *match_set_ssid;
+                       wpa_hexdump_ascii(MSG_MSGDUMP,
+                                         "nl80211: Sched scan filter SSID",
+                                         drv->filter_ssids[i].ssid,
+                                         drv->filter_ssids[i].ssid_len);
+
+                       match_set_ssid = nla_nest_start(msg, i + 1);
+                       if (match_set_ssid == NULL ||
+                           nla_put(msg, NL80211_ATTR_SCHED_SCAN_MATCH_SSID,
+                                   drv->filter_ssids[i].ssid_len,
+                                   drv->filter_ssids[i].ssid) ||
+                           (params->filter_rssi &&
+                            nla_put_u32(msg,
+                                        NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
+                                        params->filter_rssi)))
+                               goto fail;
+
+                       nla_nest_end(msg, match_set_ssid);
+               }
+
+               /*
+                * Due to backward compatibility code, newer kernels treat this
+                * matchset (with only an RSSI filter) as the default for all
+                * other matchsets, unless it's the only one, in which case the
+                * matchset will actually allow all SSIDs above the RSSI.
+                */
+               if (params->filter_rssi) {
+                       struct nlattr *match_set_rssi;
+                       match_set_rssi = nla_nest_start(msg, 0);
+                       if (match_set_rssi == NULL ||
+                           nla_put_u32(msg, NL80211_SCHED_SCAN_MATCH_ATTR_RSSI,
+                                       params->filter_rssi))
+                               goto fail;
+                       wpa_printf(MSG_MSGDUMP,
+                                  "nl80211: Sched scan RSSI filter %d dBm",
+                                  params->filter_rssi);
+                       nla_nest_end(msg, match_set_rssi);
+               }
+
+               nla_nest_end(msg, match_sets);
+       }
+
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+
+       /* TODO: if we get an error here, we should fall back to normal scan */
+
+       msg = NULL;
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "nl80211: Sched scan start failed: "
+                          "ret=%d (%s)", ret, strerror(-ret));
+               goto fail;
+       }
+
+       wpa_printf(MSG_DEBUG, "nl80211: Sched scan requested (ret=%d) - "
+                  "scan interval %d msec", ret, interval);
+
+fail:
+       nlmsg_free(msg);
+       return ret;
+}
+
+
+/**
+ * wpa_driver_nl80211_stop_sched_scan - Stop a scheduled scan
+ * @priv: Pointer to private driver data from wpa_driver_nl80211_init()
+ * Returns: 0 on success, -1 on failure or if not supported
+ */
+int wpa_driver_nl80211_stop_sched_scan(void *priv)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       int ret;
+       struct nl_msg *msg;
+
+#ifdef ANDROID
+       if (!drv->capa.sched_scan_supported)
+               return android_pno_stop(bss);
+#endif /* ANDROID */
+
+       msg = nl80211_drv_msg(drv, 0, NL80211_CMD_STOP_SCHED_SCAN);
+       ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+       if (ret) {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Sched scan stop failed: ret=%d (%s)",
+                          ret, strerror(-ret));
+       } else {
+               wpa_printf(MSG_DEBUG,
+                          "nl80211: Sched scan stop sent");
+       }
+
+       return ret;
+}
+
+
+static const u8 * nl80211_get_ie(const u8 *ies, size_t ies_len, u8 ie)
+{
+       const u8 *end, *pos;
+
+       if (ies == NULL)
+               return NULL;
+
+       pos = ies;
+       end = ies + ies_len;
+
+       while (pos + 1 < end) {
+               if (pos + 2 + pos[1] > end)
+                       break;
+               if (pos[0] == ie)
+                       return pos;
+               pos += 2 + pos[1];
+       }
+
+       return NULL;
+}
+
+
+static int nl80211_scan_filtered(struct wpa_driver_nl80211_data *drv,
+                                const u8 *ie, size_t ie_len)
+{
+       const u8 *ssid;
+       size_t i;
+
+       if (drv->filter_ssids == NULL)
+               return 0;
+
+       ssid = nl80211_get_ie(ie, ie_len, WLAN_EID_SSID);
+       if (ssid == NULL)
+               return 1;
+
+       for (i = 0; i < drv->num_filter_ssids; i++) {
+               if (ssid[1] == drv->filter_ssids[i].ssid_len &&
+                   os_memcmp(ssid + 2, drv->filter_ssids[i].ssid, ssid[1]) ==
+                   0)
+                       return 0;
+       }
+
+       return 1;
+}
+
+
+int bss_info_handler(struct nl_msg *msg, void *arg)
+{
+       struct nlattr *tb[NL80211_ATTR_MAX + 1];
+       struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+       struct nlattr *bss[NL80211_BSS_MAX + 1];
+       static struct nla_policy bss_policy[NL80211_BSS_MAX + 1] = {
+               [NL80211_BSS_BSSID] = { .type = NLA_UNSPEC },
+               [NL80211_BSS_FREQUENCY] = { .type = NLA_U32 },
+               [NL80211_BSS_TSF] = { .type = NLA_U64 },
+               [NL80211_BSS_BEACON_INTERVAL] = { .type = NLA_U16 },
+               [NL80211_BSS_CAPABILITY] = { .type = NLA_U16 },
+               [NL80211_BSS_INFORMATION_ELEMENTS] = { .type = NLA_UNSPEC },
+               [NL80211_BSS_SIGNAL_MBM] = { .type = NLA_U32 },
+               [NL80211_BSS_SIGNAL_UNSPEC] = { .type = NLA_U8 },
+               [NL80211_BSS_STATUS] = { .type = NLA_U32 },
+               [NL80211_BSS_SEEN_MS_AGO] = { .type = NLA_U32 },
+               [NL80211_BSS_BEACON_IES] = { .type = NLA_UNSPEC },
+       };
+       struct nl80211_bss_info_arg *_arg = arg;
+       struct wpa_scan_results *res = _arg->res;
+       struct wpa_scan_res **tmp;
+       struct wpa_scan_res *r;
+       const u8 *ie, *beacon_ie;
+       size_t ie_len, beacon_ie_len;
+       u8 *pos;
+       size_t i;
+
+       nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+                 genlmsg_attrlen(gnlh, 0), NULL);
+       if (!tb[NL80211_ATTR_BSS])
+               return NL_SKIP;
+       if (nla_parse_nested(bss, NL80211_BSS_MAX, tb[NL80211_ATTR_BSS],
+                            bss_policy))
+               return NL_SKIP;
+       if (bss[NL80211_BSS_STATUS]) {
+               enum nl80211_bss_status status;
+               status = nla_get_u32(bss[NL80211_BSS_STATUS]);
+               if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+                   bss[NL80211_BSS_FREQUENCY]) {
+                       _arg->assoc_freq =
+                               nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+                       wpa_printf(MSG_DEBUG, "nl80211: Associated on %u MHz",
+                                  _arg->assoc_freq);
+               }
+               if (status == NL80211_BSS_STATUS_IBSS_JOINED &&
+                   bss[NL80211_BSS_FREQUENCY]) {
+                       _arg->ibss_freq =
+                               nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+                       wpa_printf(MSG_DEBUG, "nl80211: IBSS-joined on %u MHz",
+                                  _arg->ibss_freq);
+               }
+               if (status == NL80211_BSS_STATUS_ASSOCIATED &&
+                   bss[NL80211_BSS_BSSID]) {
+                       os_memcpy(_arg->assoc_bssid,
+                                 nla_data(bss[NL80211_BSS_BSSID]), ETH_ALEN);
+                       wpa_printf(MSG_DEBUG, "nl80211: Associated with "
+                                  MACSTR, MAC2STR(_arg->assoc_bssid));
+               }
+       }
+       if (!res)
+               return NL_SKIP;
+       if (bss[NL80211_BSS_INFORMATION_ELEMENTS]) {
+               ie = nla_data(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+               ie_len = nla_len(bss[NL80211_BSS_INFORMATION_ELEMENTS]);
+       } else {
+               ie = NULL;
+               ie_len = 0;
+       }
+       if (bss[NL80211_BSS_BEACON_IES]) {
+               beacon_ie = nla_data(bss[NL80211_BSS_BEACON_IES]);
+               beacon_ie_len = nla_len(bss[NL80211_BSS_BEACON_IES]);
+       } else {
+               beacon_ie = NULL;
+               beacon_ie_len = 0;
+       }
+
+       if (nl80211_scan_filtered(_arg->drv, ie ? ie : beacon_ie,
+                                 ie ? ie_len : beacon_ie_len))
+               return NL_SKIP;
+
+       r = os_zalloc(sizeof(*r) + ie_len + beacon_ie_len);
+       if (r == NULL)
+               return NL_SKIP;
+       if (bss[NL80211_BSS_BSSID])
+               os_memcpy(r->bssid, nla_data(bss[NL80211_BSS_BSSID]),
+                         ETH_ALEN);
+       if (bss[NL80211_BSS_FREQUENCY])
+               r->freq = nla_get_u32(bss[NL80211_BSS_FREQUENCY]);
+       if (bss[NL80211_BSS_BEACON_INTERVAL])
+               r->beacon_int = nla_get_u16(bss[NL80211_BSS_BEACON_INTERVAL]);
+       if (bss[NL80211_BSS_CAPABILITY])
+               r->caps = nla_get_u16(bss[NL80211_BSS_CAPABILITY]);
+       r->flags |= WPA_SCAN_NOISE_INVALID;
+       if (bss[NL80211_BSS_SIGNAL_MBM]) {
+               r->level = nla_get_u32(bss[NL80211_BSS_SIGNAL_MBM]);
+               r->level /= 100; /* mBm to dBm */
+               r->flags |= WPA_SCAN_LEVEL_DBM | WPA_SCAN_QUAL_INVALID;
+       } else if (bss[NL80211_BSS_SIGNAL_UNSPEC]) {
+               r->level = nla_get_u8(bss[NL80211_BSS_SIGNAL_UNSPEC]);
+               r->flags |= WPA_SCAN_QUAL_INVALID;
+       } else
+               r->flags |= WPA_SCAN_LEVEL_INVALID | WPA_SCAN_QUAL_INVALID;
+       if (bss[NL80211_BSS_TSF])
+               r->tsf = nla_get_u64(bss[NL80211_BSS_TSF]);
+       if (bss[NL80211_BSS_SEEN_MS_AGO])
+               r->age = nla_get_u32(bss[NL80211_BSS_SEEN_MS_AGO]);
+       r->ie_len = ie_len;
+       pos = (u8 *) (r + 1);
+       if (ie) {
+               os_memcpy(pos, ie, ie_len);
+               pos += ie_len;
+       }
+       r->beacon_ie_len = beacon_ie_len;
+       if (beacon_ie)
+               os_memcpy(pos, beacon_ie, beacon_ie_len);
+
+       if (bss[NL80211_BSS_STATUS]) {
+               enum nl80211_bss_status status;
+               status = nla_get_u32(bss[NL80211_BSS_STATUS]);
+               switch (status) {
+               case NL80211_BSS_STATUS_ASSOCIATED:
+                       r->flags |= WPA_SCAN_ASSOCIATED;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       /*
+        * cfg80211 maintains separate BSS table entries for APs if the same
+        * BSSID,SSID pair is seen on multiple channels. wpa_supplicant does
+        * not use frequency as a separate key in the BSS table, so filter out
+        * duplicated entries. Prefer associated BSS entry in such a case in
+        * order to get the correct frequency into the BSS table. Similarly,
+        * prefer newer entries over older.
+        */
+       for (i = 0; i < res->num; i++) {
+               const u8 *s1, *s2;
+               if (os_memcmp(res->res[i]->bssid, r->bssid, ETH_ALEN) != 0)
+                       continue;
+
+               s1 = nl80211_get_ie((u8 *) (res->res[i] + 1),
+                                   res->res[i]->ie_len, WLAN_EID_SSID);
+               s2 = nl80211_get_ie((u8 *) (r + 1), r->ie_len, WLAN_EID_SSID);
+               if (s1 == NULL || s2 == NULL || s1[1] != s2[1] ||
+                   os_memcmp(s1, s2, 2 + s1[1]) != 0)
+                       continue;
+
+               /* Same BSSID,SSID was already included in scan results */
+               wpa_printf(MSG_DEBUG, "nl80211: Remove duplicated scan result "
+                          "for " MACSTR, MAC2STR(r->bssid));
+
+               if (((r->flags & WPA_SCAN_ASSOCIATED) &&
+                    !(res->res[i]->flags & WPA_SCAN_ASSOCIATED)) ||
+                   r->age < res->res[i]->age) {
+                       os_free(res->res[i]);
+                       res->res[i] = r;
+               } else
+                       os_free(r);
+               return NL_SKIP;
+       }
+
+       tmp = os_realloc_array(res->res, res->num + 1,
+                              sizeof(struct wpa_scan_res *));
+       if (tmp == NULL) {
+               os_free(r);
+               return NL_SKIP;
+       }
+       tmp[res->num++] = r;
+       res->res = tmp;
+
+       return NL_SKIP;
+}
+
+
+static void clear_state_mismatch(struct wpa_driver_nl80211_data *drv,
+                                const u8 *addr)
+{
+       if (drv->capa.flags & WPA_DRIVER_FLAGS_SME) {
+               wpa_printf(MSG_DEBUG, "nl80211: Clear possible state "
+                          "mismatch (" MACSTR ")", MAC2STR(addr));
+               wpa_driver_nl80211_mlme(drv, addr,
+                                       NL80211_CMD_DEAUTHENTICATE,
+                                       WLAN_REASON_PREV_AUTH_NOT_VALID, 1);
+       }
+}
+
+
+static void wpa_driver_nl80211_check_bss_status(
+       struct wpa_driver_nl80211_data *drv, struct wpa_scan_results *res)
+{
+       size_t i;
+
+       for (i = 0; i < res->num; i++) {
+               struct wpa_scan_res *r = res->res[i];
+
+               if (r->flags & WPA_SCAN_ASSOCIATED) {
+                       wpa_printf(MSG_DEBUG, "nl80211: Scan results "
+                                  "indicate BSS status with " MACSTR
+                                  " as associated",
+                                  MAC2STR(r->bssid));
+                       if (is_sta_interface(drv->nlmode) &&
+                           !drv->associated) {
+                               wpa_printf(MSG_DEBUG, "nl80211: Local state "
+                                          "(not associated) does not match "
+                                          "with BSS state");
+                               clear_state_mismatch(drv, r->bssid);
+                       } else if (is_sta_interface(drv->nlmode) &&
+                                  os_memcmp(drv->bssid, r->bssid, ETH_ALEN) !=
+                                  0) {
+                               wpa_printf(MSG_DEBUG, "nl80211: Local state "
+                                          "(associated with " MACSTR ") does "
+                                          "not match with BSS state",
+                                          MAC2STR(drv->bssid));
+                               clear_state_mismatch(drv, r->bssid);
+                               clear_state_mismatch(drv, drv->bssid);
+                       }
+               }
+       }
+}
+
+
+static struct wpa_scan_results *
+nl80211_get_scan_results(struct wpa_driver_nl80211_data *drv)
+{
+       struct nl_msg *msg;
+       struct wpa_scan_results *res;
+       int ret;
+       struct nl80211_bss_info_arg arg;
+
+       res = os_zalloc(sizeof(*res));
+       if (res == NULL)
+               return NULL;
+       if (!(msg = nl80211_cmd_msg(drv->first_bss, NLM_F_DUMP,
+                                   NL80211_CMD_GET_SCAN))) {
+               wpa_scan_results_free(res);
+               return NULL;
+       }
+
+       arg.drv = drv;
+       arg.res = res;
+       ret = send_and_recv_msgs(drv, msg, bss_info_handler, &arg);
+       if (ret == 0) {
+               wpa_printf(MSG_DEBUG, "nl80211: Received scan results (%lu "
+                          "BSSes)", (unsigned long) res->num);
+               nl80211_get_noise_for_scan_results(drv, res);
+               return res;
+       }
+       wpa_printf(MSG_DEBUG, "nl80211: Scan result fetch failed: ret=%d "
+                  "(%s)", ret, strerror(-ret));
+       wpa_scan_results_free(res);
+       return NULL;
+}
+
+
+/**
+ * wpa_driver_nl80211_get_scan_results - Fetch the latest scan results
+ * @priv: Pointer to private wext data from wpa_driver_nl80211_init()
+ * Returns: Scan results on success, -1 on failure
+ */
+struct wpa_scan_results * wpa_driver_nl80211_get_scan_results(void *priv)
+{
+       struct i802_bss *bss = priv;
+       struct wpa_driver_nl80211_data *drv = bss->drv;
+       struct wpa_scan_results *res;
+
+       res = nl80211_get_scan_results(drv);
+       if (res)
+               wpa_driver_nl80211_check_bss_status(drv, res);
+       return res;
+}
+
+
+void nl80211_dump_scan(struct wpa_driver_nl80211_data *drv)
+{
+       struct wpa_scan_results *res;
+       size_t i;
+
+       res = nl80211_get_scan_results(drv);
+       if (res == NULL) {
+               wpa_printf(MSG_DEBUG, "nl80211: Failed to get scan results");
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "nl80211: Scan result dump");
+       for (i = 0; i < res->num; i++) {
+               struct wpa_scan_res *r = res->res[i];
+               wpa_printf(MSG_DEBUG, "nl80211: %d/%d " MACSTR "%s",
+                          (int) i, (int) res->num, MAC2STR(r->bssid),
+                          r->flags & WPA_SCAN_ASSOCIATED ? " [assoc]" : "");
+       }
+
+       wpa_scan_results_free(res);
+}
index d75c14b..6ff3eae 100644 (file)
@@ -74,13 +74,6 @@ static void none_driver_deinit(void *priv)
 }
 
 
-static int none_driver_send_eapol(void *priv, const u8 *dest, u16 proto,
-                                 const u8 *data, size_t data_len)
-{
-       return -1;
-}
-
-
 const struct wpa_driver_ops wpa_driver_none_ops = {
        .name = "none",
        .desc = "no driver (RADIUS server/WPS ER)",
@@ -89,5 +82,4 @@ const struct wpa_driver_ops wpa_driver_none_ops = {
        .send_ether = none_driver_send_ether,
        .init = none_driver_init,
        .deinit = none_driver_deinit,
-       .send_eapol = none_driver_send_eapol,
 };
index ed88e71..de23fbd 100644 (file)
@@ -35,7 +35,7 @@ static int wpa_priv_reg_cmd(struct wpa_driver_privsep_data *drv, int cmd)
                     (struct sockaddr *) &drv->priv_addr,
                     sizeof(drv->priv_addr));
        if (res < 0)
-               perror("sendto");
+               wpa_printf(MSG_ERROR, "sendto: %s", strerror(errno));
        return res < 0 ? -1 : 0;
 }
 
@@ -59,7 +59,8 @@ static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd,
        msg.msg_namelen = sizeof(drv->priv_addr);
 
        if (sendmsg(drv->cmd_socket, &msg, 0) < 0) {
-               perror("sendmsg(cmd_socket)");
+               wpa_printf(MSG_ERROR, "sendmsg(cmd_socket): %s",
+                          strerror(errno));
                return -1;
        }
 
@@ -74,14 +75,15 @@ static int wpa_priv_cmd(struct wpa_driver_privsep_data *drv, int cmd,
                tv.tv_usec = 0;
                res = select(drv->cmd_socket + 1, &rfds, NULL, NULL, &tv);
                if (res < 0 && errno != EINTR) {
-                       perror("select");
+                       wpa_printf(MSG_ERROR, "select: %s", strerror(errno));
                        return -1;
                }
 
                if (FD_ISSET(drv->cmd_socket, &rfds)) {
                        res = recv(drv->cmd_socket, reply, *reply_len, 0);
                        if (res < 0) {
-                               perror("recv");
+                               wpa_printf(MSG_ERROR, "recv: %s",
+                                          strerror(errno));
                                return -1;
                        }
                        *reply_len = res;
@@ -228,7 +230,7 @@ static int wpa_driver_privsep_associate(
 
        wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d "
                   "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d",
-                  __func__, priv, params->freq, params->pairwise_suite,
+                  __func__, priv, params->freq.freq, params->pairwise_suite,
                   params->group_suite, params->key_mgmt_suite,
                   params->auth_alg, params->mode);
 
@@ -241,7 +243,9 @@ static int wpa_driver_privsep_associate(
                os_memcpy(data->bssid, params->bssid, ETH_ALEN);
        os_memcpy(data->ssid, params->ssid, params->ssid_len);
        data->ssid_len = params->ssid_len;
-       data->freq = params->freq;
+       data->hwmode = params->freq.mode;
+       data->freq = params->freq.freq;
+       data->channel = params->freq.channel;
        data->pairwise_suite = params->pairwise_suite;
        data->group_suite = params->group_suite;
        data->key_mgmt_suite = params->key_mgmt_suite;
@@ -439,7 +443,8 @@ static void wpa_driver_privsep_receive(int sock, void *eloop_ctx,
        res = recvfrom(sock, buf, buflen, 0,
                       (struct sockaddr *) &from, &fromlen);
        if (res < 0) {
-               perror("recvfrom(priv_socket)");
+               wpa_printf(MSG_ERROR, "recvfrom(priv_socket): %s",
+                          strerror(errno));
                os_free(buf);
                return;
        }
@@ -629,7 +634,7 @@ static int wpa_driver_privsep_set_param(void *priv, const char *param)
 
        drv->priv_socket = socket(PF_UNIX, SOCK_DGRAM, 0);
        if (drv->priv_socket < 0) {
-               perror("socket(PF_UNIX)");
+               wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
                os_free(drv->own_socket_path);
                drv->own_socket_path = NULL;
                return -1;
@@ -640,7 +645,9 @@ static int wpa_driver_privsep_set_param(void *priv, const char *param)
        os_strlcpy(addr.sun_path, drv->own_socket_path, sizeof(addr.sun_path));
        if (bind(drv->priv_socket, (struct sockaddr *) &addr, sizeof(addr)) <
            0) {
-               perror("privsep-set-params priv-sock: bind(PF_UNIX)");
+               wpa_printf(MSG_ERROR,
+                          "privsep-set-params priv-sock: bind(PF_UNIX): %s",
+                          strerror(errno));
                close(drv->priv_socket);
                drv->priv_socket = -1;
                unlink(drv->own_socket_path);
@@ -654,7 +661,7 @@ static int wpa_driver_privsep_set_param(void *priv, const char *param)
 
        drv->cmd_socket = socket(PF_UNIX, SOCK_DGRAM, 0);
        if (drv->cmd_socket < 0) {
-               perror("socket(PF_UNIX)");
+               wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
                os_free(drv->own_cmd_path);
                drv->own_cmd_path = NULL;
                return -1;
@@ -665,7 +672,9 @@ static int wpa_driver_privsep_set_param(void *priv, const char *param)
        os_strlcpy(addr.sun_path, drv->own_cmd_path, sizeof(addr.sun_path));
        if (bind(drv->cmd_socket, (struct sockaddr *) &addr, sizeof(addr)) < 0)
        {
-               perror("privsep-set-params cmd-sock: bind(PF_UNIX)");
+               wpa_printf(MSG_ERROR,
+                          "privsep-set-params cmd-sock: bind(PF_UNIX): %s",
+                          strerror(errno));
                close(drv->cmd_socket);
                drv->cmd_socket = -1;
                unlink(drv->own_cmd_path);
index 0a9078a..d3e0595 100644 (file)
@@ -91,7 +91,8 @@ static u16 wpa_driver_roboswitch_mdio_read(
        mii->reg_num = reg;
 
        if (ioctl(drv->fd, SIOCGMIIREG, &drv->ifr) < 0) {
-               perror("ioctl[SIOCGMIIREG]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCGMIIREG]: %s",
+                          strerror(errno));
                return 0x00;
        }
        return mii->val_out;
@@ -108,7 +109,8 @@ static void wpa_driver_roboswitch_mdio_write(
        mii->val_in = val;
 
        if (ioctl(drv->fd, SIOCSMIIREG, &drv->ifr) < 0) {
-               perror("ioctl[SIOCSMIIREG");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCSMIIREG]: %s",
+                          strerror(errno));
        }
 }
 
@@ -260,17 +262,17 @@ static int wpa_driver_roboswitch_join(struct wpa_driver_roboswitch_data *drv,
                                            ROBO_ARLCTRL_CONF, read1, 1);
        } else {
                /* if both multiport addresses are the same we can add */
-               wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
-                                          ROBO_ARLCTRL_ADDR_1, read1, 3);
-               wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
-                                          ROBO_ARLCTRL_ADDR_2, read2, 3);
-               if (os_memcmp(read1, read2, 6) != 0)
+               if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+                                              ROBO_ARLCTRL_ADDR_1, read1, 3) ||
+                   wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+                                              ROBO_ARLCTRL_ADDR_2, read2, 3) ||
+                   os_memcmp(read1, read2, 6) != 0)
                        return -1;
-               wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
-                                          ROBO_ARLCTRL_VEC_1, read1, 1);
-               wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
-                                          ROBO_ARLCTRL_VEC_2, read2, 1);
-               if (read1[0] != read2[0])
+               if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+                                              ROBO_ARLCTRL_VEC_1, read1, 1) ||
+                   wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+                                              ROBO_ARLCTRL_VEC_2, read2, 1) ||
+                   read1[0] != read2[0])
                        return -1;
                wpa_driver_roboswitch_write(drv, ROBO_ARLCTRL_PAGE,
                                            ROBO_ARLCTRL_ADDR_1, addr_be16, 3);
@@ -394,7 +396,8 @@ static void * wpa_driver_roboswitch_init(void *ctx, const char *ifname)
        os_memset(&drv->ifr, 0, sizeof(drv->ifr));
        os_strlcpy(drv->ifr.ifr_name, drv->ifname, IFNAMSIZ);
        if (ioctl(drv->fd, SIOCGMIIPHY, &drv->ifr) < 0) {
-               perror("ioctl[SIOCGMIIPHY]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCGMIIPHY]: %s",
+                          strerror(errno));
                os_free(drv);
                return NULL;
        }
index 541ebcc..66edfa7 100644 (file)
@@ -28,7 +28,6 @@
 #include "common/ieee802_11_defs.h"
 #include "crypto/sha1.h"
 #include "l2_packet/l2_packet.h"
-#include "p2p/p2p.h"
 #include "wps/wps.h"
 #include "driver.h"
 
@@ -102,20 +101,6 @@ struct wpa_driver_test_data {
        unsigned int remain_on_channel_duration;
 
        int current_freq;
-
-       struct p2p_data *p2p;
-       unsigned int off_channel_freq;
-       struct wpabuf *pending_action_tx;
-       u8 pending_action_src[ETH_ALEN];
-       u8 pending_action_dst[ETH_ALEN];
-       u8 pending_action_bssid[ETH_ALEN];
-       unsigned int pending_action_freq;
-       unsigned int pending_action_no_cck;
-       unsigned int pending_listen_freq;
-       unsigned int pending_listen_duration;
-       int pending_p2p_scan;
-       struct sockaddr *probe_from;
-       socklen_t probe_from_len;
 };
 
 
@@ -125,7 +110,6 @@ static int wpa_driver_test_attach(struct wpa_driver_test_data *drv,
 static void wpa_driver_test_close_test_socket(
        struct wpa_driver_test_data *drv);
 static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx);
-static int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv);
 
 
 static void test_driver_free_bss(struct test_driver_bss *bss)
@@ -479,34 +463,6 @@ static int wpa_driver_test_send_mlme(void *priv, const u8 *data,
        event.tx_status.ack = ret >= 0;
        wpa_supplicant_event(drv->ctx, EVENT_TX_STATUS, &event);
 
-#ifdef CONFIG_P2P
-       if (drv->p2p &&
-           WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
-           WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) {
-               if (drv->pending_action_tx == NULL) {
-                       wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - "
-                                  "no pending operation");
-                       return ret;
-               }
-
-               if (os_memcmp(hdr->addr1, drv->pending_action_dst, ETH_ALEN) !=
-                   0) {
-                       wpa_printf(MSG_DEBUG, "P2P: Ignore Action TX status - "
-                                  "unknown destination address");
-                       return ret;
-               }
-
-               wpabuf_free(drv->pending_action_tx);
-               drv->pending_action_tx = NULL;
-
-               p2p_send_action_cb(drv->p2p, drv->pending_action_freq,
-                                  drv->pending_action_dst,
-                                  drv->pending_action_src,
-                                  drv->pending_action_bssid,
-                                  ret >= 0);
-       }
-#endif /* CONFIG_P2P */
-
        return ret;
 }
 
@@ -553,10 +509,6 @@ static void test_driver_scan(struct wpa_driver_test_data *drv,
                event.rx_probe_req.ie = ie;
                event.rx_probe_req.ie_len = ielen;
                wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ, &event);
-#ifdef CONFIG_P2P
-               if (drv->p2p)
-                       p2p_probe_req_rx(drv->p2p, sa, NULL, NULL, ie, ielen);
-#endif /* CONFIG_P2P */
        }
 
        dl_list_for_each(bss, &drv->bss, struct test_driver_bss, list) {
@@ -1059,7 +1011,7 @@ static int test_driver_if_add(void *priv, enum wpa_driver_if_type type,
                              const char *ifname, const u8 *addr,
                              void *bss_ctx, void **drv_priv,
                              char *force_ifname, u8 *if_addr,
-                             const char *bridge)
+                             const char *bridge, int use_existing)
 {
        struct test_driver_bss *dbss = priv;
        struct wpa_driver_test_data *drv = dbss->drv;
@@ -1313,25 +1265,7 @@ static void wpa_driver_test_poll(void *eloop_ctx, void *timeout_ctx)
 
 static void wpa_driver_test_scan_timeout(void *eloop_ctx, void *timeout_ctx)
 {
-       struct wpa_driver_test_data *drv = eloop_ctx;
        wpa_printf(MSG_DEBUG, "Scan timeout - try to get results");
-       if (drv->pending_p2p_scan && drv->p2p) {
-#ifdef CONFIG_P2P
-               size_t i;
-               struct os_time now;
-               os_get_time(&now);
-               for (i = 0; i < drv->num_scanres; i++) {
-                       struct wpa_scan_res *bss = drv->scanres[i];
-                       if (p2p_scan_res_handler(drv->p2p, bss->bssid,
-                                                bss->freq, &now, bss->level,
-                                                (const u8 *) (bss + 1),
-                                                bss->ie_len) > 0)
-                               return;
-               }
-               p2p_scan_res_handled(drv->p2p);
-#endif /* CONFIG_P2P */
-               return;
-       }
        wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL);
 }
 
@@ -1544,7 +1478,7 @@ static int wpa_driver_test_associate(
        struct wpa_driver_test_data *drv = dbss->drv;
        wpa_printf(MSG_DEBUG, "%s: priv=%p freq=%d pairwise_suite=%d "
                   "group_suite=%d key_mgmt_suite=%d auth_alg=%d mode=%d",
-                  __func__, priv, params->freq, params->pairwise_suite,
+                  __func__, priv, params->freq.freq, params->pairwise_suite,
                   params->group_suite, params->key_mgmt_suite,
                   params->auth_alg, params->mode);
        wpa_driver_update_mode(drv, params->mode == IEEE80211_MODE_AP);
@@ -1595,7 +1529,8 @@ static int wpa_driver_test_associate(
 #endif /* DRIVER_TEST_UNIX */
 
        if (params->mode == IEEE80211_MODE_AP) {
-               os_memcpy(dbss->ssid, params->ssid, params->ssid_len);
+               if (params->ssid)
+                       os_memcpy(dbss->ssid, params->ssid, params->ssid_len);
                dbss->ssid_len = params->ssid_len;
                os_memcpy(dbss->bssid, drv->own_addr, ETH_ALEN);
                if (params->wpa_ie && params->wpa_ie_len) {
@@ -1616,8 +1551,9 @@ static int wpa_driver_test_associate(
                                  MAC2STR(drv->own_addr));
                if (ret >= 0 && ret < end - pos)
                        pos += ret;
-               pos += wpa_snprintf_hex(pos, end - pos, params->ssid,
-                                       params->ssid_len);
+               if (params->ssid)
+                       pos += wpa_snprintf_hex(pos, end - pos, params->ssid,
+                                               params->ssid_len);
                ret = os_snprintf(pos, end - pos, " ");
                if (ret >= 0 && ret < end - pos)
                        pos += ret;
@@ -1641,12 +1577,15 @@ static int wpa_driver_test_associate(
                        return -1;
                }
 
-               os_memcpy(dbss->ssid, params->ssid, params->ssid_len);
+               if (params->ssid)
+                       os_memcpy(dbss->ssid, params->ssid, params->ssid_len);
                dbss->ssid_len = params->ssid_len;
        } else {
                drv->associated = 1;
                if (params->mode == IEEE80211_MODE_IBSS) {
-                       os_memcpy(dbss->ssid, params->ssid, params->ssid_len);
+                       if (params->ssid)
+                               os_memcpy(dbss->ssid, params->ssid,
+                                         params->ssid_len);
                        dbss->ssid_len = params->ssid_len;
                        if (params->bssid)
                                os_memcpy(dbss->bssid, params->bssid,
@@ -1949,30 +1888,8 @@ static void wpa_driver_test_mlme(struct wpa_driver_test_data *drv,
                                data_len - (mgmt->u.probe_req.variable - data);
                        wpa_supplicant_event(drv->ctx, EVENT_RX_PROBE_REQ,
                                             &event);
-#ifdef CONFIG_P2P
-                       if (drv->p2p)
-                               p2p_probe_req_rx(drv->p2p, mgmt->sa,
-                                                mgmt->da, mgmt->bssid,
-                                                event.rx_probe_req.ie,
-                                                event.rx_probe_req.ie_len);
-#endif /* CONFIG_P2P */
                }
        }
-
-#ifdef CONFIG_P2P
-       if (drv->p2p &&
-           WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
-           WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_ACTION) {
-               size_t hdr_len;
-               hdr_len = (const u8 *)
-                       &mgmt->u.action.u.vs_public_action.action - data;
-               p2p_rx_action(drv->p2p, mgmt->da, mgmt->sa, mgmt->bssid,
-                             mgmt->u.action.category,
-                             &mgmt->u.action.u.vs_public_action.action,
-                             data_len - hdr_len, freq);
-       }
-#endif /* CONFIG_P2P */
-
 }
 
 
@@ -1988,31 +1905,8 @@ static void wpa_driver_test_scan_cmd(struct wpa_driver_test_data *drv,
        bss = dl_list_first(&drv->bss, struct test_driver_bss, list);
 
        /* data: optional [ STA-addr | ' ' | IEs(hex) ] */
-#ifdef CONFIG_P2P
-       if (drv->probe_req_report && drv->p2p && data_len) {
-               const char *d = (const char *) data;
-               u8 sa[ETH_ALEN];
-               u8 ie[512];
-               size_t ielen;
-
-               if (hwaddr_aton(d, sa))
-                       return;
-               d += 18;
-               while (*d == ' ')
-                       d++;
-               ielen = os_strlen(d) / 2;
-               if (ielen > sizeof(ie))
-                       ielen = sizeof(ie);
-               if (hexstr2bin(d, ie, ielen) < 0)
-                       ielen = 0;
-               drv->probe_from = from;
-               drv->probe_from_len = fromlen;
-               p2p_probe_req_rx(drv->p2p, sa, NULL, NULL, ie, ielen);
-               drv->probe_from = NULL;
-       }
-#endif /* CONFIG_P2P */
 
-       if (!drv->ibss)
+       if (bss == NULL || !drv->ibss)
                return;
 
        pos = buf;
@@ -2167,12 +2061,6 @@ static void wpa_driver_test_deinit(void *priv)
        struct test_client_socket *cli, *prev;
        int i;
 
-#ifdef CONFIG_P2P
-       if (drv->p2p)
-               p2p_deinit(drv->p2p);
-       wpabuf_free(drv->pending_action_tx);
-#endif /* CONFIG_P2P */
-
        cli = drv->cli;
        while (cli) {
                prev = cli;
@@ -2369,13 +2257,6 @@ static int wpa_driver_test_set_param(void *priv, const char *param)
                drv->use_associnfo = 1;
        }
 
-       if (os_strstr(param, "p2p_mgmt=1")) {
-               wpa_printf(MSG_DEBUG, "test_driver: Use internal P2P "
-                          "management");
-               if (wpa_driver_test_init_p2p(drv) < 0)
-                       return -1;
-       }
-
        return 0;
 }
 
@@ -2465,8 +2346,6 @@ static int wpa_driver_test_send_eapol(void *priv, const u8 *dest, u16 proto,
 
 static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa)
 {
-       struct test_driver_bss *dbss = priv;
-       struct wpa_driver_test_data *drv = dbss->drv;
        os_memset(capa, 0, sizeof(*capa));
        capa->key_mgmt = WPA_DRIVER_CAPA_KEY_MGMT_WPA |
                WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
@@ -2482,8 +2361,6 @@ static int wpa_driver_test_get_capa(void *priv, struct wpa_driver_capa *capa)
        capa->auth = WPA_DRIVER_AUTH_OPEN |
                WPA_DRIVER_AUTH_SHARED |
                WPA_DRIVER_AUTH_LEAP;
-       if (drv->p2p)
-               capa->flags |= WPA_DRIVER_FLAGS_P2P_MGMT;
        capa->flags |= WPA_DRIVER_FLAGS_AP;
        capa->flags |= WPA_DRIVER_FLAGS_P2P_CONCURRENT;
        capa->flags |= WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE;
@@ -2691,33 +2568,6 @@ static int wpa_driver_test_send_action(void *priv, unsigned int freq,
 }
 
 
-#ifdef CONFIG_P2P
-static void test_send_action_cb(void *eloop_ctx, void *timeout_ctx)
-{
-       struct wpa_driver_test_data *drv = eloop_ctx;
-
-       if (drv->pending_action_tx == NULL)
-               return;
-
-       if (drv->off_channel_freq != drv->pending_action_freq) {
-               wpa_printf(MSG_DEBUG, "P2P: Pending Action frame TX "
-                          "waiting for another freq=%u",
-                          drv->pending_action_freq);
-               return;
-       }
-       wpa_printf(MSG_DEBUG, "P2P: Sending pending Action frame to "
-                  MACSTR, MAC2STR(drv->pending_action_dst));
-       wpa_driver_test_send_action(drv, drv->pending_action_freq, 0,
-                                   drv->pending_action_dst,
-                                   drv->pending_action_src,
-                                   drv->pending_action_bssid,
-                                   wpabuf_head(drv->pending_action_tx),
-                                   wpabuf_len(drv->pending_action_tx),
-                                   drv->pending_action_no_cck);
-}
-#endif /* CONFIG_P2P */
-
-
 static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx)
 {
        struct wpa_driver_test_data *drv = eloop_ctx;
@@ -2729,9 +2579,6 @@ static void test_remain_on_channel_timeout(void *eloop_ctx, void *timeout_ctx)
        data.remain_on_channel.freq = drv->remain_on_channel_freq;
        data.remain_on_channel.duration = drv->remain_on_channel_duration;
 
-       if (drv->p2p)
-               drv->off_channel_freq = 0;
-
        drv->remain_on_channel_freq = 0;
 
        wpa_supplicant_event(drv->ctx, EVENT_CANCEL_REMAIN_ON_CHANNEL, &data);
@@ -2765,18 +2612,6 @@ static int wpa_driver_test_remain_on_channel(void *priv, unsigned int freq,
        data.remain_on_channel.duration = duration;
        wpa_supplicant_event(drv->ctx, EVENT_REMAIN_ON_CHANNEL, &data);
 
-#ifdef CONFIG_P2P
-       if (drv->p2p) {
-               drv->off_channel_freq = drv->remain_on_channel_freq;
-               test_send_action_cb(drv, NULL);
-               if (drv->off_channel_freq == drv->pending_listen_freq) {
-                       p2p_listen_cb(drv->p2p, drv->pending_listen_freq,
-                                     drv->pending_listen_duration);
-                       drv->pending_listen_freq = 0;
-               }
-       }
-#endif /* CONFIG_P2P */
-
        return 0;
 }
 
@@ -2804,470 +2639,6 @@ static int wpa_driver_test_probe_req_report(void *priv, int report)
 }
 
 
-#ifdef CONFIG_P2P
-
-static int wpa_driver_test_p2p_find(void *priv, unsigned int timeout, int type)
-{
-       struct test_driver_bss *dbss = priv;
-       struct wpa_driver_test_data *drv = dbss->drv;
-       wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout);
-       if (!drv->p2p)
-               return -1;
-       return p2p_find(drv->p2p, timeout, type, 0, NULL, NULL, 0);
-}
-
-
-static int wpa_driver_test_p2p_stop_find(void *priv)
-{
-       struct test_driver_bss *dbss = priv;
-       struct wpa_driver_test_data *drv = dbss->drv;
-       wpa_printf(MSG_DEBUG, "%s", __func__);
-       if (!drv->p2p)
-               return -1;
-       p2p_stop_find(drv->p2p);
-       return 0;
-}
-
-
-static int wpa_driver_test_p2p_listen(void *priv, unsigned int timeout)
-{
-       struct test_driver_bss *dbss = priv;
-       struct wpa_driver_test_data *drv = dbss->drv;
-       wpa_printf(MSG_DEBUG, "%s(timeout=%u)", __func__, timeout);
-       if (!drv->p2p)
-               return -1;
-       return p2p_listen(drv->p2p, timeout);
-}
-
-
-static int wpa_driver_test_p2p_connect(void *priv, const u8 *peer_addr,
-                                      int wps_method, int go_intent,
-                                      const u8 *own_interface_addr,
-                                      unsigned int force_freq,
-                                      int persistent_group)
-{
-       struct test_driver_bss *dbss = priv;
-       struct wpa_driver_test_data *drv = dbss->drv;
-       wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR " wps_method=%d "
-                  "go_intent=%d "
-                  "own_interface_addr=" MACSTR " force_freq=%u "
-                  "persistent_group=%d)",
-                  __func__, MAC2STR(peer_addr), wps_method, go_intent,
-                  MAC2STR(own_interface_addr), force_freq, persistent_group);
-       if (!drv->p2p)
-               return -1;
-       return p2p_connect(drv->p2p, peer_addr, wps_method, go_intent,
-                          own_interface_addr, force_freq, persistent_group,
-                          NULL, 0, 0, 0);
-}
-
-
-static int wpa_driver_test_wps_success_cb(void *priv, const u8 *peer_addr)
-{
-       struct test_driver_bss *dbss = priv;
-       struct wpa_driver_test_data *drv = dbss->drv;
-       wpa_printf(MSG_DEBUG, "%s(peer_addr=" MACSTR ")",
-                  __func__, MAC2STR(peer_addr));
-       if (!drv->p2p)
-               return -1;
-       p2p_wps_success_cb(drv->p2p, peer_addr);
-       return 0;
-}
-
-
-static int wpa_driver_test_p2p_group_formation_failed(void *priv)
-{
-       struct test_driver_bss *dbss = priv;
-       struct wpa_driver_test_data *drv = dbss->drv;
-       wpa_printf(MSG_DEBUG, "%s", __func__);
-       if (!drv->p2p)
-               return -1;
-       p2p_group_formation_failed(drv->p2p);
-       return 0;
-}
-
-
-static int wpa_driver_test_p2p_set_params(void *priv,
-                                         const struct p2p_params *params)
-{
-       struct test_driver_bss *dbss = priv;
-       struct wpa_driver_test_data *drv = dbss->drv;
-       wpa_printf(MSG_DEBUG, "%s", __func__);
-       if (!drv->p2p)
-               return -1;
-       if (p2p_set_dev_name(drv->p2p, params->dev_name) < 0 ||
-           p2p_set_pri_dev_type(drv->p2p, params->pri_dev_type) < 0 ||
-           p2p_set_sec_dev_types(drv->p2p, params->sec_dev_type,
-                                 params->num_sec_dev_types) < 0)
-               return -1;
-       return 0;
-}
-
-
-static int test_p2p_scan(void *ctx, enum p2p_scan_type type, int freq,
-                        unsigned int num_req_dev_types,
-                        const u8 *req_dev_types, const u8 *dev_id, u16 pw_id)
-{
-       struct wpa_driver_test_data *drv = ctx;
-       struct wpa_driver_scan_params params;
-       int ret;
-       struct wpabuf *wps_ie, *ies;
-       int social_channels[] = { 2412, 2437, 2462, 0, 0 };
-       size_t ielen;
-
-       wpa_printf(MSG_DEBUG, "%s(type=%d freq=%d)",
-                  __func__, type, freq);
-
-       os_memset(&params, 0, sizeof(params));
-
-       /* P2P Wildcard SSID */
-       params.num_ssids = 1;
-       params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
-       params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
-
-#if 0 /* TODO: WPS IE */
-       wpa_s->wps->dev.p2p = 1;
-       wps_ie = wps_build_probe_req_ie(pw_id, &wpa_s->wps->dev,
-                                       wpa_s->wps->uuid, WPS_REQ_ENROLLEE);
-#else
-       wps_ie = wpabuf_alloc(1);
-#endif
-       if (wps_ie == NULL)
-               return -1;
-
-       ielen = p2p_scan_ie_buf_len(drv->p2p);
-       ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen);
-       if (ies == NULL) {
-               wpabuf_free(wps_ie);
-               return -1;
-       }
-       wpabuf_put_buf(ies, wps_ie);
-       wpabuf_free(wps_ie);
-
-       p2p_scan_ie(drv->p2p, ies, dev_id);
-
-       params.extra_ies = wpabuf_head(ies);
-       params.extra_ies_len = wpabuf_len(ies);
-
-       switch (type) {
-       case P2P_SCAN_SOCIAL:
-               params.freqs = social_channels;
-               break;
-       case P2P_SCAN_FULL:
-               break;
-       case P2P_SCAN_SOCIAL_PLUS_ONE:
-               social_channels[3] = freq;
-               params.freqs = social_channels;
-               break;
-       }
-
-       drv->pending_p2p_scan = 1;
-       ret = wpa_driver_test_scan(drv, &params);
-
-       wpabuf_free(ies);
-
-       return ret;
-}
-
-
-static int test_send_action(void *ctx, unsigned int freq, const u8 *dst,
-                           const u8 *src, const u8 *bssid, const u8 *buf,
-                           size_t len, unsigned int wait_time)
-{
-       struct wpa_driver_test_data *drv = ctx;
-
-       wpa_printf(MSG_DEBUG, "%s(freq=%u dst=" MACSTR " src=" MACSTR
-                  " bssid=" MACSTR " len=%d",
-                  __func__, freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
-                  (int) len);
-       if (freq <= 0) {
-               wpa_printf(MSG_WARNING, "P2P: No frequency specified for "
-                          "action frame TX");
-               return -1;
-       }
-
-       if (drv->pending_action_tx) {
-               wpa_printf(MSG_DEBUG, "P2P: Dropped pending Action frame TX "
-                          "to " MACSTR, MAC2STR(drv->pending_action_dst));
-               wpabuf_free(drv->pending_action_tx);
-       }
-       drv->pending_action_tx = wpabuf_alloc(len);
-       if (drv->pending_action_tx == NULL)
-               return -1;
-       wpabuf_put_data(drv->pending_action_tx, buf, len);
-       os_memcpy(drv->pending_action_src, src, ETH_ALEN);
-       os_memcpy(drv->pending_action_dst, dst, ETH_ALEN);
-       os_memcpy(drv->pending_action_bssid, bssid, ETH_ALEN);
-       drv->pending_action_freq = freq;
-       drv->pending_action_no_cck = 1;
-
-       if (drv->off_channel_freq == freq) {
-               /* Already on requested channel; send immediately */
-               /* TODO: Would there ever be need to extend the current
-                * duration on the channel? */
-               eloop_cancel_timeout(test_send_action_cb, drv, NULL);
-               eloop_register_timeout(0, 0, test_send_action_cb, drv, NULL);
-               return 0;
-       }
-
-       wpa_printf(MSG_DEBUG, "P2P: Schedule Action frame to be transmitted "
-                  "once the driver gets to the requested channel");
-       if (wpa_driver_test_remain_on_channel(drv, freq, wait_time) < 0) {
-               wpa_printf(MSG_DEBUG, "P2P: Failed to request driver "
-                          "to remain on channel (%u MHz) for Action "
-                          "Frame TX", freq);
-               return -1;
-       }
-
-       return 0;
-}
-
-
-static void test_send_action_done(void *ctx)
-{
-       wpa_printf(MSG_DEBUG, "%s", __func__);
-       /* TODO */
-}
-
-
-static void test_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
-{
-       struct wpa_driver_test_data *drv = ctx;
-       union wpa_event_data event;
-       wpa_printf(MSG_DEBUG, "%s", __func__);
-       os_memset(&event, 0, sizeof(event));
-       event.p2p_go_neg_completed.res = res;
-       wpa_supplicant_event(drv->ctx, EVENT_P2P_GO_NEG_COMPLETED, &event);
-}
-
-
-static void test_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id)
-{
-       struct wpa_driver_test_data *drv = ctx;
-       union wpa_event_data event;
-       wpa_printf(MSG_DEBUG, "%s(src=" MACSTR ")", __func__, MAC2STR(src));
-       os_memset(&event, 0, sizeof(event));
-       event.p2p_go_neg_req_rx.src = src;
-       event.p2p_go_neg_req_rx.dev_passwd_id = dev_passwd_id;
-       wpa_supplicant_event(drv->ctx, EVENT_P2P_GO_NEG_REQ_RX, &event);
-}
-
-
-static void test_dev_found(void *ctx, const u8 *addr,
-                          const struct p2p_peer_info *info, int new_device)
-{
-       struct wpa_driver_test_data *drv = ctx;
-       union wpa_event_data event;
-       char devtype[WPS_DEV_TYPE_BUFSIZE];
-       wpa_printf(MSG_DEBUG, "%s(" MACSTR " p2p_dev_addr=" MACSTR
-                  " pri_dev_type=%s name='%s' config_methods=0x%x "
-                  "dev_capab=0x%x group_capab=0x%x)",
-                  __func__, MAC2STR(addr), MAC2STR(info->p2p_device_addr),
-                  wps_dev_type_bin2str(info->pri_dev_type, devtype,
-                                       sizeof(devtype)),
-                  info->device_name, info->config_methods, info->dev_capab,
-                  info->group_capab);
-
-       os_memset(&event, 0, sizeof(event));
-       event.p2p_dev_found.addr = addr;
-       event.p2p_dev_found.dev_addr = info->p2p_device_addr;
-       event.p2p_dev_found.pri_dev_type = info->pri_dev_type;
-       event.p2p_dev_found.dev_name = info->device_name;
-       event.p2p_dev_found.config_methods = info->config_methods;
-       event.p2p_dev_found.dev_capab = info->dev_capab;
-       event.p2p_dev_found.group_capab = info->group_capab;
-       wpa_supplicant_event(drv->ctx, EVENT_P2P_DEV_FOUND, &event);
-}
-
-
-static int test_start_listen(void *ctx, unsigned int freq,
-                            unsigned int duration,
-                            const struct wpabuf *probe_resp_ie)
-{
-       struct wpa_driver_test_data *drv = ctx;
-
-       wpa_printf(MSG_DEBUG, "%s(freq=%u duration=%u)",
-                  __func__, freq, duration);
-
-       if (wpa_driver_test_probe_req_report(drv, 1) < 0)
-               return -1;
-
-       drv->pending_listen_freq = freq;
-       drv->pending_listen_duration = duration;
-
-       if (wpa_driver_test_remain_on_channel(drv, freq, duration) < 0) {
-               drv->pending_listen_freq = 0;
-               return -1;
-       }
-
-       return 0;
-}
-
-
-static void test_stop_listen(void *ctx)
-{
-       wpa_printf(MSG_DEBUG, "%s", __func__);
-       /* TODO */
-}
-
-
-static int test_send_probe_resp(void *ctx, const struct wpabuf *buf)
-{
-       struct wpa_driver_test_data *drv = ctx;
-       char resp[512], *pos, *end;
-       int ret;
-       const struct ieee80211_mgmt *mgmt;
-       const u8 *ie, *ie_end;
-
-       wpa_printf(MSG_DEBUG, "%s", __func__);
-       wpa_hexdump_buf(MSG_MSGDUMP, "Probe Response", buf);
-       if (wpabuf_len(buf) < 24)
-               return -1;
-       if (!drv->probe_from) {
-               wpa_printf(MSG_DEBUG, "%s: probe_from not set", __func__);
-               return -1;
-       }
-
-       pos = resp;
-       end = resp + sizeof(resp);
-
-       mgmt = wpabuf_head(buf);
-
-       /* reply: SCANRESP BSSID SSID IEs */
-       ret = os_snprintf(pos, end - pos, "SCANRESP " MACSTR " ",
-                         MAC2STR(mgmt->bssid));
-       if (ret < 0 || ret >= end - pos)
-               return -1;
-       pos += ret;
-
-       ie = mgmt->u.probe_resp.variable;
-       ie_end = wpabuf_head_u8(buf) + wpabuf_len(buf);
-       if (ie_end - ie < 2 || ie[0] != WLAN_EID_SSID ||
-           ie + 2 + ie[1] > ie_end)
-               return -1;
-       pos += wpa_snprintf_hex(pos, end - pos, ie + 2, ie[1]);
-
-       ret = os_snprintf(pos, end - pos, " ");
-       if (ret < 0 || ret >= end - pos)
-               return -1;
-       pos += ret;
-       pos += wpa_snprintf_hex(pos, end - pos, ie, ie_end - ie);
-
-       sendto(drv->test_socket, resp, pos - resp, 0,
-              drv->probe_from, drv->probe_from_len);
-
-       return 0;
-}
-
-
-static void test_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
-                           u16 update_indic, const u8 *tlvs, size_t tlvs_len)
-{
-       wpa_printf(MSG_DEBUG, "%s", __func__);
-       /* TODO */
-}
-
-
-static void test_sd_response(void *ctx, const u8 *sa, u16 update_indic,
-                            const u8 *tlvs, size_t tlvs_len)
-{
-       wpa_printf(MSG_DEBUG, "%s", __func__);
-       /* TODO */
-}
-
-
-static void test_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods,
-                              const u8 *dev_addr, const u8 *pri_dev_type,
-                              const char *dev_name, u16 supp_config_methods,
-                              u8 dev_capab, u8 group_capab,
-                              const u8 *group_id, size_t group_id_len)
-{
-       wpa_printf(MSG_DEBUG, "%s(peer=" MACSTR " config_methods=0x%x)",
-                  __func__, MAC2STR(peer), config_methods);
-       /* TODO */
-}
-
-
-static void test_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
-{
-       wpa_printf(MSG_DEBUG, "%s(peer=" MACSTR " config_methods=0x%x)",
-                  __func__, MAC2STR(peer), config_methods);
-       /* TODO */
-}
-
-
-static void test_p2p_debug_print(void *ctx, int level, const char *msg)
-{
-       wpa_printf(level, "P2P: %s", msg);
-}
-
-#endif /* CONFIG_P2P */
-
-
-static int wpa_driver_test_init_p2p(struct wpa_driver_test_data *drv)
-{
-#ifdef CONFIG_P2P
-       struct p2p_config p2p;
-       unsigned int r;
-       int i;
-
-       os_memset(&p2p, 0, sizeof(p2p));
-       p2p.cb_ctx = drv;
-       p2p.debug_print = test_p2p_debug_print;
-       p2p.p2p_scan = test_p2p_scan;
-       p2p.send_action = test_send_action;
-       p2p.send_action_done = test_send_action_done;
-       p2p.go_neg_completed = test_go_neg_completed;
-       p2p.go_neg_req_rx = test_go_neg_req_rx;
-       p2p.dev_found = test_dev_found;
-       p2p.start_listen = test_start_listen;
-       p2p.stop_listen = test_stop_listen;
-       p2p.send_probe_resp = test_send_probe_resp;
-       p2p.sd_request = test_sd_request;
-       p2p.sd_response = test_sd_response;
-       p2p.prov_disc_req = test_prov_disc_req;
-       p2p.prov_disc_resp = test_prov_disc_resp;
-
-       os_memcpy(p2p.dev_addr, drv->own_addr, ETH_ALEN);
-
-       p2p.reg_class = 12; /* TODO: change depending on location */
-       /*
-        * Pick one of the social channels randomly as the listen
-        * channel.
-        */
-       os_get_random((u8 *) &r, sizeof(r));
-       p2p.channel = 1 + (r % 3) * 5;
-
-       /* TODO: change depending on location */
-       p2p.op_reg_class = 12;
-       /*
-        * For initial tests, pick the operation channel randomly.
-        * TODO: Use scan results (etc.) to select the best channel.
-        */
-       p2p.op_channel = 1 + r % 11;
-
-       os_memcpy(p2p.country, "US ", 3);
-
-       /* FIX: fetch available channels from the driver */
-       p2p.channels.reg_classes = 1;
-       p2p.channels.reg_class[0].reg_class = 12; /* US/12 = 2.4 GHz band */
-       p2p.channels.reg_class[0].channels = 11;
-       for (i = 0; i < 11; i++)
-               p2p.channels.reg_class[0].channel[i] = i + 1;
-
-       p2p.max_peers = 100;
-
-       drv->p2p = p2p_init(&p2p);
-       if (drv->p2p == NULL)
-               return -1;
-       return 0;
-#else /* CONFIG_P2P */
-       wpa_printf(MSG_INFO, "driver_test: P2P support not included");
-       return -1;
-#endif /* CONFIG_P2P */
-}
-
-
 const struct wpa_driver_ops wpa_driver_test_ops = {
        "test",
        "wpa_supplicant test driver",
@@ -3309,14 +2680,4 @@ const struct wpa_driver_ops wpa_driver_test_ops = {
        .remain_on_channel = wpa_driver_test_remain_on_channel,
        .cancel_remain_on_channel = wpa_driver_test_cancel_remain_on_channel,
        .probe_req_report = wpa_driver_test_probe_req_report,
-#ifdef CONFIG_P2P
-       .p2p_find = wpa_driver_test_p2p_find,
-       .p2p_stop_find = wpa_driver_test_p2p_stop_find,
-       .p2p_listen = wpa_driver_test_p2p_listen,
-       .p2p_connect = wpa_driver_test_p2p_connect,
-       .wps_success_cb = wpa_driver_test_wps_success_cb,
-       .p2p_group_formation_failed =
-       wpa_driver_test_p2p_group_formation_failed,
-       .p2p_set_params = wpa_driver_test_p2p_set_params,
-#endif /* CONFIG_P2P */
 };
old mode 100644 (file)
new mode 100755 (executable)
index 1401050..a1581b8
 #include "driver.h"
 #include "driver_wext.h"
 
-#ifdef ANDROID
-#include "android_drv.h"
-#endif /* ANDROID */
-
 static int wpa_driver_wext_flush_pmkid(void *priv);
 static int wpa_driver_wext_get_range(void *priv);
 static int wpa_driver_wext_finish_drv_init(struct wpa_driver_wext_data *drv);
@@ -82,7 +78,7 @@ int wpa_driver_wext_get_bssid(void *priv, u8 *bssid)
        os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
 
        if (ioctl(drv->ioctl_sock, SIOCGIWAP, &iwr) < 0) {
-               perror("ioctl[SIOCGIWAP]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCGIWAP]: %s", strerror(errno));
                ret = -1;
        }
        os_memcpy(bssid, iwr.u.ap_addr.sa_data, ETH_ALEN);
@@ -112,7 +108,7 @@ int wpa_driver_wext_set_bssid(void *priv, const u8 *bssid)
                os_memset(iwr.u.ap_addr.sa_data, 0, ETH_ALEN);
 
        if (ioctl(drv->ioctl_sock, SIOCSIWAP, &iwr) < 0) {
-               perror("ioctl[SIOCSIWAP]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCSIWAP]: %s", strerror(errno));
                ret = -1;
        }
 
@@ -138,7 +134,8 @@ int wpa_driver_wext_get_ssid(void *priv, u8 *ssid)
        iwr.u.essid.length = 32;
 
        if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) {
-               perror("ioctl[SIOCGIWESSID]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCGIWESSID]: %s",
+                          strerror(errno));
                ret = -1;
        } else {
                ret = iwr.u.essid.length;
@@ -196,7 +193,8 @@ int wpa_driver_wext_set_ssid(void *priv, const u8 *ssid, size_t ssid_len)
        iwr.u.essid.length = ssid_len;
 
        if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) {
-               perror("ioctl[SIOCSIWESSID]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCSIWESSID]: %s",
+                          strerror(errno));
                ret = -1;
        }
 
@@ -222,7 +220,8 @@ int wpa_driver_wext_set_freq(void *priv, int freq)
        iwr.u.freq.e = 1;
 
        if (ioctl(drv->ioctl_sock, SIOCSIWFREQ, &iwr) < 0) {
-               perror("ioctl[SIOCSIWFREQ]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCSIWFREQ]: %s",
+                          strerror(errno));
                ret = -1;
        }
 
@@ -299,14 +298,6 @@ wpa_driver_wext_event_wireless_custom(void *ctx, char *custom)
                }
                wpa_supplicant_event(ctx, EVENT_STKSTART, &data);
 #endif /* CONFIG_PEERKEY */
-#ifdef ANDROID
-       } else if (os_strncmp(custom, "STOP", 4) == 0) {
-               wpa_msg(ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STOPPED");
-       } else if (os_strncmp(custom, "START", 5) == 0) {
-               wpa_msg(ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "STARTED");
-       } else if (os_strncmp(custom, "HANG", 4) == 0) {
-               wpa_msg(ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE "HANGED");
-#endif /* ANDROID */
        }
 }
 
@@ -827,7 +818,8 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname)
 
        drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0);
        if (drv->ioctl_sock < 0) {
-               perror("socket(PF_INET,SOCK_DGRAM)");
+               wpa_printf(MSG_ERROR, "socket(PF_INET,SOCK_DGRAM): %s",
+                          strerror(errno));
                goto err1;
        }
 
@@ -858,12 +850,6 @@ void * wpa_driver_wext_init(void *ctx, const char *ifname)
 
        drv->mlme_sock = -1;
 
-#ifdef ANDROID
-       drv->errors = 0;
-       drv->driver_is_started = TRUE;
-       drv->bgscan_enabled = 0;
-#endif /* ANDROID */
-
        if (wpa_driver_wext_finish_drv_init(drv) < 0)
                goto err3;
 
@@ -1045,7 +1031,8 @@ int wpa_driver_wext_scan(void *priv, struct wpa_driver_scan_params *params)
        }
 
        if (ioctl(drv->ioctl_sock, SIOCSIWSCAN, &iwr) < 0) {
-               perror("ioctl[SIOCSIWSCAN]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCSIWSCAN]: %s",
+                          strerror(errno));
                ret = -1;
        }
 
@@ -1100,7 +1087,8 @@ static u8 * wpa_driver_wext_giwscan(struct wpa_driver_wext_data *drv,
                                   "trying larger buffer (%lu bytes)",
                                   (unsigned long) res_buf_len);
                } else {
-                       perror("ioctl[SIOCGIWSCAN]");
+                       wpa_printf(MSG_ERROR, "ioctl[SIOCGIWSCAN]: %s",
+                                  strerror(errno));
                        os_free(res_buf);
                        return NULL;
                }
@@ -1551,7 +1539,8 @@ static int wpa_driver_wext_get_range(void *priv)
                sizeof(range->enc_capa);
 
        if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) {
-               perror("ioctl[SIOCGIWRANGE]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCGIWRANGE]: %s",
+                          strerror(errno));
                os_free(range);
                return -1;
        } else if (iwr.u.data.length >= minlen &&
@@ -1586,8 +1575,9 @@ static int wpa_driver_wext_get_range(void *priv)
                drv->capa.max_scan_ssids = 1;
 
                wpa_printf(MSG_DEBUG, "  capabilities: key_mgmt 0x%x enc 0x%x "
-                          "flags 0x%x",
-                          drv->capa.key_mgmt, drv->capa.enc, drv->capa.flags);
+                          "flags 0x%llx",
+                          drv->capa.key_mgmt, drv->capa.enc,
+                          (unsigned long long) drv->capa.flags);
        } else {
                wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: too old (short) data - "
                           "assuming WPA is not supported");
@@ -1630,7 +1620,8 @@ static int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv,
 
        ret = ioctl(drv->ioctl_sock, SIOCSIWENCODEEXT, &iwr);
        if (ret < 0)
-               perror("ioctl[SIOCSIWENCODEEXT] PMK");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODEEXT] PMK: %s",
+                          strerror(errno));
        os_free(ext);
 
        return ret;
@@ -1722,7 +1713,8 @@ static int wpa_driver_wext_set_key_ext(void *priv, enum wpa_alg alg,
                        ret = -2;
                }
 
-               perror("ioctl[SIOCSIWENCODEEXT]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODEEXT]: %s",
+                          strerror(errno));
        }
 
        os_free(ext);
@@ -1796,7 +1788,8 @@ int wpa_driver_wext_set_key(const char *ifname, void *priv, enum wpa_alg alg,
        iwr.u.encoding.length = key_len;
 
        if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
-               perror("ioctl[SIOCSIWENCODE]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODE]: %s",
+                          strerror(errno));
                ret = -1;
        }
 
@@ -1808,7 +1801,9 @@ int wpa_driver_wext_set_key(const char *ifname, void *priv, enum wpa_alg alg,
                iwr.u.encoding.pointer = (caddr_t) NULL;
                iwr.u.encoding.length = 0;
                if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
-                       perror("ioctl[SIOCSIWENCODE] (set_tx)");
+                       wpa_printf(MSG_ERROR,
+                                  "ioctl[SIOCSIWENCODE] (set_tx): %s",
+                                  strerror(errno));
                        ret = -1;
                }
        }
@@ -1857,7 +1852,8 @@ static int wpa_driver_wext_mlme(struct wpa_driver_wext_data *drv,
        iwr.u.data.length = sizeof(mlme);
 
        if (ioctl(drv->ioctl_sock, SIOCSIWMLME, &iwr) < 0) {
-               perror("ioctl[SIOCSIWMLME]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMLME]: %s",
+                          strerror(errno));
                ret = -1;
        }
 
@@ -1869,10 +1865,8 @@ static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv)
 {
        struct iwreq iwr;
        const u8 null_bssid[ETH_ALEN] = { 0, 0, 0, 0, 0, 0 };
-#ifndef ANDROID
        u8 ssid[32];
        int i;
-#endif /* ANDROID */
 
        /*
         * Only force-disconnect when the card is in infrastructure mode,
@@ -1882,7 +1876,8 @@ static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv)
        os_memset(&iwr, 0, sizeof(iwr));
        os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
        if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) {
-               perror("ioctl[SIOCGIWMODE]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCGIWMODE]: %s",
+                          strerror(errno));
                iwr.u.mode = IW_MODE_INFRA;
        }
 
@@ -1893,7 +1888,6 @@ static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv)
                                   "selection on disconnect");
                }
 
-#ifndef ANDROID
                if (drv->cfg80211) {
                        /*
                         * cfg80211 supports SIOCSIWMLME commands, so there is
@@ -1919,7 +1913,6 @@ static void wpa_driver_wext_disconnect(struct wpa_driver_wext_data *drv)
                        wpa_printf(MSG_DEBUG, "WEXT: Failed to set bogus "
                                   "SSID to disconnect");
                }
-#endif /* ANDROID */
        }
 }
 
@@ -1949,7 +1942,8 @@ static int wpa_driver_wext_set_gen_ie(void *priv, const u8 *ie,
        iwr.u.data.length = ie_len;
 
        if (ioctl(drv->ioctl_sock, SIOCSIWGENIE, &iwr) < 0) {
-               perror("ioctl[SIOCSIWGENIE]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCSIWGENIE]: %s",
+                          strerror(errno));
                ret = -1;
        }
 
@@ -1960,15 +1954,15 @@ static int wpa_driver_wext_set_gen_ie(void *priv, const u8 *ie,
 int wpa_driver_wext_cipher2wext(int cipher)
 {
        switch (cipher) {
-       case CIPHER_NONE:
+       case WPA_CIPHER_NONE:
                return IW_AUTH_CIPHER_NONE;
-       case CIPHER_WEP40:
+       case WPA_CIPHER_WEP40:
                return IW_AUTH_CIPHER_WEP40;
-       case CIPHER_TKIP:
+       case WPA_CIPHER_TKIP:
                return IW_AUTH_CIPHER_TKIP;
-       case CIPHER_CCMP:
+       case WPA_CIPHER_CCMP:
                return IW_AUTH_CIPHER_CCMP;
-       case CIPHER_WEP104:
+       case WPA_CIPHER_WEP104:
                return IW_AUTH_CIPHER_WEP104;
        default:
                return 0;
@@ -1979,10 +1973,10 @@ int wpa_driver_wext_cipher2wext(int cipher)
 int wpa_driver_wext_keymgmt2wext(int keymgmt)
 {
        switch (keymgmt) {
-       case KEY_MGMT_802_1X:
-       case KEY_MGMT_802_1X_NO_WPA:
+       case WPA_KEY_MGMT_IEEE8021X:
+       case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
                return IW_AUTH_KEY_MGMT_802_1X;
-       case KEY_MGMT_PSK:
+       case WPA_KEY_MGMT_PSK:
                return IW_AUTH_KEY_MGMT_PSK;
        default:
                return 0;
@@ -2026,7 +2020,8 @@ wpa_driver_wext_auth_alg_fallback(struct wpa_driver_wext_data *drv,
        }
 
        if (ioctl(drv->ioctl_sock, SIOCSIWENCODE, &iwr) < 0) {
-               perror("ioctl[SIOCSIWENCODE]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCSIWENCODE]: %s",
+                          strerror(errno));
                ret = -1;
        }
 
@@ -2049,7 +2044,11 @@ int wpa_driver_wext_associate(void *priv,
                 * Stop cfg80211 from trying to associate before we are done
                 * with all parameters.
                 */
-               wpa_driver_wext_set_ssid(drv, (u8 *) "", 0);
+               if (wpa_driver_wext_set_ssid(drv, (u8 *) "", 0) < 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "WEXT: Failed to clear SSID to stop pending cfg80211 association attempts (if any)");
+                       /* continue anyway */
+               }
        }
 
        if (wpa_driver_wext_set_drop_unencrypted(drv, params->drop_unencrypted)
@@ -2078,12 +2077,12 @@ int wpa_driver_wext_associate(void *priv,
        if (wpa_driver_wext_set_gen_ie(drv, params->wpa_ie, params->wpa_ie_len)
            < 0)
                ret = -1;
-       if (params->wpa_ie == NULL || params->wpa_ie_len == 0)
-               value = IW_AUTH_WPA_VERSION_DISABLED;
-       else if (params->wpa_ie[0] == WLAN_EID_RSN)
+       if (params->wpa_proto & WPA_PROTO_RSN)
                value = IW_AUTH_WPA_VERSION_WPA2;
-       else
+       else if (params->wpa_proto & WPA_PROTO_WPA)
                value = IW_AUTH_WPA_VERSION_WPA;
+       else
+               value = IW_AUTH_WPA_VERSION_DISABLED;
        if (wpa_driver_wext_set_auth_param(drv,
                                           IW_AUTH_WPA_VERSION, value) < 0)
                ret = -1;
@@ -2099,10 +2098,10 @@ int wpa_driver_wext_associate(void *priv,
        if (wpa_driver_wext_set_auth_param(drv,
                                           IW_AUTH_KEY_MGMT, value) < 0)
                ret = -1;
-       value = params->key_mgmt_suite != KEY_MGMT_NONE ||
-               params->pairwise_suite != CIPHER_NONE ||
-               params->group_suite != CIPHER_NONE ||
-               params->wpa_ie_len;
+       value = params->key_mgmt_suite != WPA_KEY_MGMT_NONE ||
+               params->pairwise_suite != WPA_CIPHER_NONE ||
+               params->group_suite != WPA_CIPHER_NONE ||
+               (params->wpa_proto & (WPA_PROTO_RSN | WPA_PROTO_WPA));
        if (wpa_driver_wext_set_auth_param(drv,
                                           IW_AUTH_PRIVACY_INVOKED, value) < 0)
                ret = -1;
@@ -2110,8 +2109,8 @@ int wpa_driver_wext_associate(void *priv,
        /* Allow unencrypted EAPOL messages even if pairwise keys are set when
         * not using WPA. IEEE 802.1X specifies that these frames are not
         * encrypted, but WPA encrypts them when pairwise keys are in use. */
-       if (params->key_mgmt_suite == KEY_MGMT_802_1X ||
-           params->key_mgmt_suite == KEY_MGMT_PSK)
+       if (params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
+           params->key_mgmt_suite == WPA_KEY_MGMT_PSK)
                allow_unencrypted_eapol = 0;
        else
                allow_unencrypted_eapol = 1;
@@ -2137,7 +2136,8 @@ int wpa_driver_wext_associate(void *priv,
        if (wpa_driver_wext_set_auth_param(drv, IW_AUTH_MFP, value) < 0)
                ret = -1;
 #endif /* CONFIG_IEEE80211W */
-       if (params->freq && wpa_driver_wext_set_freq(drv, params->freq) < 0)
+       if (params->freq.freq &&
+           wpa_driver_wext_set_freq(drv, params->freq.freq) < 0)
                ret = -1;
        if (!drv->cfg80211 &&
            wpa_driver_wext_set_ssid(drv, params->ssid, params->ssid_len) < 0)
@@ -2198,7 +2198,8 @@ int wpa_driver_wext_set_mode(void *priv, int mode)
        }
 
        if (errno != EBUSY) {
-               perror("ioctl[SIOCSIWMODE]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMODE]: %s",
+                          strerror(errno));
                goto done;
        }
 
@@ -2207,7 +2208,8 @@ int wpa_driver_wext_set_mode(void *priv, int mode)
         * down, try to set the mode again, and bring it back up.
         */
        if (ioctl(drv->ioctl_sock, SIOCGIWMODE, &iwr) < 0) {
-               perror("ioctl[SIOCGIWMODE]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCGIWMODE]: %s",
+                          strerror(errno));
                goto done;
        }
 
@@ -2220,7 +2222,8 @@ int wpa_driver_wext_set_mode(void *priv, int mode)
                /* Try to set the mode again while the interface is down */
                iwr.u.mode = new_mode;
                if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0)
-                       perror("ioctl[SIOCSIWMODE]");
+                       wpa_printf(MSG_ERROR, "ioctl[SIOCSIWMODE]: %s",
+                                  strerror(errno));
                else
                        ret = 0;
 
@@ -2253,7 +2256,8 @@ static int wpa_driver_wext_pmksa(struct wpa_driver_wext_data *drv,
 
        if (ioctl(drv->ioctl_sock, SIOCSIWPMKSA, &iwr) < 0) {
                if (errno != EOPNOTSUPP)
-                       perror("ioctl[SIOCSIWPMKSA]");
+                       wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPMKSA]: %s",
+                                  strerror(errno));
                ret = -1;
        }
 
@@ -2338,129 +2342,64 @@ static const char * wext_get_radio_name(void *priv)
 }
 
 
-#ifdef ANDROID
-
-static int android_wext_cmd(struct wpa_driver_wext_data *drv, const char *cmd)
+static int wpa_driver_wext_signal_poll(void *priv, struct wpa_signal_info *si)
 {
+       struct wpa_driver_wext_data *drv = priv;
+       struct iw_statistics stats;
        struct iwreq iwr;
-       char buf[MAX_DRV_CMD_SIZE];
-       int ret;
+
+       os_memset(si, 0, sizeof(*si));
+       si->current_signal = -9999;
+       si->current_noise = 9999;
+       si->chanwidth = CHAN_WIDTH_UNKNOWN;
 
        os_memset(&iwr, 0, sizeof(iwr));
        os_strlcpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
+       iwr.u.data.pointer = (caddr_t) &stats;
+       iwr.u.data.length = sizeof(stats);
+       iwr.u.data.flags = 1;
 
-       os_memset(buf, 0, sizeof(buf));
-       os_strlcpy(buf, cmd, sizeof(buf));
-
-       iwr.u.data.pointer = buf;
-       iwr.u.data.length = sizeof(buf);
-
-       ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr);
-
-       if (ret < 0) {
-               wpa_printf(MSG_ERROR, "%s failed (%d): %s", __func__, ret,
-                          cmd);
-               drv->errors++;
-               if (drv->errors > DRV_NUMBER_SEQUENTIAL_ERRORS) {
-                       drv->errors = 0;
-                       wpa_msg(drv->ctx, MSG_INFO, WPA_EVENT_DRIVER_STATE
-                               "HANGED");
-               }
-               return ret;
+       if (ioctl(drv->ioctl_sock, SIOCGIWSTATS, &iwr) < 0) {
+               wpa_printf(MSG_ERROR, "WEXT: SIOCGIWSTATS: %s",
+                          strerror(errno));
+               return -1;
        }
 
-       drv->errors = 0;
+       si->current_signal = stats.qual.level -
+               ((stats.qual.updated & IW_QUAL_DBM) ? 0x100 : 0);
+       si->current_noise = stats.qual.noise -
+               ((stats.qual.updated & IW_QUAL_DBM) ? 0x100 : 0);
        return 0;
 }
 
 
-static int wext_sched_scan(void *priv, struct wpa_driver_scan_params *params,
-                          u32 interval)
+static int wpa_driver_wext_status(void *priv, char *buf, size_t buflen)
 {
        struct wpa_driver_wext_data *drv = priv;
-       struct iwreq iwr;
-       int ret = 0, i = 0, bp;
-       char buf[WEXT_PNO_MAX_COMMAND_SIZE];
-
-       bp = WEXT_PNOSETUP_HEADER_SIZE;
-       os_memcpy(buf, WEXT_PNOSETUP_HEADER, bp);
-       buf[bp++] = WEXT_PNO_TLV_PREFIX;
-       buf[bp++] = WEXT_PNO_TLV_VERSION;
-       buf[bp++] = WEXT_PNO_TLV_SUBVERSION;
-       buf[bp++] = WEXT_PNO_TLV_RESERVED;
-
-       while (i < WEXT_PNO_AMOUNT && (size_t) i < params->num_ssids) {
-               /*
-                * Check that there is enough space needed for 1 more SSID, the
-                * other sections and null termination.
-                */
-               if ((bp + WEXT_PNO_SSID_HEADER_SIZE + IW_ESSID_MAX_SIZE +
-                    WEXT_PNO_NONSSID_SECTIONS_SIZE + 1) >= (int) sizeof(buf))
-                       break;
-
-               wpa_hexdump_ascii(MSG_DEBUG, "For PNO Scan",
-                                 params->ssids[i].ssid,
-                                 params->ssids[i].ssid_len);
-               buf[bp++] = WEXT_PNO_SSID_SECTION;
-               buf[bp++] = params->ssids[i].ssid_len;
-               os_memcpy(&buf[bp], params->ssids[i].ssid,
-                         params->ssids[i].ssid_len);
-               bp += params->ssids[i].ssid_len;
-               i++;
-       }
-
-       buf[bp++] = WEXT_PNO_SCAN_INTERVAL_SECTION;
-       /* TODO: consider using interval parameter (interval in msec) instead
-        * of hardcoded value here */
-       os_snprintf(&buf[bp], WEXT_PNO_SCAN_INTERVAL_LENGTH + 1, "%x",
-                   WEXT_PNO_SCAN_INTERVAL);
-       bp += WEXT_PNO_SCAN_INTERVAL_LENGTH;
-
-       buf[bp++] = WEXT_PNO_REPEAT_SECTION;
-       os_snprintf(&buf[bp], WEXT_PNO_REPEAT_LENGTH + 1, "%x",
-                   WEXT_PNO_REPEAT);
-       bp += WEXT_PNO_REPEAT_LENGTH;
-
-       buf[bp++] = WEXT_PNO_MAX_REPEAT_SECTION;
-       os_snprintf(&buf[bp], WEXT_PNO_MAX_REPEAT_LENGTH + 1, "%x",
-                   WEXT_PNO_MAX_REPEAT);
-       bp += WEXT_PNO_MAX_REPEAT_LENGTH + 1;
-
-       os_memset(&iwr, 0, sizeof(iwr));
-       os_strncpy(iwr.ifr_name, drv->ifname, IFNAMSIZ);
-       iwr.u.data.pointer = buf;
-       iwr.u.data.length = bp;
-
-       ret = ioctl(drv->ioctl_sock, SIOCSIWPRIV, &iwr);
-       if (ret < 0) {
-               wpa_printf(MSG_ERROR, "ioctl[SIOCSIWPRIV] (pnosetup): %d",
-                          ret);
-               drv->errors++;
-               if (drv->errors > DRV_NUMBER_SEQUENTIAL_ERRORS) {
-                       drv->errors = 0;
-                       wpa_msg(drv->ctx, MSG_INFO,
-                               WPA_EVENT_DRIVER_STATE "HANGED");
-               }
-               return ret;
-       }
+       int res;
+       char *pos, *end;
+       unsigned char addr[ETH_ALEN];
 
-       drv->errors = 0;
-       drv->bgscan_enabled = 1;
+       pos = buf;
+       end = buf + buflen;
 
-       return android_wext_cmd(drv, "PNOFORCE 1");
-}
+       if (linux_get_ifhwaddr(drv->ioctl_sock, drv->ifname, addr))
+               return -1;
 
+       res = os_snprintf(pos, end - pos,
+                         "ifindex=%d\n"
+                         "ifname=%s\n"
+                         "addr=" MACSTR "\n",
+                         drv->ifindex,
+                         drv->ifname,
+                         MAC2STR(addr));
+       if (os_snprintf_error(end - pos, res))
+               return pos - buf;
+       pos += res;
 
-static int wext_stop_sched_scan(void *priv)
-{
-       struct wpa_driver_wext_data *drv = priv;
-       drv->bgscan_enabled = 0;
-       return android_wext_cmd(drv, "PNOFORCE 0");
+       return pos - buf;
 }
 
-#endif /* ANDROID */
-
-
 const struct wpa_driver_ops wpa_driver_wext_ops = {
        .name = "wext",
        .desc = "Linux wireless extensions (generic)",
@@ -2480,8 +2419,6 @@ const struct wpa_driver_ops wpa_driver_wext_ops = {
        .get_capa = wpa_driver_wext_get_capa,
        .set_operstate = wpa_driver_wext_set_operstate,
        .get_radio_name = wext_get_radio_name,
-#ifdef ANDROID
-       .sched_scan = wext_sched_scan,
-       .stop_sched_scan = wext_stop_sched_scan,
-#endif /* ANDROID */
+       .signal_poll = wpa_driver_wext_signal_poll,
+       .status = wpa_driver_wext_status,
 };
index c4a5bc9..b4b5960 100644 (file)
@@ -44,12 +44,6 @@ struct wpa_driver_wext_data {
        int cfg80211; /* whether driver is using cfg80211 */
 
        u8 max_level;
-
-#ifdef ANDROID
-       int errors;
-       int driver_is_started;
-       int bgscan_enabled;
-#endif /* ANDROID */
 };
 
 int wpa_driver_wext_get_bssid(void *priv, u8 *bssid);
index e0f0f22..f95f3cc 100644 (file)
@@ -17,6 +17,7 @@
 #endif /* __linux__ */
 #if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
 #include <net/if_dl.h>
+#include <net/if_media.h>
 #endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */
 #ifdef __sun__
 #include <sys/sockio.h>
@@ -99,7 +100,7 @@ static int wired_multicast_membership(int sock, int ifindex,
        if (setsockopt(sock, SOL_PACKET,
                       add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP,
                       &mreq, sizeof(mreq)) < 0) {
-               perror("setsockopt");
+               wpa_printf(MSG_ERROR, "setsockopt: %s", strerror(errno));
                return -1;
        }
        return 0;
@@ -157,7 +158,7 @@ static void handle_read(int sock, void *eloop_ctx, void *sock_ctx)
 
        len = recv(sock, buf, sizeof(buf), 0);
        if (len < 0) {
-               perror("recv");
+               wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
                return;
        }
 
@@ -175,7 +176,7 @@ static void handle_dhcp(int sock, void *eloop_ctx, void *sock_ctx)
 
        len = recv(sock, buf, sizeof(buf), 0);
        if (len < 0) {
-               perror("recv");
+               wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
                return;
        }
 
@@ -208,19 +209,21 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr)
 
        drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE));
        if (drv->sock < 0) {
-               perror("socket[PF_PACKET,SOCK_RAW]");
+               wpa_printf(MSG_ERROR, "socket[PF_PACKET,SOCK_RAW]: %s",
+                          strerror(errno));
                return -1;
        }
 
        if (eloop_register_read_sock(drv->sock, handle_read, drv->ctx, NULL)) {
-               printf("Could not register read socket\n");
+               wpa_printf(MSG_INFO, "Could not register read socket");
                return -1;
        }
 
        os_memset(&ifr, 0, sizeof(ifr));
        os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
        if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) {
-               perror("ioctl(SIOCGIFINDEX)");
+               wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
+                          strerror(errno));
                return -1;
        }
 
@@ -231,7 +234,7 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr)
                   addr.sll_ifindex);
 
        if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-               perror("bind");
+               wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
                return -1;
        }
 
@@ -246,26 +249,28 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr)
        os_memset(&ifr, 0, sizeof(ifr));
        os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name));
        if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) {
-               perror("ioctl(SIOCGIFHWADDR)");
+               wpa_printf(MSG_ERROR, "ioctl(SIOCGIFHWADDR): %s",
+                          strerror(errno));
                return -1;
        }
 
        if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) {
-               printf("Invalid HW-addr family 0x%04x\n",
-                      ifr.ifr_hwaddr.sa_family);
+               wpa_printf(MSG_INFO, "Invalid HW-addr family 0x%04x",
+                          ifr.ifr_hwaddr.sa_family);
                return -1;
        }
        os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN);
 
        /* setup dhcp listen socket for sta detection */
        if ((drv->dhcp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
-               perror("socket call failed for dhcp");
+               wpa_printf(MSG_ERROR, "socket call failed for dhcp: %s",
+                          strerror(errno));
                return -1;
        }
 
        if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, drv->ctx,
                                     NULL)) {
-               printf("Could not register read socket\n");
+               wpa_printf(MSG_INFO, "Could not register read socket");
                return -1;
        }
 
@@ -276,12 +281,14 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr)
 
        if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_REUSEADDR, (char *) &n,
                       sizeof(n)) == -1) {
-               perror("setsockopt[SOL_SOCKET,SO_REUSEADDR]");
+               wpa_printf(MSG_ERROR, "setsockopt[SOL_SOCKET,SO_REUSEADDR]: %s",
+                          strerror(errno));
                return -1;
        }
        if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BROADCAST, (char *) &n,
                       sizeof(n)) == -1) {
-               perror("setsockopt[SOL_SOCKET,SO_BROADCAST]");
+               wpa_printf(MSG_ERROR, "setsockopt[SOL_SOCKET,SO_BROADCAST]: %s",
+                          strerror(errno));
                return -1;
        }
 
@@ -289,13 +296,15 @@ static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr)
        os_strlcpy(ifr.ifr_ifrn.ifrn_name, drv->ifname, IFNAMSIZ);
        if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE,
                       (char *) &ifr, sizeof(ifr)) < 0) {
-               perror("setsockopt[SOL_SOCKET,SO_BINDTODEVICE]");
+               wpa_printf(MSG_ERROR,
+                          "setsockopt[SOL_SOCKET,SO_BINDTODEVICE]: %s",
+                          strerror(errno));
                return -1;
        }
 
        if (bind(drv->dhcp_sock, (struct sockaddr *) &addr2,
                 sizeof(struct sockaddr)) == -1) {
-               perror("bind");
+               wpa_printf(MSG_ERROR, "bind: %s", strerror(errno));
                return -1;
        }
 
@@ -319,8 +328,9 @@ static int wired_send_eapol(void *priv, const u8 *addr,
        len = sizeof(*hdr) + data_len;
        hdr = os_zalloc(len);
        if (hdr == NULL) {
-               printf("malloc() failed for wired_send_eapol(len=%lu)\n",
-                      (unsigned long) len);
+               wpa_printf(MSG_INFO,
+                          "malloc() failed for wired_send_eapol(len=%lu)",
+                          (unsigned long) len);
                return -1;
        }
 
@@ -336,9 +346,9 @@ static int wired_send_eapol(void *priv, const u8 *addr,
        os_free(hdr);
 
        if (res < 0) {
-               perror("wired_send_eapol: send");
-               printf("wired_send_eapol - packet len: %lu - failed\n",
-                      (unsigned long) len);
+               wpa_printf(MSG_ERROR,
+                          "wired_send_eapol - packet len: %lu - failed: send: %s",
+                          (unsigned long) len, strerror(errno));
        }
 
        return res;
@@ -352,7 +362,8 @@ static void * wired_driver_hapd_init(struct hostapd_data *hapd,
 
        drv = os_zalloc(sizeof(struct wpa_driver_wired_data));
        if (drv == NULL) {
-               printf("Could not allocate memory for wired driver data\n");
+               wpa_printf(MSG_INFO,
+                          "Could not allocate memory for wired driver data");
                return NULL;
        }
 
@@ -373,11 +384,15 @@ static void wired_driver_hapd_deinit(void *priv)
 {
        struct wpa_driver_wired_data *drv = priv;
 
-       if (drv->sock >= 0)
+       if (drv->sock >= 0) {
+               eloop_unregister_read_sock(drv->sock);
                close(drv->sock);
+       }
 
-       if (drv->dhcp_sock >= 0)
+       if (drv->dhcp_sock >= 0) {
+               eloop_unregister_read_sock(drv->dhcp_sock);
                close(drv->dhcp_sock);
+       }
 
        os_free(drv);
 }
@@ -413,14 +428,15 @@ static int wpa_driver_wired_get_ifflags(const char *ifname, int *flags)
 
        s = socket(PF_INET, SOCK_DGRAM, 0);
        if (s < 0) {
-               perror("socket");
+               wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
                return -1;
        }
 
        os_memset(&ifr, 0, sizeof(ifr));
        os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
        if (ioctl(s, SIOCGIFFLAGS, (caddr_t) &ifr) < 0) {
-               perror("ioctl[SIOCGIFFLAGS]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCGIFFLAGS]: %s",
+                          strerror(errno));
                close(s);
                return -1;
        }
@@ -437,7 +453,7 @@ static int wpa_driver_wired_set_ifflags(const char *ifname, int flags)
 
        s = socket(PF_INET, SOCK_DGRAM, 0);
        if (s < 0) {
-               perror("socket");
+               wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
                return -1;
        }
 
@@ -445,7 +461,8 @@ static int wpa_driver_wired_set_ifflags(const char *ifname, int flags)
        os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ);
        ifr.ifr_flags = flags & 0xffff;
        if (ioctl(s, SIOCSIFFLAGS, (caddr_t) &ifr) < 0) {
-               perror("ioctl[SIOCSIFFLAGS]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOCSIFFLAGS]: %s",
+                          strerror(errno));
                close(s);
                return -1;
        }
@@ -454,6 +471,35 @@ static int wpa_driver_wired_set_ifflags(const char *ifname, int flags)
 }
 
 
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+static int wpa_driver_wired_get_ifstatus(const char *ifname, int *status)
+{
+       struct ifmediareq ifmr;
+       int s;
+
+       s = socket(PF_INET, SOCK_DGRAM, 0);
+       if (s < 0) {
+               wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
+               return -1;
+       }
+
+       os_memset(&ifmr, 0, sizeof(ifmr));
+       os_strlcpy(ifmr.ifm_name, ifname, IFNAMSIZ);
+       if (ioctl(s, SIOCGIFMEDIA, (caddr_t) &ifmr) < 0) {
+               wpa_printf(MSG_ERROR, "ioctl[SIOCGIFMEDIA]: %s",
+                          strerror(errno));
+               close(s);
+               return -1;
+       }
+       close(s);
+       *status = (ifmr.ifm_status & (IFM_ACTIVE | IFM_AVALID)) ==
+               (IFM_ACTIVE | IFM_AVALID);
+
+       return 0;
+}
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
+
+
 static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add)
 {
        struct ifreq ifr;
@@ -465,7 +511,7 @@ static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add)
 
        s = socket(PF_INET, SOCK_DGRAM, 0);
        if (s < 0) {
-               perror("socket");
+               wpa_printf(MSG_ERROR, "socket: %s", strerror(errno));
                return -1;
        }
 
@@ -499,7 +545,8 @@ static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add)
 #endif /* defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) */
 
        if (ioctl(s, add ? SIOCADDMULTI : SIOCDELMULTI, (caddr_t) &ifr) < 0) {
-               perror("ioctl[SIOC{ADD/DEL}MULTI]");
+               wpa_printf(MSG_ERROR, "ioctl[SIOC{ADD/DEL}MULTI]: %s",
+                          strerror(errno));
                close(s);
                return -1;
        }
@@ -522,7 +569,7 @@ static void * wpa_driver_wired_init(void *ctx, const char *ifname)
 #ifdef __linux__
        drv->pf_sock = socket(PF_PACKET, SOCK_DGRAM, 0);
        if (drv->pf_sock < 0)
-               perror("socket(PF_PACKET)");
+               wpa_printf(MSG_ERROR, "socket(PF_PACKET): %s", strerror(errno));
 #else /* __linux__ */
        drv->pf_sock = -1;
 #endif /* __linux__ */
@@ -562,6 +609,16 @@ static void * wpa_driver_wired_init(void *ctx, const char *ifname)
                           __func__);
                drv->iff_allmulti = 1;
        }
+#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__)
+       {
+               int status;
+               wpa_printf(MSG_DEBUG, "%s: waiting for link to become active",
+                          __func__);
+               while (wpa_driver_wired_get_ifstatus(ifname, &status) == 0 &&
+                      status == 0)
+                       sleep(1);
+       }
+#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */
 
        return drv;
 }
index 04eb4fd..f0c3bb3 100644 (file)
@@ -6,8 +6,9 @@
  * See README for more details.
  */
 
-#include "includes.h"
-
+#include "utils/includes.h"
+#include "utils/common.h"
+#include "driver.h"
 
 #ifdef CONFIG_DRIVER_WEXT
 extern struct wpa_driver_ops wpa_driver_wext_ops; /* driver_wext.c */
@@ -18,9 +19,6 @@ extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */
 #ifdef CONFIG_DRIVER_HOSTAP
 extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */
 #endif /* CONFIG_DRIVER_HOSTAP */
-#ifdef CONFIG_DRIVER_MADWIFI
-extern struct wpa_driver_ops wpa_driver_madwifi_ops; /* driver_madwifi.c */
-#endif /* CONFIG_DRIVER_MADWIFI */
 #ifdef CONFIG_DRIVER_BSD
 extern struct wpa_driver_ops wpa_driver_bsd_ops; /* driver_bsd.c */
 #endif /* CONFIG_DRIVER_BSD */
@@ -33,9 +31,10 @@ extern struct wpa_driver_ops wpa_driver_ndis_ops; /* driver_ndis.c */
 #ifdef CONFIG_DRIVER_WIRED
 extern struct wpa_driver_ops wpa_driver_wired_ops; /* driver_wired.c */
 #endif /* CONFIG_DRIVER_WIRED */
-#ifdef CONFIG_DRIVER_TEST
-extern struct wpa_driver_ops wpa_driver_test_ops; /* driver_test.c */
-#endif /* CONFIG_DRIVER_TEST */
+#ifdef CONFIG_DRIVER_MACSEC_QCA
+ /* driver_macsec_qca.c */
+extern struct wpa_driver_ops wpa_driver_macsec_qca_ops;
+#endif /* CONFIG_DRIVER_MACSEC_QCA */
 #ifdef CONFIG_DRIVER_ROBOSWITCH
 /* driver_roboswitch.c */
 extern struct wpa_driver_ops wpa_driver_roboswitch_ops;
@@ -59,9 +58,6 @@ struct wpa_driver_ops *wpa_drivers[] =
 #ifdef CONFIG_DRIVER_HOSTAP
        &wpa_driver_hostap_ops,
 #endif /* CONFIG_DRIVER_HOSTAP */
-#ifdef CONFIG_DRIVER_MADWIFI
-       &wpa_driver_madwifi_ops,
-#endif /* CONFIG_DRIVER_MADWIFI */
 #ifdef CONFIG_DRIVER_BSD
        &wpa_driver_bsd_ops,
 #endif /* CONFIG_DRIVER_BSD */
@@ -74,9 +70,9 @@ struct wpa_driver_ops *wpa_drivers[] =
 #ifdef CONFIG_DRIVER_WIRED
        &wpa_driver_wired_ops,
 #endif /* CONFIG_DRIVER_WIRED */
-#ifdef CONFIG_DRIVER_TEST
-       &wpa_driver_test_ops,
-#endif /* CONFIG_DRIVER_TEST */
+#ifdef CONFIG_DRIVER_MACSEC_QCA
+       &wpa_driver_macsec_qca_ops,
+#endif /* CONFIG_DRIVER_MACSEC_QCA */
 #ifdef CONFIG_DRIVER_ROBOSWITCH
        &wpa_driver_roboswitch_ops,
 #endif /* CONFIG_DRIVER_ROBOSWITCH */
index 68ff910..9434078 100644 (file)
@@ -17,9 +17,18 @@ DRV_CFLAGS += -DCONFIG_DRIVER_WIRED
 DRV_OBJS += ../src/drivers/driver_wired.o
 endif
 
+ifdef CONFIG_DRIVER_MACSEC_QCA
+DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_QCA
+DRV_OBJS += ../src/drivers/driver_macsec_qca.o
+endif
+
 ifdef CONFIG_DRIVER_NL80211
 DRV_CFLAGS += -DCONFIG_DRIVER_NL80211
 DRV_OBJS += ../src/drivers/driver_nl80211.o
+DRV_OBJS += ../src/drivers/driver_nl80211_capa.o
+DRV_OBJS += ../src/drivers/driver_nl80211_event.o
+DRV_OBJS += ../src/drivers/driver_nl80211_monitor.o
+DRV_OBJS += ../src/drivers/driver_nl80211_scan.o
 DRV_OBJS += ../src/utils/radiotap.o
 NEED_SME=y
 NEED_AP_MLME=y
@@ -30,7 +39,17 @@ NEED_RFKILL=y
 ifdef CONFIG_LIBNL32
   DRV_LIBS += -lnl-3
   DRV_LIBS += -lnl-genl-3
-  DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3
+  DRV_CFLAGS += -DCONFIG_LIBNL20
+  ifdef LIBNL_INC
+    DRV_CFLAGS += -I$(LIBNL_INC)
+  else
+    PKG_CONFIG ?= pkg-config
+    DRV_CFLAGS += $(shell $(PKG_CONFIG) --cflags libnl-3.0)
+  endif
+ifdef CONFIG_LIBNL3_ROUTE
+  DRV_LIBS += -lnl-route-3
+  DRV_CFLAGS += -DCONFIG_LIBNL3_ROUTE
+endif
 else
   ifdef CONFIG_LIBNL_TINY
     DRV_LIBS += -lnl-tiny
@@ -63,12 +82,6 @@ DRV_CFLAGS += -DCONFIG_DRIVER_OPENBSD
 DRV_OBJS += ../src/drivers/driver_openbsd.o
 endif
 
-ifdef CONFIG_DRIVER_TEST
-DRV_CFLAGS += -DCONFIG_DRIVER_TEST
-DRV_OBJS += ../src/drivers/driver_test.o
-NEED_AP_MLME=y
-endif
-
 ifdef CONFIG_DRIVER_NONE
 DRV_CFLAGS += -DCONFIG_DRIVER_NONE
 DRV_OBJS += ../src/drivers/driver_none.o
@@ -85,21 +98,15 @@ NEED_NETLINK=y
 NEED_LINUX_IOCTL=y
 endif
 
-ifdef CONFIG_DRIVER_MADWIFI
-DRV_AP_CFLAGS += -DCONFIG_DRIVER_MADWIFI
-DRV_AP_OBJS += ../src/drivers/driver_madwifi.o
-CONFIG_WIRELESS_EXTENSION=y
-CONFIG_L2_PACKET=linux
-NEED_NETLINK=y
-NEED_LINUX_IOCTL=y
-endif
-
 ifdef CONFIG_DRIVER_ATHEROS
 DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS
 DRV_AP_OBJS += ../src/drivers/driver_atheros.o
 CONFIG_L2_PACKET=linux
 NEED_NETLINK=y
 NEED_LINUX_IOCTL=y
+ifdef ATH_GCM_SUPPORT
+CFLAGS += -DATH_GCM_SUPPORT
+endif
 endif
 
 ##### PURE CLIENT DRIVERS
index db8561a..8da4c53 100644 (file)
@@ -20,6 +20,11 @@ endif
 ifdef CONFIG_DRIVER_NL80211
 DRV_CFLAGS += -DCONFIG_DRIVER_NL80211
 DRV_OBJS += src/drivers/driver_nl80211.c
+DRV_OBJS += src/drivers/driver_nl80211_android.c
+DRV_OBJS += src/drivers/driver_nl80211_capa.c
+DRV_OBJS += src/drivers/driver_nl80211_event.c
+DRV_OBJS += src/drivers/driver_nl80211_monitor.c
+DRV_OBJS += src/drivers/driver_nl80211_scan.c
 DRV_OBJS += src/utils/radiotap.c
 NEED_SME=y
 NEED_AP_MLME=y
@@ -31,6 +36,10 @@ ifdef CONFIG_LIBNL32
   DRV_LIBS += -lnl-3
   DRV_LIBS += -lnl-genl-3
   DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3
+ifdef CONFIG_LIBNL3_ROUTE
+  DRV_LIBS += -lnl-route-3
+  DRV_CFLAGS += -DCONFIG_LIBNL3_ROUTE
+endif
 else
   ifdef CONFIG_LIBNL_TINY
     DRV_LIBS += -lnl-tiny
@@ -63,12 +72,6 @@ DRV_CFLAGS += -DCONFIG_DRIVER_OPENBSD
 DRV_OBJS += src/drivers/driver_openbsd.c
 endif
 
-ifdef CONFIG_DRIVER_TEST
-DRV_CFLAGS += -DCONFIG_DRIVER_TEST
-DRV_OBJS += src/drivers/driver_test.c
-NEED_AP_MLME=y
-endif
-
 ifdef CONFIG_DRIVER_NONE
 DRV_CFLAGS += -DCONFIG_DRIVER_NONE
 DRV_OBJS += src/drivers/driver_none.c
@@ -85,15 +88,6 @@ NEED_NETLINK=y
 NEED_LINUX_IOCTL=y
 endif
 
-ifdef CONFIG_DRIVER_MADWIFI
-DRV_AP_CFLAGS += -DCONFIG_DRIVER_MADWIFI
-DRV_AP_OBJS += src/drivers/driver_madwifi.c
-CONFIG_WIRELESS_EXTENSION=y
-CONFIG_L2_PACKET=linux
-NEED_NETLINK=y
-NEED_LINUX_IOCTL=y
-endif
-
 ifdef CONFIG_DRIVER_ATHEROS
 DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS
 DRV_AP_OBJS += src/drivers/driver_atheros.c
diff --git a/src/drivers/linux_defines.h b/src/drivers/linux_defines.h
new file mode 100644 (file)
index 0000000..a107479
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Linux defines for values that are not yet included in common C libraries
+ * 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.
+ */
+
+#ifndef LINUX_DEFINES_H
+#define LINUX_DEFINES_H
+
+#ifndef SO_WIFI_STATUS
+# if defined(__sparc__)
+#  define SO_WIFI_STATUS       0x0025
+# elif defined(__parisc__)
+#  define SO_WIFI_STATUS       0x4022
+# else
+#  define SO_WIFI_STATUS       41
+# endif
+
+# define SCM_WIFI_STATUS       SO_WIFI_STATUS
+#endif
+
+#ifndef SO_EE_ORIGIN_TXSTATUS
+#define SO_EE_ORIGIN_TXSTATUS  4
+#endif
+
+#ifndef PACKET_TX_TIMESTAMP
+#define PACKET_TX_TIMESTAMP    16
+#endif
+
+#ifndef IFF_LOWER_UP
+#define IFF_LOWER_UP   0x10000         /* driver signals L1 up         */
+#endif
+#ifndef IFF_DORMANT
+#define IFF_DORMANT    0x20000         /* driver signals dormant       */
+#endif
+
+#ifndef IF_OPER_DORMANT
+#define IF_OPER_DORMANT 5
+#endif
+#ifndef IF_OPER_UP
+#define IF_OPER_UP 6
+#endif
+
+#endif /* LINUX_DEFINES_H */
index 4380428..837971d 100644 (file)
@@ -204,11 +204,14 @@ int linux_br_del_if(int sock, const char *brname, const char *ifname)
 int linux_br_get(char *brname, const char *ifname)
 {
        char path[128], brlink[128], *pos;
+       ssize_t res;
+
        os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/bridge",
                    ifname);
-       os_memset(brlink, 0, sizeof(brlink));
-       if (readlink(path, brlink, sizeof(brlink) - 1) < 0)
+       res = readlink(path, brlink, sizeof(brlink));
+       if (res < 0 || (size_t) res >= sizeof(brlink))
                return -1;
+       brlink[res] = '\0';
        pos = os_strrchr(brlink, '/');
        if (pos == NULL)
                return -1;
index 55cf955..e7c7001 100644 (file)
 #define _LINUX_SOCKET_H
 #define _LINUX_IF_H
 
-#include <sys/types.h>
+#include <stdint.h>
 #include <net/if.h>
-typedef __uint32_t __u32;
-typedef __int32_t __s32;
-typedef __uint16_t __u16;
-typedef __int16_t __s16;
-typedef __uint8_t __u8;
+typedef uint32_t __u32;
+typedef int32_t __s32;
+typedef uint16_t __u16;
+typedef int16_t __s16;
+typedef uint8_t __u8;
 #ifndef __user
 #define __user
 #endif /* __user */
index 6c60550..0e960f4 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Netlink helper functions for driver wrappers
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -137,6 +137,35 @@ void netlink_deinit(struct netlink_data *netlink)
        os_free(netlink);
 }
 
+
+static const char * linkmode_str(int mode)
+{
+       switch (mode) {
+       case -1:
+               return "no change";
+       case 0:
+               return "kernel-control";
+       case 1:
+               return "userspace-control";
+       }
+       return "?";
+}
+
+
+static const char * operstate_str(int state)
+{
+       switch (state) {
+       case -1:
+               return "no change";
+       case IF_OPER_DORMANT:
+               return "IF_OPER_DORMANT";
+       case IF_OPER_UP:
+               return "IF_OPER_UP";
+       }
+       return "?";
+}
+
+
 int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex,
                           int linkmode, int operstate)
 {
@@ -170,8 +199,7 @@ int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex,
                rta->rta_type = IFLA_LINKMODE;
                rta->rta_len = RTA_LENGTH(sizeof(char));
                *((char *) RTA_DATA(rta)) = linkmode;
-               req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
-                       RTA_LENGTH(sizeof(char));
+               req.hdr.nlmsg_len += RTA_SPACE(sizeof(char));
        }
        if (operstate != -1) {
                rta = aliasing_hide_typecast(
@@ -180,12 +208,12 @@ int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex,
                rta->rta_type = IFLA_OPERSTATE;
                rta->rta_len = RTA_LENGTH(sizeof(char));
                *((char *) RTA_DATA(rta)) = operstate;
-               req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) +
-                       RTA_LENGTH(sizeof(char));
+               req.hdr.nlmsg_len += RTA_SPACE(sizeof(char));
        }
 
-       wpa_printf(MSG_DEBUG, "netlink: Operstate: linkmode=%d, operstate=%d",
-                  linkmode, operstate);
+       wpa_printf(MSG_DEBUG, "netlink: Operstate: ifindex=%d linkmode=%d (%s), operstate=%d (%s)",
+                  ifindex, linkmode, linkmode_str(linkmode),
+                  operstate, operstate_str(operstate));
 
        ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0);
        if (ret < 0) {
index 32b060e..b37bd5a 100644 (file)
  */
 
 /**
+ * DOC: packet coalesce support
+ *
+ * In most cases, host that receives IPv4 and IPv6 multicast/broadcast
+ * packets does not do anything with these packets. Therefore the
+ * reception of these unwanted packets causes unnecessary processing
+ * and power consumption.
+ *
+ * Packet coalesce feature helps to reduce number of received interrupts
+ * to host by buffering these packets in firmware/hardware for some
+ * predefined time. Received interrupt will be generated when one of the
+ * following events occur.
+ * a) Expiration of hardware timer whose expiration time is set to maximum
+ * coalescing delay of matching coalesce rule.
+ * b) Coalescing buffer in hardware reaches it's limit.
+ * c) Packet doesn't match any of the configured coalesce rules.
+ *
+ * User needs to configure following parameters for creating a coalesce
+ * rule.
+ * a) Maximum coalescing delay
+ * b) List of packet patterns which needs to be matched
+ * c) Condition for coalescence. pattern 'match' or 'no match'
+ * Multiple such rules can be created.
+ */
+
+/**
  * enum nl80211_commands - supported nl80211 commands
  *
  * @NL80211_CMD_UNSPEC: unspecified command to catch errors
  *     the interface identified by %NL80211_ATTR_IFINDEX.
  * @NL80211_CMD_DEL_STATION: Remove a station identified by %NL80211_ATTR_MAC
  *     or, if no MAC address given, all stations, on the interface identified
- *     by %NL80211_ATTR_IFINDEX.
+ *     by %NL80211_ATTR_IFINDEX. %NL80211_ATTR_MGMT_SUBTYPE and
+ *     %NL80211_ATTR_REASON_CODE can optionally be used to specify which type
+ *     of disconnection indication should be sent to the station
+ *     (Deauthentication or Disassociation frame and reason code for that
+ *     frame).
  *
  * @NL80211_CMD_GET_MPATH: Get mesh path attributes for mesh path to
  *     destination %NL80211_ATTR_MAC on the interface identified by
  *     passed, all channels allowed for the current regulatory domain
  *     are used.  Extra IEs can also be passed from the userspace by
  *     using the %NL80211_ATTR_IE attribute.
- * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan.  Returns -ENOENT
- *     if scheduled scan is not running.
+ * @NL80211_CMD_STOP_SCHED_SCAN: stop a scheduled scan. Returns -ENOENT if
+ *     scheduled scan is not running. The caller may assume that as soon
+ *     as the call returns, it is safe to start a new scheduled scan again.
  * @NL80211_CMD_SCHED_SCAN_RESULTS: indicates that there are scheduled scan
  *     results available.
  * @NL80211_CMD_SCHED_SCAN_STOPPED: indicates that the scheduled scan has
  *     %NL80211_ATTR_SSID attribute, and can optionally specify the association
  *     IEs in %NL80211_ATTR_IE, %NL80211_ATTR_AUTH_TYPE, %NL80211_ATTR_USE_MFP,
  *     %NL80211_ATTR_MAC, %NL80211_ATTR_WIPHY_FREQ, %NL80211_ATTR_CONTROL_PORT,
- *     %NL80211_ATTR_CONTROL_PORT_ETHERTYPE and
- *     %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT.
+ *     %NL80211_ATTR_CONTROL_PORT_ETHERTYPE,
+ *     %NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT, %NL80211_ATTR_MAC_HINT, and
+ *     %NL80211_ATTR_WIPHY_FREQ_HINT.
+ *     If included, %NL80211_ATTR_MAC and %NL80211_ATTR_WIPHY_FREQ are
+ *     restrictions on BSS selection, i.e., they effectively prevent roaming
+ *     within the ESS. %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT
+ *     can be included to provide a recommendation of the initial BSS while
+ *     allowing the driver to roam to other BSSes within the ESS and also to
+ *     ignore this recommendation if the indicated BSS is not ideal. Only one
+ *     set of BSSID,frequency parameters is used (i.e., either the enforcing
+ *     %NL80211_ATTR_MAC,%NL80211_ATTR_WIPHY_FREQ or the less strict
+ *     %NL80211_ATTR_MAC_HINT and %NL80211_ATTR_WIPHY_FREQ_HINT).
  *     Background scan period can optionally be
  *     specified in %NL80211_ATTR_BG_SCAN_PERIOD,
  *     if not specified default background scan configuration
  *     TX status event pertaining to the TX request.
  *     %NL80211_ATTR_TX_NO_CCK_RATE is used to decide whether to send the
  *     management frames at CCK rate or not in 2GHz band.
+ *     %NL80211_ATTR_CSA_C_OFFSETS_TX is an array of offsets to CSA
+ *     counters which will be updated to the current value. This attribute
+ *     is used during CSA period.
  * @NL80211_CMD_FRAME_WAIT_CANCEL: When an off-channel TX was requested, this
  *     command may be used with the corresponding cookie to cancel the wait
  *     time if it is known that it is no longer necessary.
  *     operation, %NL80211_ATTR_MAC contains the peer MAC address, and
  *     %NL80211_ATTR_REASON_CODE the reason code to be used (only with
  *     %NL80211_TDLS_TEARDOWN).
- * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame.
+ * @NL80211_CMD_TDLS_MGMT: Send a TDLS management frame. The
+ *     %NL80211_ATTR_TDLS_ACTION attribute determines the type of frame to be
+ *     sent. Public Action codes (802.11-2012 8.1.5.1) will be sent as
+ *     802.11 management frames, while TDLS action codes (802.11-2012
+ *     8.5.13.1) will be encapsulated and sent as data frames. The currently
+ *     supported Public Action code is %WLAN_PUB_ACTION_TDLS_DISCOVER_RES
+ *     and the currently supported TDLS actions codes are given in
+ *     &enum ieee80211_tdls_actioncode.
  *
  * @NL80211_CMD_UNEXPECTED_FRAME: Used by an application controlling an AP
  *     (or GO) interface (i.e. hostapd) to ask for unexpected frames to
  * @NL80211_CMD_CH_SWITCH_NOTIFY: An AP or GO may decide to switch channels
  *     independently of the userspace SME, send this event indicating
  *     %NL80211_ATTR_IFINDEX is now on %NL80211_ATTR_WIPHY_FREQ and the
- *     attributes determining channel width.
+ *     attributes determining channel width.  This indication may also be
+ *     sent when a remotely-initiated switch (e.g., when a STA receives a CSA
+ *     from the remote AP) is completed;
+ *
+ * @NL80211_CMD_CH_SWITCH_STARTED_NOTIFY: Notify that a channel switch
+ *     has been started on an interface, regardless of the initiator
+ *     (ie. whether it was requested from a remote device or
+ *     initiated on our own).  It indicates that
+ *     %NL80211_ATTR_IFINDEX will be on %NL80211_ATTR_WIPHY_FREQ
+ *     after %NL80211_ATTR_CH_SWITCH_COUNT TBTT's.  The userspace may
+ *     decide to react to this indication by requesting other
+ *     interfaces to change channel as well.
  *
  * @NL80211_CMD_START_P2P_DEVICE: Start the given P2P Device, identified by
  *     its %NL80211_ATTR_WDEV identifier. It must have been created with
  * @NL80211_CMD_CRIT_PROTOCOL_STOP: Indicates the connection reliability can
  *     return back to normal.
  *
+ * @NL80211_CMD_GET_COALESCE: Get currently supported coalesce rules.
+ * @NL80211_CMD_SET_COALESCE: Configure coalesce rules or clear existing rules.
+ *
+ * @NL80211_CMD_CHANNEL_SWITCH: Perform a channel switch by announcing the
+ *     the new channel information (Channel Switch Announcement - CSA)
+ *     in the beacon for some time (as defined in the
+ *     %NL80211_ATTR_CH_SWITCH_COUNT parameter) and then change to the
+ *     new channel. Userspace provides the new channel information (using
+ *     %NL80211_ATTR_WIPHY_FREQ and the attributes determining channel
+ *     width). %NL80211_ATTR_CH_SWITCH_BLOCK_TX may be supplied to inform
+ *     other station that transmission must be blocked until the channel
+ *     switch is complete.
+ *
+ * @NL80211_CMD_VENDOR: Vendor-specified command/event. The command is specified
+ *     by the %NL80211_ATTR_VENDOR_ID attribute and a sub-command in
+ *     %NL80211_ATTR_VENDOR_SUBCMD. Parameter(s) can be transported in
+ *     %NL80211_ATTR_VENDOR_DATA.
+ *     For feature advertisement, the %NL80211_ATTR_VENDOR_DATA attribute is
+ *     used in the wiphy data as a nested attribute containing descriptions
+ *     (&struct nl80211_vendor_cmd_info) of the supported vendor commands.
+ *     This may also be sent as an event with the same attributes.
+ *
+ * @NL80211_CMD_SET_QOS_MAP: Set Interworking QoS mapping for IP DSCP values.
+ *     The QoS mapping information is included in %NL80211_ATTR_QOS_MAP. If
+ *     that attribute is not included, QoS mapping is disabled. Since this
+ *     QoS mapping is relevant for IP packets, it is only valid during an
+ *     association. This is cleared on disassociation and AP restart.
+ *
+ * @NL80211_CMD_ADD_TX_TS: Ask the kernel to add a traffic stream for the given
+ *     %NL80211_ATTR_TSID and %NL80211_ATTR_MAC with %NL80211_ATTR_USER_PRIO
+ *     and %NL80211_ATTR_ADMITTED_TIME parameters.
+ *     Note that the action frame handshake with the AP shall be handled by
+ *     userspace via the normal management RX/TX framework, this only sets
+ *     up the TX TS in the driver/device.
+ *     If the admitted time attribute is not added then the request just checks
+ *     if a subsequent setup could be successful, the intent is to use this to
+ *     avoid setting up a session with the AP when local restrictions would
+ *     make that impossible. However, the subsequent "real" setup may still
+ *     fail even if the check was successful.
+ * @NL80211_CMD_DEL_TX_TS: Remove an existing TS with the %NL80211_ATTR_TSID
+ *     and %NL80211_ATTR_MAC parameters. It isn't necessary to call this
+ *     before removing a station entry entirely, or before disassociating
+ *     or similar, cleanup will happen in the driver/device in this case.
+ *
+ * @NL80211_CMD_GET_MPP: Get mesh path attributes for mesh proxy path to
+ *     destination %NL80211_ATTR_MAC on the interface identified by
+ *     %NL80211_ATTR_IFINDEX.
+ *
+ * @NL80211_CMD_JOIN_OCB: Join the OCB network. The center frequency and
+ *     bandwidth of a channel must be given.
+ * @NL80211_CMD_LEAVE_OCB: Leave the OCB network -- no special arguments, the
+ *     network is determined by the network interface.
+ *
+ * @NL80211_CMD_TDLS_CHANNEL_SWITCH: Start channel-switching with a TDLS peer,
+ *     identified by the %NL80211_ATTR_MAC parameter. A target channel is
+ *     provided via %NL80211_ATTR_WIPHY_FREQ and other attributes determining
+ *     channel width/type. The target operating class is given via
+ *     %NL80211_ATTR_OPER_CLASS.
+ *     The driver is responsible for continually initiating channel-switching
+ *     operations and returning to the base channel for communication with the
+ *     AP.
+ * @NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH: Stop channel-switching with a TDLS
+ *     peer given by %NL80211_ATTR_MAC. Both peers must be on the base channel
+ *     when this command completes.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -810,6 +936,28 @@ enum nl80211_commands {
        NL80211_CMD_CRIT_PROTOCOL_START,
        NL80211_CMD_CRIT_PROTOCOL_STOP,
 
+       NL80211_CMD_GET_COALESCE,
+       NL80211_CMD_SET_COALESCE,
+
+       NL80211_CMD_CHANNEL_SWITCH,
+
+       NL80211_CMD_VENDOR,
+
+       NL80211_CMD_SET_QOS_MAP,
+
+       NL80211_CMD_ADD_TX_TS,
+       NL80211_CMD_DEL_TX_TS,
+
+       NL80211_CMD_GET_MPP,
+
+       NL80211_CMD_JOIN_OCB,
+       NL80211_CMD_LEAVE_OCB,
+
+       NL80211_CMD_CH_SWITCH_STARTED_NOTIFY,
+
+       NL80211_CMD_TDLS_CHANNEL_SWITCH,
+       NL80211_CMD_TDLS_CANCEL_CHANNEL_SWITCH,
+
        /* add new commands above here */
 
        /* used to define NL80211_CMD_MAX below */
@@ -945,7 +1093,7 @@ enum nl80211_commands {
  *     to query the CRDA to retrieve one regulatory domain. This attribute can
  *     also be used by userspace to query the kernel for the currently set
  *     regulatory domain. We chose an alpha2 as that is also used by the
- *     IEEE-802.11d country information element to identify a country.
+ *     IEEE-802.11 country information element to identify a country.
  *     Users can also simply ask the wireless core to set regulatory domain
  *     to a specific alpha2.
  * @NL80211_ATTR_REG_RULES: a nested array of regulatory domain regulatory
@@ -1436,6 +1584,111 @@ enum nl80211_commands {
  *     allowed to be used with the first @NL80211_CMD_SET_STATION command to
  *     update a TDLS peer STA entry.
  *
+ * @NL80211_ATTR_COALESCE_RULE: Coalesce rule information.
+ *
+ * @NL80211_ATTR_CH_SWITCH_COUNT: u32 attribute specifying the number of TBTT's
+ *     until the channel switch event.
+ * @NL80211_ATTR_CH_SWITCH_BLOCK_TX: flag attribute specifying that transmission
+ *     must be blocked on the current channel (before the channel switch
+ *     operation).
+ * @NL80211_ATTR_CSA_IES: Nested set of attributes containing the IE information
+ *     for the time while performing a channel switch.
+ * @NL80211_ATTR_CSA_C_OFF_BEACON: An array of offsets (u16) to the channel
+ *     switch counters in the beacons tail (%NL80211_ATTR_BEACON_TAIL).
+ * @NL80211_ATTR_CSA_C_OFF_PRESP: An array of offsets (u16) to the channel
+ *     switch counters in the probe response (%NL80211_ATTR_PROBE_RESP).
+ *
+ * @NL80211_ATTR_RXMGMT_FLAGS: flags for nl80211_send_mgmt(), u32.
+ *     As specified in the &enum nl80211_rxmgmt_flags.
+ *
+ * @NL80211_ATTR_STA_SUPPORTED_CHANNELS: array of supported channels.
+ *
+ * @NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES: array of supported
+ *      supported operating classes.
+ *
+ * @NL80211_ATTR_HANDLE_DFS: A flag indicating whether user space
+ *     controls DFS operation in IBSS mode. If the flag is included in
+ *     %NL80211_CMD_JOIN_IBSS request, the driver will allow use of DFS
+ *     channels and reports radar events to userspace. Userspace is required
+ *     to react to radar events, e.g. initiate a channel switch or leave the
+ *     IBSS network.
+ *
+ * @NL80211_ATTR_SUPPORT_5_MHZ: A flag indicating that the device supports
+ *     5 MHz channel bandwidth.
+ * @NL80211_ATTR_SUPPORT_10_MHZ: A flag indicating that the device supports
+ *     10 MHz channel bandwidth.
+ *
+ * @NL80211_ATTR_OPMODE_NOTIF: Operating mode field from Operating Mode
+ *     Notification Element based on association request when used with
+ *     %NL80211_CMD_NEW_STATION; u8 attribute.
+ *
+ * @NL80211_ATTR_VENDOR_ID: The vendor ID, either a 24-bit OUI or, if
+ *     %NL80211_VENDOR_ID_IS_LINUX is set, a special Linux ID (not used yet)
+ * @NL80211_ATTR_VENDOR_SUBCMD: vendor sub-command
+ * @NL80211_ATTR_VENDOR_DATA: data for the vendor command, if any; this
+ *     attribute is also used for vendor command feature advertisement
+ * @NL80211_ATTR_VENDOR_EVENTS: used for event list advertising in the wiphy
+ *     info, containing a nested array of possible events
+ *
+ * @NL80211_ATTR_QOS_MAP: IP DSCP mapping for Interworking QoS mapping. This
+ *     data is in the format defined for the payload of the QoS Map Set element
+ *     in IEEE Std 802.11-2012, 8.4.2.97.
+ *
+ * @NL80211_ATTR_MAC_HINT: MAC address recommendation as initial BSS
+ * @NL80211_ATTR_WIPHY_FREQ_HINT: frequency of the recommended initial BSS
+ *
+ * @NL80211_ATTR_MAX_AP_ASSOC_STA: Device attribute that indicates how many
+ *     associated stations are supported in AP mode (including P2P GO); u32.
+ *     Since drivers may not have a fixed limit on the maximum number (e.g.,
+ *     other concurrent operations may affect this), drivers are allowed to
+ *     advertise values that cannot always be met. In such cases, an attempt
+ *     to add a new station entry with @NL80211_CMD_NEW_STATION may fail.
+ *
+ * @NL80211_ATTR_CSA_C_OFFSETS_TX: An array of csa counter offsets (u16) which
+ *     should be updated when the frame is transmitted.
+ * @NL80211_ATTR_MAX_CSA_COUNTERS: U8 attribute used to advertise the maximum
+ *     supported number of csa counters.
+ *
+ * @NL80211_ATTR_TDLS_PEER_CAPABILITY: flags for TDLS peer capabilities, u32.
+ *     As specified in the &enum nl80211_tdls_peer_capability.
+ *
+ * @NL80211_ATTR_SOCKET_OWNER: Flag attribute, if set during interface
+ *     creation then the new interface will be owned by the netlink socket
+ *     that created it and will be destroyed when the socket is closed.
+ *
+ * @NL80211_ATTR_TDLS_INITIATOR: flag attribute indicating the current end is
+ *     the TDLS link initiator.
+ *
+ * @NL80211_ATTR_USE_RRM: flag for indicating whether the current connection
+ *     shall support Radio Resource Measurements (11k). This attribute can be
+ *     used with %NL80211_CMD_ASSOCIATE and %NL80211_CMD_CONNECT requests.
+ *     User space applications are expected to use this flag only if the
+ *     underlying device supports these minimal RRM features:
+ *             %NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES,
+ *             %NL80211_FEATURE_QUIET,
+ *     If this flag is used, driver must add the Power Capabilities IE to the
+ *     association request. In addition, it must also set the RRM capability
+ *     flag in the association request's Capability Info field.
+ *
+ * @NL80211_ATTR_WIPHY_DYN_ACK: flag attribute used to enable ACK timeout
+ *     estimation algorithm (dynack). In order to activate dynack
+ *     %NL80211_FEATURE_ACKTO_ESTIMATION feature flag must be set by lower
+ *     drivers to indicate dynack capability. Dynack is automatically disabled
+ *     setting valid value for coverage class.
+ *
+ * @NL80211_ATTR_TSID: a TSID value (u8 attribute)
+ * @NL80211_ATTR_USER_PRIO: user priority value (u8 attribute)
+ * @NL80211_ATTR_ADMITTED_TIME: admitted time in units of 32 microseconds
+ *     (per second) (u16 attribute)
+ *
+ * @NL80211_ATTR_SMPS_MODE: SMPS mode to use (ap mode). see
+ *     &enum nl80211_smps_mode.
+ *
+ * @NL80211_ATTR_OPER_CLASS: operating class
+ *
+ * @NL80211_ATTR_MAC_MASK: MAC address mask
+ *
+ * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1736,15 +1989,73 @@ enum nl80211_attrs {
 
        NL80211_ATTR_PEER_AID,
 
+       NL80211_ATTR_COALESCE_RULE,
+
+       NL80211_ATTR_CH_SWITCH_COUNT,
+       NL80211_ATTR_CH_SWITCH_BLOCK_TX,
+       NL80211_ATTR_CSA_IES,
+       NL80211_ATTR_CSA_C_OFF_BEACON,
+       NL80211_ATTR_CSA_C_OFF_PRESP,
+
+       NL80211_ATTR_RXMGMT_FLAGS,
+
+       NL80211_ATTR_STA_SUPPORTED_CHANNELS,
+
+       NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES,
+
+       NL80211_ATTR_HANDLE_DFS,
+
+       NL80211_ATTR_SUPPORT_5_MHZ,
+       NL80211_ATTR_SUPPORT_10_MHZ,
+
+       NL80211_ATTR_OPMODE_NOTIF,
+
+       NL80211_ATTR_VENDOR_ID,
+       NL80211_ATTR_VENDOR_SUBCMD,
+       NL80211_ATTR_VENDOR_DATA,
+       NL80211_ATTR_VENDOR_EVENTS,
+
+       NL80211_ATTR_QOS_MAP,
+
+       NL80211_ATTR_MAC_HINT,
+       NL80211_ATTR_WIPHY_FREQ_HINT,
+
+       NL80211_ATTR_MAX_AP_ASSOC_STA,
+
+       NL80211_ATTR_TDLS_PEER_CAPABILITY,
+
+       NL80211_ATTR_SOCKET_OWNER,
+
+       NL80211_ATTR_CSA_C_OFFSETS_TX,
+       NL80211_ATTR_MAX_CSA_COUNTERS,
+
+       NL80211_ATTR_TDLS_INITIATOR,
+
+       NL80211_ATTR_USE_RRM,
+
+       NL80211_ATTR_WIPHY_DYN_ACK,
+
+       NL80211_ATTR_TSID,
+       NL80211_ATTR_USER_PRIO,
+       NL80211_ATTR_ADMITTED_TIME,
+
+       NL80211_ATTR_SMPS_MODE,
+
+       NL80211_ATTR_OPER_CLASS,
+
+       NL80211_ATTR_MAC_MASK,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
+       NUM_NL80211_ATTR = __NL80211_ATTR_AFTER_LAST,
        NL80211_ATTR_MAX = __NL80211_ATTR_AFTER_LAST - 1
 };
 
 /* source-level API compatibility */
 #define NL80211_ATTR_SCAN_GENERATION NL80211_ATTR_GENERATION
 #define        NL80211_ATTR_MESH_PARAMS NL80211_ATTR_MESH_CONFIG
+#define NL80211_ATTR_IFACE_SOCKET_OWNER NL80211_ATTR_SOCKET_OWNER
 
 /*
  * Allow user space programs to use #ifdef on new attributes by defining them
@@ -1810,6 +2121,8 @@ enum nl80211_attrs {
  *     and therefore can't be created in the normal ways, use the
  *     %NL80211_CMD_START_P2P_DEVICE and %NL80211_CMD_STOP_P2P_DEVICE
  *     commands to create and destroy one
+ * @NL80211_IF_TYPE_OCB: Outside Context of a BSS
+ *     This mode corresponds to the MIB variable dot11OCBActivated=true
  * @NL80211_IFTYPE_MAX: highest interface type number currently defined
  * @NUM_NL80211_IFTYPES: number of defined interface types
  *
@@ -1829,6 +2142,7 @@ enum nl80211_iftype {
        NL80211_IFTYPE_P2P_CLIENT,
        NL80211_IFTYPE_P2P_GO,
        NL80211_IFTYPE_P2P_DEVICE,
+       NL80211_IFTYPE_OCB,
 
        /* keep last */
        NUM_NL80211_IFTYPES,
@@ -2004,6 +2318,8 @@ enum nl80211_sta_bss_param {
  *     Contains a nested array of signal strength attributes (u8, dBm)
  * @NL80211_STA_INFO_CHAIN_SIGNAL_AVG: per-chain signal strength average
  *     Same format as NL80211_STA_INFO_CHAIN_SIGNAL.
+ * @NL80211_STA_EXPECTED_THROUGHPUT: expected throughput considering also the
+ *     802.11 header (u32, kbps)
  * @__NL80211_STA_INFO_AFTER_LAST: internal
  * @NL80211_STA_INFO_MAX: highest possible station info attribute
  */
@@ -2035,6 +2351,7 @@ enum nl80211_sta_info {
        NL80211_STA_INFO_TX_BYTES64,
        NL80211_STA_INFO_CHAIN_SIGNAL,
        NL80211_STA_INFO_CHAIN_SIGNAL_AVG,
+       NL80211_STA_INFO_EXPECTED_THROUGHPUT,
 
        /* keep last */
        __NL80211_STA_INFO_AFTER_LAST,
@@ -2136,10 +2453,9 @@ enum nl80211_band_attr {
  * @NL80211_FREQUENCY_ATTR_FREQ: Frequency in MHz
  * @NL80211_FREQUENCY_ATTR_DISABLED: Channel is disabled in current
  *     regulatory domain.
- * @NL80211_FREQUENCY_ATTR_PASSIVE_SCAN: Only passive scanning is
- *     permitted on this channel in current regulatory domain.
- * @NL80211_FREQUENCY_ATTR_NO_IBSS: IBSS networks are not permitted
- *     on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_NO_IR: no mechanisms that initiate radiation
+ *     are permitted on this channel, this includes sending probe
+ *     requests, or modes of operation that require beaconing.
  * @NL80211_FREQUENCY_ATTR_RADAR: Radar detection is mandatory
  *     on this channel in current regulatory domain.
  * @NL80211_FREQUENCY_ATTR_MAX_TX_POWER: Maximum transmission power in mBm
@@ -2158,16 +2474,42 @@ enum nl80211_band_attr {
  * @NL80211_FREQUENCY_ATTR_NO_160MHZ: any 160 MHz (but not 80+80) channel
  *     using this channel as the primary or any of the secondary channels
  *     isn't possible
+ * @NL80211_FREQUENCY_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
+ * @NL80211_FREQUENCY_ATTR_INDOOR_ONLY: Only indoor use is permitted on this
+ *     channel. A channel that has the INDOOR_ONLY attribute can only be
+ *     used when there is a clear assessment that the device is operating in
+ *     an indoor surroundings, i.e., it is connected to AC power (and not
+ *     through portable DC inverters) or is under the control of a master
+ *     that is acting as an AP and is connected to AC power.
+ * @NL80211_FREQUENCY_ATTR_GO_CONCURRENT: GO operation is allowed on this
+ *     channel if it's connected concurrently to a BSS on the same channel on
+ *     the 2 GHz band or to a channel in the same UNII band (on the 5 GHz
+ *     band), and IEEE80211_CHAN_RADAR is not set. Instantiating a GO on a
+ *     channel that has the GO_CONCURRENT attribute set can be done when there
+ *     is a clear assessment that the device is operating under the guidance of
+ *     an authorized master, i.e., setting up a GO while the device is also
+ *     connected to an AP with DFS and radar detection on the UNII band (it is
+ *     up to user-space, i.e., wpa_supplicant to perform the required
+ *     verifications)
+ * @NL80211_FREQUENCY_ATTR_NO_20MHZ: 20 MHz operation is not allowed
+ *     on this channel in current regulatory domain.
+ * @NL80211_FREQUENCY_ATTR_NO_10MHZ: 10 MHz operation is not allowed
+ *     on this channel in current regulatory domain.
  * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number
  *     currently defined
  * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use
+ *
+ * See https://apps.fcc.gov/eas/comments/GetPublishedDocument.html?id=327&tn=528122
+ * for more information on the FCC description of the relaxations allowed
+ * by NL80211_FREQUENCY_ATTR_INDOOR_ONLY and
+ * NL80211_FREQUENCY_ATTR_GO_CONCURRENT.
  */
 enum nl80211_frequency_attr {
        __NL80211_FREQUENCY_ATTR_INVALID,
        NL80211_FREQUENCY_ATTR_FREQ,
        NL80211_FREQUENCY_ATTR_DISABLED,
-       NL80211_FREQUENCY_ATTR_PASSIVE_SCAN,
-       NL80211_FREQUENCY_ATTR_NO_IBSS,
+       NL80211_FREQUENCY_ATTR_NO_IR,
+       __NL80211_FREQUENCY_ATTR_NO_IBSS,
        NL80211_FREQUENCY_ATTR_RADAR,
        NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
        NL80211_FREQUENCY_ATTR_DFS_STATE,
@@ -2176,6 +2518,11 @@ enum nl80211_frequency_attr {
        NL80211_FREQUENCY_ATTR_NO_HT40_PLUS,
        NL80211_FREQUENCY_ATTR_NO_80MHZ,
        NL80211_FREQUENCY_ATTR_NO_160MHZ,
+       NL80211_FREQUENCY_ATTR_DFS_CAC_TIME,
+       NL80211_FREQUENCY_ATTR_INDOOR_ONLY,
+       NL80211_FREQUENCY_ATTR_GO_CONCURRENT,
+       NL80211_FREQUENCY_ATTR_NO_20MHZ,
+       NL80211_FREQUENCY_ATTR_NO_10MHZ,
 
        /* keep last */
        __NL80211_FREQUENCY_ATTR_AFTER_LAST,
@@ -2183,6 +2530,9 @@ enum nl80211_frequency_attr {
 };
 
 #define NL80211_FREQUENCY_ATTR_MAX_TX_POWER NL80211_FREQUENCY_ATTR_MAX_TX_POWER
+#define NL80211_FREQUENCY_ATTR_PASSIVE_SCAN    NL80211_FREQUENCY_ATTR_NO_IR
+#define NL80211_FREQUENCY_ATTR_NO_IBSS         NL80211_FREQUENCY_ATTR_NO_IR
+#define NL80211_FREQUENCY_ATTR_NO_IR           NL80211_FREQUENCY_ATTR_NO_IR
 
 /**
  * enum nl80211_bitrate_attr - bitrate attributes
@@ -2263,12 +2613,14 @@ enum nl80211_reg_type {
  *     in KHz. This is not a center a frequency but an actual regulatory
  *     band edge.
  * @NL80211_ATTR_FREQ_RANGE_MAX_BW: maximum allowed bandwidth for this
- *     frequency range, in KHz.
+ *     frequency range, in KHz.
  * @NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN: the maximum allowed antenna gain
  *     for a given frequency range. The value is in mBi (100 * dBi).
  *     If you don't have one then don't send this.
  * @NL80211_ATTR_POWER_RULE_MAX_EIRP: the maximum allowed EIRP for
  *     a given frequency range. The value is in mBm (100 * dBm).
+ * @NL80211_ATTR_DFS_CAC_TIME: DFS CAC time in milliseconds.
+ *     If not present or 0 default CAC time will be used.
  * @NL80211_REG_RULE_ATTR_MAX: highest regulatory rule attribute number
  *     currently defined
  * @__NL80211_REG_RULE_ATTR_AFTER_LAST: internal use
@@ -2284,6 +2636,8 @@ enum nl80211_reg_rule_attr {
        NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
        NL80211_ATTR_POWER_RULE_MAX_EIRP,
 
+       NL80211_ATTR_DFS_CAC_TIME,
+
        /* keep last */
        __NL80211_REG_RULE_ATTR_AFTER_LAST,
        NL80211_REG_RULE_ATTR_MAX = __NL80211_REG_RULE_ATTR_AFTER_LAST - 1
@@ -2293,9 +2647,15 @@ enum nl80211_reg_rule_attr {
  * enum nl80211_sched_scan_match_attr - scheduled scan match attributes
  * @__NL80211_SCHED_SCAN_MATCH_ATTR_INVALID: attribute number 0 is reserved
  * @NL80211_SCHED_SCAN_MATCH_ATTR_SSID: SSID to be used for matching,
- * only report BSS with matching SSID.
+ *     only report BSS with matching SSID.
  * @NL80211_SCHED_SCAN_MATCH_ATTR_RSSI: RSSI threshold (in dBm) for reporting a
- *     BSS in scan results. Filtering is turned off if not specified.
+ *     BSS in scan results. Filtering is turned off if not specified. Note that
+ *     if this attribute is in a match set of its own, then it is treated as
+ *     the default value for all matchsets with an SSID, rather than being a
+ *     matchset of its own without an RSSI filter. This is due to problems with
+ *     how this API was implemented in the past. Also, due to the same problem,
+ *     the only way to create a matchset with only an RSSI filter (with this
+ *     attribute) is if there's only a single matchset with the RSSI attribute.
  * @NL80211_SCHED_SCAN_MATCH_ATTR_MAX: highest scheduled scan filter
  *     attribute number currently defined
  * @__NL80211_SCHED_SCAN_MATCH_ATTR_AFTER_LAST: internal use
@@ -2325,8 +2685,17 @@ enum nl80211_sched_scan_match_attr {
  * @NL80211_RRF_DFS: DFS support is required to be used
  * @NL80211_RRF_PTP_ONLY: this is only for Point To Point links
  * @NL80211_RRF_PTMP_ONLY: this is only for Point To Multi Point links
- * @NL80211_RRF_PASSIVE_SCAN: passive scan is required
- * @NL80211_RRF_NO_IBSS: no IBSS is allowed
+ * @NL80211_RRF_NO_IR: no mechanisms that initiate radiation are allowed,
+ *     this includes probe requests or modes of operation that require
+ *     beaconing.
+ * @NL80211_RRF_AUTO_BW: maximum available bandwidth should be calculated
+ *     base on contiguous rules and wider channels will be allowed to cross
+ *     multiple contiguous/overlapping frequency ranges.
+ * @NL80211_RRF_GO_CONCURRENT: See &NL80211_FREQUENCY_ATTR_GO_CONCURRENT
+ * @NL80211_RRF_NO_HT40MINUS: channels can't be used in HT40- operation
+ * @NL80211_RRF_NO_HT40PLUS: channels can't be used in HT40+ operation
+ * @NL80211_RRF_NO_80MHZ: 80MHz operation not allowed
+ * @NL80211_RRF_NO_160MHZ: 160MHz operation not allowed
  */
 enum nl80211_reg_rule_flags {
        NL80211_RRF_NO_OFDM             = 1<<0,
@@ -2336,10 +2705,25 @@ enum nl80211_reg_rule_flags {
        NL80211_RRF_DFS                 = 1<<4,
        NL80211_RRF_PTP_ONLY            = 1<<5,
        NL80211_RRF_PTMP_ONLY           = 1<<6,
-       NL80211_RRF_PASSIVE_SCAN        = 1<<7,
-       NL80211_RRF_NO_IBSS             = 1<<8,
+       NL80211_RRF_NO_IR               = 1<<7,
+       __NL80211_RRF_NO_IBSS           = 1<<8,
+       NL80211_RRF_AUTO_BW             = 1<<11,
+       NL80211_RRF_GO_CONCURRENT       = 1<<12,
+       NL80211_RRF_NO_HT40MINUS        = 1<<13,
+       NL80211_RRF_NO_HT40PLUS         = 1<<14,
+       NL80211_RRF_NO_80MHZ            = 1<<15,
+       NL80211_RRF_NO_160MHZ           = 1<<16,
 };
 
+#define NL80211_RRF_PASSIVE_SCAN       NL80211_RRF_NO_IR
+#define NL80211_RRF_NO_IBSS            NL80211_RRF_NO_IR
+#define NL80211_RRF_NO_IR              NL80211_RRF_NO_IR
+#define NL80211_RRF_NO_HT40            (NL80211_RRF_NO_HT40MINUS |\
+                                        NL80211_RRF_NO_HT40PLUS)
+
+/* For backport compatibility with older userspace */
+#define NL80211_RRF_NO_IR_ALL          (NL80211_RRF_NO_IR | __NL80211_RRF_NO_IBSS)
+
 /**
  * enum nl80211_dfs_regions - regulatory DFS regions
  *
@@ -2369,10 +2753,13 @@ enum nl80211_dfs_regions {
  *     present has been registered with the wireless core that
  *     has listed NL80211_FEATURE_CELL_BASE_REG_HINTS as a
  *     supported feature.
+ * @NL80211_USER_REG_HINT_INDOOR: a user sent an hint indicating that the
+ *     platform is operating in an indoor environment.
  */
 enum nl80211_user_reg_hint_type {
        NL80211_USER_REG_HINT_USER      = 0,
        NL80211_USER_REG_HINT_CELL_BASE = 1,
+       NL80211_USER_REG_HINT_INDOOR    = 2,
 };
 
 /**
@@ -2428,6 +2815,8 @@ enum nl80211_survey_info {
  * @NL80211_MNTR_FLAG_OTHER_BSS: disable BSSID filtering
  * @NL80211_MNTR_FLAG_COOK_FRAMES: report frames after processing.
  *     overrides all other flags.
+ * @NL80211_MNTR_FLAG_ACTIVE: use the configured MAC address
+ *     and ACK incoming unicast packets.
  *
  * @__NL80211_MNTR_FLAG_AFTER_LAST: internal use
  * @NL80211_MNTR_FLAG_MAX: highest possible monitor flag
@@ -2439,6 +2828,7 @@ enum nl80211_mntr_flags {
        NL80211_MNTR_FLAG_CONTROL,
        NL80211_MNTR_FLAG_OTHER_BSS,
        NL80211_MNTR_FLAG_COOK_FRAMES,
+       NL80211_MNTR_FLAG_ACTIVE,
 
        /* keep last */
        __NL80211_MNTR_FLAG_AFTER_LAST,
@@ -2574,6 +2964,10 @@ enum nl80211_mesh_power_mode {
  *
  * @NL80211_MESHCONF_AWAKE_WINDOW: awake window duration (in TUs)
  *
+ * @NL80211_MESHCONF_PLINK_TIMEOUT: If no tx activity is seen from a STA we've
+ *     established peering with for longer than this time (in seconds), then
+ *     remove it from the STA's list of peers.  Default is 30 minutes.
+ *
  * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
  */
 enum nl80211_meshconf_params {
@@ -2605,6 +2999,7 @@ enum nl80211_meshconf_params {
        NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
        NL80211_MESHCONF_POWER_MODE,
        NL80211_MESHCONF_AWAKE_WINDOW,
+       NL80211_MESHCONF_PLINK_TIMEOUT,
 
        /* keep last */
        __NL80211_MESHCONF_ATTR_AFTER_LAST,
@@ -2750,6 +3145,8 @@ enum nl80211_channel_type {
  *     and %NL80211_ATTR_CENTER_FREQ2 attributes must be provided as well
  * @NL80211_CHAN_WIDTH_160: 160 MHz channel, the %NL80211_ATTR_CENTER_FREQ1
  *     attribute must be provided as well
+ * @NL80211_CHAN_WIDTH_5: 5 MHz OFDM channel
+ * @NL80211_CHAN_WIDTH_10: 10 MHz OFDM channel
  */
 enum nl80211_chan_width {
        NL80211_CHAN_WIDTH_20_NOHT,
@@ -2758,6 +3155,23 @@ enum nl80211_chan_width {
        NL80211_CHAN_WIDTH_80,
        NL80211_CHAN_WIDTH_80P80,
        NL80211_CHAN_WIDTH_160,
+       NL80211_CHAN_WIDTH_5,
+       NL80211_CHAN_WIDTH_10,
+};
+
+/**
+ * enum nl80211_bss_scan_width - control channel width for a BSS
+ *
+ * These values are used with the %NL80211_BSS_CHAN_WIDTH attribute.
+ *
+ * @NL80211_BSS_CHAN_WIDTH_20: control channel is 20 MHz wide or compatible
+ * @NL80211_BSS_CHAN_WIDTH_10: control channel is 10 MHz wide
+ * @NL80211_BSS_CHAN_WIDTH_5: control channel is 5 MHz wide
+ */
+enum nl80211_bss_scan_width {
+       NL80211_BSS_CHAN_WIDTH_20,
+       NL80211_BSS_CHAN_WIDTH_10,
+       NL80211_BSS_CHAN_WIDTH_5,
 };
 
 /**
@@ -2767,14 +3181,20 @@ enum nl80211_chan_width {
  * @NL80211_BSS_BSSID: BSSID of the BSS (6 octets)
  * @NL80211_BSS_FREQUENCY: frequency in MHz (u32)
  * @NL80211_BSS_TSF: TSF of the received probe response/beacon (u64)
+ *     (if @NL80211_BSS_PRESP_DATA is present then this is known to be
+ *     from a probe response, otherwise it may be from the same beacon
+ *     that the NL80211_BSS_BEACON_TSF will be from)
  * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16)
  * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16)
  * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the
  *     raw information elements from the probe response/beacon (bin);
- *     if the %NL80211_BSS_BEACON_IES attribute is present, the IEs here are
- *     from a Probe Response frame; otherwise they are from a Beacon frame.
+ *     if the %NL80211_BSS_BEACON_IES attribute is present and the data is
+ *     different then the IEs here are from a Probe Response frame; otherwise
+ *     they are from a Beacon frame.
  *     However, if the driver does not indicate the source of the IEs, these
  *     IEs may be from either frame subtype.
+ *     If present, the @NL80211_BSS_PRESP_DATA attribute indicates that the
+ *     data here is known to be from a probe response, without any heuristics.
  * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon
  *     in mBm (100 * dBm) (s32)
  * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon
@@ -2784,6 +3204,12 @@ enum nl80211_chan_width {
  * @NL80211_BSS_BEACON_IES: binary attribute containing the raw information
  *     elements from a Beacon frame (bin); not present if no Beacon frame has
  *     yet been received
+ * @NL80211_BSS_CHAN_WIDTH: channel width of the control channel
+ *     (u32, enum nl80211_bss_scan_width)
+ * @NL80211_BSS_BEACON_TSF: TSF of the last received beacon (u64)
+ *     (not present if no beacon frame has been received yet)
+ * @NL80211_BSS_PRESP_DATA: the data in @NL80211_BSS_INFORMATION_ELEMENTS and
+ *     @NL80211_BSS_TSF is known to be from a probe response (flag attribute)
  * @__NL80211_BSS_AFTER_LAST: internal
  * @NL80211_BSS_MAX: highest BSS attribute
  */
@@ -2800,6 +3226,9 @@ enum nl80211_bss {
        NL80211_BSS_STATUS,
        NL80211_BSS_SEEN_MS_AGO,
        NL80211_BSS_BEACON_IES,
+       NL80211_BSS_CHAN_WIDTH,
+       NL80211_BSS_BEACON_TSF,
+       NL80211_BSS_PRESP_DATA,
 
        /* keep last */
        __NL80211_BSS_AFTER_LAST,
@@ -2940,21 +3369,43 @@ enum nl80211_key_attributes {
  *     in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with
  *     1 = 500 kbps) but without the IE length restriction (at most
  *     %NL80211_MAX_SUPP_RATES in a single array).
- * @NL80211_TXRATE_MCS: HT (MCS) rates allowed for TX rate selection
+ * @NL80211_TXRATE_HT: HT (MCS) rates allowed for TX rate selection
  *     in an array of MCS numbers.
+ * @NL80211_TXRATE_VHT: VHT rates allowed for TX rate selection,
+ *     see &struct nl80211_txrate_vht
+ * @NL80211_TXRATE_GI: configure GI, see &enum nl80211_txrate_gi
  * @__NL80211_TXRATE_AFTER_LAST: internal
  * @NL80211_TXRATE_MAX: highest TX rate attribute
  */
 enum nl80211_tx_rate_attributes {
        __NL80211_TXRATE_INVALID,
        NL80211_TXRATE_LEGACY,
-       NL80211_TXRATE_MCS,
+       NL80211_TXRATE_HT,
+       NL80211_TXRATE_VHT,
+       NL80211_TXRATE_GI,
 
        /* keep last */
        __NL80211_TXRATE_AFTER_LAST,
        NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1
 };
 
+#define NL80211_TXRATE_MCS NL80211_TXRATE_HT
+#define NL80211_VHT_NSS_MAX            8
+
+/**
+ * struct nl80211_txrate_vht - VHT MCS/NSS txrate bitmap
+ * @mcs: MCS bitmap table for each NSS (array index 0 for 1 stream, etc.)
+ */
+struct nl80211_txrate_vht {
+       __u16 mcs[NL80211_VHT_NSS_MAX];
+};
+
+enum nl80211_txrate_gi {
+       NL80211_TXRATE_DEFAULT_GI,
+       NL80211_TXRATE_FORCE_SGI,
+       NL80211_TXRATE_FORCE_LGI,
+};
+
 /**
  * enum nl80211_band - Frequency band
  * @NL80211_BAND_2GHZ: 2.4 GHz ISM band
@@ -3000,6 +3451,8 @@ enum nl80211_ps_state {
  *     interval in which %NL80211_ATTR_CQM_TXE_PKTS and
  *     %NL80211_ATTR_CQM_TXE_RATE must be satisfied before generating an
  *     %NL80211_CMD_NOTIFY_CQM. Set to 0 to turn off TX error reporting.
+ * @NL80211_ATTR_CQM_BEACON_LOSS_EVENT: flag attribute that's set in a beacon
+ *     loss event
  * @__NL80211_ATTR_CQM_AFTER_LAST: internal
  * @NL80211_ATTR_CQM_MAX: highest key attribute
  */
@@ -3012,6 +3465,7 @@ enum nl80211_attr_cqm {
        NL80211_ATTR_CQM_TXE_RATE,
        NL80211_ATTR_CQM_TXE_PKTS,
        NL80211_ATTR_CQM_TXE_INTVL,
+       NL80211_ATTR_CQM_BEACON_LOSS_EVENT,
 
        /* keep last */
        __NL80211_ATTR_CQM_AFTER_LAST,
@@ -3024,9 +3478,7 @@ enum nl80211_attr_cqm {
  *      configured threshold
  * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH: The RSSI is higher than the
  *      configured threshold
- * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: The device experienced beacon loss.
- *     (Note that deauth/disassoc will still follow if the AP is not
- *     available. This event might get used as roaming event, etc.)
+ * @NL80211_CQM_RSSI_BEACON_LOSS_EVENT: (reserved, never sent)
  */
 enum nl80211_cqm_rssi_threshold_event {
        NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
@@ -3048,11 +3500,11 @@ enum nl80211_tx_power_setting {
 };
 
 /**
- * enum nl80211_wowlan_packet_pattern_attr - WoWLAN packet pattern attribute
- * @__NL80211_WOWLAN_PKTPAT_INVALID: invalid number for nested attribute
- * @NL80211_WOWLAN_PKTPAT_PATTERN: the pattern, values where the mask has
+ * enum nl80211_packet_pattern_attr - packet pattern attribute
+ * @__NL80211_PKTPAT_INVALID: invalid number for nested attribute
+ * @NL80211_PKTPAT_PATTERN: the pattern, values where the mask has
  *     a zero bit are ignored
- * @NL80211_WOWLAN_PKTPAT_MASK: pattern mask, must be long enough to have
+ * @NL80211_PKTPAT_MASK: pattern mask, must be long enough to have
  *     a bit for each byte in the pattern. The lowest-order bit corresponds
  *     to the first byte of the pattern, but the bytes of the pattern are
  *     in a little-endian-like format, i.e. the 9th byte of the pattern
@@ -3063,39 +3515,50 @@ enum nl80211_tx_power_setting {
  *     Note that the pattern matching is done as though frames were not
  *     802.11 frames but 802.3 frames, i.e. the frame is fully unpacked
  *     first (including SNAP header unpacking) and then matched.
- * @NL80211_WOWLAN_PKTPAT_OFFSET: packet offset, pattern is matched after
+ * @NL80211_PKTPAT_OFFSET: packet offset, pattern is matched after
  *     these fixed number of bytes of received packet
- * @NUM_NL80211_WOWLAN_PKTPAT: number of attributes
- * @MAX_NL80211_WOWLAN_PKTPAT: max attribute number
+ * @NUM_NL80211_PKTPAT: number of attributes
+ * @MAX_NL80211_PKTPAT: max attribute number
  */
-enum nl80211_wowlan_packet_pattern_attr {
-       __NL80211_WOWLAN_PKTPAT_INVALID,
-       NL80211_WOWLAN_PKTPAT_MASK,
-       NL80211_WOWLAN_PKTPAT_PATTERN,
-       NL80211_WOWLAN_PKTPAT_OFFSET,
-
-       NUM_NL80211_WOWLAN_PKTPAT,
-       MAX_NL80211_WOWLAN_PKTPAT = NUM_NL80211_WOWLAN_PKTPAT - 1,
+enum nl80211_packet_pattern_attr {
+       __NL80211_PKTPAT_INVALID,
+       NL80211_PKTPAT_MASK,
+       NL80211_PKTPAT_PATTERN,
+       NL80211_PKTPAT_OFFSET,
+
+       NUM_NL80211_PKTPAT,
+       MAX_NL80211_PKTPAT = NUM_NL80211_PKTPAT - 1,
 };
 
 /**
- * struct nl80211_wowlan_pattern_support - pattern support information
+ * struct nl80211_pattern_support - packet pattern support information
  * @max_patterns: maximum number of patterns supported
  * @min_pattern_len: minimum length of each pattern
  * @max_pattern_len: maximum length of each pattern
  * @max_pkt_offset: maximum Rx packet offset
  *
  * This struct is carried in %NL80211_WOWLAN_TRIG_PKT_PATTERN when
- * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED in the
- * capability information given by the kernel to userspace.
+ * that is part of %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED or in
+ * %NL80211_ATTR_COALESCE_RULE_PKT_PATTERN when that is part of
+ * %NL80211_ATTR_COALESCE_RULE in the capability information given
+ * by the kernel to userspace.
  */
-struct nl80211_wowlan_pattern_support {
+struct nl80211_pattern_support {
        __u32 max_patterns;
        __u32 min_pattern_len;
        __u32 max_pattern_len;
        __u32 max_pkt_offset;
 } __attribute__((packed));
 
+/* only for backward compatibility */
+#define __NL80211_WOWLAN_PKTPAT_INVALID __NL80211_PKTPAT_INVALID
+#define NL80211_WOWLAN_PKTPAT_MASK NL80211_PKTPAT_MASK
+#define NL80211_WOWLAN_PKTPAT_PATTERN NL80211_PKTPAT_PATTERN
+#define NL80211_WOWLAN_PKTPAT_OFFSET NL80211_PKTPAT_OFFSET
+#define NUM_NL80211_WOWLAN_PKTPAT NUM_NL80211_PKTPAT
+#define MAX_NL80211_WOWLAN_PKTPAT MAX_NL80211_PKTPAT
+#define nl80211_wowlan_pattern_support nl80211_pattern_support
+
 /**
  * enum nl80211_wowlan_triggers - WoWLAN trigger definitions
  * @__NL80211_WOWLAN_TRIG_INVALID: invalid number for nested attributes
@@ -3115,7 +3578,7 @@ struct nl80211_wowlan_pattern_support {
  *     pattern matching is done after the packet is converted to the MSDU.
  *
  *     In %NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED, it is a binary attribute
- *     carrying a &struct nl80211_wowlan_pattern_support.
+ *     carrying a &struct nl80211_pattern_support.
  *
  *     When reporting wakeup. it is a u32 attribute containing the 0-based
  *     index of the pattern that caused the wakeup, in the patterns passed
@@ -3155,6 +3618,25 @@ struct nl80211_wowlan_pattern_support {
  * @NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS: For wakeup reporting only,
  *     the TCP connection ran out of tokens to use for data to send to the
  *     service
+ * @NL80211_WOWLAN_TRIG_NET_DETECT: wake up when a configured network
+ *     is detected.  This is a nested attribute that contains the
+ *     same attributes used with @NL80211_CMD_START_SCHED_SCAN.  It
+ *     specifies how the scan is performed (e.g. the interval and the
+ *     channels to scan) as well as the scan results that will
+ *     trigger a wake (i.e. the matchsets).
+ * @NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS: nested attribute
+ *     containing an array with information about what triggered the
+ *     wake up.  If no elements are present in the array, it means
+ *     that the information is not available.  If more than one
+ *     element is present, it means that more than one match
+ *     occurred.
+ *     Each element in the array is a nested attribute that contains
+ *     one optional %NL80211_ATTR_SSID attribute and one optional
+ *     %NL80211_ATTR_SCAN_FREQUENCIES attribute.  At least one of
+ *     these attributes must be present.  If
+ *     %NL80211_ATTR_SCAN_FREQUENCIES contains more than one
+ *     frequency, it means that the match occurred in more than one
+ *     channel.
  * @NUM_NL80211_WOWLAN_TRIG: number of wake on wireless triggers
  * @MAX_NL80211_WOWLAN_TRIG: highest wowlan trigger attribute number
  *
@@ -3180,6 +3662,8 @@ enum nl80211_wowlan_triggers {
        NL80211_WOWLAN_TRIG_WAKEUP_TCP_MATCH,
        NL80211_WOWLAN_TRIG_WAKEUP_TCP_CONNLOST,
        NL80211_WOWLAN_TRIG_WAKEUP_TCP_NOMORETOKENS,
+       NL80211_WOWLAN_TRIG_NET_DETECT,
+       NL80211_WOWLAN_TRIG_NET_DETECT_RESULTS,
 
        /* keep last */
        NUM_NL80211_WOWLAN_TRIG,
@@ -3272,7 +3756,7 @@ struct nl80211_wowlan_tcp_data_token_feature {
  * @NL80211_WOWLAN_TCP_WAKE_PAYLOAD: wake packet payload, for advertising a
  *     u32 attribute holding the maximum length
  * @NL80211_WOWLAN_TCP_WAKE_MASK: Wake packet payload mask, not used for
- *     feature advertising. The mask works like @NL80211_WOWLAN_PKTPAT_MASK
+ *     feature advertising. The mask works like @NL80211_PKTPAT_MASK
  *     but on the TCP payload only.
  * @NUM_NL80211_WOWLAN_TCP: number of TCP attributes
  * @MAX_NL80211_WOWLAN_TCP: highest attribute number
@@ -3297,6 +3781,55 @@ enum nl80211_wowlan_tcp_attrs {
 };
 
 /**
+ * struct nl80211_coalesce_rule_support - coalesce rule support information
+ * @max_rules: maximum number of rules supported
+ * @pat: packet pattern support information
+ * @max_delay: maximum supported coalescing delay in msecs
+ *
+ * This struct is carried in %NL80211_ATTR_COALESCE_RULE in the
+ * capability information given by the kernel to userspace.
+ */
+struct nl80211_coalesce_rule_support {
+       __u32 max_rules;
+       struct nl80211_pattern_support pat;
+       __u32 max_delay;
+} __attribute__((packed));
+
+/**
+ * enum nl80211_attr_coalesce_rule - coalesce rule attribute
+ * @__NL80211_COALESCE_RULE_INVALID: invalid number for nested attribute
+ * @NL80211_ATTR_COALESCE_RULE_DELAY: delay in msecs used for packet coalescing
+ * @NL80211_ATTR_COALESCE_RULE_CONDITION: condition for packet coalescence,
+ *     see &enum nl80211_coalesce_condition.
+ * @NL80211_ATTR_COALESCE_RULE_PKT_PATTERN: packet offset, pattern is matched
+ *     after these fixed number of bytes of received packet
+ * @NUM_NL80211_ATTR_COALESCE_RULE: number of attributes
+ * @NL80211_ATTR_COALESCE_RULE_MAX: max attribute number
+ */
+enum nl80211_attr_coalesce_rule {
+       __NL80211_COALESCE_RULE_INVALID,
+       NL80211_ATTR_COALESCE_RULE_DELAY,
+       NL80211_ATTR_COALESCE_RULE_CONDITION,
+       NL80211_ATTR_COALESCE_RULE_PKT_PATTERN,
+
+       /* keep last */
+       NUM_NL80211_ATTR_COALESCE_RULE,
+       NL80211_ATTR_COALESCE_RULE_MAX = NUM_NL80211_ATTR_COALESCE_RULE - 1
+};
+
+/**
+ * enum nl80211_coalesce_condition - coalesce rule conditions
+ * @NL80211_COALESCE_CONDITION_MATCH: coalaesce Rx packets when patterns
+ *     in a rule are matched.
+ * @NL80211_COALESCE_CONDITION_NO_MATCH: coalesce Rx packets when patterns
+ *     in a rule are not matched.
+ */
+enum nl80211_coalesce_condition {
+       NL80211_COALESCE_CONDITION_MATCH,
+       NL80211_COALESCE_CONDITION_NO_MATCH
+};
+
+/**
  * enum nl80211_iface_limit_attrs - limit attributes
  * @NL80211_IFACE_LIMIT_UNSPEC: (reserved)
  * @NL80211_IFACE_LIMIT_MAX: maximum number of interfaces that
@@ -3334,6 +3867,8 @@ enum nl80211_iface_limit_attrs {
  *     different channels may be used within this group.
  * @NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS: u32 attribute containing the bitmap
  *     of supported channel widths for radar detection.
+ * @NL80211_IFACE_COMB_RADAR_DETECT_REGIONS: u32 attribute containing the bitmap
+ *     of supported regulatory regions for radar detection.
  * @NUM_NL80211_IFACE_COMB: number of attributes
  * @MAX_NL80211_IFACE_COMB: highest attribute number
  *
@@ -3367,6 +3902,7 @@ enum nl80211_if_combination_attrs {
        NL80211_IFACE_COMB_STA_AP_BI_MATCH,
        NL80211_IFACE_COMB_NUM_CHANNELS,
        NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS,
+       NL80211_IFACE_COMB_RADAR_DETECT_REGIONS,
 
        /* keep last */
        NUM_NL80211_IFACE_COMB,
@@ -3540,11 +4076,8 @@ enum nl80211_ap_sme_features {
  * @NL80211_FEATURE_CELL_BASE_REG_HINTS: This driver has been tested
  *     to work properly to suppport receiving regulatory hints from
  *     cellular base stations.
- * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: If this is set, an active
- *     P2P Device (%NL80211_IFTYPE_P2P_DEVICE) requires its own channel
- *     in the interface combinations, even when it's only used for scan
- *     and remain-on-channel. This could be due to, for example, the
- *     remain-on-channel implementation requiring a channel context.
+ * @NL80211_FEATURE_P2P_DEVICE_NEEDS_CHANNEL: (no longer available, only
+ *     here to reserve the value for API/ABI compatibility)
  * @NL80211_FEATURE_SAE: This driver supports simultaneous authentication of
  *     equals (SAE) with user space SME (NL80211_CMD_AUTHENTICATE) in station
  *     mode
@@ -3576,6 +4109,54 @@ enum nl80211_ap_sme_features {
  *     Peering Management entity which may be implemented by registering for
  *     beacons or NL80211_CMD_NEW_PEER_CANDIDATE events. The mesh beacon is
  *     still generated by the driver.
+ * @NL80211_FEATURE_ACTIVE_MONITOR: This driver supports an active monitor
+ *     interface. An active monitor interface behaves like a normal monitor
+ *     interface, but gets added to the driver. It ensures that incoming
+ *     unicast packets directed at the configured interface address get ACKed.
+ * @NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE: This driver supports dynamic
+ *     channel bandwidth change (e.g., HT 20 <-> 40 MHz channel) during the
+ *     lifetime of a BSS.
+ * @NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES: This device adds a DS Parameter
+ *     Set IE to probe requests.
+ * @NL80211_FEATURE_WFA_TPC_IE_IN_PROBES: This device adds a WFA TPC Report IE
+ *     to probe requests.
+ * @NL80211_FEATURE_QUIET: This device, in client mode, supports Quiet Period
+ *     requests sent to it by an AP.
+ * @NL80211_FEATURE_TX_POWER_INSERTION: This device is capable of inserting the
+ *     current tx power value into the TPC Report IE in the spectrum
+ *     management TPC Report action frame, and in the Radio Measurement Link
+ *     Measurement Report action frame.
+ * @NL80211_FEATURE_ACKTO_ESTIMATION: This driver supports dynamic ACK timeout
+ *     estimation (dynack). %NL80211_ATTR_WIPHY_DYN_ACK flag attribute is used
+ *     to enable dynack.
+ * @NL80211_FEATURE_STATIC_SMPS: Device supports static spatial
+ *     multiplexing powersave, ie. can turn off all but one chain
+ *     even on HT connections that should be using more chains.
+ * @NL80211_FEATURE_DYNAMIC_SMPS: Device supports dynamic spatial
+ *     multiplexing powersave, ie. can turn off all but one chain
+ *     and then wake the rest up as required after, for example,
+ *     rts/cts handshake.
+ * @NL80211_FEATURE_SUPPORTS_WMM_ADMISSION: the device supports setting up WMM
+ *     TSPEC sessions (TID aka TSID 0-7) with the %NL80211_CMD_ADD_TX_TS
+ *     command. Standard IEEE 802.11 TSPEC setup is not yet supported, it
+ *     needs to be able to handle Block-Ack agreements and other things.
+ * @NL80211_FEATURE_MAC_ON_CREATE: Device supports configuring
+ *     the vif's MAC address upon creation.
+ *     See 'macaddr' field in the vif_params (cfg80211.h).
+ * @NL80211_FEATURE_TDLS_CHANNEL_SWITCH: Driver supports channel switching when
+ *     operating as a TDLS peer.
+ * @NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR: This device/driver supports using a
+ *     random MAC address during scan (if the device is unassociated); the
+ *     %NL80211_SCAN_FLAG_RANDOM_ADDR flag may be set for scans and the MAC
+ *     address mask/value will be used.
+ * @NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR: This device/driver supports
+ *     using a random MAC address for every scan iteration during scheduled
+ *     scan (while not associated), the %NL80211_SCAN_FLAG_RANDOM_ADDR may
+ *     be set for scheduled scan and the MAC address mask/value will be used.
+ * @NL80211_FEATURE_ND_RANDOM_MAC_ADDR: This device/driver supports using a
+ *     random MAC address for every scan iteration during "net detect", i.e.
+ *     scan in unassociated WoWLAN, the %NL80211_SCAN_FLAG_RANDOM_ADDR may
+ *     be set for scheduled scan and the MAC address mask/value will be used.
  */
 enum nl80211_feature_flags {
        NL80211_FEATURE_SK_TX_STATUS                    = 1 << 0,
@@ -3595,6 +4176,21 @@ enum nl80211_feature_flags {
        NL80211_FEATURE_ADVERTISE_CHAN_LIMITS           = 1 << 14,
        NL80211_FEATURE_FULL_AP_CLIENT_STATE            = 1 << 15,
        NL80211_FEATURE_USERSPACE_MPM                   = 1 << 16,
+       NL80211_FEATURE_ACTIVE_MONITOR                  = 1 << 17,
+       NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE       = 1 << 18,
+       NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES       = 1 << 19,
+       NL80211_FEATURE_WFA_TPC_IE_IN_PROBES            = 1 << 20,
+       NL80211_FEATURE_QUIET                           = 1 << 21,
+       NL80211_FEATURE_TX_POWER_INSERTION              = 1 << 22,
+       NL80211_FEATURE_ACKTO_ESTIMATION                = 1 << 23,
+       NL80211_FEATURE_STATIC_SMPS                     = 1 << 24,
+       NL80211_FEATURE_DYNAMIC_SMPS                    = 1 << 25,
+       NL80211_FEATURE_SUPPORTS_WMM_ADMISSION          = 1 << 26,
+       NL80211_FEATURE_MAC_ON_CREATE                   = 1 << 27,
+       NL80211_FEATURE_TDLS_CHANNEL_SWITCH             = 1 << 28,
+       NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR            = 1 << 29,
+       NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR      = 1 << 30,
+       NL80211_FEATURE_ND_RANDOM_MAC_ADDR              = 1 << 31,
 };
 
 /**
@@ -3643,11 +4239,21 @@ enum nl80211_connect_failed_reason {
  *     dangerous because will destroy stations performance as a lot of frames
  *     will be lost while scanning off-channel, therefore it must be used only
  *     when really needed
+ * @NL80211_SCAN_FLAG_RANDOM_ADDR: use a random MAC address for this scan (or
+ *     for scheduled scan: a different one for every scan iteration). When the
+ *     flag is set, depending on device capabilities the @NL80211_ATTR_MAC and
+ *     @NL80211_ATTR_MAC_MASK attributes may also be given in which case only
+ *     the masked bits will be preserved from the MAC address and the remainder
+ *     randomised. If the attributes are not given full randomisation (46 bits,
+ *     locally administered 1, multicast 0) is assumed.
+ *     This flag must not be requested when the feature isn't supported, check
+ *     the nl80211 feature flags for the device.
  */
 enum nl80211_scan_flags {
        NL80211_SCAN_FLAG_LOW_PRIORITY                  = 1<<0,
        NL80211_SCAN_FLAG_FLUSH                         = 1<<1,
        NL80211_SCAN_FLAG_AP                            = 1<<2,
+       NL80211_SCAN_FLAG_RANDOM_ADDR                   = 1<<3,
 };
 
 /**
@@ -3669,6 +4275,25 @@ enum nl80211_acl_policy {
 };
 
 /**
+ * enum nl80211_smps_mode - SMPS mode
+ *
+ * Requested SMPS mode (for AP mode)
+ *
+ * @NL80211_SMPS_OFF: SMPS off (use all antennas).
+ * @NL80211_SMPS_STATIC: static SMPS (use a single antenna)
+ * @NL80211_SMPS_DYNAMIC: dynamic smps (start with a single antenna and
+ *     turn on other antennas after CTS/RTS).
+ */
+enum nl80211_smps_mode {
+       NL80211_SMPS_OFF,
+       NL80211_SMPS_STATIC,
+       NL80211_SMPS_DYNAMIC,
+
+       __NL80211_SMPS_AFTER_LAST,
+       NL80211_SMPS_MAX = __NL80211_SMPS_AFTER_LAST - 1
+};
+
+/**
  * enum nl80211_radar_event - type of radar event for DFS operation
  *
  * Type of event to be used with NL80211_ATTR_RADAR_EVENT to inform userspace
@@ -3695,13 +4320,12 @@ enum nl80211_radar_event {
  *
  * Channel states used by the DFS code.
  *
- * @IEEE80211_DFS_USABLE: The channel can be used, but channel availability
+ * @NL80211_DFS_USABLE: The channel can be used, but channel availability
  *     check (CAC) must be performed before using it for AP or IBSS.
- * @IEEE80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
+ * @NL80211_DFS_UNAVAILABLE: A radar has been detected on this channel, it
  *     is therefore marked as not available.
- * @IEEE80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
+ * @NL80211_DFS_AVAILABLE: The channel has been CAC checked and is available.
  */
-
 enum nl80211_dfs_state {
        NL80211_DFS_USABLE,
        NL80211_DFS_UNAVAILABLE,
@@ -3741,4 +4365,51 @@ enum nl80211_crit_proto_id {
 /* maximum duration for critical protocol measures */
 #define NL80211_CRIT_PROTO_MAX_DURATION                5000 /* msec */
 
+/**
+ * enum nl80211_rxmgmt_flags - flags for received management frame.
+ *
+ * Used by cfg80211_rx_mgmt()
+ *
+ * @NL80211_RXMGMT_FLAG_ANSWERED: frame was answered by device/driver.
+ */
+enum nl80211_rxmgmt_flags {
+       NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0,
+};
+
+/*
+ * If this flag is unset, the lower 24 bits are an OUI, if set
+ * a Linux nl80211 vendor ID is used (no such IDs are allocated
+ * yet, so that's not valid so far)
+ */
+#define NL80211_VENDOR_ID_IS_LINUX     0x80000000
+
+/**
+ * struct nl80211_vendor_cmd_info - vendor command data
+ * @vendor_id: If the %NL80211_VENDOR_ID_IS_LINUX flag is clear, then the
+ *     value is a 24-bit OUI; if it is set then a separately allocated ID
+ *     may be used, but no such IDs are allocated yet. New IDs should be
+ *     added to this file when needed.
+ * @subcmd: sub-command ID for the command
+ */
+struct nl80211_vendor_cmd_info {
+       __u32 vendor_id;
+       __u32 subcmd;
+};
+
+/**
+ * enum nl80211_tdls_peer_capability - TDLS peer flags.
+ *
+ * Used by tdls_mgmt() to determine which conditional elements need
+ * to be added to TDLS Setup frames.
+ *
+ * @NL80211_TDLS_PEER_HT: TDLS peer is HT capable.
+ * @NL80211_TDLS_PEER_VHT: TDLS peer is VHT capable.
+ * @NL80211_TDLS_PEER_WMM: TDLS peer is WMM capable.
+ */
+enum nl80211_tdls_peer_capability {
+       NL80211_TDLS_PEER_HT = 1<<0,
+       NL80211_TDLS_PEER_VHT = 1<<1,
+       NL80211_TDLS_PEER_WMM = 1<<2,
+};
+
 #endif /* __LINUX_NL80211_H */
index 74d6ce5..d3f091c 100644 (file)
@@ -68,7 +68,9 @@
 ((attrlen) -= RTA_ALIGN((rta)->rta_len), \
 (struct rtattr *) (((char *)(rta)) + RTA_ALIGN((rta)->rta_len)))
 #define RTA_LENGTH(len) (RTA_ALIGN(sizeof(struct rtattr)) + (len))
+#define RTA_SPACE(len) RTA_ALIGN(RTA_LENGTH(len))
 #define RTA_DATA(rta) ((void *) (((char *) (rta)) + RTA_LENGTH(0)))
+#define RTA_PAYLOAD(rta) ((int) ((rta)->rta_len) - RTA_LENGTH(0))
 
 
 struct sockaddr_nl
index 9c41962..adfd3df 100644 (file)
@@ -2,7 +2,7 @@ all:
        @echo Nothing to be made.
 
 clean:
-       rm -f *~ *.o *.d
+       rm -f *~ *.o *.d *.gcno *.gcda *.gcov
 
 install:
        @echo Nothing to be made.
index 7b077cb..1de1328 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * EAP common peer/server definitions
- * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -203,3 +203,86 @@ EapType eap_get_type(const struct wpabuf *msg)
 
        return ((const u8 *) wpabuf_head(msg))[sizeof(struct eap_hdr)];
 }
+
+
+#ifdef CONFIG_ERP
+int erp_parse_tlvs(const u8 *pos, const u8 *end, struct erp_tlvs *tlvs,
+                  int stop_at_keyname)
+{
+       os_memset(tlvs, 0, sizeof(*tlvs));
+
+       while (pos < end) {
+               u8 tlv_type, tlv_len;
+
+               tlv_type = *pos++;
+               switch (tlv_type) {
+               case EAP_ERP_TV_RRK_LIFETIME:
+               case EAP_ERP_TV_RMSK_LIFETIME:
+                       /* 4-octet TV */
+                       if (pos + 4 > end) {
+                               wpa_printf(MSG_DEBUG, "EAP: Too short TV");
+                               return -1;
+                       }
+                       pos += 4;
+                       break;
+               case EAP_ERP_TLV_DOMAIN_NAME:
+               case EAP_ERP_TLV_KEYNAME_NAI:
+               case EAP_ERP_TLV_CRYPTOSUITES:
+               case EAP_ERP_TLV_AUTHORIZATION_INDICATION:
+               case EAP_ERP_TLV_CALLED_STATION_ID:
+               case EAP_ERP_TLV_CALLING_STATION_ID:
+               case EAP_ERP_TLV_NAS_IDENTIFIER:
+               case EAP_ERP_TLV_NAS_IP_ADDRESS:
+               case EAP_ERP_TLV_NAS_IPV6_ADDRESS:
+                       if (pos >= end) {
+                               wpa_printf(MSG_DEBUG, "EAP: Too short TLV");
+                               return -1;
+                       }
+                       tlv_len = *pos++;
+                       if (tlv_len > (unsigned) (end - pos)) {
+                               wpa_printf(MSG_DEBUG, "EAP: Truncated TLV");
+                               return -1;
+                       }
+                       if (tlv_type == EAP_ERP_TLV_KEYNAME_NAI) {
+                               if (tlvs->keyname) {
+                                       wpa_printf(MSG_DEBUG,
+                                                  "EAP: More than one keyName-NAI");
+                                       return -1;
+                               }
+                               tlvs->keyname = pos;
+                               tlvs->keyname_len = tlv_len;
+                               if (stop_at_keyname)
+                                       return 0;
+                       } else if (tlv_type == EAP_ERP_TLV_DOMAIN_NAME) {
+                               tlvs->domain = pos;
+                               tlvs->domain_len = tlv_len;
+                       }
+                       pos += tlv_len;
+                       break;
+               default:
+                       if (tlv_type >= 128 && tlv_type <= 191) {
+                               /* Undefined TLV */
+                               if (pos >= end) {
+                                       wpa_printf(MSG_DEBUG,
+                                                  "EAP: Too short TLV");
+                                       return -1;
+                               }
+                               tlv_len = *pos++;
+                               if (tlv_len > (unsigned) (end - pos)) {
+                                       wpa_printf(MSG_DEBUG,
+                                                  "EAP: Truncated TLV");
+                                       return -1;
+                               }
+                               pos += tlv_len;
+                               break;
+                       }
+                       wpa_printf(MSG_DEBUG, "EAP: Unknown TV/TLV type %u",
+                                  tlv_type);
+                       pos = end;
+                       break;
+               }
+       }
+
+       return 0;
+}
+#endif /* CONFIG_ERP */
index 8850c1f..e62f167 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * EAP common peer/server definitions
- * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 
 #include "wpabuf.h"
 
+struct erp_tlvs {
+       const u8 *keyname;
+       const u8 *domain;
+
+       u8 keyname_len;
+       u8 domain_len;
+};
+
 int eap_hdr_len_valid(const struct wpabuf *msg, size_t min_payload);
 const u8 * eap_hdr_validate(int vendor, EapType eap_type,
                            const struct wpabuf *msg, size_t *plen);
@@ -19,5 +27,7 @@ struct wpabuf * eap_msg_alloc(int vendor, EapType type, size_t payload_len,
 void eap_update_len(struct wpabuf *msg);
 u8 eap_get_id(const struct wpabuf *msg);
 EapType eap_get_type(const struct wpabuf *msg);
+int erp_parse_tlvs(const u8 *pos, const u8 *end, struct erp_tlvs *tlvs,
+                  int stop_at_keyname);
 
 #endif /* EAP_COMMON_H */
index 0d247c4..54f26ca 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * EAP server/peer: Shared EAP definitions
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -27,11 +27,39 @@ struct eap_hdr {
 #endif /* _MSC_VER */
 
 enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3,
-       EAP_CODE_FAILURE = 4 };
+       EAP_CODE_FAILURE = 4, EAP_CODE_INITIATE = 5, EAP_CODE_FINISH = 6 };
 
 /* EAP Request and Response data begins with one octet Type. Success and
  * Failure do not have additional data. */
 
+/* Type field in EAP-Initiate and EAP-Finish messages */
+enum eap_erp_type {
+       EAP_ERP_TYPE_REAUTH_START = 1,
+       EAP_ERP_TYPE_REAUTH = 2,
+};
+
+/* ERP TV/TLV types */
+enum eap_erp_tlv_type {
+       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,
+};
+
+/* ERP Cryptosuite */
+enum eap_erp_cryptosuite {
+       EAP_ERP_CS_HMAC_SHA256_64 = 1,
+       EAP_ERP_CS_HMAC_SHA256_128 = 2,
+       EAP_ERP_CS_HMAC_SHA256_256 = 3,
+};
+
 /*
  * EAP Method Types as allocated by IANA:
  * http://www.iana.org/assignments/eap-numbers
@@ -63,6 +91,7 @@ typedef enum {
        EAP_TYPE_AKA_PRIME = 50 /* RFC 5448 */,
        EAP_TYPE_GPSK = 51 /* RFC 5433 */,
        EAP_TYPE_PWD = 52 /* RFC 5931 */,
+       EAP_TYPE_EKE = 53 /* RFC 6124 */,
        EAP_TYPE_EXPANDED = 254 /* RFC 3748 */
 } EapType;
 
@@ -71,14 +100,19 @@ typedef enum {
 enum {
        EAP_VENDOR_IETF = 0,
        EAP_VENDOR_MICROSOFT = 0x000137 /* Microsoft */,
-       EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance */,
-       EAP_VENDOR_HOSTAP = 39068 /* hostapd/wpa_supplicant project */
+       EAP_VENDOR_WFA = 0x00372A /* Wi-Fi Alliance (moved to WBA) */,
+       EAP_VENDOR_HOSTAP = 39068 /* hostapd/wpa_supplicant project */,
+       EAP_VENDOR_WFA_NEW = 40808 /* Wi-Fi Alliance */
 };
 
 #define EAP_VENDOR_UNAUTH_TLS EAP_VENDOR_HOSTAP
 #define EAP_VENDOR_TYPE_UNAUTH_TLS 1
 
+#define EAP_VENDOR_WFA_UNAUTH_TLS 13
+
 #define EAP_MSK_LEN 64
 #define EAP_EMSK_LEN 64
+#define EAP_EMSK_NAME_LEN 8
+#define ERP_MAX_KEY_LEN 64
 
 #endif /* EAP_DEFS_H */
diff --git a/src/eap_common/eap_eke_common.c b/src/eap_common/eap_eke_common.c
new file mode 100644 (file)
index 0000000..4dfdb3f
--- /dev/null
@@ -0,0 +1,768 @@
+/*
+ * EAP server/peer: EAP-EKE shared routines
+ * Copyright (c) 2011-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/aes.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "crypto/dh_groups.h"
+#include "crypto/random.h"
+#include "crypto/sha1.h"
+#include "crypto/sha256.h"
+#include "eap_common/eap_defs.h"
+#include "eap_eke_common.h"
+
+
+static int eap_eke_dh_len(u8 group)
+{
+       switch (group) {
+       case EAP_EKE_DHGROUP_EKE_2:
+               return 128;
+       case EAP_EKE_DHGROUP_EKE_5:
+               return 192;
+       case EAP_EKE_DHGROUP_EKE_14:
+               return 256;
+       case EAP_EKE_DHGROUP_EKE_15:
+               return 384;
+       case EAP_EKE_DHGROUP_EKE_16:
+               return 512;
+       }
+
+       return -1;
+}
+
+
+static int eap_eke_dhcomp_len(u8 dhgroup, u8 encr)
+{
+       int dhlen;
+
+       dhlen = eap_eke_dh_len(dhgroup);
+       if (dhlen < 0)
+               return -1;
+       if (encr != EAP_EKE_ENCR_AES128_CBC)
+               return -1;
+       return AES_BLOCK_SIZE + dhlen;
+}
+
+
+static const struct dh_group * eap_eke_dh_group(u8 group)
+{
+       switch (group) {
+       case EAP_EKE_DHGROUP_EKE_2:
+               return dh_groups_get(2);
+       case EAP_EKE_DHGROUP_EKE_5:
+               return dh_groups_get(5);
+       case EAP_EKE_DHGROUP_EKE_14:
+               return dh_groups_get(14);
+       case EAP_EKE_DHGROUP_EKE_15:
+               return dh_groups_get(15);
+       case EAP_EKE_DHGROUP_EKE_16:
+               return dh_groups_get(16);
+       }
+
+       return NULL;
+}
+
+
+static int eap_eke_dh_generator(u8 group)
+{
+       switch (group) {
+       case EAP_EKE_DHGROUP_EKE_2:
+               return 5;
+       case EAP_EKE_DHGROUP_EKE_5:
+               return 31;
+       case EAP_EKE_DHGROUP_EKE_14:
+               return 11;
+       case EAP_EKE_DHGROUP_EKE_15:
+               return 5;
+       case EAP_EKE_DHGROUP_EKE_16:
+               return 5;
+       }
+
+       return -1;
+}
+
+
+static int eap_eke_pnonce_len(u8 mac)
+{
+       int mac_len;
+
+       if (mac == EAP_EKE_MAC_HMAC_SHA1)
+               mac_len = SHA1_MAC_LEN;
+       else if (mac == EAP_EKE_MAC_HMAC_SHA2_256)
+               mac_len = SHA256_MAC_LEN;
+       else
+               return -1;
+
+       return AES_BLOCK_SIZE + 16 + mac_len;
+}
+
+
+static int eap_eke_pnonce_ps_len(u8 mac)
+{
+       int mac_len;
+
+       if (mac == EAP_EKE_MAC_HMAC_SHA1)
+               mac_len = SHA1_MAC_LEN;
+       else if (mac == EAP_EKE_MAC_HMAC_SHA2_256)
+               mac_len = SHA256_MAC_LEN;
+       else
+               return -1;
+
+       return AES_BLOCK_SIZE + 2 * 16 + mac_len;
+}
+
+
+static int eap_eke_prf_len(u8 prf)
+{
+       if (prf == EAP_EKE_PRF_HMAC_SHA1)
+               return 20;
+       if (prf == EAP_EKE_PRF_HMAC_SHA2_256)
+               return 32;
+       return -1;
+}
+
+
+static int eap_eke_nonce_len(u8 prf)
+{
+       int prf_len;
+
+       prf_len = eap_eke_prf_len(prf);
+       if (prf_len < 0)
+               return -1;
+
+       if (prf_len > 2 * 16)
+               return (prf_len + 1) / 2;
+
+       return 16;
+}
+
+
+static int eap_eke_auth_len(u8 prf)
+{
+       switch (prf) {
+       case EAP_EKE_PRF_HMAC_SHA1:
+               return SHA1_MAC_LEN;
+       case EAP_EKE_PRF_HMAC_SHA2_256:
+               return SHA256_MAC_LEN;
+       }
+
+       return -1;
+}
+
+
+int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub)
+{
+       int generator;
+       u8 gen;
+       const struct dh_group *dh;
+       size_t pub_len, i;
+
+       generator = eap_eke_dh_generator(group);
+       if (generator < 0 || generator > 255)
+               return -1;
+       gen = generator;
+
+       dh = eap_eke_dh_group(group);
+       if (dh == NULL)
+               return -1;
+
+       /* x = random number 2 .. p-1 */
+       if (random_get_bytes(ret_priv, dh->prime_len))
+               return -1;
+       if (os_memcmp(ret_priv, dh->prime, dh->prime_len) > 0) {
+               /* Make sure private value is smaller than prime */
+               ret_priv[0] = 0;
+       }
+       for (i = 0; i < dh->prime_len - 1; i++) {
+               if (ret_priv[i])
+                       break;
+       }
+       if (i == dh->prime_len - 1 && (ret_priv[i] == 0 || ret_priv[i] == 1))
+               return -1;
+       wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: DH private value",
+                       ret_priv, dh->prime_len);
+
+       /* y = g ^ x (mod p) */
+       pub_len = dh->prime_len;
+       if (crypto_mod_exp(&gen, 1, ret_priv, dh->prime_len,
+                          dh->prime, dh->prime_len, ret_pub, &pub_len) < 0)
+               return -1;
+       if (pub_len < dh->prime_len) {
+               size_t pad = dh->prime_len - pub_len;
+               os_memmove(ret_pub + pad, ret_pub, pub_len);
+               os_memset(ret_pub, 0, pad);
+       }
+
+       wpa_hexdump(MSG_DEBUG, "EAP-EKE: DH public value",
+                   ret_pub, dh->prime_len);
+
+       return 0;
+}
+
+
+static int eap_eke_prf(u8 prf, const u8 *key, size_t key_len, const u8 *data,
+                      size_t data_len, const u8 *data2, size_t data2_len,
+                      u8 *res)
+{
+       const u8 *addr[2];
+       size_t len[2];
+       size_t num_elem = 1;
+
+       addr[0] = data;
+       len[0] = data_len;
+       if (data2) {
+               num_elem++;
+               addr[1] = data2;
+               len[1] = data2_len;
+       }
+
+       if (prf == EAP_EKE_PRF_HMAC_SHA1)
+               return hmac_sha1_vector(key, key_len, num_elem, addr, len, res);
+       if (prf == EAP_EKE_PRF_HMAC_SHA2_256)
+               return hmac_sha256_vector(key, key_len, num_elem, addr, len,
+                                         res);
+       return -1;
+}
+
+
+static int eap_eke_prf_hmac_sha1(const u8 *key, size_t key_len, const u8 *data,
+                                size_t data_len, u8 *res, size_t len)
+{
+       u8 hash[SHA1_MAC_LEN];
+       u8 idx;
+       const u8 *addr[3];
+       size_t vlen[3];
+       int ret;
+
+       idx = 0;
+       addr[0] = hash;
+       vlen[0] = SHA1_MAC_LEN;
+       addr[1] = data;
+       vlen[1] = data_len;
+       addr[2] = &idx;
+       vlen[2] = 1;
+
+       while (len > 0) {
+               idx++;
+               if (idx == 1)
+                       ret = hmac_sha1_vector(key, key_len, 2, &addr[1],
+                                              &vlen[1], hash);
+               else
+                       ret = hmac_sha1_vector(key, key_len, 3, addr, vlen,
+                                              hash);
+               if (ret < 0)
+                       return -1;
+               if (len > SHA1_MAC_LEN) {
+                       os_memcpy(res, hash, SHA1_MAC_LEN);
+                       res += SHA1_MAC_LEN;
+                       len -= SHA1_MAC_LEN;
+               } else {
+                       os_memcpy(res, hash, len);
+                       len = 0;
+               }
+       }
+
+       return 0;
+}
+
+
+static int eap_eke_prf_hmac_sha256(const u8 *key, size_t key_len, const u8 *data,
+                                  size_t data_len, u8 *res, size_t len)
+{
+       u8 hash[SHA256_MAC_LEN];
+       u8 idx;
+       const u8 *addr[3];
+       size_t vlen[3];
+       int ret;
+
+       idx = 0;
+       addr[0] = hash;
+       vlen[0] = SHA256_MAC_LEN;
+       addr[1] = data;
+       vlen[1] = data_len;
+       addr[2] = &idx;
+       vlen[2] = 1;
+
+       while (len > 0) {
+               idx++;
+               if (idx == 1)
+                       ret = hmac_sha256_vector(key, key_len, 2, &addr[1],
+                                                &vlen[1], hash);
+               else
+                       ret = hmac_sha256_vector(key, key_len, 3, addr, vlen,
+                                                hash);
+               if (ret < 0)
+                       return -1;
+               if (len > SHA256_MAC_LEN) {
+                       os_memcpy(res, hash, SHA256_MAC_LEN);
+                       res += SHA256_MAC_LEN;
+                       len -= SHA256_MAC_LEN;
+               } else {
+                       os_memcpy(res, hash, len);
+                       len = 0;
+               }
+       }
+
+       return 0;
+}
+
+
+static int eap_eke_prfplus(u8 prf, const u8 *key, size_t key_len,
+                          const u8 *data, size_t data_len, u8 *res, size_t len)
+{
+       if (prf == EAP_EKE_PRF_HMAC_SHA1)
+               return eap_eke_prf_hmac_sha1(key, key_len, data, data_len, res,
+                                            len);
+       if (prf == EAP_EKE_PRF_HMAC_SHA2_256)
+               return eap_eke_prf_hmac_sha256(key, key_len, data, data_len,
+                                              res, len);
+       return -1;
+}
+
+
+int eap_eke_derive_key(struct eap_eke_session *sess,
+                      const u8 *password, size_t password_len,
+                      const u8 *id_s, size_t id_s_len, const u8 *id_p,
+                      size_t id_p_len, u8 *key)
+{
+       u8 zeros[EAP_EKE_MAX_HASH_LEN];
+       u8 temp[EAP_EKE_MAX_HASH_LEN];
+       size_t key_len = 16; /* Only AES-128-CBC is used here */
+       u8 *id;
+
+       /* temp = prf(0+, password) */
+       os_memset(zeros, 0, sess->prf_len);
+       if (eap_eke_prf(sess->prf, zeros, sess->prf_len,
+                       password, password_len, NULL, 0, temp) < 0)
+               return -1;
+       wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: temp = prf(0+, password)",
+                       temp, sess->prf_len);
+
+       /* key = prf+(temp, ID_S | ID_P) */
+       id = os_malloc(id_s_len + id_p_len);
+       if (id == NULL)
+               return -1;
+       os_memcpy(id, id_s, id_s_len);
+       os_memcpy(id + id_s_len, id_p, id_p_len);
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: ID_S | ID_P",
+                         id, id_s_len + id_p_len);
+       if (eap_eke_prfplus(sess->prf, temp, sess->prf_len,
+                           id, id_s_len + id_p_len, key, key_len) < 0) {
+               os_free(id);
+               return -1;
+       }
+       os_free(id);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: key = prf+(temp, ID_S | ID_P)",
+                       key, key_len);
+
+       return 0;
+}
+
+
+int eap_eke_dhcomp(struct eap_eke_session *sess, const u8 *key, const u8 *dhpub,
+                  u8 *ret_dhcomp)
+{
+       u8 pub[EAP_EKE_MAX_DH_LEN];
+       int dh_len;
+       u8 iv[AES_BLOCK_SIZE];
+
+       dh_len = eap_eke_dh_len(sess->dhgroup);
+       if (dh_len < 0)
+               return -1;
+
+       /*
+        * DHComponent = Encr(key, y)
+        *
+        * All defined DH groups use primes that have length devisible by 16, so
+        * no need to do extra padding for y (= pub).
+        */
+       if (sess->encr != EAP_EKE_ENCR_AES128_CBC)
+               return -1;
+       if (random_get_bytes(iv, AES_BLOCK_SIZE))
+               return -1;
+       wpa_hexdump(MSG_DEBUG, "EAP-EKE: IV for Encr(key, y)",
+                   iv, AES_BLOCK_SIZE);
+       os_memcpy(pub, dhpub, dh_len);
+       if (aes_128_cbc_encrypt(key, iv, pub, dh_len) < 0)
+               return -1;
+       os_memcpy(ret_dhcomp, iv, AES_BLOCK_SIZE);
+       os_memcpy(ret_dhcomp + AES_BLOCK_SIZE, pub, dh_len);
+       wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent = Encr(key, y)",
+                   ret_dhcomp, AES_BLOCK_SIZE + dh_len);
+
+       return 0;
+}
+
+
+int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key,
+                         const u8 *dhpriv, const u8 *peer_dhcomp)
+{
+       u8 zeros[EAP_EKE_MAX_HASH_LEN];
+       u8 peer_pub[EAP_EKE_MAX_DH_LEN];
+       u8 modexp[EAP_EKE_MAX_DH_LEN];
+       size_t len;
+       const struct dh_group *dh;
+
+       if (sess->encr != EAP_EKE_ENCR_AES128_CBC)
+               return -1;
+
+       dh = eap_eke_dh_group(sess->dhgroup);
+       if (dh == NULL)
+               return -1;
+
+       /* Decrypt peer DHComponent */
+       os_memcpy(peer_pub, peer_dhcomp + AES_BLOCK_SIZE, dh->prime_len);
+       if (aes_128_cbc_decrypt(key, peer_dhcomp, peer_pub, dh->prime_len) < 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt DHComponent");
+               return -1;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Decrypted peer DH pubkey",
+                       peer_pub, dh->prime_len);
+
+       /* SharedSecret = prf(0+, g ^ (x_s * x_p) (mod p)) */
+       len = dh->prime_len;
+       if (crypto_mod_exp(peer_pub, dh->prime_len, dhpriv, dh->prime_len,
+                          dh->prime, dh->prime_len, modexp, &len) < 0)
+               return -1;
+       if (len < dh->prime_len) {
+               size_t pad = dh->prime_len - len;
+               os_memmove(modexp + pad, modexp, len);
+               os_memset(modexp, 0, pad);
+       }
+
+       os_memset(zeros, 0, sess->auth_len);
+       if (eap_eke_prf(sess->prf, zeros, sess->auth_len, modexp, dh->prime_len,
+                       NULL, 0, sess->shared_secret) < 0)
+               return -1;
+       wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: SharedSecret",
+                       sess->shared_secret, sess->auth_len);
+
+       return 0;
+}
+
+
+int eap_eke_derive_ke_ki(struct eap_eke_session *sess,
+                        const u8 *id_s, size_t id_s_len,
+                        const u8 *id_p, size_t id_p_len)
+{
+       u8 buf[EAP_EKE_MAX_KE_LEN + EAP_EKE_MAX_KI_LEN];
+       size_t ke_len, ki_len;
+       u8 *data;
+       size_t data_len;
+       const char *label = "EAP-EKE Keys";
+       size_t label_len;
+
+       /*
+        * Ke | Ki = prf+(SharedSecret, "EAP-EKE Keys" | ID_S | ID_P)
+        * Ke = encryption key
+        * Ki = integrity protection key
+        * Length of each key depends on the selected algorithms.
+        */
+
+       if (sess->encr == EAP_EKE_ENCR_AES128_CBC)
+               ke_len = 16;
+       else
+               return -1;
+
+       if (sess->mac == EAP_EKE_PRF_HMAC_SHA1)
+               ki_len = 20;
+       else if (sess->mac == EAP_EKE_PRF_HMAC_SHA2_256)
+               ki_len = 32;
+       else
+               return -1;
+
+       label_len = os_strlen(label);
+       data_len = label_len + id_s_len + id_p_len;
+       data = os_malloc(data_len);
+       if (data == NULL)
+               return -1;
+       os_memcpy(data, label, label_len);
+       os_memcpy(data + label_len, id_s, id_s_len);
+       os_memcpy(data + label_len + id_s_len, id_p, id_p_len);
+       if (eap_eke_prfplus(sess->prf, sess->shared_secret, sess->prf_len,
+                           data, data_len, buf, ke_len + ki_len) < 0) {
+               os_free(data);
+               return -1;
+       }
+
+       os_memcpy(sess->ke, buf, ke_len);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ke", sess->ke, ke_len);
+       os_memcpy(sess->ki, buf + ke_len, ki_len);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ki", sess->ki, ki_len);
+
+       os_free(data);
+       return 0;
+}
+
+
+int eap_eke_derive_ka(struct eap_eke_session *sess,
+                     const u8 *id_s, size_t id_s_len,
+                     const u8 *id_p, size_t id_p_len,
+                     const u8 *nonce_p, const u8 *nonce_s)
+{
+       u8 *data, *pos;
+       size_t data_len;
+       const char *label = "EAP-EKE Ka";
+       size_t label_len;
+
+       /*
+        * Ka = prf+(SharedSecret, "EAP-EKE Ka" | ID_S | ID_P | Nonce_P |
+        *           Nonce_S)
+        * Ka = authentication key
+        * Length of the key depends on the selected algorithms.
+        */
+
+       label_len = os_strlen(label);
+       data_len = label_len + id_s_len + id_p_len + 2 * sess->nonce_len;
+       data = os_malloc(data_len);
+       if (data == NULL)
+               return -1;
+       pos = data;
+       os_memcpy(pos, label, label_len);
+       pos += label_len;
+       os_memcpy(pos, id_s, id_s_len);
+       pos += id_s_len;
+       os_memcpy(pos, id_p, id_p_len);
+       pos += id_p_len;
+       os_memcpy(pos, nonce_p, sess->nonce_len);
+       pos += sess->nonce_len;
+       os_memcpy(pos, nonce_s, sess->nonce_len);
+       if (eap_eke_prfplus(sess->prf, sess->shared_secret, sess->prf_len,
+                           data, data_len, sess->ka, sess->prf_len) < 0) {
+               os_free(data);
+               return -1;
+       }
+       os_free(data);
+
+       wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ka", sess->ka, sess->prf_len);
+
+       return 0;
+}
+
+
+int eap_eke_derive_msk(struct eap_eke_session *sess,
+                      const u8 *id_s, size_t id_s_len,
+                      const u8 *id_p, size_t id_p_len,
+                      const u8 *nonce_p, const u8 *nonce_s,
+                      u8 *msk, u8 *emsk)
+{
+       u8 *data, *pos;
+       size_t data_len;
+       const char *label = "EAP-EKE Exported Keys";
+       size_t label_len;
+       u8 buf[EAP_MSK_LEN + EAP_EMSK_LEN];
+
+       /*
+        * MSK | EMSK = prf+(SharedSecret, "EAP-EKE Exported Keys" | ID_S |
+        *                   ID_P | Nonce_P | Nonce_S)
+        */
+
+       label_len = os_strlen(label);
+       data_len = label_len + id_s_len + id_p_len + 2 * sess->nonce_len;
+       data = os_malloc(data_len);
+       if (data == NULL)
+               return -1;
+       pos = data;
+       os_memcpy(pos, label, label_len);
+       pos += label_len;
+       os_memcpy(pos, id_s, id_s_len);
+       pos += id_s_len;
+       os_memcpy(pos, id_p, id_p_len);
+       pos += id_p_len;
+       os_memcpy(pos, nonce_p, sess->nonce_len);
+       pos += sess->nonce_len;
+       os_memcpy(pos, nonce_s, sess->nonce_len);
+       if (eap_eke_prfplus(sess->prf, sess->shared_secret, sess->prf_len,
+                           data, data_len, buf, EAP_MSK_LEN + EAP_EMSK_LEN) <
+           0) {
+               os_free(data);
+               return -1;
+       }
+       os_free(data);
+
+       os_memcpy(msk, buf, EAP_MSK_LEN);
+       os_memcpy(emsk, buf + EAP_MSK_LEN, EAP_EMSK_LEN);
+       os_memset(buf, 0, sizeof(buf));
+
+       wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: MSK", msk, EAP_MSK_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: EMSK", msk, EAP_EMSK_LEN);
+
+       return 0;
+}
+
+
+static int eap_eke_mac(u8 mac, const u8 *key, const u8 *data, size_t data_len,
+                      u8 *res)
+{
+       if (mac == EAP_EKE_MAC_HMAC_SHA1)
+               return hmac_sha1(key, SHA1_MAC_LEN, data, data_len, res);
+       if (mac == EAP_EKE_MAC_HMAC_SHA2_256)
+               return hmac_sha256(key, SHA256_MAC_LEN, data, data_len, res);
+       return -1;
+}
+
+
+int eap_eke_prot(struct eap_eke_session *sess,
+                const u8 *data, size_t data_len,
+                u8 *prot, size_t *prot_len)
+{
+       size_t block_size, icv_len, pad;
+       u8 *pos, *iv, *e;
+
+       if (sess->encr == EAP_EKE_ENCR_AES128_CBC)
+               block_size = AES_BLOCK_SIZE;
+       else
+               return -1;
+
+       if (sess->mac == EAP_EKE_PRF_HMAC_SHA1)
+               icv_len = SHA1_MAC_LEN;
+       else if (sess->mac == EAP_EKE_PRF_HMAC_SHA2_256)
+               icv_len = SHA256_MAC_LEN;
+       else
+               return -1;
+
+       pad = data_len % block_size;
+       if (pad)
+               pad = block_size - pad;
+
+       if (*prot_len < block_size + data_len + pad + icv_len) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Not enough room for Prot() data");
+       }
+       pos = prot;
+
+       if (random_get_bytes(pos, block_size))
+               return -1;
+       iv = pos;
+       wpa_hexdump(MSG_DEBUG, "EAP-EKE: IV for Prot()", iv, block_size);
+       pos += block_size;
+
+       e = pos;
+       os_memcpy(pos, data, data_len);
+       pos += data_len;
+       if (pad) {
+               if (random_get_bytes(pos, pad))
+                       return -1;
+               pos += pad;
+       }
+
+       if (aes_128_cbc_encrypt(sess->ke, iv, e, data_len + pad) < 0)
+               return -1;
+
+       if (eap_eke_mac(sess->mac, sess->ki, e, data_len + pad, pos) < 0)
+               return -1;
+       pos += icv_len;
+
+       *prot_len = pos - prot;
+       return 0;
+}
+
+
+int eap_eke_decrypt_prot(struct eap_eke_session *sess,
+                        const u8 *prot, size_t prot_len,
+                        u8 *data, size_t *data_len)
+{
+       size_t block_size, icv_len;
+       u8 icv[EAP_EKE_MAX_HASH_LEN];
+
+       if (sess->encr == EAP_EKE_ENCR_AES128_CBC)
+               block_size = AES_BLOCK_SIZE;
+       else
+               return -1;
+
+       if (sess->mac == EAP_EKE_PRF_HMAC_SHA1)
+               icv_len = SHA1_MAC_LEN;
+       else if (sess->mac == EAP_EKE_PRF_HMAC_SHA2_256)
+               icv_len = SHA256_MAC_LEN;
+       else
+               return -1;
+
+       if (prot_len < 2 * block_size + icv_len)
+               return -1;
+       if ((prot_len - icv_len) % block_size)
+               return -1;
+
+       if (eap_eke_mac(sess->mac, sess->ki, prot + block_size,
+                       prot_len - block_size - icv_len, icv) < 0)
+               return -1;
+       if (os_memcmp_const(icv, prot + prot_len - icv_len, icv_len) != 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: ICV mismatch in Prot() data");
+               return -1;
+       }
+
+       if (*data_len < prot_len - block_size - icv_len) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Not enough room for decrypted Prot() data");
+               return -1;
+       }
+
+       *data_len = prot_len - block_size - icv_len;
+       os_memcpy(data, prot + block_size, *data_len);
+       if (aes_128_cbc_decrypt(sess->ke, prot, data, *data_len) < 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt Prot() data");
+               return -1;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Decrypted Prot() data",
+                       data, *data_len);
+
+       return 0;
+}
+
+
+int eap_eke_auth(struct eap_eke_session *sess, const char *label,
+                const struct wpabuf *msgs, u8 *auth)
+{
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Auth(%s)", label);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Ka for Auth",
+                       sess->ka, sess->auth_len);
+       wpa_hexdump_buf(MSG_MSGDUMP, "EAP-EKE: Messages for Auth", msgs);
+       return eap_eke_prf(sess->prf, sess->ka, sess->auth_len,
+                          (const u8 *) label, os_strlen(label),
+                          wpabuf_head(msgs), wpabuf_len(msgs), auth);
+}
+
+
+int eap_eke_session_init(struct eap_eke_session *sess, u8 dhgroup, u8 encr,
+                        u8 prf, u8 mac)
+{
+       sess->dhgroup = dhgroup;
+       sess->encr = encr;
+       sess->prf = prf;
+       sess->mac = mac;
+
+       sess->prf_len = eap_eke_prf_len(prf);
+       if (sess->prf_len < 0)
+               return -1;
+       sess->nonce_len = eap_eke_nonce_len(prf);
+       if (sess->nonce_len < 0)
+               return -1;
+       sess->auth_len = eap_eke_auth_len(prf);
+       if (sess->auth_len < 0)
+               return -1;
+       sess->dhcomp_len = eap_eke_dhcomp_len(sess->dhgroup, sess->encr);
+       if (sess->dhcomp_len < 0)
+               return -1;
+       sess->pnonce_len = eap_eke_pnonce_len(sess->mac);
+       if (sess->pnonce_len < 0)
+               return -1;
+       sess->pnonce_ps_len = eap_eke_pnonce_ps_len(sess->mac);
+       if (sess->pnonce_ps_len < 0)
+               return -1;
+
+       return 0;
+}
+
+
+void eap_eke_session_clean(struct eap_eke_session *sess)
+{
+       os_memset(sess->shared_secret, 0, EAP_EKE_MAX_HASH_LEN);
+       os_memset(sess->ke, 0, EAP_EKE_MAX_KE_LEN);
+       os_memset(sess->ki, 0, EAP_EKE_MAX_KI_LEN);
+       os_memset(sess->ka, 0, EAP_EKE_MAX_KA_LEN);
+}
diff --git a/src/eap_common/eap_eke_common.h b/src/eap_common/eap_eke_common.h
new file mode 100644 (file)
index 0000000..a4c0422
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * EAP server/peer: EAP-EKE shared routines
+ * Copyright (c) 2011-2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef EAP_EKE_COMMON_H
+#define EAP_EKE_COMMON_H
+
+/* EKE Exchange */
+#define EAP_EKE_ID 1
+#define EAP_EKE_COMMIT 2
+#define EAP_EKE_CONFIRM 3
+#define EAP_EKE_FAILURE 4
+
+/* Diffie-Hellman Group Registry */
+#define EAP_EKE_DHGROUP_EKE_2 1
+#define EAP_EKE_DHGROUP_EKE_5 2
+#define EAP_EKE_DHGROUP_EKE_14 3 /* mandatory to implement */
+#define EAP_EKE_DHGROUP_EKE_15 4
+#define EAP_EKE_DHGROUP_EKE_16 5
+
+/* Encryption Algorithm Registry */
+#define EAP_EKE_ENCR_AES128_CBC 1 /* mandatory to implement */
+
+/* Pseudo Random Function Registry */
+#define EAP_EKE_PRF_HMAC_SHA1 1 /* mandatory to implement */
+#define EAP_EKE_PRF_HMAC_SHA2_256 2
+
+/* Keyed Message Digest (MAC) Registry */
+#define EAP_EKE_MAC_HMAC_SHA1 1 /* mandatory to implement */
+#define EAP_EKE_MAC_HMAC_SHA2_256 2
+
+/* Identity Type Registry */
+#define EAP_EKE_ID_OPAQUE 1
+#define EAP_EKE_ID_NAI 2
+#define EAP_EKE_ID_IPv4 3
+#define EAP_EKE_ID_IPv6 4
+#define EAP_EKE_ID_FQDN 5
+#define EAP_EKE_ID_DN 6
+
+/* Failure-Code */
+#define EAP_EKE_FAIL_NO_ERROR 1
+#define EAP_EKE_FAIL_PROTO_ERROR 2
+#define EAP_EKE_FAIL_PASSWD_NOT_FOUND 3
+#define EAP_EKE_FAIL_AUTHENTICATION_FAIL 4
+#define EAP_EKE_FAIL_AUTHORIZATION_FAIL 5
+#define EAP_EKE_FAIL_NO_PROPOSAL_CHOSEN 6
+#define EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR 0xffffffff
+
+#define EAP_EKE_MAX_DH_LEN 512
+#define EAP_EKE_MAX_HASH_LEN 32
+#define EAP_EKE_MAX_KEY_LEN 16
+#define EAP_EKE_MAX_KE_LEN 16
+#define EAP_EKE_MAX_KI_LEN 32
+#define EAP_EKE_MAX_KA_LEN 32
+#define EAP_EKE_MAX_NONCE_LEN 16
+
+struct eap_eke_session {
+       /* Selected proposal */
+       u8 dhgroup;
+       u8 encr;
+       u8 prf;
+       u8 mac;
+
+       u8 shared_secret[EAP_EKE_MAX_HASH_LEN];
+       u8 ke[EAP_EKE_MAX_KE_LEN];
+       u8 ki[EAP_EKE_MAX_KI_LEN];
+       u8 ka[EAP_EKE_MAX_KA_LEN];
+
+       int prf_len;
+       int nonce_len;
+       int auth_len;
+       int dhcomp_len;
+       int pnonce_len;
+       int pnonce_ps_len;
+};
+
+int eap_eke_session_init(struct eap_eke_session *sess, u8 dhgroup, u8 encr,
+                        u8 prf, u8 mac);
+void eap_eke_session_clean(struct eap_eke_session *sess);
+int eap_eke_dh_init(u8 group, u8 *ret_priv, u8 *ret_pub);
+int eap_eke_derive_key(struct eap_eke_session *sess,
+                      const u8 *password, size_t password_len,
+                      const u8 *id_s, size_t id_s_len, const u8 *id_p,
+                      size_t id_p_len, u8 *key);
+int eap_eke_dhcomp(struct eap_eke_session *sess, const u8 *key, const u8 *dhpub,
+                  u8 *ret_dhcomp);
+int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key,
+                         const u8 *dhpriv, const u8 *peer_dhcomp);
+int eap_eke_derive_ke_ki(struct eap_eke_session *sess,
+                        const u8 *id_s, size_t id_s_len,
+                        const u8 *id_p, size_t id_p_len);
+int eap_eke_derive_ka(struct eap_eke_session *sess,
+                     const u8 *id_s, size_t id_s_len,
+                     const u8 *id_p, size_t id_p_len,
+                     const u8 *nonce_p, const u8 *nonce_s);
+int eap_eke_derive_msk(struct eap_eke_session *sess,
+                      const u8 *id_s, size_t id_s_len,
+                      const u8 *id_p, size_t id_p_len,
+                      const u8 *nonce_p, const u8 *nonce_s,
+                      u8 *msk, u8 *emsk);
+int eap_eke_prot(struct eap_eke_session *sess,
+                const u8 *data, size_t data_len,
+                u8 *prot, size_t *prot_len);
+int eap_eke_decrypt_prot(struct eap_eke_session *sess,
+                        const u8 *prot, size_t prot_len,
+                        u8 *data, size_t *data_len);
+int eap_eke_auth(struct eap_eke_session *sess, const char *label,
+                const struct wpabuf *msgs, u8 *auth);
+
+#endif /* EAP_EKE_COMMON_H */
index 04b987d..fceb1b0 100644 (file)
@@ -174,7 +174,7 @@ void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk)
 
 
 int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
-                      int tlv_type, u8 *pos, int len)
+                      int tlv_type, u8 *pos, size_t len)
 {
        switch (tlv_type) {
        case EAP_TLV_EAP_PAYLOAD_TLV:
index 8955617..d59a845 100644 (file)
@@ -102,6 +102,6 @@ u8 * eap_fast_derive_key(void *ssl_ctx, struct tls_connection *conn,
 void eap_fast_derive_eap_msk(const u8 *simck, u8 *msk);
 void eap_fast_derive_eap_emsk(const u8 *simck, u8 *emsk);
 int eap_fast_parse_tlv(struct eap_fast_tlv_parse *tlv,
-                      int tlv_type, u8 *pos, int len);
+                      int tlv_type, u8 *pos, size_t len);
 
 #endif /* EAP_FAST_H */
index 7a33215..8c7ae27 100644 (file)
@@ -284,7 +284,6 @@ int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor,
                         u8 *pk, size_t *pk_len)
 {
        u8 *seed, *pos;
-       size_t seed_len;
        int ret;
 
        wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving keys (%d:%d)",
@@ -296,8 +295,7 @@ int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor,
        wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len);
 
        /* Seed = RAND_Peer || ID_Peer || RAND_Server || ID_Server */
-       seed_len = 2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len;
-       seed = os_malloc(seed_len);
+       seed = os_malloc(2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len);
        if (seed == NULL) {
                wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory "
                           "for key derivation");
@@ -313,17 +311,18 @@ int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor,
        pos += EAP_GPSK_RAND_LEN;
        os_memcpy(pos, id_server, id_server_len);
        pos += id_server_len;
-       wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, seed_len);
+       wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, pos - seed);
 
        switch (specifier) {
        case EAP_GPSK_CIPHER_AES:
-               ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, seed_len,
+               ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, pos - seed,
                                               msk, emsk, sk, sk_len,
                                               pk, pk_len);
                break;
 #ifdef EAP_GPSK_SHA256
        case EAP_GPSK_CIPHER_SHA256:
-               ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed, seed_len,
+               ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed,
+                                                 pos - seed,
                                                  msk, emsk, sk, sk_len);
                break;
 #endif /* EAP_GPSK_SHA256 */
@@ -423,7 +422,6 @@ int eap_gpsk_derive_session_id(const u8 *psk, size_t psk_len, int vendor,
 {
        u8 *seed, *pos;
        u8 kdf_out[16];
-       size_t seed_len;
        int ret;
 
        wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving Session ID(%d:%d)",
@@ -441,8 +439,7 @@ int eap_gpsk_derive_session_id(const u8 *psk, size_t psk_len, int vendor,
         * Method-ID = GKDF-16 (zero, "Method ID" || EAP_Method_Type ||
         *                      CSuite_Sel || inputString)
         */
-       seed_len = 2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len;
-       seed = os_malloc(seed_len);
+       seed = os_malloc(2 * EAP_GPSK_RAND_LEN + id_server_len + id_peer_len);
        if (seed == NULL) {
                wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory "
                           "for Session-Id derivation");
@@ -458,11 +455,11 @@ int eap_gpsk_derive_session_id(const u8 *psk, size_t psk_len, int vendor,
        pos += EAP_GPSK_RAND_LEN;
        os_memcpy(pos, id_server, id_server_len);
        pos += id_server_len;
-       wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, seed_len);
+       wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, pos - seed);
 
        ret = eap_gpsk_derive_mid_helper(specifier,
                                         kdf_out, sizeof(kdf_out),
-                                        psk, seed, seed_len,
+                                        psk, seed, pos - seed,
                                         method_type);
 
        sid[0] = method_type;
index 6095fd8..585c79c 100644 (file)
@@ -52,22 +52,12 @@ struct wpabuf * eap_ikev2_build_frag_ack(u8 id, u8 code)
 {
        struct wpabuf *msg;
 
-#ifdef CCNS_PL
-       msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, 1, code, id);
-       if (msg == NULL) {
-               wpa_printf(MSG_ERROR, "EAP-IKEV2: Failed to allocate memory "
-                          "for fragment ack");
-               return NULL;
-       }
-       wpabuf_put_u8(msg, 0); /* Flags */
-#else /* CCNS_PL */
        msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IKEV2, 0, code, id);
        if (msg == NULL) {
                wpa_printf(MSG_ERROR, "EAP-IKEV2: Failed to allocate memory "
                           "for fragment ack");
                return NULL;
        }
-#endif /* CCNS_PL */
 
        wpa_printf(MSG_DEBUG, "EAP-IKEV2: Send fragment ack");
 
@@ -110,7 +100,7 @@ int eap_ikev2_validate_icv(int integ_alg, struct ikev2_keys *keys,
                return -1;
        }
 
-       if (os_memcmp(icv, end - icv_len, icv_len) != 0) {
+       if (os_memcmp_const(icv, end - icv_len, icv_len) != 0) {
                wpa_printf(MSG_INFO, "EAP-IKEV2: Invalid ICV");
                wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Calculated ICV",
                            icv, icv_len);
index 329ccc4..e7502d7 100644 (file)
@@ -9,16 +9,9 @@
 #ifndef EAP_IKEV2_COMMON_H
 #define EAP_IKEV2_COMMON_H
 
-#ifdef CCNS_PL
-/* incorrect bit order */
-#define IKEV2_FLAGS_LENGTH_INCLUDED 0x01
-#define IKEV2_FLAGS_MORE_FRAGMENTS 0x02
-#define IKEV2_FLAGS_ICV_INCLUDED 0x04
-#else /* CCNS_PL */
 #define IKEV2_FLAGS_LENGTH_INCLUDED 0x80
 #define IKEV2_FLAGS_MORE_FRAGMENTS 0x40
 #define IKEV2_FLAGS_ICV_INCLUDED 0x20
-#endif /* CCNS_PL */
 
 #define IKEV2_FRAGMENT_SIZE 1400
 
index b3bbacc..0e80ef5 100644 (file)
@@ -121,10 +121,11 @@ int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len,
  * @mk: Buffer for the derived Master Key
  * @ck: Buffer for the derived Confirmation Key
  * @ick: Buffer for the derived Integrity Check Key
+ * @mid: Buffer for the derived Method ID
  * Returns: 0 on success, -1 on failure
  */
 int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e,
-                                  u8 *mk, u8 *ck, u8 *ick)
+                                  u8 *mk, u8 *ck, u8 *ick, u8 *mid)
 {
        wpa_printf(MSG_DEBUG, "EAP-PAX: initial key derivation");
        if (eap_pax_kdf(mac_id, ak, EAP_PAX_AK_LEN, "Master Key",
@@ -132,13 +133,16 @@ int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e,
            eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Confirmation Key",
                        e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_CK_LEN, ck) ||
            eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Integrity Check Key",
-                       e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_ICK_LEN, ick))
+                       e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_ICK_LEN, ick) ||
+           eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Method ID",
+                       e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_MID_LEN, mid))
                return -1;
 
        wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: AK", ak, EAP_PAX_AK_LEN);
        wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: MK", mk, EAP_PAX_MK_LEN);
        wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: CK", ck, EAP_PAX_CK_LEN);
        wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: ICK", ick, EAP_PAX_ICK_LEN);
+       wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: MID", mid, EAP_PAX_MID_LEN);
 
        return 0;
 }
index fb03df2..e6cdf4d 100644 (file)
@@ -74,6 +74,7 @@ enum {
 #define EAP_PAX_MK_LEN 16
 #define EAP_PAX_CK_LEN 16
 #define EAP_PAX_ICK_LEN 16
+#define EAP_PAX_MID_LEN 16
 
 
 int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len,
@@ -86,6 +87,6 @@ int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len,
                const u8 *data3, size_t data3_len,
                u8 *mac);
 int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e,
-                                  u8 *mk, u8 *ck, u8 *ick);
+                                  u8 *mk, u8 *ck, u8 *ick, u8 *mid);
 
 #endif /* EAP_PAX_COMMON_H */
index 7d6e6b8..631c363 100644 (file)
@@ -106,9 +106,11 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
         case 21:
                nid = NID_secp521r1;
                break;
+#ifndef OPENSSL_IS_BORINGSSL
         case 25:
                nid = NID_X9_62_prime192v1;
                break;
+#endif /* OPENSSL_IS_BORINGSSL */
         case 26:
                nid = NID_secp224r1;
                break;
@@ -263,18 +265,18 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
  fail:
                EC_GROUP_free(grp->group);
                grp->group = NULL;
-               EC_POINT_free(grp->pwe);
+               EC_POINT_clear_free(grp->pwe);
                grp->pwe = NULL;
-               BN_free(grp->order);
+               BN_clear_free(grp->order);
                grp->order = NULL;
-               BN_free(grp->prime);
+               BN_clear_free(grp->prime);
                grp->prime = NULL;
                ret = 1;
        }
        /* cleanliness and order.... */
-       BN_free(cofactor);
-       BN_free(x_candidate);
-       BN_free(rnd);
+       BN_clear_free(cofactor);
+       BN_clear_free(x_candidate);
+       BN_clear_free(rnd);
        os_free(prfbuf);
 
        return ret;
@@ -284,11 +286,10 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
 int compute_keys(EAP_PWD_group *grp, BN_CTX *bnctx, BIGNUM *k,
                 BIGNUM *peer_scalar, BIGNUM *server_scalar,
                 u8 *confirm_peer, u8 *confirm_server,
-                u32 *ciphersuite, u8 *msk, u8 *emsk)
+                u32 *ciphersuite, u8 *msk, u8 *emsk, u8 *session_id)
 {
        struct crypto_hash *hash;
        u8 mk[SHA256_MAC_LEN], *cruft;
-       u8 session_id[SHA256_MAC_LEN + 1];
        u8 msk_emsk[EAP_MSK_LEN + EAP_EMSK_LEN];
        int offset;
 
index 816e58c..c54c441 100644 (file)
@@ -59,7 +59,7 @@ struct eap_pwd_id {
 int compute_password_element(EAP_PWD_group *, u16, u8 *, int, u8 *, int, u8 *,
                             int, u8 *);
 int compute_keys(EAP_PWD_group *, BN_CTX *, BIGNUM *, BIGNUM *, BIGNUM *,
-                u8 *, u8 *, u32 *, u8 *, u8 *);
+                u8 *, u8 *, u32 *, u8 *, u8 *, u8 *);
 struct crypto_hash * eap_pwd_h_init(void);
 void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len);
 void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest);
index e1773bf..2adc3b3 100644 (file)
@@ -198,7 +198,7 @@ int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req,
                    hmac, EAP_SIM_MAC_LEN);
        os_free(tmp);
 
-       return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
+       return (os_memcmp_const(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
 }
 
 
@@ -393,7 +393,7 @@ int eap_sim_verify_mac_sha256(const u8 *k_aut, const struct wpabuf *req,
                    hmac, EAP_SIM_MAC_LEN);
        os_free(tmp);
 
-       return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
+       return (os_memcmp_const(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1;
 }
 
 
@@ -893,7 +893,7 @@ int eap_sim_parse_attr(const u8 *start, const u8 *end,
                        if (attr->kdf_count == EAP_AKA_PRIME_KDF_MAX) {
                                wpa_printf(MSG_DEBUG, "EAP-AKA': Too many "
                                           "AT_KDF attributes - ignore this");
-                               continue;
+                               break;
                        }
                        attr->kdf[attr->kdf_count] = WPA_GET_BE16(apos);
                        attr->kdf_count++;
@@ -972,7 +972,6 @@ u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data,
 struct eap_sim_msg {
        struct wpabuf *buf;
        size_t mac, iv, encr; /* index from buf */
-       int type;
 };
 
 
@@ -986,7 +985,6 @@ struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype)
        if (msg == NULL)
                return NULL;
 
-       msg->type = type;
        msg->buf = wpabuf_alloc(EAP_SIM_INIT_LEN);
        if (msg->buf == NULL) {
                os_free(msg);
@@ -1006,7 +1004,8 @@ struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype)
 }
 
 
-struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut,
+struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, int type,
+                                  const u8 *k_aut,
                                   const u8 *extra, size_t extra_len)
 {
        struct eap_hdr *eap;
@@ -1019,7 +1018,7 @@ struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut,
        eap->length = host_to_be16(wpabuf_len(msg->buf));
 
 #if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME)
-       if (k_aut && msg->mac && msg->type == EAP_TYPE_AKA_PRIME) {
+       if (k_aut && msg->mac && type == EAP_TYPE_AKA_PRIME) {
                eap_sim_add_mac_sha256(k_aut, (u8 *) wpabuf_head(msg->buf),
                                       wpabuf_len(msg->buf),
                                       (u8 *) wpabuf_mhead(msg->buf) +
index 6021bd2..daeb0e2 100644 (file)
@@ -211,7 +211,8 @@ u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data,
 struct eap_sim_msg;
 
 struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype);
-struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut,
+struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, int type,
+                                  const u8 *k_aut,
                                   const u8 *extra, size_t extra_len);
 void eap_sim_msg_free(struct eap_sim_msg *msg);
 u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr,
index 376fcad..4f9e64e 100644 (file)
@@ -21,7 +21,7 @@ static struct ikev2_integ_alg ikev2_integ_algs[] = {
        { AUTH_HMAC_MD5_96, 16, 12 }
 };
 
-#define NUM_INTEG_ALGS (sizeof(ikev2_integ_algs) / sizeof(ikev2_integ_algs[0]))
+#define NUM_INTEG_ALGS ARRAY_SIZE(ikev2_integ_algs)
 
 
 static struct ikev2_prf_alg ikev2_prf_algs[] = {
@@ -29,7 +29,7 @@ static struct ikev2_prf_alg ikev2_prf_algs[] = {
        { PRF_HMAC_MD5, 16, 16 }
 };
 
-#define NUM_PRF_ALGS (sizeof(ikev2_prf_algs) / sizeof(ikev2_prf_algs[0]))
+#define NUM_PRF_ALGS ARRAY_SIZE(ikev2_prf_algs)
 
 
 static struct ikev2_encr_alg ikev2_encr_algs[] = {
@@ -37,7 +37,7 @@ static struct ikev2_encr_alg ikev2_encr_algs[] = {
        { ENCR_3DES, 24, 8 }
 };
 
-#define NUM_ENCR_ALGS (sizeof(ikev2_encr_algs) / sizeof(ikev2_encr_algs[0]))
+#define NUM_ENCR_ALGS ARRAY_SIZE(ikev2_encr_algs)
 
 
 const struct ikev2_integ_alg * ikev2_get_integ(int id)
@@ -173,46 +173,12 @@ const struct ikev2_encr_alg * ikev2_get_encr(int id)
 }
 
 
-#ifdef CCNS_PL
-/* from des.c */
-struct des3_key_s {
-       u32 ek[3][32];
-       u32 dk[3][32];
-};
-
-void des3_key_setup(const u8 *key, struct des3_key_s *dkey);
-void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt);
-void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain);
-#endif /* CCNS_PL */
-
-
 int ikev2_encr_encrypt(int alg, const u8 *key, size_t key_len, const u8 *iv,
                       const u8 *plain, u8 *crypt, size_t len)
 {
        struct crypto_cipher *cipher;
        int encr_alg;
 
-#ifdef CCNS_PL
-       if (alg == ENCR_3DES) {
-               struct des3_key_s des3key;
-               size_t i, blocks;
-               u8 *pos;
-
-               /* ECB mode is used incorrectly for 3DES!? */
-               if (key_len != 24) {
-                       wpa_printf(MSG_INFO, "IKEV2: Invalid encr key length");
-                       return -1;
-               }
-               des3_key_setup(key, &des3key);
-
-               blocks = len / 8;
-               pos = crypt;
-               for (i = 0; i < blocks; i++) {
-                       des3_encrypt(pos, &des3key, pos);
-                       pos += 8;
-               }
-       } else {
-#endif /* CCNS_PL */
        switch (alg) {
        case ENCR_3DES:
                encr_alg = CRYPTO_CIPHER_ALG_3DES;
@@ -237,9 +203,6 @@ int ikev2_encr_encrypt(int alg, const u8 *key, size_t key_len, const u8 *iv,
                return -1;
        }
        crypto_cipher_deinit(cipher);
-#ifdef CCNS_PL
-       }
-#endif /* CCNS_PL */
 
        return 0;
 }
@@ -251,31 +214,6 @@ int ikev2_encr_decrypt(int alg, const u8 *key, size_t key_len, const u8 *iv,
        struct crypto_cipher *cipher;
        int encr_alg;
 
-#ifdef CCNS_PL
-       if (alg == ENCR_3DES) {
-               struct des3_key_s des3key;
-               size_t i, blocks;
-
-               /* ECB mode is used incorrectly for 3DES!? */
-               if (key_len != 24) {
-                       wpa_printf(MSG_INFO, "IKEV2: Invalid encr key length");
-                       return -1;
-               }
-               des3_key_setup(key, &des3key);
-
-               if (len % 8) {
-                       wpa_printf(MSG_INFO, "IKEV2: Invalid encrypted "
-                                  "length");
-                       return -1;
-               }
-               blocks = len / 8;
-               for (i = 0; i < blocks; i++) {
-                       des3_decrypt(crypt, &des3key, plain);
-                       plain += 8;
-                       crypt += 8;
-               }
-       } else {
-#endif /* CCNS_PL */
        switch (alg) {
        case ENCR_3DES:
                encr_alg = CRYPTO_CIPHER_ALG_3DES;
@@ -300,9 +238,6 @@ int ikev2_encr_decrypt(int alg, const u8 *key, size_t key_len, const u8 *iv,
                return -1;
        }
        crypto_cipher_deinit(cipher);
-#ifdef CCNS_PL
-       }
-#endif /* CCNS_PL */
 
        return 0;
 }
@@ -316,25 +251,29 @@ int ikev2_parse_payloads(struct ikev2_payloads *payloads,
        os_memset(payloads, 0, sizeof(*payloads));
 
        while (next_payload != IKEV2_PAYLOAD_NO_NEXT_PAYLOAD) {
-               int plen, pdatalen;
+               unsigned int plen, pdatalen, left;
                const u8 *pdata;
                wpa_printf(MSG_DEBUG, "IKEV2: Processing payload %u",
                           next_payload);
-               if (end - pos < (int) sizeof(*phdr)) {
+               if (end < pos)
+                       return -1;
+               left = end - pos;
+               if (left < sizeof(*phdr)) {
                        wpa_printf(MSG_INFO, "IKEV2:   Too short message for "
                                   "payload header (left=%ld)",
                                   (long) (end - pos));
+                       return -1;
                }
                phdr = (const struct ikev2_payload_hdr *) pos;
                plen = WPA_GET_BE16(phdr->payload_length);
-               if (plen < (int) sizeof(*phdr) || pos + plen > end) {
+               if (plen < sizeof(*phdr) || plen > left) {
                        wpa_printf(MSG_INFO, "IKEV2:   Invalid payload header "
                                   "length %d", plen);
                        return -1;
                }
 
                wpa_printf(MSG_DEBUG, "IKEV2:   Next Payload: %u  Flags: 0x%x"
-                          "  Payload Length: %d",
+                          "  Payload Length: %u",
                           phdr->next_payload, phdr->flags, plen);
 
                pdata = (const u8 *) (phdr + 1);
@@ -542,7 +481,7 @@ u8 * ikev2_decrypt_payload(int encr_id, int integ_id,
                           "hash");
                return NULL;
        }
-       if (os_memcmp(integ, hash, integ_alg->hash_len) != 0) {
+       if (os_memcmp_const(integ, hash, integ_alg->hash_len) != 0) {
                wpa_printf(MSG_INFO, "IKEV2: Incorrect Integrity Checksum "
                           "Data");
                return NULL;
@@ -706,10 +645,6 @@ int ikev2_derive_sk_keys(const struct ikev2_prf_alg *prf,
        keys->SK_integ_len = integ->key_len;
        keys->SK_encr_len = encr->key_len;
        keys->SK_prf_len = prf->key_len;
-#ifdef CCNS_PL
-       /* Uses encryption key length for SK_d; should be PRF length */
-       keys->SK_d_len = keys->SK_encr_len;
-#endif /* CCNS_PL */
 
        keybuf_len = keys->SK_d_len + 2 * keys->SK_integ_len +
                2 * keys->SK_encr_len + 2 * keys->SK_prf_len;
index 45c970b..8a7982a 100644 (file)
@@ -70,11 +70,7 @@ struct ikev2_transform {
 /* Current IKEv2 version from RFC 4306 */
 #define IKEV2_MjVer 2
 #define IKEV2_MnVer 0
-#ifdef CCNS_PL
-#define IKEV2_VERSION ((IKEV2_MjVer) | ((IKEV2_MnVer) << 4))
-#else /* CCNS_PL */
 #define IKEV2_VERSION (((IKEV2_MjVer) << 4) | (IKEV2_MnVer))
-#endif /* CCNS_PL */
 
 /* IKEv2 Exchange Types */
 enum {
index 3651056..f79519b 100644 (file)
@@ -2,7 +2,7 @@ all:
        @echo Nothing to be made.
 
 clean:
-       rm -f *~ *.o *.so *.d
+       rm -f *~ *.o *.so *.d *.gcno *.gcda *.gcov
 
 install:
        if ls *.so >/dev/null 2>&1; then \
index c0d7078..35433f3 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * EAP peer state machines (RFC 4137)
- * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -23,6 +23,7 @@
 #include "ext_password.h"
 #include "crypto/crypto.h"
 #include "crypto/tls.h"
+#include "crypto/sha256.h"
 #include "common/wpa_ctrl.h"
 #include "eap_common/eap_wsc_common.h"
 #include "eap_i.h"
@@ -92,6 +93,15 @@ static void eap_notify_status(struct eap_sm *sm, const char *status,
 }
 
 
+static void eap_sm_free_key(struct eap_sm *sm)
+{
+       if (sm->eapKeyData) {
+               bin_clear_free(sm->eapKeyData, sm->eapKeyDataLen);
+               sm->eapKeyData = NULL;
+       }
+}
+
+
 static void eap_deinit_prev_method(struct eap_sm *sm, const char *txt)
 {
        ext_password_free(sm->ext_pw_buf);
@@ -144,11 +154,13 @@ SM_STATE(EAP, INITIALIZE)
        SM_ENTRY(EAP, INITIALIZE);
        if (sm->fast_reauth && sm->m && sm->m->has_reauth_data &&
            sm->m->has_reauth_data(sm, sm->eap_method_priv) &&
-           !sm->prev_failure) {
+           !sm->prev_failure &&
+           sm->last_config == eap_get_config(sm)) {
                wpa_printf(MSG_DEBUG, "EAP: maintaining EAP method data for "
                           "fast reauthentication");
                sm->m->deinit_for_reauth(sm, sm->eap_method_priv);
        } else {
+               sm->last_config = eap_get_config(sm);
                eap_deinit_prev_method(sm, "INITIALIZE");
        }
        sm->selectedMethod = EAP_TYPE_NONE;
@@ -159,8 +171,7 @@ SM_STATE(EAP, INITIALIZE)
        eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout);
        eapol_set_bool(sm, EAPOL_eapSuccess, FALSE);
        eapol_set_bool(sm, EAPOL_eapFail, FALSE);
-       os_free(sm->eapKeyData);
-       sm->eapKeyData = NULL;
+       eap_sm_free_key(sm);
        os_free(sm->eapSessionId);
        sm->eapSessionId = NULL;
        sm->eapKeyAvailable = FALSE;
@@ -179,6 +190,9 @@ SM_STATE(EAP, INITIALIZE)
        eapol_set_bool(sm, EAPOL_eapNoResp, FALSE);
        sm->num_rounds = 0;
        sm->prev_failure = 0;
+       sm->expected_failure = 0;
+       sm->reauthInit = FALSE;
+       sm->erp_seq = (u32) -1;
 }
 
 
@@ -342,6 +356,267 @@ nak:
 }
 
 
+#ifdef CONFIG_ERP
+
+static char * eap_home_realm(struct eap_sm *sm)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+       char *realm;
+       size_t i, realm_len;
+
+       if (!config)
+               return NULL;
+
+       if (config->identity) {
+               for (i = 0; i < config->identity_len; i++) {
+                       if (config->identity[i] == '@')
+                               break;
+               }
+               if (i < config->identity_len) {
+                       realm_len = config->identity_len - i - 1;
+                       realm = os_malloc(realm_len + 1);
+                       if (realm == NULL)
+                               return NULL;
+                       os_memcpy(realm, &config->identity[i + 1], realm_len);
+                       realm[realm_len] = '\0';
+                       return realm;
+               }
+       }
+
+       if (config->anonymous_identity) {
+               for (i = 0; i < config->anonymous_identity_len; i++) {
+                       if (config->anonymous_identity[i] == '@')
+                               break;
+               }
+               if (i < config->anonymous_identity_len) {
+                       realm_len = config->anonymous_identity_len - i - 1;
+                       realm = os_malloc(realm_len + 1);
+                       if (realm == NULL)
+                               return NULL;
+                       os_memcpy(realm, &config->anonymous_identity[i + 1],
+                                 realm_len);
+                       realm[realm_len] = '\0';
+                       return realm;
+               }
+       }
+
+       return os_strdup("");
+}
+
+
+static struct eap_erp_key *
+eap_erp_get_key(struct eap_sm *sm, const char *realm)
+{
+       struct eap_erp_key *erp;
+
+       dl_list_for_each(erp, &sm->erp_keys, struct eap_erp_key, list) {
+               char *pos;
+
+               pos = os_strchr(erp->keyname_nai, '@');
+               if (!pos)
+                       continue;
+               pos++;
+               if (os_strcmp(pos, realm) == 0)
+                       return erp;
+       }
+
+       return NULL;
+}
+
+
+static struct eap_erp_key *
+eap_erp_get_key_nai(struct eap_sm *sm, const char *nai)
+{
+       struct eap_erp_key *erp;
+
+       dl_list_for_each(erp, &sm->erp_keys, struct eap_erp_key, list) {
+               if (os_strcmp(erp->keyname_nai, nai) == 0)
+                       return erp;
+       }
+
+       return NULL;
+}
+
+
+static void eap_peer_erp_free_key(struct eap_erp_key *erp)
+{
+       dl_list_del(&erp->list);
+       bin_clear_free(erp, sizeof(*erp));
+}
+
+
+static void eap_erp_remove_keys_realm(struct eap_sm *sm, const char *realm)
+{
+       struct eap_erp_key *erp;
+
+       while ((erp = eap_erp_get_key(sm, realm)) != NULL) {
+               wpa_printf(MSG_DEBUG, "EAP: Delete old ERP key %s",
+                          erp->keyname_nai);
+               eap_peer_erp_free_key(erp);
+       }
+}
+
+#endif /* CONFIG_ERP */
+
+
+void eap_peer_erp_free_keys(struct eap_sm *sm)
+{
+#ifdef CONFIG_ERP
+       struct eap_erp_key *erp, *tmp;
+
+       dl_list_for_each_safe(erp, tmp, &sm->erp_keys, struct eap_erp_key, list)
+               eap_peer_erp_free_key(erp);
+#endif /* CONFIG_ERP */
+}
+
+
+static void eap_peer_erp_init(struct eap_sm *sm)
+{
+#ifdef CONFIG_ERP
+       u8 *emsk = NULL;
+       size_t emsk_len = 0;
+       u8 EMSKname[EAP_EMSK_NAME_LEN];
+       u8 len[2];
+       char *realm;
+       size_t realm_len, nai_buf_len;
+       struct eap_erp_key *erp = NULL;
+       int pos;
+
+       realm = eap_home_realm(sm);
+       if (!realm)
+               return;
+       realm_len = os_strlen(realm);
+       wpa_printf(MSG_DEBUG, "EAP: Realm for ERP keyName-NAI: %s", realm);
+       eap_erp_remove_keys_realm(sm, realm);
+
+       nai_buf_len = 2 * EAP_EMSK_NAME_LEN + 1 + realm_len;
+       if (nai_buf_len > 253) {
+               /*
+                * keyName-NAI has a maximum length of 253 octet to fit in
+                * RADIUS attributes.
+                */
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Too long realm for ERP keyName-NAI maximum length");
+               goto fail;
+       }
+       nai_buf_len++; /* null termination */
+       erp = os_zalloc(sizeof(*erp) + nai_buf_len);
+       if (erp == NULL)
+               goto fail;
+
+       emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len);
+       if (!emsk || emsk_len == 0 || emsk_len > ERP_MAX_KEY_LEN) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: No suitable EMSK available for ERP");
+               goto fail;
+       }
+
+       wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len);
+
+       WPA_PUT_BE16(len, 8);
+       if (hmac_sha256_kdf(sm->eapSessionId, sm->eapSessionIdLen, "EMSK",
+                           len, sizeof(len),
+                           EMSKname, EAP_EMSK_NAME_LEN) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP: Could not derive EMSKname");
+               goto fail;
+       }
+       wpa_hexdump(MSG_DEBUG, "EAP: EMSKname", EMSKname, EAP_EMSK_NAME_LEN);
+
+       pos = wpa_snprintf_hex(erp->keyname_nai, nai_buf_len,
+                              EMSKname, EAP_EMSK_NAME_LEN);
+       erp->keyname_nai[pos] = '@';
+       os_memcpy(&erp->keyname_nai[pos + 1], realm, realm_len);
+
+       WPA_PUT_BE16(len, emsk_len);
+       if (hmac_sha256_kdf(emsk, emsk_len,
+                           "EAP Re-authentication Root Key@ietf.org",
+                           len, sizeof(len), erp->rRK, emsk_len) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP: Could not derive rRK for ERP");
+               goto fail;
+       }
+       erp->rRK_len = emsk_len;
+       wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len);
+
+       if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
+                           "EAP Re-authentication Integrity Key@ietf.org",
+                           len, sizeof(len), erp->rIK, erp->rRK_len) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP");
+               goto fail;
+       }
+       erp->rIK_len = erp->rRK_len;
+       wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rIK", erp->rIK, erp->rIK_len);
+
+       wpa_printf(MSG_DEBUG, "EAP: Stored ERP keys %s", erp->keyname_nai);
+       dl_list_add(&sm->erp_keys, &erp->list);
+       erp = NULL;
+fail:
+       bin_clear_free(emsk, emsk_len);
+       bin_clear_free(erp, sizeof(*erp));
+       os_free(realm);
+#endif /* CONFIG_ERP */
+}
+
+
+#ifdef CONFIG_ERP
+static int eap_peer_erp_reauth_start(struct eap_sm *sm,
+                                    const struct eap_hdr *hdr, size_t len)
+{
+       char *realm;
+       struct eap_erp_key *erp;
+       struct wpabuf *msg;
+       u8 hash[SHA256_MAC_LEN];
+
+       realm = eap_home_realm(sm);
+       if (!realm)
+               return -1;
+
+       erp = eap_erp_get_key(sm, realm);
+       os_free(realm);
+       realm = NULL;
+       if (!erp)
+               return -1;
+
+       if (erp->next_seq >= 65536)
+               return -1; /* SEQ has range of 0..65535 */
+
+       /* TODO: check rRK lifetime expiration */
+
+       wpa_printf(MSG_DEBUG, "EAP: Valid ERP key found %s (SEQ=%u)",
+                  erp->keyname_nai, erp->next_seq);
+
+       msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH,
+                           1 + 2 + 2 + os_strlen(erp->keyname_nai) + 1 + 16,
+                           EAP_CODE_INITIATE, hdr->identifier);
+       if (msg == NULL)
+               return -1;
+
+       wpabuf_put_u8(msg, 0x20); /* Flags: R=0 B=0 L=1 */
+       wpabuf_put_be16(msg, erp->next_seq);
+
+       wpabuf_put_u8(msg, EAP_ERP_TLV_KEYNAME_NAI);
+       wpabuf_put_u8(msg, os_strlen(erp->keyname_nai));
+       wpabuf_put_str(msg, erp->keyname_nai);
+
+       wpabuf_put_u8(msg, EAP_ERP_CS_HMAC_SHA256_128); /* Cryptosuite */
+
+       if (hmac_sha256(erp->rIK, erp->rIK_len,
+                       wpabuf_head(msg), wpabuf_len(msg), hash) < 0) {
+               wpabuf_free(msg);
+               return -1;
+       }
+       wpabuf_put_data(msg, hash, 16);
+
+       wpa_printf(MSG_DEBUG, "EAP: Sending EAP-Initiate/Re-auth");
+       sm->erp_seq = erp->next_seq;
+       erp->next_seq++;
+       wpabuf_free(sm->eapRespData);
+       sm->eapRespData = msg;
+       sm->reauthInit = TRUE;
+       return 0;
+}
+#endif /* CONFIG_ERP */
+
+
 /*
  * The method processing happens here. The request from the authenticator is
  * processed, and an appropriate response packet is built.
@@ -388,10 +663,11 @@ SM_STATE(EAP, METHOD)
        sm->eapRespData = sm->m->process(sm, sm->eap_method_priv, &ret,
                                         eapReqData);
        wpa_printf(MSG_DEBUG, "EAP: method process -> ignore=%s "
-                  "methodState=%s decision=%s",
+                  "methodState=%s decision=%s eapRespData=%p",
                   ret.ignore ? "TRUE" : "FALSE",
                   eap_sm_method_state_txt(ret.methodState),
-                  eap_sm_decision_txt(ret.decision));
+                  eap_sm_decision_txt(ret.decision),
+                  sm->eapRespData);
 
        sm->ignore = ret.ignore;
        if (sm->ignore)
@@ -402,7 +678,9 @@ SM_STATE(EAP, METHOD)
 
        if (sm->m->isKeyAvailable && sm->m->getKey &&
            sm->m->isKeyAvailable(sm, sm->eap_method_priv)) {
-               os_free(sm->eapKeyData);
+               struct eap_peer_config *config = eap_get_config(sm);
+
+               eap_sm_free_key(sm);
                sm->eapKeyData = sm->m->getKey(sm, sm->eap_method_priv,
                                               &sm->eapKeyDataLen);
                os_free(sm->eapSessionId);
@@ -414,6 +692,8 @@ SM_STATE(EAP, METHOD)
                        wpa_hexdump(MSG_DEBUG, "EAP: Session-Id",
                                    sm->eapSessionId, sm->eapSessionIdLen);
                }
+               if (config->erp && sm->m->get_emsk && sm->eapSessionId)
+                       eap_peer_erp_init(sm);
        }
 }
 
@@ -432,10 +712,13 @@ SM_STATE(EAP, SEND_RESPONSE)
                sm->lastId = sm->reqId;
                sm->lastRespData = wpabuf_dup(sm->eapRespData);
                eapol_set_bool(sm, EAPOL_eapResp, TRUE);
-       } else
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP: No eapRespData available");
                sm->lastRespData = NULL;
+       }
        eapol_set_bool(sm, EAPOL_eapReq, FALSE);
        eapol_set_int(sm, EAPOL_idleWhile, sm->ClientTimeout);
+       sm->reauthInit = FALSE;
 }
 
 
@@ -651,6 +934,15 @@ static int eap_peer_req_is_duplicate(struct eap_sm *sm)
 }
 
 
+static int eap_peer_sm_allow_canned(struct eap_sm *sm)
+{
+       struct eap_peer_config *config = eap_get_config(sm);
+
+       return config && config->phase1 &&
+               os_strstr(config->phase1, "allow_canned_success=1");
+}
+
+
 static void eap_peer_sm_step_received(struct eap_sm *sm)
 {
        int duplicate = eap_peer_req_is_duplicate(sm);
@@ -664,6 +956,17 @@ static void eap_peer_sm_step_received(struct eap_sm *sm)
            (sm->reqId == sm->lastId ||
             eap_success_workaround(sm, sm->reqId, sm->lastId)))
                SM_ENTER(EAP, SUCCESS);
+       else if (sm->workaround && sm->lastId == -1 && sm->rxSuccess &&
+                !sm->rxFailure && !sm->rxReq && eap_peer_sm_allow_canned(sm))
+               SM_ENTER(EAP, SUCCESS); /* EAP-Success prior any EAP method */
+       else if (sm->workaround && sm->lastId == -1 && sm->rxFailure &&
+                !sm->rxReq && sm->methodState != METHOD_CONT &&
+                eap_peer_sm_allow_canned(sm))
+               SM_ENTER(EAP, FAILURE); /* EAP-Failure prior any EAP method */
+       else if (sm->workaround && sm->rxSuccess && !sm->rxFailure &&
+                !sm->rxReq && sm->methodState != METHOD_CONT &&
+                eap_peer_sm_allow_canned(sm))
+               SM_ENTER(EAP, SUCCESS); /* EAP-Success after Identity */
        else if (sm->methodState != METHOD_CONT &&
                 ((sm->rxFailure &&
                   sm->decision != DECISION_UNCOND_SUCC) ||
@@ -695,6 +998,8 @@ static void eap_peer_sm_step_received(struct eap_sm *sm)
        else if (sm->selectedMethod == EAP_TYPE_LEAP &&
                 (sm->rxSuccess || sm->rxResp))
                SM_ENTER(EAP, METHOD);
+       else if (sm->reauthInit)
+               SM_ENTER(EAP, SEND_RESPONSE);
        else
                SM_ENTER(EAP, DISCARD);
 }
@@ -724,8 +1029,19 @@ static void eap_peer_sm_step_local(struct eap_sm *sm)
                        SM_ENTER(EAP, SEND_RESPONSE);
                break;
        case EAP_METHOD:
+               /*
+                * Note: RFC 4137 uses methodState == DONE && decision == FAIL
+                * as the condition. eapRespData == NULL here is used to allow
+                * final EAP method response to be sent without having to change
+                * all methods to either use methodState MAY_CONT or leaving
+                * decision to something else than FAIL in cases where the only
+                * expected response is EAP-Failure.
+                */
                if (sm->ignore)
                        SM_ENTER(EAP, DISCARD);
+               else if (sm->methodState == METHOD_DONE &&
+                        sm->decision == DECISION_FAIL && !sm->eapRespData)
+                       SM_ENTER(EAP, FAILURE);
                else
                        SM_ENTER(EAP, SEND_RESPONSE);
                break;
@@ -938,6 +1254,8 @@ static int mnc_len_from_imsi(const char *imsi)
        mcc_str[3] = '\0';
        mcc = atoi(mcc_str);
 
+       if (mcc == 228)
+               return 2; /* Networks in Switzerland use 2-digit MNC */
        if (mcc == 244)
                return 2; /* Networks in Finland use 2-digit MNC */
 
@@ -1204,6 +1522,219 @@ static struct wpabuf * eap_sm_buildNotify(int id)
 }
 
 
+static void eap_peer_initiate(struct eap_sm *sm, const struct eap_hdr *hdr,
+                             size_t len)
+{
+#ifdef CONFIG_ERP
+       const u8 *pos = (const u8 *) (hdr + 1);
+       const u8 *end = ((const u8 *) hdr) + len;
+       struct erp_tlvs parse;
+
+       if (len < sizeof(*hdr) + 1) {
+               wpa_printf(MSG_DEBUG, "EAP: Ignored too short EAP-Initiate");
+               return;
+       }
+
+       if (*pos != EAP_ERP_TYPE_REAUTH_START) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Ignored unexpected EAP-Initiate Type=%u",
+                          *pos);
+               return;
+       }
+
+       pos++;
+       if (pos >= end) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Too short EAP-Initiate/Re-auth-Start");
+               return;
+       }
+       pos++; /* Reserved */
+       wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-auth-Start TVs/TLVs",
+                   pos, end - pos);
+
+       if (erp_parse_tlvs(pos, end, &parse, 0) < 0)
+               goto invalid;
+
+       if (parse.domain) {
+               wpa_hexdump_ascii(MSG_DEBUG,
+                                 "EAP: EAP-Initiate/Re-auth-Start - Domain name",
+                                 parse.domain, parse.domain_len);
+               /* TODO: Derivation of domain specific keys for local ER */
+       }
+
+       if (eap_peer_erp_reauth_start(sm, hdr, len) == 0)
+               return;
+
+invalid:
+#endif /* CONFIG_ERP */
+       wpa_printf(MSG_DEBUG,
+                  "EAP: EAP-Initiate/Re-auth-Start - No suitable ERP keys available - try to start full EAP authentication");
+       eapol_set_bool(sm, EAPOL_eapTriggerStart, TRUE);
+}
+
+
+static void eap_peer_finish(struct eap_sm *sm, const struct eap_hdr *hdr,
+                           size_t len)
+{
+#ifdef CONFIG_ERP
+       const u8 *pos = (const u8 *) (hdr + 1);
+       const u8 *end = ((const u8 *) hdr) + len;
+       const u8 *start;
+       struct erp_tlvs parse;
+       u8 flags;
+       u16 seq;
+       u8 hash[SHA256_MAC_LEN];
+       size_t hash_len;
+       struct eap_erp_key *erp;
+       int max_len;
+       char nai[254];
+       u8 seed[4];
+       int auth_tag_ok = 0;
+
+       if (len < sizeof(*hdr) + 1) {
+               wpa_printf(MSG_DEBUG, "EAP: Ignored too short EAP-Finish");
+               return;
+       }
+
+       if (*pos != EAP_ERP_TYPE_REAUTH) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Ignored unexpected EAP-Finish Type=%u", *pos);
+               return;
+       }
+
+       if (len < sizeof(*hdr) + 4) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Ignored too short EAP-Finish/Re-auth");
+               return;
+       }
+
+       pos++;
+       flags = *pos++;
+       seq = WPA_GET_BE16(pos);
+       pos += 2;
+       wpa_printf(MSG_DEBUG, "EAP: Flags=0x%x SEQ=%u", flags, seq);
+
+       if (seq != sm->erp_seq) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Unexpected EAP-Finish/Re-auth SEQ=%u", seq);
+               return;
+       }
+
+       /*
+        * Parse TVs/TLVs. Since we do not yet know the length of the
+        * Authentication Tag, stop parsing if an unknown TV/TLV is seen and
+        * just try to find the keyName-NAI first so that we can check the
+        * Authentication Tag.
+        */
+       if (erp_parse_tlvs(pos, end, &parse, 1) < 0)
+               return;
+
+       if (!parse.keyname) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: No keyName-NAI in EAP-Finish/Re-auth Packet");
+               return;
+       }
+
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Finish/Re-auth - keyName-NAI",
+                         parse.keyname, parse.keyname_len);
+       if (parse.keyname_len > 253) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Too long keyName-NAI in EAP-Finish/Re-auth");
+               return;
+       }
+       os_memcpy(nai, parse.keyname, parse.keyname_len);
+       nai[parse.keyname_len] = '\0';
+
+       erp = eap_erp_get_key_nai(sm, nai);
+       if (!erp) {
+               wpa_printf(MSG_DEBUG, "EAP: No matching ERP key found for %s",
+                          nai);
+               return;
+       }
+
+       /* Is there enough room for Cryptosuite and Authentication Tag? */
+       start = parse.keyname + parse.keyname_len;
+       max_len = end - start;
+       hash_len = 16;
+       if (max_len < 1 + (int) hash_len) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Not enough room for Authentication Tag");
+               if (flags & 0x80)
+                       goto no_auth_tag;
+               return;
+       }
+       if (end[-17] != EAP_ERP_CS_HMAC_SHA256_128) {
+               wpa_printf(MSG_DEBUG, "EAP: Different Cryptosuite used");
+               if (flags & 0x80)
+                       goto no_auth_tag;
+               return;
+       }
+
+       if (hmac_sha256(erp->rIK, erp->rIK_len, (const u8 *) hdr,
+                       end - ((const u8 *) hdr) - hash_len, hash) < 0)
+               return;
+       if (os_memcmp(end - hash_len, hash, hash_len) != 0) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Authentication Tag mismatch");
+               return;
+       }
+       auth_tag_ok = 1;
+       end -= 1 + hash_len;
+
+no_auth_tag:
+       /*
+        * Parse TVs/TLVs again now that we know the exact part of the buffer
+        * that contains them.
+        */
+       wpa_hexdump(MSG_DEBUG, "EAP: EAP-Finish/Re-Auth TVs/TLVs",
+                   pos, end - pos);
+       if (erp_parse_tlvs(pos, end, &parse, 0) < 0)
+               return;
+
+       if (flags & 0x80 || !auth_tag_ok) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: EAP-Finish/Re-auth indicated failure");
+               eapol_set_bool(sm, EAPOL_eapFail, TRUE);
+               eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+               eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+               wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
+                       "EAP authentication failed");
+               sm->prev_failure = 1;
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Drop ERP key to try full authentication on next attempt");
+               eap_peer_erp_free_key(erp);
+               return;
+       }
+
+       eap_sm_free_key(sm);
+       sm->eapKeyDataLen = 0;
+       sm->eapKeyData = os_malloc(erp->rRK_len);
+       if (!sm->eapKeyData)
+               return;
+       sm->eapKeyDataLen = erp->rRK_len;
+
+       WPA_PUT_BE16(seed, seq);
+       WPA_PUT_BE16(&seed[2], erp->rRK_len);
+       if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
+                           "Re-authentication Master Session Key@ietf.org",
+                           seed, sizeof(seed),
+                           sm->eapKeyData, erp->rRK_len) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP: Could not derive rMSK for ERP");
+               eap_sm_free_key(sm);
+               return;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rMSK",
+                       sm->eapKeyData, sm->eapKeyDataLen);
+       sm->eapKeyAvailable = TRUE;
+       eapol_set_bool(sm, EAPOL_eapSuccess, TRUE);
+       eapol_set_bool(sm, EAPOL_eapReq, FALSE);
+       eapol_set_bool(sm, EAPOL_eapNoResp, TRUE);
+       wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+               "EAP re-authentication completed successfully");
+#endif /* CONFIG_ERP */
+}
+
+
 static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req)
 {
        const struct eap_hdr *hdr;
@@ -1295,6 +1826,12 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req)
                eap_notify_status(sm, "completion", "failure");
                sm->rxFailure = TRUE;
                break;
+       case EAP_CODE_INITIATE:
+               eap_peer_initiate(sm, hdr, plen);
+               break;
+       case EAP_CODE_FINISH:
+               eap_peer_finish(sm, hdr, plen);
+               break;
        default:
                wpa_printf(MSG_DEBUG, "EAP: Ignored EAP-Packet with unknown "
                           "code %d", hdr->code);
@@ -1341,6 +1878,8 @@ static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev,
                sm->eapol_cb->notify_cert(sm->eapol_ctx,
                                          data->peer_cert.depth,
                                          data->peer_cert.subject,
+                                         data->peer_cert.altsubject,
+                                         data->peer_cert.num_altsubject,
                                          hash_hex, data->peer_cert.cert);
                break;
        case TLS_ALERT:
@@ -1386,11 +1925,13 @@ struct eap_sm * eap_peer_sm_init(void *eapol_ctx,
        sm->msg_ctx = msg_ctx;
        sm->ClientTimeout = EAP_CLIENT_TIMEOUT_DEFAULT;
        sm->wps = conf->wps;
+       dl_list_init(&sm->erp_keys);
 
        os_memset(&tlsconf, 0, sizeof(tlsconf));
        tlsconf.opensc_engine_path = conf->opensc_engine_path;
        tlsconf.pkcs11_engine_path = conf->pkcs11_engine_path;
        tlsconf.pkcs11_module_path = conf->pkcs11_module_path;
+       tlsconf.openssl_ciphers = conf->openssl_ciphers;
 #ifdef CONFIG_FIPS
        tlsconf.fips_mode = 1;
 #endif /* CONFIG_FIPS */
@@ -1432,6 +1973,7 @@ void eap_peer_sm_deinit(struct eap_sm *sm)
        if (sm->ssl_ctx2)
                tls_deinit(sm->ssl_ctx2);
        tls_deinit(sm->ssl_ctx);
+       eap_peer_erp_free_keys(sm);
        os_free(sm);
 }
 
@@ -1471,8 +2013,7 @@ void eap_sm_abort(struct eap_sm *sm)
        sm->lastRespData = NULL;
        wpabuf_free(sm->eapRespData);
        sm->eapRespData = NULL;
-       os_free(sm->eapKeyData);
-       sm->eapKeyData = NULL;
+       eap_sm_free_key(sm);
        os_free(sm->eapSessionId);
        sm->eapSessionId = NULL;
 
@@ -1581,7 +2122,7 @@ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose)
        len = os_snprintf(buf, buflen,
                          "EAP state=%s\n",
                          eap_sm_state_txt(sm->EAP_state));
-       if (len < 0 || (size_t) len >= buflen)
+       if (os_snprintf_error(buflen, len))
                return 0;
 
        if (sm->selectedMethod != EAP_TYPE_NONE) {
@@ -1600,7 +2141,7 @@ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose)
                ret = os_snprintf(buf + len, buflen - len,
                                  "selectedMethod=%d (EAP-%s)\n",
                                  sm->selectedMethod, name);
-               if (ret < 0 || (size_t) ret >= buflen - len)
+               if (os_snprintf_error(buflen - len, ret))
                        return len;
                len += ret;
 
@@ -1621,7 +2162,7 @@ int eap_sm_get_status(struct eap_sm *sm, char *buf, size_t buflen, int verbose)
                                  eap_sm_method_state_txt(sm->methodState),
                                  eap_sm_decision_txt(sm->decision),
                                  sm->ClientTimeout);
-               if (ret < 0 || (size_t) ret >= buflen - len)
+               if (os_snprintf_error(buflen - len, ret))
                        return len;
                len += ret;
        }
@@ -1636,7 +2177,8 @@ static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field,
                           const char *msg, size_t msglen)
 {
        struct eap_peer_config *config;
-       char *txt = NULL, *tmp;
+       const char *txt = NULL;
+       char *tmp;
 
        if (sm == NULL)
                return;
@@ -1679,6 +2221,9 @@ static void eap_sm_request(struct eap_sm *sm, enum wpa_ctrl_req_type field,
        case WPA_CTRL_REQ_EAP_PASSPHRASE:
                config->pending_req_passphrase++;
                break;
+       case WPA_CTRL_REQ_SIM:
+               txt = msg;
+               break;
        default:
                return;
        }
@@ -1790,6 +2335,17 @@ void eap_sm_request_passphrase(struct eap_sm *sm)
 
 
 /**
+ * eap_sm_request_sim - Request external SIM processing
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @req: EAP method specific request
+ */
+void eap_sm_request_sim(struct eap_sm *sm, const char *req)
+{
+       eap_sm_request(sm, WPA_CTRL_REQ_SIM, req, os_strlen(req));
+}
+
+
+/**
  * eap_sm_notify_ctrl_attached - Notification of attached monitor
  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
  *
@@ -2015,6 +2571,8 @@ const u8 * eap_get_config_password2(struct eap_sm *sm, size_t *len, int *hash)
        if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
                if (eap_get_ext_password(sm, config) < 0)
                        return NULL;
+               if (hash)
+                       *hash = 0;
                *len = wpabuf_len(sm->ext_pw_buf);
                return wpabuf_head(sm->ext_pw_buf);
        }
@@ -2302,6 +2860,17 @@ void eap_set_force_disabled(struct eap_sm *sm, int disabled)
 }
 
 
+/**
+ * eap_set_external_sim - Set external_sim flag
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @external_sim: Whether external SIM/USIM processing is used
+ */
+void eap_set_external_sim(struct eap_sm *sm, int external_sim)
+{
+       sm->external_sim = external_sim;
+}
+
+
  /**
  * eap_notify_pending - Notify that EAP method is ready to re-process a request
  * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
@@ -2373,3 +2942,9 @@ void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len)
        if (sm->eapol_cb->set_anon_id)
                sm->eapol_cb->set_anon_id(sm->eapol_ctx, id, len);
 }
+
+
+int eap_peer_was_failure_expected(struct eap_sm *sm)
+{
+       return sm->expected_failure;
+}
index f87f9b3..702463b 100644 (file)
@@ -94,7 +94,14 @@ enum eapol_bool_var {
         *
         * EAP state machines reads this value.
         */
-       EAPOL_altReject
+       EAPOL_altReject,
+
+       /**
+        * EAPOL_eapTriggerStart - EAP-based trigger to send EAPOL-Start
+        *
+        * EAP state machine writes this value.
+        */
+       EAPOL_eapTriggerStart
 };
 
 /**
@@ -221,10 +228,13 @@ struct eapol_callbacks {
         * @ctx: eapol_ctx from eap_peer_sm_init() call
         * @depth: Depth in certificate chain (0 = server)
         * @subject: Subject of the peer certificate
+        * @altsubject: Select fields from AltSubject of the peer certificate
+        * @num_altsubject: Number of altsubject values
         * @cert_hash: SHA-256 hash of the certificate
         * @cert: Peer certificate
         */
        void (*notify_cert)(void *ctx, int depth, const char *subject,
+                           const char *altsubject[], int num_altsubject,
                            const char *cert_hash, const struct wpabuf *cert);
 
        /**
@@ -236,6 +246,14 @@ struct eapol_callbacks {
        void (*notify_status)(void *ctx, const char *status,
                              const char *parameter);
 
+#ifdef CONFIG_EAP_PROXY
+       /**
+        * eap_proxy_cb - Callback signifying any updates from eap_proxy
+        * @ctx: eapol_ctx from eap_peer_sm_init() call
+        */
+       void (*eap_proxy_cb)(void *ctx);
+#endif /* CONFIG_EAP_PROXY */
+
        /**
         * set_anon_id - Set or add anonymous identity
         * @ctx: eapol_ctx from eap_peer_sm_init() call
@@ -268,6 +286,14 @@ struct eap_config {
         */
        const char *pkcs11_module_path;
        /**
+        * openssl_ciphers - OpenSSL cipher string
+        *
+        * This is an OpenSSL specific configuration option for configuring the
+        * default ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the
+        * default.
+        */
+       const char *openssl_ciphers;
+       /**
         * wps - WPS context data
         *
         * This is only used by EAP-WSC and can be left %NULL if not available.
@@ -296,6 +322,7 @@ void eap_sm_request_new_password(struct eap_sm *sm);
 void eap_sm_request_pin(struct eap_sm *sm);
 void eap_sm_request_otp(struct eap_sm *sm, const char *msg, size_t msg_len);
 void eap_sm_request_passphrase(struct eap_sm *sm);
+void eap_sm_request_sim(struct eap_sm *sm, const char *req);
 void eap_sm_notify_ctrl_attached(struct eap_sm *sm);
 u32 eap_get_phase2_type(const char *name, int *vendor);
 struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config,
@@ -303,6 +330,7 @@ struct eap_method_type * eap_get_phase2_types(struct eap_peer_config *config,
 void eap_set_fast_reauth(struct eap_sm *sm, int enabled);
 void eap_set_workaround(struct eap_sm *sm, unsigned int workaround);
 void eap_set_force_disabled(struct eap_sm *sm, int disabled);
+void eap_set_external_sim(struct eap_sm *sm, int external_sim);
 int eap_key_available(struct eap_sm *sm);
 void eap_notify_success(struct eap_sm *sm);
 void eap_notify_lower_layer_success(struct eap_sm *sm);
@@ -318,6 +346,8 @@ int eap_is_wps_pin_enrollee(struct eap_peer_config *conf);
 struct ext_password_data;
 void eap_sm_set_ext_pw_ctx(struct eap_sm *sm, struct ext_password_data *ext);
 void eap_set_anon_id(struct eap_sm *sm, const u8 *id, size_t len);
+int eap_peer_was_failure_expected(struct eap_sm *sm);
+void eap_peer_erp_free_keys(struct eap_sm *sm);
 
 #endif /* IEEE8021X_EAPOL */
 
index dc424d7..0662ae7 100644 (file)
@@ -42,7 +42,7 @@ struct eap_aka_data {
        u8 *last_eap_identity;
        size_t last_eap_identity_len;
        enum {
-               CONTINUE, RESULT_SUCCESS, RESULT_FAILURE, SUCCESS, FAILURE
+               CONTINUE, RESULT_SUCCESS, SUCCESS, FAILURE
        } state;
 
        struct wpabuf *id_msgs;
@@ -64,8 +64,6 @@ static const char * eap_aka_state_txt(int state)
                return "CONTINUE";
        case RESULT_SUCCESS:
                return "RESULT_SUCCESS";
-       case RESULT_FAILURE:
-               return "RESULT_FAILURE";
        case SUCCESS:
                return "SUCCESS";
        case FAILURE:
@@ -128,6 +126,21 @@ static void * eap_aka_prime_init(struct eap_sm *sm)
 #endif /* EAP_AKA_PRIME */
 
 
+static void eap_aka_clear_keys(struct eap_aka_data *data, int reauth)
+{
+       if (!reauth) {
+               os_memset(data->mk, 0, EAP_SIM_MK_LEN);
+               os_memset(data->k_aut, 0, EAP_AKA_PRIME_K_AUT_LEN);
+               os_memset(data->k_encr, 0, EAP_SIM_K_ENCR_LEN);
+               os_memset(data->k_re, 0, EAP_AKA_PRIME_K_RE_LEN);
+       }
+       os_memset(data->msk, 0, EAP_SIM_KEYING_DATA_LEN);
+       os_memset(data->emsk, 0, EAP_EMSK_LEN);
+       os_memset(data->autn, 0, EAP_AKA_AUTN_LEN);
+       os_memset(data->auts, 0, EAP_AKA_AUTS_LEN);
+}
+
+
 static void eap_aka_deinit(struct eap_sm *sm, void *priv)
 {
        struct eap_aka_data *data = priv;
@@ -137,11 +150,95 @@ static void eap_aka_deinit(struct eap_sm *sm, void *priv)
                os_free(data->last_eap_identity);
                wpabuf_free(data->id_msgs);
                os_free(data->network_name);
+               eap_aka_clear_keys(data, 0);
                os_free(data);
        }
 }
 
 
+static int eap_aka_ext_sim_req(struct eap_sm *sm, struct eap_aka_data *data)
+{
+       char req[200], *pos, *end;
+
+       wpa_printf(MSG_DEBUG, "EAP-AKA: Use external USIM processing");
+       pos = req;
+       end = pos + sizeof(req);
+       pos += os_snprintf(pos, end - pos, "UMTS-AUTH");
+       pos += os_snprintf(pos, end - pos, ":");
+       pos += wpa_snprintf_hex(pos, end - pos, data->rand, EAP_AKA_RAND_LEN);
+       pos += os_snprintf(pos, end - pos, ":");
+       wpa_snprintf_hex(pos, end - pos, data->autn, EAP_AKA_AUTN_LEN);
+
+       eap_sm_request_sim(sm, req);
+       return 1;
+}
+
+
+static int eap_aka_ext_sim_result(struct eap_sm *sm, struct eap_aka_data *data,
+                                 struct eap_peer_config *conf)
+{
+       char *resp, *pos;
+
+       wpa_printf(MSG_DEBUG,
+                  "EAP-AKA: Use result from external USIM processing");
+
+       resp = conf->external_sim_resp;
+       conf->external_sim_resp = NULL;
+
+       if (os_strncmp(resp, "UMTS-AUTS:", 10) == 0) {
+               pos = resp + 10;
+               if (hexstr2bin(pos, data->auts, EAP_AKA_AUTS_LEN) < 0)
+                       goto invalid;
+               wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: AUTS", data->auts,
+                               EAP_AKA_AUTS_LEN);
+               os_free(resp);
+               return -2;
+       }
+
+       if (os_strncmp(resp, "UMTS-AUTH:", 10) != 0) {
+               wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized external USIM processing response");
+               os_free(resp);
+               return -1;
+       }
+
+       pos = resp + 10;
+       wpa_hexdump(MSG_DEBUG, "EAP-AKA: RAND", data->rand, EAP_AKA_RAND_LEN);
+
+       if (hexstr2bin(pos, data->ik, EAP_AKA_IK_LEN) < 0)
+               goto invalid;
+       wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", data->ik, EAP_AKA_IK_LEN);
+       pos += EAP_AKA_IK_LEN * 2;
+       if (*pos != ':')
+               goto invalid;
+       pos++;
+
+       if (hexstr2bin(pos, data->ck, EAP_AKA_CK_LEN) < 0)
+               goto invalid;
+       wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", data->ck, EAP_AKA_CK_LEN);
+       pos += EAP_AKA_CK_LEN * 2;
+       if (*pos != ':')
+               goto invalid;
+       pos++;
+
+       data->res_len = os_strlen(pos) / 2;
+       if (data->res_len > EAP_AKA_RES_MAX_LEN) {
+               data->res_len = 0;
+               goto invalid;
+       }
+       if (hexstr2bin(pos, data->res, data->res_len) < 0)
+               goto invalid;
+       wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: RES", data->res, data->res_len);
+
+       os_free(resp);
+       return 0;
+
+invalid:
+       wpa_printf(MSG_DEBUG, "EAP-AKA: Invalid external USIM processing UMTS-AUTH response");
+       os_free(resp);
+       return -1;
+}
+
+
 static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data)
 {
        struct eap_peer_config *conf;
@@ -151,6 +248,14 @@ static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data)
        conf = eap_get_config(sm);
        if (conf == NULL)
                return -1;
+
+       if (sm->external_sim) {
+               if (conf->external_sim_resp)
+                       return eap_aka_ext_sim_result(sm, data, conf);
+               else
+                       return eap_aka_ext_sim_req(sm, data);
+       }
+
        if (conf->pcsc) {
                return scard_umts_auth(sm->scard_ctx, data->rand,
                                       data->autn, data->res, &data->res_len,
@@ -205,7 +310,7 @@ static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data)
        {
                u8 autn[EAP_AKA_AUTN_LEN];
                os_memset(autn, '1', EAP_AKA_AUTN_LEN);
-               if (os_memcmp(autn, data->autn, EAP_AKA_AUTN_LEN) != 0) {
+               if (os_memcmp_const(autn, data->autn, EAP_AKA_AUTN_LEN) != 0) {
                        wpa_printf(MSG_WARNING, "EAP-AKA: AUTN did not match "
                                   "with expected value");
                        return -1;
@@ -225,7 +330,7 @@ static int eap_aka_umts_auth(struct eap_sm *sm, struct eap_aka_data *data)
 
 #else /* CONFIG_USIM_HARDCODED */
 
-       wpa_printf(MSG_DEBUG, "EAP-AKA: No UMTS authentication algorith "
+       wpa_printf(MSG_DEBUG, "EAP-AKA: No UMTS authentication algorithm "
                   "enabled");
        return -1;
 
@@ -420,7 +525,7 @@ static int eap_aka_verify_checkcode(struct eap_aka_data *data,
 #endif /* EAP_AKA_PRIME */
                sha1_vector(1, &addr, &len, hash);
 
-       if (os_memcmp(hash, checkcode, hash_len) != 0) {
+       if (os_memcmp_const(hash, checkcode, hash_len) != 0) {
                wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE");
                return -1;
        }
@@ -443,7 +548,7 @@ static struct wpabuf * eap_aka_client_error(struct eap_aka_data *data, u8 id,
        msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
                               EAP_AKA_SUBTYPE_CLIENT_ERROR);
        eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0);
-       return eap_sim_msg_finish(msg, NULL, NULL, 0);
+       return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
 }
 
 
@@ -460,7 +565,7 @@ static struct wpabuf * eap_aka_authentication_reject(struct eap_aka_data *data,
                   "(id=%d)", id);
        msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, data->eap_method,
                               EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT);
-       return eap_sim_msg_finish(msg, NULL, NULL, 0);
+       return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
 }
 
 
@@ -479,7 +584,7 @@ static struct wpabuf * eap_aka_synchronization_failure(
        wpa_printf(MSG_DEBUG, "   AT_AUTS");
        eap_sim_msg_add_full(msg, EAP_SIM_AT_AUTS, data->auts,
                             EAP_AKA_AUTS_LEN);
-       return eap_sim_msg_finish(msg, NULL, NULL, 0);
+       return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
 }
 
 
@@ -523,7 +628,7 @@ static struct wpabuf * eap_aka_response_identity(struct eap_sm *sm,
                                identity, identity_len);
        }
 
-       return eap_sim_msg_finish(msg, NULL, NULL, 0);
+       return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
 }
 
 
@@ -545,7 +650,8 @@ static struct wpabuf * eap_aka_response_challenge(struct eap_aka_data *data,
        }
        wpa_printf(MSG_DEBUG, "   AT_MAC");
        eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
-       return eap_sim_msg_finish(msg, data->k_aut, (u8 *) "", 0);
+       return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, (u8 *) "",
+                                 0);
 }
 
 
@@ -587,7 +693,7 @@ static struct wpabuf * eap_aka_response_reauth(struct eap_aka_data *data,
        }
        wpa_printf(MSG_DEBUG, "   AT_MAC");
        eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
-       return eap_sim_msg_finish(msg, data->k_aut, nonce_s,
+       return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, nonce_s,
                                  EAP_SIM_NONCE_S_LEN);
 }
 
@@ -621,7 +727,7 @@ static struct wpabuf * eap_aka_response_notification(struct eap_aka_data *data,
                wpa_printf(MSG_DEBUG, "   AT_MAC");
                eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
        }
-       return eap_sim_msg_finish(msg, k_aut, (u8 *) "", 0);
+       return eap_sim_msg_finish(msg, data->eap_method, k_aut, (u8 *) "", 0);
 }
 
 
@@ -701,7 +807,7 @@ static struct wpabuf * eap_aka_prime_kdf_select(struct eap_aka_data *data,
                               EAP_AKA_SUBTYPE_CHALLENGE);
        wpa_printf(MSG_DEBUG, "   AT_KDF");
        eap_sim_msg_add(msg, EAP_SIM_AT_KDF, kdf, NULL, 0);
-       return eap_sim_msg_finish(msg, NULL, NULL, 0);
+       return eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
 }
 
 
@@ -861,6 +967,9 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm,
                wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication "
                           "failed (AUTN seq# -> AUTS)");
                return eap_aka_synchronization_failure(data, id);
+       } else if (res > 0) {
+               wpa_printf(MSG_DEBUG, "EAP-AKA: Wait for external USIM processing");
+               return NULL;
        } else if (res) {
                wpa_printf(MSG_WARNING, "EAP-AKA: UMTS authentication failed");
                return eap_aka_client_error(data, id,
@@ -931,7 +1040,7 @@ static struct wpabuf * eap_aka_process_challenge(struct eap_sm *sm,
        if (data->result_ind && attr->result_ind)
                data->use_result_ind = 1;
 
-       if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+       if (data->state != FAILURE) {
                eap_aka_state(data, data->use_result_ind ?
                              RESULT_SUCCESS : SUCCESS);
        }
@@ -1147,7 +1256,7 @@ static struct wpabuf * eap_aka_process_reauthentication(
        if (data->result_ind && attr->result_ind)
                data->use_result_ind = 1;
 
-       if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+       if (data->state != FAILURE) {
                eap_aka_state(data, data->use_result_ind ?
                              RESULT_SUCCESS : SUCCESS);
        }
@@ -1253,9 +1362,7 @@ done:
                 */
                ret->methodState = data->use_result_ind ?
                        METHOD_DONE : METHOD_MAY_CONT;
-       } else if (data->state == RESULT_FAILURE)
-               ret->methodState = METHOD_CONT;
-       else if (data->state == RESULT_SUCCESS)
+       } else if (data->state == RESULT_SUCCESS)
                ret->methodState = METHOD_CONT;
 
        if (ret->methodState == METHOD_DONE) {
@@ -1282,6 +1389,7 @@ static void eap_aka_deinit_for_reauth(struct eap_sm *sm, void *priv)
        data->id_msgs = NULL;
        data->use_result_ind = 0;
        data->kdf_negotiation = 0;
+       eap_aka_clear_keys(data, 1);
 }
 
 
index 42f525b..2b1a1d5 100644 (file)
@@ -157,7 +157,7 @@ struct eap_peer_config {
         *
         * If left out, this will be asked through control interface.
         */
-       u8 *private_key_passwd;
+       char *private_key_passwd;
 
        /**
         * dh_file - File path to DH/DSA parameters file (in PEM format)
@@ -186,6 +186,10 @@ struct eap_peer_config {
         * string is in following format:
         *
         * /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@n.example.com
+        *
+        * Note: Since this is a substring match, this cannot be used securily
+        * to do a suffix match against a possible domain name in the CN entry.
+        * For such a use case, domain_suffix_match should be used instead.
         */
        u8 *subject_match;
 
@@ -208,6 +212,39 @@ struct eap_peer_config {
        u8 *altsubject_match;
 
        /**
+        * domain_suffix_match - Constraint for server domain name
+        *
+        * If set, this FQDN is used as a suffix match requirement for the
+        * server certificate in SubjectAltName dNSName element(s). If a
+        * matching dNSName is found, this constraint is met. If no dNSName
+        * values are present, this constraint is matched against SubjectName CN
+        * using same suffix match comparison. Suffix match here means that the
+        * host/domain name is compared one label at a time starting from the
+        * top-level domain and all the labels in domain_suffix_match shall be
+        * included in the certificate. The certificate may include additional
+        * sub-level labels in addition to the required labels.
+        *
+        * For example, domain_suffix_match=example.com would match
+        * test.example.com but would not match test-example.com.
+        */
+       char *domain_suffix_match;
+
+       /**
+        * domain_match - Constraint for server domain name
+        *
+        * If set, this FQDN is used as a full match requirement for the
+        * server certificate in SubjectAltName dNSName element(s). If a
+        * matching dNSName is found, this constraint is met. If no dNSName
+        * values are present, this constraint is matched against SubjectName CN
+        * using same full match comparison. This behavior is similar to
+        * domain_suffix_match, but has the requirement of a full match, i.e.,
+        * no subdomains or wildcard matches are allowed. Case-insensitive
+        * comparison is used, so "Example.com" matches "example.com", but would
+        * not match "test.Example.com".
+        */
+       char *domain_match;
+
+       /**
         * ca_cert2 - File path to CA certificate file (PEM/DER) (Phase 2)
         *
         * This file can have one or more trusted CA certificates. If ca_cert2
@@ -271,7 +308,7 @@ struct eap_peer_config {
         * This field is like private_key_passwd, but used for phase 2 (inside
         * EAP-TTLS/PEAP/FAST tunnel) authentication.
         */
-       u8 *private_key2_passwd;
+       char *private_key2_passwd;
 
        /**
         * dh_file2 - File path to DH/DSA parameters file (in PEM format)
@@ -303,6 +340,22 @@ struct eap_peer_config {
        u8 *altsubject_match2;
 
        /**
+        * domain_suffix_match2 - Constraint for server domain name
+        *
+        * This field is like domain_suffix_match, but used for phase 2 (inside
+        * EAP-TTLS/PEAP/FAST tunnel) authentication.
+        */
+       char *domain_suffix_match2;
+
+       /**
+        * domain_match2 - Constraint for server domain name
+        *
+        * This field is like domain_match, but used for phase 2 (inside
+        * EAP-TTLS/PEAP/FAST tunnel) authentication.
+        */
+       char *domain_match2;
+
+       /**
         * eap_methods - Allowed EAP methods
         *
         * (vendor=EAP_VENDOR_IETF,method=EAP_TYPE_NONE) terminated list of
@@ -365,6 +418,16 @@ struct eap_peer_config {
         *
         * EAP-WSC (WPS) uses following options: pin=Device_Password and
         * uuid=Device_UUID
+        *
+        * For wired IEEE 802.1X authentication, "allow_canned_success=1" can be
+        * used to configure a mode that allows EAP-Success (and EAP-Failure)
+        * without going through authentication step. Some switches use such
+        * sequence when forcing the port to be authorized/unauthorized or as a
+        * fallback option if the authentication server is unreachable. By
+        * default, wpa_supplicant discards such frames to protect against
+        * potential attacks by rogue devices, but this option can be used to
+        * disable that protection for cases where the server/authenticator does
+        * not need to be authenticated.
         */
        char *phase1;
 
@@ -372,7 +435,9 @@ struct eap_peer_config {
         * phase2 - Phase2 (inner authentication with TLS tunnel) parameters
         *
         * String with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or
-        * "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS.
+        * "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS. "mschapv2_retry=0" can
+        * be used to disable MSCHAPv2 password retry in authentication failure
+        * cases.
         */
        char *phase2;
 
@@ -643,6 +708,37 @@ struct eap_peer_config {
         * 2 = require valid OCSP stapling response
         */
        int ocsp;
+
+       /**
+        * external_sim_resp - Response from external SIM processing
+        *
+        * This field should not be set in configuration step. It is only used
+        * internally when control interface is used to request external
+        * SIM/USIM processing.
+        */
+       char *external_sim_resp;
+
+       /**
+        * sim_num - User selected SIM identifier
+        *
+        * This variable is used for identifying which SIM is used if the system
+        * has more than one.
+        */
+       int sim_num;
+
+       /**
+        * openssl_ciphers - OpenSSL cipher string
+        *
+        * This is an OpenSSL specific configuration option for configuring the
+        * ciphers for this connection. If not set, the default cipher suite
+        * list is used.
+        */
+       char *openssl_ciphers;
+
+       /**
+        * erp - Whether EAP Re-authentication Protocol (ERP) is enabled
+        */
+       int erp;
 };
 
 
diff --git a/src/eap_peer/eap_eke.c b/src/eap_peer/eap_eke.c
new file mode 100644 (file)
index 0000000..9fec66c
--- /dev/null
@@ -0,0 +1,765 @@
+/*
+ * EAP peer method: EAP-EKE (RFC 6124)
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/random.h"
+#include "eap_peer/eap_i.h"
+#include "eap_common/eap_eke_common.h"
+
+struct eap_eke_data {
+       enum {
+               IDENTITY, COMMIT, CONFIRM, SUCCESS, FAILURE
+       } state;
+       u8 msk[EAP_MSK_LEN];
+       u8 emsk[EAP_EMSK_LEN];
+       u8 *peerid;
+       size_t peerid_len;
+       u8 *serverid;
+       size_t serverid_len;
+       u8 dh_priv[EAP_EKE_MAX_DH_LEN];
+       struct eap_eke_session sess;
+       u8 nonce_p[EAP_EKE_MAX_NONCE_LEN];
+       u8 nonce_s[EAP_EKE_MAX_NONCE_LEN];
+       struct wpabuf *msgs;
+       u8 dhgroup; /* forced DH group or 0 to allow all supported */
+       u8 encr; /* forced encryption algorithm or 0 to allow all supported */
+       u8 prf; /* forced PRF or 0 to allow all supported */
+       u8 mac; /* forced MAC or 0 to allow all supported */
+};
+
+
+static const char * eap_eke_state_txt(int state)
+{
+       switch (state) {
+       case IDENTITY:
+               return "IDENTITY";
+       case COMMIT:
+               return "COMMIT";
+       case CONFIRM:
+               return "CONFIRM";
+       case SUCCESS:
+               return "SUCCESS";
+       case FAILURE:
+               return "FAILURE";
+       default:
+               return "?";
+       }
+}
+
+
+static void eap_eke_state(struct eap_eke_data *data, int state)
+{
+       wpa_printf(MSG_DEBUG, "EAP-EKE: %s -> %s",
+                  eap_eke_state_txt(data->state), eap_eke_state_txt(state));
+       data->state = state;
+}
+
+
+static void eap_eke_deinit(struct eap_sm *sm, void *priv);
+
+
+static void * eap_eke_init(struct eap_sm *sm)
+{
+       struct eap_eke_data *data;
+       const u8 *identity, *password;
+       size_t identity_len, password_len;
+       const char *phase1;
+
+       password = eap_get_config_password(sm, &password_len);
+       if (!password) {
+               wpa_printf(MSG_INFO, "EAP-EKE: No password configured");
+               return NULL;
+       }
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       eap_eke_state(data, IDENTITY);
+
+       identity = eap_get_config_identity(sm, &identity_len);
+       if (identity) {
+               data->peerid = os_malloc(identity_len);
+               if (data->peerid == NULL) {
+                       eap_eke_deinit(sm, data);
+                       return NULL;
+               }
+               os_memcpy(data->peerid, identity, identity_len);
+               data->peerid_len = identity_len;
+       }
+
+       phase1 = eap_get_config_phase1(sm);
+       if (phase1) {
+               const char *pos;
+
+               pos = os_strstr(phase1, "dhgroup=");
+               if (pos) {
+                       data->dhgroup = atoi(pos + 8);
+                       wpa_printf(MSG_DEBUG, "EAP-EKE: Forced dhgroup %u",
+                                  data->dhgroup);
+               }
+
+               pos = os_strstr(phase1, "encr=");
+               if (pos) {
+                       data->encr = atoi(pos + 5);
+                       wpa_printf(MSG_DEBUG, "EAP-EKE: Forced encr %u",
+                                  data->encr);
+               }
+
+               pos = os_strstr(phase1, "prf=");
+               if (pos) {
+                       data->prf = atoi(pos + 4);
+                       wpa_printf(MSG_DEBUG, "EAP-EKE: Forced prf %u",
+                                  data->prf);
+               }
+
+               pos = os_strstr(phase1, "mac=");
+               if (pos) {
+                       data->mac = atoi(pos + 4);
+                       wpa_printf(MSG_DEBUG, "EAP-EKE: Forced mac %u",
+                                  data->mac);
+               }
+       }
+
+       return data;
+}
+
+
+static void eap_eke_deinit(struct eap_sm *sm, void *priv)
+{
+       struct eap_eke_data *data = priv;
+       eap_eke_session_clean(&data->sess);
+       os_free(data->serverid);
+       os_free(data->peerid);
+       wpabuf_free(data->msgs);
+       bin_clear_free(data, sizeof(*data));
+}
+
+
+static struct wpabuf * eap_eke_build_msg(struct eap_eke_data *data, int id,
+                                        size_t length, u8 eke_exch)
+{
+       struct wpabuf *msg;
+       size_t plen;
+
+       plen = 1 + length;
+
+       msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EKE, plen,
+                           EAP_CODE_RESPONSE, id);
+       if (msg == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-EKE: Failed to allocate memory");
+               return NULL;
+       }
+
+       wpabuf_put_u8(msg, eke_exch);
+
+       return msg;
+}
+
+
+static int eap_eke_supp_dhgroup(u8 dhgroup)
+{
+       return dhgroup == EAP_EKE_DHGROUP_EKE_2 ||
+               dhgroup == EAP_EKE_DHGROUP_EKE_5 ||
+               dhgroup == EAP_EKE_DHGROUP_EKE_14 ||
+               dhgroup == EAP_EKE_DHGROUP_EKE_15 ||
+               dhgroup == EAP_EKE_DHGROUP_EKE_16;
+}
+
+
+static int eap_eke_supp_encr(u8 encr)
+{
+       return encr == EAP_EKE_ENCR_AES128_CBC;
+}
+
+
+static int eap_eke_supp_prf(u8 prf)
+{
+       return prf == EAP_EKE_PRF_HMAC_SHA1 ||
+               prf == EAP_EKE_PRF_HMAC_SHA2_256;
+}
+
+
+static int eap_eke_supp_mac(u8 mac)
+{
+       return mac == EAP_EKE_MAC_HMAC_SHA1 ||
+               mac == EAP_EKE_MAC_HMAC_SHA2_256;
+}
+
+
+static struct wpabuf * eap_eke_build_fail(struct eap_eke_data *data,
+                                         struct eap_method_ret *ret,
+                                         const struct wpabuf *reqData,
+                                         u32 failure_code)
+{
+       struct wpabuf *resp;
+
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Failure/Response - code=0x%x",
+                  failure_code);
+
+       resp = eap_eke_build_msg(data, eap_get_id(reqData), 4, EAP_EKE_FAILURE);
+       if (resp)
+               wpabuf_put_be32(resp, failure_code);
+
+       os_memset(data->dh_priv, 0, sizeof(data->dh_priv));
+       eap_eke_session_clean(&data->sess);
+
+       eap_eke_state(data, FAILURE);
+       ret->methodState = METHOD_DONE;
+       ret->decision = DECISION_FAIL;
+       ret->allowNotifications = FALSE;
+
+       return resp;
+}
+
+
+static struct wpabuf * eap_eke_process_id(struct eap_eke_data *data,
+                                         struct eap_method_ret *ret,
+                                         const struct wpabuf *reqData,
+                                         const u8 *payload,
+                                         size_t payload_len)
+{
+       struct wpabuf *resp;
+       unsigned num_prop, i;
+       const u8 *pos, *end;
+       const u8 *prop = NULL;
+       u8 idtype;
+
+       if (data->state != IDENTITY) {
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PROTO_ERROR);
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-ID/Request");
+
+       if (payload_len < 2 + 4) {
+               wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data");
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PROTO_ERROR);
+       }
+
+       pos = payload;
+       end = payload + payload_len;
+
+       num_prop = *pos++;
+       pos++; /* Ignore Reserved field */
+
+       if (pos + num_prop * 4 > end) {
+               wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data (num_prop=%u)",
+                          num_prop);
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PROTO_ERROR);
+       }
+
+       for (i = 0; i < num_prop; i++) {
+               const u8 *tmp = pos;
+
+               wpa_printf(MSG_DEBUG, "EAP-EKE: Proposal #%u: dh=%u encr=%u prf=%u mac=%u",
+                          i, pos[0], pos[1], pos[2], pos[3]);
+               pos += 4;
+
+               if ((data->dhgroup && data->dhgroup != *tmp) ||
+                   !eap_eke_supp_dhgroup(*tmp))
+                       continue;
+               tmp++;
+               if ((data->encr && data->encr != *tmp) ||
+                   !eap_eke_supp_encr(*tmp))
+                       continue;
+               tmp++;
+               if ((data->prf && data->prf != *tmp) ||
+                   !eap_eke_supp_prf(*tmp))
+                       continue;
+               tmp++;
+               if ((data->mac && data->mac != *tmp) ||
+                   !eap_eke_supp_mac(*tmp))
+                       continue;
+
+               prop = tmp - 3;
+               if (eap_eke_session_init(&data->sess, prop[0], prop[1], prop[2],
+                                        prop[3]) < 0) {
+                       prop = NULL;
+                       continue;
+               }
+
+               wpa_printf(MSG_DEBUG, "EAP-EKE: Selected proposal");
+               break;
+       }
+
+       if (prop == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP-EKE: No acceptable proposal found");
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_NO_PROPOSAL_CHOSEN);
+       }
+
+       pos += (num_prop - i - 1) * 4;
+
+       if (pos == end) {
+               wpa_printf(MSG_DEBUG, "EAP-EKE: Too short ID/Request Data to include IDType/Identity");
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PROTO_ERROR);
+       }
+
+       idtype = *pos++;
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Server IDType %u", idtype);
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Server Identity",
+                         pos, end - pos);
+       os_free(data->serverid);
+       data->serverid = os_malloc(end - pos);
+       if (data->serverid == NULL) {
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+       }
+       os_memcpy(data->serverid, pos, end - pos);
+       data->serverid_len = end - pos;
+
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-ID/Response");
+
+       resp = eap_eke_build_msg(data, eap_get_id(reqData),
+                                2 + 4 + 1 + data->peerid_len,
+                                EAP_EKE_ID);
+       if (resp == NULL) {
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+       }
+
+       wpabuf_put_u8(resp, 1); /* NumProposals */
+       wpabuf_put_u8(resp, 0); /* Reserved */
+       wpabuf_put_data(resp, prop, 4); /* Selected Proposal */
+       wpabuf_put_u8(resp, EAP_EKE_ID_NAI);
+       if (data->peerid)
+               wpabuf_put_data(resp, data->peerid, data->peerid_len);
+
+       wpabuf_free(data->msgs);
+       data->msgs = wpabuf_alloc(wpabuf_len(reqData) + wpabuf_len(resp));
+       if (data->msgs == NULL) {
+               wpabuf_free(resp);
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+       }
+       wpabuf_put_buf(data->msgs, reqData);
+       wpabuf_put_buf(data->msgs, resp);
+
+       eap_eke_state(data, COMMIT);
+
+       return resp;
+}
+
+
+static struct wpabuf * eap_eke_process_commit(struct eap_sm *sm,
+                                             struct eap_eke_data *data,
+                                             struct eap_method_ret *ret,
+                                             const struct wpabuf *reqData,
+                                             const u8 *payload,
+                                             size_t payload_len)
+{
+       struct wpabuf *resp;
+       const u8 *pos, *end, *dhcomp;
+       size_t prot_len;
+       u8 *rpos;
+       u8 key[EAP_EKE_MAX_KEY_LEN];
+       u8 pub[EAP_EKE_MAX_DH_LEN];
+       const u8 *password;
+       size_t password_len;
+
+       if (data->state != COMMIT) {
+               wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Commit/Request received in unexpected state (%d)", data->state);
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PROTO_ERROR);
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Commit/Request");
+
+       password = eap_get_config_password(sm, &password_len);
+       if (password == NULL) {
+               wpa_printf(MSG_INFO, "EAP-EKE: No password configured!");
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PASSWD_NOT_FOUND);
+       }
+
+       pos = payload;
+       end = payload + payload_len;
+
+       if (pos + data->sess.dhcomp_len > end) {
+               wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit");
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PROTO_ERROR);
+       }
+
+       wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_S",
+                   pos, data->sess.dhcomp_len);
+       dhcomp = pos;
+       pos += data->sess.dhcomp_len;
+       wpa_hexdump(MSG_DEBUG, "EAP-EKE: CBValue", pos, end - pos);
+
+       /*
+        * temp = prf(0+, password)
+        * key = prf+(temp, ID_S | ID_P)
+        */
+       if (eap_eke_derive_key(&data->sess, password, password_len,
+                              data->serverid, data->serverid_len,
+                              data->peerid, data->peerid_len, key) < 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key");
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+       }
+
+       /*
+        * y_p = g ^ x_p (mod p)
+        * x_p = random number 2 .. p-1
+        */
+       if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH");
+               os_memset(key, 0, sizeof(key));
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+       }
+
+       if (eap_eke_shared_secret(&data->sess, key, data->dh_priv, dhcomp) < 0)
+       {
+               wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret");
+               os_memset(key, 0, sizeof(key));
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+       }
+
+       if (eap_eke_derive_ke_ki(&data->sess,
+                                data->serverid, data->serverid_len,
+                                data->peerid, data->peerid_len) < 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki");
+               os_memset(key, 0, sizeof(key));
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Commit/Response");
+
+       resp = eap_eke_build_msg(data, eap_get_id(reqData),
+                                data->sess.dhcomp_len + data->sess.pnonce_len,
+                                EAP_EKE_COMMIT);
+       if (resp == NULL) {
+               os_memset(key, 0, sizeof(key));
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+       }
+
+       /* DHComponent_P = Encr(key, y_p) */
+       rpos = wpabuf_put(resp, data->sess.dhcomp_len);
+       if (eap_eke_dhcomp(&data->sess, key, pub, rpos) < 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_P");
+               os_memset(key, 0, sizeof(key));
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+       }
+       os_memset(key, 0, sizeof(key));
+
+       wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_P",
+                   rpos, data->sess.dhcomp_len);
+
+       if (random_get_bytes(data->nonce_p, data->sess.nonce_len)) {
+               wpabuf_free(resp);
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+       }
+       wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P",
+                       data->nonce_p, data->sess.nonce_len);
+       prot_len = wpabuf_tailroom(resp);
+       if (eap_eke_prot(&data->sess, data->nonce_p, data->sess.nonce_len,
+                        wpabuf_put(resp, 0), &prot_len) < 0) {
+               wpabuf_free(resp);
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+       }
+       wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P",
+                   wpabuf_put(resp, 0), prot_len);
+       wpabuf_put(resp, prot_len);
+
+       /* TODO: CBValue */
+
+       if (wpabuf_resize(&data->msgs, wpabuf_len(reqData) + wpabuf_len(resp))
+           < 0) {
+               wpabuf_free(resp);
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+       }
+       wpabuf_put_buf(data->msgs, reqData);
+       wpabuf_put_buf(data->msgs, resp);
+
+       eap_eke_state(data, CONFIRM);
+
+       return resp;
+}
+
+
+static struct wpabuf * eap_eke_process_confirm(struct eap_eke_data *data,
+                                              struct eap_method_ret *ret,
+                                              const struct wpabuf *reqData,
+                                              const u8 *payload,
+                                              size_t payload_len)
+{
+       struct wpabuf *resp;
+       const u8 *pos, *end;
+       size_t prot_len;
+       u8 nonces[2 * EAP_EKE_MAX_NONCE_LEN];
+       u8 auth_s[EAP_EKE_MAX_HASH_LEN];
+       size_t decrypt_len;
+       u8 *auth;
+
+       if (data->state != CONFIRM) {
+               wpa_printf(MSG_DEBUG, "EAP-EKE: EAP-EKE-Confirm/Request received in unexpected state (%d)",
+                          data->state);
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PROTO_ERROR);
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Confirm/Request");
+
+       pos = payload;
+       end = payload + payload_len;
+
+       if (pos + data->sess.pnonce_ps_len + data->sess.prf_len > end) {
+               wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Confirm");
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PROTO_ERROR);
+       }
+
+       decrypt_len = sizeof(nonces);
+       if (eap_eke_decrypt_prot(&data->sess, pos, data->sess.pnonce_ps_len,
+                                nonces, &decrypt_len) < 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_PS");
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_AUTHENTICATION_FAIL);
+       }
+       if (decrypt_len != (size_t) 2 * data->sess.nonce_len) {
+               wpa_printf(MSG_INFO, "EAP-EKE: PNonce_PS protected data length does not match length of Nonce_P and Nonce_S");
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_AUTHENTICATION_FAIL);
+       }
+       wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_P | Nonce_S",
+                       nonces, 2 * data->sess.nonce_len);
+       if (os_memcmp(data->nonce_p, nonces, data->sess.nonce_len) != 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_P does not match transmitted Nonce_P");
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_AUTHENTICATION_FAIL);
+       }
+
+       os_memcpy(data->nonce_s, nonces + data->sess.nonce_len,
+                 data->sess.nonce_len);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_S",
+                       data->nonce_s, data->sess.nonce_len);
+
+       if (eap_eke_derive_ka(&data->sess, data->serverid, data->serverid_len,
+                             data->peerid, data->peerid_len,
+                             data->nonce_p, data->nonce_s) < 0) {
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+       }
+
+       if (eap_eke_auth(&data->sess, "EAP-EKE server", data->msgs, auth_s) < 0)
+       {
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+       }
+       wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth_s, data->sess.prf_len);
+       if (os_memcmp_const(auth_s, pos + data->sess.pnonce_ps_len,
+                           data->sess.prf_len) != 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Auth_S does not match");
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_AUTHENTICATION_FAIL);
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Sending EAP-EKE-Confirm/Response");
+
+       resp = eap_eke_build_msg(data, eap_get_id(reqData),
+                                data->sess.pnonce_len + data->sess.prf_len,
+                                EAP_EKE_CONFIRM);
+       if (resp == NULL) {
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+       }
+
+       prot_len = wpabuf_tailroom(resp);
+       if (eap_eke_prot(&data->sess, data->nonce_s, data->sess.nonce_len,
+                        wpabuf_put(resp, 0), &prot_len) < 0) {
+               wpabuf_free(resp);
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+       }
+       wpabuf_put(resp, prot_len);
+
+       auth = wpabuf_put(resp, data->sess.prf_len);
+       if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth) < 0) {
+               wpabuf_free(resp);
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+       }
+       wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth, data->sess.prf_len);
+
+       if (eap_eke_derive_msk(&data->sess, data->serverid, data->serverid_len,
+                              data->peerid, data->peerid_len,
+                              data->nonce_s, data->nonce_p,
+                              data->msk, data->emsk) < 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive MSK/EMSK");
+               wpabuf_free(resp);
+               return eap_eke_build_fail(data, ret, reqData,
+                                         EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+       }
+
+       os_memset(data->dh_priv, 0, sizeof(data->dh_priv));
+       eap_eke_session_clean(&data->sess);
+
+       eap_eke_state(data, SUCCESS);
+       ret->methodState = METHOD_MAY_CONT;
+       ret->decision = DECISION_COND_SUCC;
+       ret->allowNotifications = FALSE;
+
+       return resp;
+}
+
+
+static struct wpabuf * eap_eke_process_failure(struct eap_eke_data *data,
+                                              struct eap_method_ret *ret,
+                                              const struct wpabuf *reqData,
+                                              const u8 *payload,
+                                              size_t payload_len)
+{
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Received EAP-EKE-Failure/Request");
+
+       if (payload_len < 4) {
+               wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Failure");
+       } else {
+               u32 code;
+               code = WPA_GET_BE32(payload);
+               wpa_printf(MSG_INFO, "EAP-EKE: Failure-Code 0x%x", code);
+       }
+
+       return eap_eke_build_fail(data, ret, reqData, EAP_EKE_FAIL_NO_ERROR);
+}
+
+
+static struct wpabuf * eap_eke_process(struct eap_sm *sm, void *priv,
+                                      struct eap_method_ret *ret,
+                                      const struct wpabuf *reqData)
+{
+       struct eap_eke_data *data = priv;
+       struct wpabuf *resp;
+       const u8 *pos, *end;
+       size_t len;
+       u8 eke_exch;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, reqData, &len);
+       if (pos == NULL || len < 1) {
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       end = pos + len;
+       eke_exch = *pos++;
+
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Received frame: exch %d", eke_exch);
+       wpa_hexdump(MSG_DEBUG, "EAP-EKE: Received Data", pos, end - pos);
+
+       ret->ignore = FALSE;
+       ret->methodState = METHOD_MAY_CONT;
+       ret->decision = DECISION_FAIL;
+       ret->allowNotifications = TRUE;
+
+       switch (eke_exch) {
+       case EAP_EKE_ID:
+               resp = eap_eke_process_id(data, ret, reqData, pos, end - pos);
+               break;
+       case EAP_EKE_COMMIT:
+               resp = eap_eke_process_commit(sm, data, ret, reqData,
+                                             pos, end - pos);
+               break;
+       case EAP_EKE_CONFIRM:
+               resp = eap_eke_process_confirm(data, ret, reqData,
+                                              pos, end - pos);
+               break;
+       case EAP_EKE_FAILURE:
+               resp = eap_eke_process_failure(data, ret, reqData,
+                                              pos, end - pos);
+               break;
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-EKE: Ignoring message with unknown EKE-Exch %d", eke_exch);
+               ret->ignore = TRUE;
+               return NULL;
+       }
+
+       if (ret->methodState == METHOD_DONE)
+               ret->allowNotifications = FALSE;
+
+       return resp;
+}
+
+
+static Boolean eap_eke_isKeyAvailable(struct eap_sm *sm, void *priv)
+{
+       struct eap_eke_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+static u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_eke_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_MSK_LEN);
+       if (key == NULL)
+               return NULL;
+       os_memcpy(key, data->msk, EAP_MSK_LEN);
+       *len = EAP_MSK_LEN;
+
+       return key;
+}
+
+
+static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_eke_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_EMSK_LEN);
+       if (key == NULL)
+               return NULL;
+       os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+       *len = EAP_EMSK_LEN;
+
+       return key;
+}
+
+
+int eap_peer_eke_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_eke_init;
+       eap->deinit = eap_eke_deinit;
+       eap->process = eap_eke_process;
+       eap->isKeyAvailable = eap_eke_isKeyAvailable;
+       eap->getKey = eap_eke_getKey;
+       eap->get_emsk = eap_eke_get_emsk;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
index 3b8d803..68d7fba 100644 (file)
@@ -149,14 +149,16 @@ static void * eap_fast_init(struct eap_sm *sm)
        struct eap_fast_data *data;
        struct eap_peer_config *config = eap_get_config(sm);
 
+       if (config == NULL)
+               return NULL;
+
        data = os_zalloc(sizeof(*data));
        if (data == NULL)
                return NULL;
        data->fast_version = EAP_FAST_VERSION;
        data->max_pac_list_len = 10;
 
-       if (config && config->phase1 &&
-           eap_fast_parse_phase1(data, config->phase1) < 0) {
+       if (config->phase1 && eap_fast_parse_phase1(data, config->phase1) < 0) {
                eap_fast_deinit(sm, data);
                return NULL;
        }
@@ -196,14 +198,22 @@ static void * eap_fast_init(struct eap_sm *sm)
                           "workarounds");
        }
 
+       if (!config->pac_file) {
+               wpa_printf(MSG_INFO, "EAP-FAST: No PAC file configured");
+               eap_fast_deinit(sm, data);
+               return NULL;
+       }
+
        if (data->use_pac_binary_format &&
            eap_fast_load_pac_bin(sm, &data->pac, config->pac_file) < 0) {
+               wpa_printf(MSG_INFO, "EAP-FAST: Failed to load PAC file");
                eap_fast_deinit(sm, data);
                return NULL;
        }
 
        if (!data->use_pac_binary_format &&
            eap_fast_load_pac(sm, &data->pac, config->pac_file) < 0) {
+               wpa_printf(MSG_INFO, "EAP-FAST: Failed to load PAC file");
                eap_fast_deinit(sm, data);
                return NULL;
        }
@@ -240,6 +250,8 @@ static void eap_fast_deinit(struct eap_sm *sm, void *priv)
                pac = pac->next;
                eap_fast_free_pac(prev);
        }
+       os_memset(data->key_data, 0, EAP_FAST_KEY_LEN);
+       os_memset(data->emsk, 0, EAP_EMSK_LEN);
        os_free(data->session_id);
        wpabuf_free(data->pending_phase2_req);
        os_free(data);
@@ -757,7 +769,7 @@ static struct wpabuf * eap_fast_process_crypto_binding(
                    "MAC calculation", (u8 *) _bind, bind_len);
        hmac_sha1(cmk, EAP_FAST_CMK_LEN, (u8 *) _bind, bind_len,
                  _bind->compound_mac);
-       res = os_memcmp(cmac, _bind->compound_mac, sizeof(cmac));
+       res = os_memcmp_const(cmac, _bind->compound_mac, sizeof(cmac));
        wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Received Compound MAC",
                    cmac, sizeof(cmac));
        wpa_hexdump(MSG_MSGDUMP, "EAP-FAST: Calculated Compound MAC",
@@ -1047,6 +1059,7 @@ static struct wpabuf * eap_fast_process_pac(struct eap_sm *sm,
                }
                wpa_printf(MSG_DEBUG, "EAP-FAST: Send PAC-Acknowledgement TLV "
                           "- Provisioning completed successfully");
+               sm->expected_failure = 1;
        } else {
                /*
                 * This is PAC refreshing, i.e., normal authentication that is
@@ -1069,7 +1082,8 @@ static int eap_fast_parse_decrypted(struct wpabuf *decrypted,
                                    struct eap_fast_tlv_parse *tlv,
                                    struct wpabuf **resp)
 {
-       int mandatory, tlv_type, len, res;
+       int mandatory, tlv_type, res;
+       size_t len;
        u8 *pos, *end;
 
        os_memset(tlv, 0, sizeof(*tlv));
@@ -1083,13 +1097,14 @@ static int eap_fast_parse_decrypted(struct wpabuf *decrypted,
                pos += 2;
                len = WPA_GET_BE16(pos);
                pos += 2;
-               if (pos + len > end) {
+               if (len > (size_t) (end - pos)) {
                        wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow");
                        return -1;
                }
                wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: "
-                          "TLV type %d length %d%s",
-                          tlv_type, len, mandatory ? " (mandatory)" : "");
+                          "TLV type %d length %u%s",
+                          tlv_type, (unsigned int) len,
+                          mandatory ? " (mandatory)" : "");
 
                res = eap_fast_parse_tlv(tlv, tlv_type, pos, len);
                if (res == -2)
@@ -1244,6 +1259,7 @@ static int eap_fast_process_decrypted(struct eap_sm *sm,
                                   "provisioning completed successfully.");
                        ret->methodState = METHOD_DONE;
                        ret->decision = DECISION_FAIL;
+                       sm->expected_failure = 1;
                } else {
                        wpa_printf(MSG_DEBUG, "EAP-FAST: Authentication "
                                   "completed successfully.");
@@ -1622,6 +1638,8 @@ static void * eap_fast_init_for_reauth(struct eap_sm *sm, void *priv)
                os_free(data);
                return NULL;
        }
+       os_memset(data->key_data, 0, EAP_FAST_KEY_LEN);
+       os_memset(data->emsk, 0, EAP_EMSK_LEN);
        os_free(data->session_id);
        data->session_id = NULL;
        if (data->phase2_priv && data->phase2_method &&
@@ -1648,7 +1666,7 @@ static int eap_fast_get_status(struct eap_sm *sm, void *priv, char *buf,
                ret = os_snprintf(buf + len, buflen - len,
                                  "EAP-FAST Phase2 method=%s\n",
                                  data->phase2_method->name);
-               if (ret < 0 || (size_t) ret >= buflen - len)
+               if (os_snprintf_error(buflen - len, ret))
                        return len;
                len += ret;
        }
index 8c480b9..89e604e 100644 (file)
@@ -330,6 +330,8 @@ static const char * eap_fast_parse_end(struct eap_fast_pac **pac_root,
 static const char * eap_fast_parse_pac_type(struct eap_fast_pac *pac,
                                            char *pos)
 {
+       if (!pos)
+               return "Cannot parse pac type";
        pac->pac_type = atoi(pos);
        if (pac->pac_type != PAC_TYPE_TUNNEL_PAC &&
            pac->pac_type != PAC_TYPE_USER_AUTHORIZATION &&
@@ -502,28 +504,28 @@ static void eap_fast_write(char **buf, char **pos, size_t *buf_len,
        end = *buf + *buf_len;
 
        ret = os_snprintf(*pos, end - *pos, "%s=", field);
-       if (ret < 0 || ret >= end - *pos)
+       if (os_snprintf_error(end - *pos, ret))
                return;
        *pos += ret;
        *pos += wpa_snprintf_hex(*pos, end - *pos, data, len);
        ret = os_snprintf(*pos, end - *pos, "\n");
-       if (ret < 0 || ret >= end - *pos)
+       if (os_snprintf_error(end - *pos, ret))
                return;
        *pos += ret;
 
        if (txt) {
                ret = os_snprintf(*pos, end - *pos, "%s-txt=", field);
-               if (ret < 0 || ret >= end - *pos)
+               if (os_snprintf_error(end - *pos, ret))
                        return;
                *pos += ret;
                for (i = 0; i < len; i++) {
                        ret = os_snprintf(*pos, end - *pos, "%c", data[i]);
-                       if (ret < 0 || ret >= end - *pos)
+                       if (os_snprintf_error(end - *pos, ret))
                                return;
                        *pos += ret;
                }
                ret = os_snprintf(*pos, end - *pos, "\n");
-               if (ret < 0 || ret >= end - *pos)
+               if (os_snprintf_error(end - *pos, ret))
                        return;
                *pos += ret;
        }
@@ -576,7 +578,7 @@ static int eap_fast_add_pac_data(struct eap_fast_pac *pac, char **buf,
 
        ret = os_snprintf(*pos, *buf + *buf_len - *pos,
                          "START\nPAC-Type=%d\n", pac->pac_type);
-       if (ret < 0 || ret >= *buf + *buf_len - *pos)
+       if (os_snprintf_error(*buf + *buf_len - *pos, ret))
                return -1;
 
        *pos += ret;
@@ -598,7 +600,7 @@ static int eap_fast_add_pac_data(struct eap_fast_pac *pac, char **buf,
                return -1;
        }
        ret = os_snprintf(*pos, *buf + *buf_len - *pos, "END\n");
-       if (ret < 0 || ret >= *buf + *buf_len - *pos)
+       if (os_snprintf_error(*buf + *buf_len - *pos, ret))
                return -1;
        *pos += ret;
 
@@ -630,7 +632,7 @@ int eap_fast_save_pac(struct eap_sm *sm, struct eap_fast_pac *pac_root,
                return -1;
 
        ret = os_snprintf(pos, buf + buf_len - pos, "%s\n", pac_file_hdr);
-       if (ret < 0 || ret >= buf + buf_len - pos) {
+       if (os_snprintf_error(buf + buf_len - pos, ret)) {
                os_free(buf);
                return -1;
        }
@@ -712,7 +714,7 @@ static void eap_fast_pac_get_a_id(struct eap_fast_pac *pac)
                pos += 2;
                len = WPA_GET_BE16(pos);
                pos += 2;
-               if (pos + len > end)
+               if (len > (unsigned int) (end - pos))
                        break;
 
                if (type == PAC_TYPE_A_ID) {
@@ -797,7 +799,9 @@ int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
        pos = buf + 6;
        end = buf + len;
        while (pos < end) {
-               if (end - pos < 2 + 32 + 2 + 2)
+               u16 val;
+
+               if (end - pos < 2 + EAP_FAST_PAC_KEY_LEN + 2 + 2)
                        goto parse_fail;
 
                pac = os_zalloc(sizeof(*pac));
@@ -808,19 +812,23 @@ int eap_fast_load_pac_bin(struct eap_sm *sm, struct eap_fast_pac **pac_root,
                pos += 2;
                os_memcpy(pac->pac_key, pos, EAP_FAST_PAC_KEY_LEN);
                pos += EAP_FAST_PAC_KEY_LEN;
-               pac->pac_opaque_len = WPA_GET_BE16(pos);
+               val = WPA_GET_BE16(pos);
                pos += 2;
-               if (pos + pac->pac_opaque_len + 2 > end)
+               if (val > end - pos)
                        goto parse_fail;
+               pac->pac_opaque_len = val;
                pac->pac_opaque = os_malloc(pac->pac_opaque_len);
                if (pac->pac_opaque == NULL)
                        goto parse_fail;
                os_memcpy(pac->pac_opaque, pos, pac->pac_opaque_len);
                pos += pac->pac_opaque_len;
-               pac->pac_info_len = WPA_GET_BE16(pos);
+               if (2 > end - pos)
+                       goto parse_fail;
+               val = WPA_GET_BE16(pos);
                pos += 2;
-               if (pos + pac->pac_info_len > end)
+               if (val > end - pos)
                        goto parse_fail;
+               pac->pac_info_len = val;
                pac->pac_info = os_malloc(pac->pac_info_len);
                if (pac->pac_info == NULL)
                        goto parse_fail;
index 8a0644d..c54bf11 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: EAP-GPSK (RFC 5433)
- * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -33,6 +33,7 @@ struct eap_gpsk_data {
        int specifier; /* CSuite/Specifier */
        u8 *psk;
        size_t psk_len;
+       u16 forced_cipher; /* force cipher or 0 to allow all supported */
 };
 
 
@@ -80,6 +81,7 @@ static void * eap_gpsk_init(struct eap_sm *sm)
        struct eap_gpsk_data *data;
        const u8 *identity, *password;
        size_t identity_len, password_len;
+       const char *phase1;
 
        password = eap_get_config_password(sm, &password_len);
        if (password == NULL) {
@@ -103,6 +105,18 @@ static void * eap_gpsk_init(struct eap_sm *sm)
                data->id_peer_len = identity_len;
        }
 
+       phase1 = eap_get_config_phase1(sm);
+       if (phase1) {
+               const char *pos;
+
+               pos = os_strstr(phase1, "cipher=");
+               if (pos) {
+                       data->forced_cipher = atoi(pos + 7);
+                       wpa_printf(MSG_DEBUG, "EAP-GPSK: Forced cipher %u",
+                                  data->forced_cipher);
+               }
+       }
+
        data->psk = os_malloc(password_len);
        if (data->psk == NULL) {
                eap_gpsk_deinit(sm, data);
@@ -120,8 +134,11 @@ static void eap_gpsk_deinit(struct eap_sm *sm, void *priv)
        struct eap_gpsk_data *data = priv;
        os_free(data->id_server);
        os_free(data->id_peer);
-       os_free(data->psk);
-       os_free(data);
+       if (data->psk) {
+               os_memset(data->psk, 0, data->psk_len);
+               os_free(data->psk);
+       }
+       bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -195,7 +212,9 @@ static int eap_gpsk_select_csuite(struct eap_sm *sm,
                           i, vendor, specifier);
                if (data->vendor == EAP_GPSK_VENDOR_IETF &&
                    data->specifier == EAP_GPSK_CIPHER_RESERVED &&
-                   eap_gpsk_supported_ciphersuite(vendor, specifier)) {
+                   eap_gpsk_supported_ciphersuite(vendor, specifier) &&
+                   (!data->forced_cipher || data->forced_cipher == specifier))
+               {
                        data->vendor = vendor;
                        data->specifier = specifier;
                }
@@ -220,6 +239,8 @@ static const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm,
                                               size_t *list_len,
                                               const u8 *pos, const u8 *end)
 {
+       size_t len;
+
        if (pos == NULL)
                return NULL;
 
@@ -227,23 +248,25 @@ static const u8 * eap_gpsk_process_csuite_list(struct eap_sm *sm,
                wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short GPSK-1 packet");
                return NULL;
        }
-       *list_len = WPA_GET_BE16(pos);
+       len = WPA_GET_BE16(pos);
        pos += 2;
-       if (end - pos < (int) *list_len) {
+       if (len > (size_t) (end - pos)) {
                wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List overflow");
                return NULL;
        }
-       if (*list_len == 0 || (*list_len % sizeof(struct eap_gpsk_csuite))) {
+       if (len == 0 || (len % sizeof(struct eap_gpsk_csuite))) {
                wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid CSuite_List len %lu",
-                          (unsigned long) *list_len);
+                          (unsigned long) len);
                return NULL;
        }
-       *list = pos;
-       pos += *list_len;
 
-       if (eap_gpsk_select_csuite(sm, data, *list, *list_len) < 0)
+       if (eap_gpsk_select_csuite(sm, data, pos, len) < 0)
                return NULL;
 
+       *list = pos;
+       *list_len = len;
+       pos += len;
+
        return pos;
 }
 
@@ -273,6 +296,7 @@ static struct wpabuf * eap_gpsk_process_gpsk_1(struct eap_sm *sm,
        pos = eap_gpsk_process_csuite_list(sm, data, &csuite_list,
                                           &csuite_list_len, pos, end);
        if (pos == NULL) {
+               ret->methodState = METHOD_DONE;
                eap_gpsk_state(data, FAILURE);
                return NULL;
        }
@@ -544,7 +568,7 @@ static const u8 * eap_gpsk_validate_gpsk_3_mic(struct eap_gpsk_data *data,
                wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC");
                return NULL;
        }
-       if (os_memcmp(mic, pos, miclen) != 0) {
+       if (os_memcmp_const(mic, pos, miclen) != 0) {
                wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-3");
                wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen);
                wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen);
index 62c867c..2d7fdea 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * EAP peer state machines internal structures (RFC 4137)
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +10,7 @@
 #define EAP_I_H
 
 #include "wpabuf.h"
+#include "utils/list.h"
 #include "eap_peer/eap.h"
 #include "eap_common/eap_common.h"
 
@@ -277,6 +278,16 @@ struct eap_method {
 };
 
 
+struct eap_erp_key {
+       struct dl_list list;
+       size_t rRK_len;
+       size_t rIK_len;
+       u8 rRK[ERP_MAX_KEY_LEN];
+       u8 rIK[ERP_MAX_KEY_LEN];
+       u32 next_seq;
+       char keyname_nai[];
+};
+
 /**
  * struct eap_sm - EAP state machine data
  */
@@ -321,6 +332,8 @@ struct eap_sm {
        void *eap_method_priv;
        int init_phase2;
        int fast_reauth;
+       Boolean reauthInit; /* send EAP-Identity/Re-auth */
+       u32 erp_seq;
 
        Boolean rxResp /* LEAP only */;
        Boolean leap_done;
@@ -345,9 +358,16 @@ struct eap_sm {
        struct wps_context *wps;
 
        int prev_failure;
+       struct eap_peer_config *last_config;
 
        struct ext_password_data *ext_pw;
        struct wpabuf *ext_pw_buf;
+
+       int external_sim;
+
+       unsigned int expected_failure:1;
+
+       struct dl_list erp_keys; /* struct eap_erp_key */
 };
 
 const u8 * eap_get_config_identity(struct eap_sm *sm, size_t *len);
index 09a655e..b5ef71b 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * EAP-IKEv2 peer (RFC 5106)
- * Copyright (c) 2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2007-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -60,6 +60,7 @@ static void * eap_ikev2_init(struct eap_sm *sm)
        struct eap_ikev2_data *data;
        const u8 *identity, *password;
        size_t identity_len, password_len;
+       int fragment_size;
 
        identity = eap_get_config_identity(sm, &identity_len);
        if (identity == NULL) {
@@ -71,7 +72,11 @@ static void * eap_ikev2_init(struct eap_sm *sm)
        if (data == NULL)
                return NULL;
        data->state = WAIT_START;
-       data->fragment_size = IKEV2_FRAGMENT_SIZE;
+       fragment_size = eap_get_config_fragment_size(sm);
+       if (fragment_size <= 0)
+               data->fragment_size = IKEV2_FRAGMENT_SIZE;
+       else
+               data->fragment_size = fragment_size;
        data->ikev2.state = SA_INIT;
        data->ikev2.peer_auth = PEER_AUTH_SECRET;
        data->ikev2.key_pad = (u8 *) os_strdup("Key Pad for EAP-IKEv2");
@@ -108,7 +113,7 @@ static void eap_ikev2_deinit(struct eap_sm *sm, void *priv)
        wpabuf_free(data->in_buf);
        wpabuf_free(data->out_buf);
        ikev2_responder_deinit(&data->ikev2);
-       os_free(data);
+       bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -149,12 +154,6 @@ static struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data,
                        send_len -= 4;
                }
        }
-#ifdef CCNS_PL
-       /* Some issues figuring out the length of the message if Message Length
-        * field not included?! */
-       if (!(flags & IKEV2_FLAGS_LENGTH_INCLUDED))
-               flags |= IKEV2_FLAGS_LENGTH_INCLUDED;
-#endif /* CCNS_PL */
 
        plen = 1 + send_len;
        if (flags & IKEV2_FLAGS_LENGTH_INCLUDED)
@@ -246,7 +245,8 @@ static struct wpabuf * eap_ikev2_build_msg(struct eap_ikev2_data *data,
 
 static int eap_ikev2_process_icv(struct eap_ikev2_data *data,
                                 const struct wpabuf *reqData,
-                                u8 flags, const u8 *pos, const u8 **end)
+                                u8 flags, const u8 *pos, const u8 **end,
+                                int frag_ack)
 {
        if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
                int icv_len = eap_ikev2_validate_icv(
@@ -256,7 +256,7 @@ static int eap_ikev2_process_icv(struct eap_ikev2_data *data,
                        return -1;
                /* Hide Integrity Checksum Data from further processing */
                *end -= icv_len;
-       } else if (data->keys_ready) {
+       } else if (data->keys_ready && !frag_ack) {
                wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have "
                           "included integrity checksum");
                return -1;
@@ -301,6 +301,13 @@ static struct wpabuf * eap_ikev2_process_fragment(struct eap_ikev2_data *data,
 
        if (data->in_buf == NULL) {
                /* First fragment of the message */
+               if (message_length > 50000) {
+                       /* Limit maximum memory allocation */
+                       wpa_printf(MSG_DEBUG,
+                                  "EAP-IKEV2: Ignore too long message");
+                       ret->ignore = TRUE;
+                       return NULL;
+               }
                data->in_buf = wpabuf_alloc(message_length);
                if (data->in_buf == NULL) {
                        wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for "
@@ -315,6 +322,7 @@ static struct wpabuf * eap_ikev2_process_fragment(struct eap_ikev2_data *data,
                           (unsigned long) wpabuf_tailroom(data->in_buf));
        }
 
+       ret->ignore = FALSE;
        return eap_ikev2_build_frag_ack(id, EAP_CODE_RESPONSE);
 }
 
@@ -346,7 +354,9 @@ static struct wpabuf * eap_ikev2_process(struct eap_sm *sm, void *priv,
        else
                flags = *pos++;
 
-       if (eap_ikev2_process_icv(data, reqData, flags, pos, &end) < 0) {
+       if (eap_ikev2_process_icv(data, reqData, flags, pos, &end,
+                                 data->state == WAIT_FRAG_ACK && len == 0) < 0)
+       {
                ret->ignore = TRUE;
                return NULL;
        }
@@ -373,12 +383,7 @@ static struct wpabuf * eap_ikev2_process(struct eap_sm *sm, void *priv,
                   "Message Length %u", flags, message_length);
 
        if (data->state == WAIT_FRAG_ACK) {
-#ifdef CCNS_PL
-               if (len > 1) /* Empty Flags field included in ACK */
-#else /* CCNS_PL */
-               if (len != 0)
-#endif /* CCNS_PL */
-               {
+               if (len != 0) {
                        wpa_printf(MSG_DEBUG, "EAP-IKEV2: Unexpected payload "
                                   "in WAIT_FRAG_ACK state");
                        ret->ignore = TRUE;
index df34013..e0f8bcf 100644 (file)
@@ -244,7 +244,7 @@ static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv,
        ret->methodState = METHOD_DONE;
        ret->allowNotifications = FALSE;
 
-       if (os_memcmp(pos, expected, LEAP_RESPONSE_LEN) != 0) {
+       if (os_memcmp_const(pos, expected, LEAP_RESPONSE_LEN) != 0) {
                wpa_printf(MSG_WARNING, "EAP-LEAP: AP sent an invalid "
                           "response - authentication failed");
                wpa_hexdump(MSG_DEBUG, "EAP-LEAP: Expected response from AP",
@@ -383,6 +383,9 @@ static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len)
        wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: master key", key, LEAP_KEY_LEN);
        *len = LEAP_KEY_LEN;
 
+       os_memset(pw_hash, 0, sizeof(pw_hash));
+       os_memset(pw_hash_hash, 0, sizeof(pw_hash_hash));
+
        return key;
 }
 
index 83a1457..1bdd81e 100644 (file)
@@ -103,7 +103,7 @@ size_t eap_get_names(char *buf, size_t buflen)
        for (m = eap_methods; m; m = m->next) {
                ret = os_snprintf(pos, end - pos, "%s%s",
                                  m == eap_methods ? "" : " ", m->name);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        break;
                pos += ret;
        }
@@ -133,7 +133,7 @@ char ** eap_get_names_as_string_array(size_t *num)
        for (m = eap_methods; m; m = m->next)
                array_len++;
 
-       array = os_zalloc(sizeof(char *) * (array_len + 1));
+       array = os_calloc(array_len + 1, sizeof(char *));
        if (array == NULL)
                return NULL;
 
index 4994ff1..e35c919 100644 (file)
@@ -86,6 +86,7 @@ static inline int eap_peer_method_unload(struct eap_method *method)
 int eap_peer_md5_register(void);
 int eap_peer_tls_register(void);
 int eap_peer_unauth_tls_register(void);
+int eap_peer_wfa_unauth_tls_register(void);
 int eap_peer_mschapv2_register(void);
 int eap_peer_peap_register(void);
 int eap_peer_ttls_register(void);
@@ -105,5 +106,6 @@ int eap_peer_ikev2_register(void);
 int eap_peer_vendor_test_register(void);
 int eap_peer_tnc_register(void);
 int eap_peer_pwd_register(void);
+int eap_peer_eke_register(void);
 
 #endif /* EAP_METHODS_H */
index f9aa742..9e486e7 100644 (file)
@@ -140,7 +140,7 @@ static void eap_mschapv2_deinit(struct eap_sm *sm, void *priv)
        os_free(data->peer_challenge);
        os_free(data->auth_challenge);
        wpabuf_free(data->prev_challenge);
-       os_free(data);
+       bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -303,18 +303,23 @@ static void eap_mschapv2_password_changed(struct eap_sm *sm,
                        WPA_EVENT_PASSWORD_CHANGED
                        "EAP-MSCHAPV2: Password changed successfully");
                data->prev_error = 0;
-               os_free(config->password);
+               bin_clear_free(config->password, config->password_len);
                if (config->flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
                        /* TODO: update external storage */
                } else if (config->flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH) {
                        config->password = os_malloc(16);
                        config->password_len = 16;
-                       if (config->password) {
-                               nt_password_hash(config->new_password,
-                                                config->new_password_len,
-                                                config->password);
+                       if (config->password &&
+                           nt_password_hash(config->new_password,
+                                            config->new_password_len,
+                                            config->password)) {
+                               bin_clear_free(config->password,
+                                              config->password_len);
+                               config->password = NULL;
+                               config->password_len = 0;
                        }
-                       os_free(config->new_password);
+                       bin_clear_free(config->new_password,
+                                      config->new_password_len);
                } else {
                        config->password = config->new_password;
                        config->password_len = config->new_password_len;
@@ -467,6 +472,13 @@ static int eap_mschapv2_failure_txt(struct eap_sm *sm,
                pos += 2;
                msg = pos;
        }
+       if (data->prev_error == ERROR_AUTHENTICATION_FAILURE && retry &&
+           config && config->phase2 &&
+           os_strstr(config->phase2, "mschapv2_retry=0")) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP-MSCHAPV2: mark password retry disabled based on local configuration");
+               retry = 0;
+       }
        wpa_msg(sm->msg_ctx, MSG_WARNING,
                "EAP-MSCHAPV2: failure message: '%s' (retry %sallowed, error "
                "%d)",
@@ -549,15 +561,17 @@ static struct wpabuf * eap_mschapv2_change_password(
        /* Encrypted-Hash */
        if (pwhash) {
                u8 new_password_hash[16];
-               nt_password_hash(new_password, new_password_len,
-                                new_password_hash);
+               if (nt_password_hash(new_password, new_password_len,
+                                    new_password_hash))
+                       goto fail;
                nt_password_hash_encrypted_with_block(password,
                                                      new_password_hash,
                                                      cp->encr_hash);
        } else {
-               old_nt_password_hash_encrypted_with_new_nt_password_hash(
-                       new_password, new_password_len,
-                       password, password_len, cp->encr_hash);
+               if (old_nt_password_hash_encrypted_with_new_nt_password_hash(
+                           new_password, new_password_len,
+                           password, password_len, cp->encr_hash))
+                       goto fail;
        }
 
        /* Peer-Challenge */
@@ -594,9 +608,13 @@ static struct wpabuf * eap_mschapv2_change_password(
 
        /* Likewise, generate master_key here since we have the needed data
         * available. */
-       nt_password_hash(new_password, new_password_len, password_hash);
-       hash_nt_password_hash(password_hash, password_hash_hash);
-       get_master_key(password_hash_hash, cp->nt_response, data->master_key);
+       if (nt_password_hash(new_password, new_password_len, password_hash) ||
+           hash_nt_password_hash(password_hash, password_hash_hash) ||
+           get_master_key(password_hash_hash, cp->nt_response,
+                          data->master_key)) {
+               data->auth_response_valid = 0;
+               goto fail;
+       }
        data->master_key_valid = 1;
 
        /* Flags */
index 7f87052..6d1ff20 100644 (file)
@@ -38,6 +38,7 @@ struct eap_pax_data {
        u8 mk[EAP_PAX_MK_LEN];
        u8 ck[EAP_PAX_CK_LEN];
        u8 ick[EAP_PAX_ICK_LEN];
+       u8 mid[EAP_PAX_MID_LEN];
 };
 
 
@@ -86,7 +87,7 @@ static void eap_pax_deinit(struct eap_sm *sm, void *priv)
 {
        struct eap_pax_data *data = priv;
        os_free(data->cid);
-       os_free(data);
+       bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -178,8 +179,8 @@ static struct wpabuf * eap_pax_process_std_1(struct eap_pax_data *data,
                    data->rand.r.y, EAP_PAX_RAND_LEN);
 
        if (eap_pax_initial_key_derivation(req->mac_id, data->ak, data->rand.e,
-                                          data->mk, data->ck, data->ick) < 0)
-       {
+                                          data->mk, data->ck, data->ick,
+                                          data->mid) < 0) {
                ret->ignore = TRUE;
                return NULL;
        }
@@ -278,7 +279,7 @@ static struct wpabuf * eap_pax_process_std_3(struct eap_pax_data *data,
        eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
                    data->rand.r.y, EAP_PAX_RAND_LEN,
                    (u8 *) data->cid, data->cid_len, NULL, 0, mac);
-       if (os_memcmp(pos, mac, EAP_PAX_MAC_LEN) != 0) {
+       if (os_memcmp_const(pos, mac, EAP_PAX_MAC_LEN) != 0) {
                wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(B, CID) "
                           "received");
                wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected MAC_CK(B, CID)",
@@ -415,7 +416,7 @@ static struct wpabuf * eap_pax_process(struct eap_sm *sm, void *priv,
                            wpabuf_head(reqData), mlen, NULL, 0, NULL, 0,
                            icvbuf);
        }
-       if (os_memcmp(icv, icvbuf, EAP_PAX_ICV_LEN) != 0) {
+       if (os_memcmp_const(icv, icvbuf, EAP_PAX_ICV_LEN) != 0) {
                wpa_printf(MSG_DEBUG, "EAP-PAX: invalid ICV - ignoring the "
                           "message");
                wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: expected ICV",
@@ -501,6 +502,26 @@ static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
 }
 
 
+static u8 * eap_pax_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_pax_data *data = priv;
+       u8 *sid;
+
+       if (data->state != PAX_DONE)
+               return NULL;
+
+       sid = os_malloc(1 + EAP_PAX_MID_LEN);
+       if (sid == NULL)
+               return NULL;
+
+       *len = 1 + EAP_PAX_MID_LEN;
+       sid[0] = EAP_TYPE_PAX;
+       os_memcpy(sid + 1, data->mid, EAP_PAX_MID_LEN);
+
+       return sid;
+}
+
+
 int eap_peer_pax_register(void)
 {
        struct eap_method *eap;
@@ -517,6 +538,7 @@ int eap_peer_pax_register(void)
        eap->isKeyAvailable = eap_pax_isKeyAvailable;
        eap->getKey = eap_pax_getKey;
        eap->get_emsk = eap_pax_get_emsk;
+       eap->getSessionId = eap_pax_get_session_id;
 
        ret = eap_peer_method_register(eap);
        if (ret)
index 3b93209..86a18bb 100644 (file)
@@ -22,7 +22,6 @@
 /* Maximum supported PEAP version
  * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt
  * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt
- * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt
  */
 #define EAP_PEAP_VERSION 1
 
@@ -171,6 +170,15 @@ static void * eap_peap_init(struct eap_sm *sm)
 }
 
 
+static void eap_peap_free_key(struct eap_peap_data *data)
+{
+       if (data->key_data) {
+               bin_clear_free(data->key_data, EAP_TLS_KEY_LEN);
+               data->key_data = NULL;
+       }
+}
+
+
 static void eap_peap_deinit(struct eap_sm *sm, void *priv)
 {
        struct eap_peap_data *data = priv;
@@ -180,7 +188,7 @@ static void eap_peap_deinit(struct eap_sm *sm, void *priv)
                data->phase2_method->deinit(sm, data->phase2_priv);
        os_free(data->phase2_types);
        eap_peer_tls_ssl_deinit(sm, &data->ssl);
-       os_free(data->key_data);
+       eap_peap_free_key(data);
        os_free(data->session_id);
        wpabuf_free(data->pending_phase2_req);
        os_free(data);
@@ -315,8 +323,6 @@ static int eap_tlv_add_cryptobinding(struct eap_sm *sm,
        len[1] = 1;
 
        tlv_type = EAP_TLV_CRYPTO_BINDING_TLV;
-       if (data->peap_version >= 2)
-               tlv_type |= EAP_TLV_TYPE_MANDATORY;
        wpabuf_put_be16(buf, tlv_type);
        wpabuf_put_be16(buf, 56);
 
@@ -426,7 +432,7 @@ static int eap_tlv_validate_cryptobinding(struct eap_sm *sm,
                    buf, sizeof(buf));
        hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac);
 
-       if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) {
+       if (os_memcmp_const(mac, pos, SHA1_MAC_LEN) != 0) {
                wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in "
                           "cryptobinding TLV");
                wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Received MAC",
@@ -580,33 +586,6 @@ static int eap_tlv_process(struct eap_sm *sm, struct eap_peap_data *data,
 }
 
 
-static struct wpabuf * eap_peapv2_tlv_eap_payload(struct wpabuf *buf)
-{
-       struct wpabuf *e;
-       struct eap_tlv_hdr *tlv;
-
-       if (buf == NULL)
-               return NULL;
-
-       /* Encapsulate EAP packet in EAP-Payload TLV */
-       wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV");
-       e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf));
-       if (e == NULL) {
-               wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory "
-                          "for TLV encapsulation");
-               wpabuf_free(buf);
-               return NULL;
-       }
-       tlv = wpabuf_put(e, sizeof(*tlv));
-       tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
-                                    EAP_TLV_EAP_PAYLOAD_TLV);
-       tlv->length = host_to_be16(wpabuf_len(buf));
-       wpabuf_put_buf(e, buf);
-       wpabuf_free(buf);
-       return e;
-}
-
-
 static int eap_peap_phase2_request(struct eap_sm *sm,
                                   struct eap_peap_data *data,
                                   struct eap_method_ret *ret,
@@ -837,49 +816,6 @@ continue_req:
                in_decrypted = nmsg;
        }
 
-       if (data->peap_version >= 2) {
-               struct eap_tlv_hdr *tlv;
-               struct wpabuf *nmsg;
-
-               if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) {
-                       wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 "
-                                  "EAP TLV");
-                       wpabuf_free(in_decrypted);
-                       return 0;
-               }
-               tlv = wpabuf_mhead(in_decrypted);
-               if ((be_to_host16(tlv->tlv_type) & 0x3fff) !=
-                   EAP_TLV_EAP_PAYLOAD_TLV) {
-                       wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV");
-                       wpabuf_free(in_decrypted);
-                       return 0;
-               }
-               if (sizeof(*tlv) + be_to_host16(tlv->length) >
-                   wpabuf_len(in_decrypted)) {
-                       wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV "
-                                  "length");
-                       wpabuf_free(in_decrypted);
-                       return 0;
-               }
-               hdr = (struct eap_hdr *) (tlv + 1);
-               if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) {
-                       wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full "
-                                  "EAP packet in EAP TLV");
-                       wpabuf_free(in_decrypted);
-                       return 0;
-               }
-
-               nmsg = wpabuf_alloc(be_to_host16(hdr->length));
-               if (nmsg == NULL) {
-                       wpabuf_free(in_decrypted);
-                       return 0;
-               }
-
-               wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length));
-               wpabuf_free(in_decrypted);
-               in_decrypted = nmsg;
-       }
-
        hdr = wpabuf_mhead(in_decrypted);
        if (wpabuf_len(in_decrypted) < sizeof(*hdr)) {
                wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 "
@@ -996,11 +932,6 @@ continue_req:
                wpa_hexdump_buf_key(MSG_DEBUG,
                                    "EAP-PEAP: Encrypting Phase 2 data", resp);
                /* PEAP version changes */
-               if (data->peap_version >= 2) {
-                       resp = eap_peapv2_tlv_eap_payload(resp);
-                       if (resp == NULL)
-                               return -1;
-               }
                if (wpabuf_len(resp) >= 5 &&
                    wpabuf_head_u8(resp)[0] == EAP_CODE_RESPONSE &&
                    eap_get_type(resp) == EAP_TYPE_TLV)
@@ -1083,7 +1014,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv,
                        char *label;
                        wpa_printf(MSG_DEBUG,
                                   "EAP-PEAP: TLS done, proceed to Phase 2");
-                       os_free(data->key_data);
+                       eap_peap_free_key(data);
                        /* draft-josefsson-ppext-eap-tls-eap-05.txt
                         * specifies that PEAPv1 would use "client PEAP
                         * encryption" as the label. However, most existing
@@ -1091,7 +1022,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv,
                         * label, "client EAP encryption", instead. Use the old
                         * label by default, but allow it to be configured with
                         * phase1 parameter peaplabel=1. */
-                       if (data->peap_version > 1 || data->force_new_label)
+                       if (data->force_new_label)
                                label = "client PEAP encryption";
                        else
                                label = "client EAP encryption";
@@ -1193,8 +1124,7 @@ static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv)
 static void * eap_peap_init_for_reauth(struct eap_sm *sm, void *priv)
 {
        struct eap_peap_data *data = priv;
-       os_free(data->key_data);
-       data->key_data = NULL;
+       eap_peap_free_key(data);
        os_free(data->session_id);
        data->session_id = NULL;
        if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
@@ -1226,7 +1156,7 @@ static int eap_peap_get_status(struct eap_sm *sm, void *priv, char *buf,
                                  "EAP-PEAPv%d Phase2 method=%s\n",
                                  data->peap_version,
                                  data->phase2_method->name);
-               if (ret < 0 || (size_t) ret >= buflen - len)
+               if (os_snprintf_error(buflen - len, ret))
                        return len;
                len += ret;
        }
index 3b4dcef..23cdbe6 100644 (file)
@@ -40,7 +40,8 @@ eap_proxy_packet_update(struct eap_proxy_sm *eap_proxy, u8 *eapReqData,
 int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen,
                            int verbose);
 
-int eap_proxy_get_imsi(char *imsi_buf, size_t *imsi_len);
+int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf,
+                      size_t *imsi_len);
 
 int eap_proxy_notify_config(struct eap_proxy_sm *sm,
                            struct eap_peer_config *config);
index cd97fb6..d84f012 100644 (file)
@@ -63,7 +63,8 @@ int eap_proxy_sm_get_status(struct eap_proxy_sm *sm, char *buf, size_t buflen,
 }
 
 
-int eap_proxy_get_imsi(char *imsi_buf, size_t *imsi_len)
+int eap_proxy_get_imsi(struct eap_proxy_sm *eap_proxy, char *imsi_buf,
+                      size_t *imsi_len)
 {
        return -1;
 }
index cd0e3f9..f012663 100644 (file)
@@ -76,7 +76,7 @@ static void eap_psk_deinit(struct eap_sm *sm, void *priv)
        struct eap_psk_data *data = priv;
        os_free(data->id_s);
        os_free(data->id_p);
-       os_free(data);
+       bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -237,7 +237,7 @@ static struct wpabuf * eap_psk_process_3(struct eap_psk_data *data,
                return NULL;
        }
        os_free(buf);
-       if (os_memcmp(mac, hdr3->mac_s, EAP_PSK_MAC_LEN) != 0) {
+       if (os_memcmp_const(mac, hdr3->mac_s, EAP_PSK_MAC_LEN) != 0) {
                wpa_printf(MSG_WARNING, "EAP-PSK: Invalid MAC_S in third "
                           "message");
                ret->methodState = METHOD_DONE;
index 267d0a5..059bbee 100644 (file)
@@ -16,7 +16,8 @@
 
 struct eap_pwd_data {
        enum {
-               PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req, SUCCESS, FAILURE
+               PWD_ID_Req, PWD_Commit_Req, PWD_Confirm_Req,
+               SUCCESS_ON_FRAG_COMPLETION, SUCCESS, FAILURE
        } state;
        u8 *id_peer;
        size_t id_peer_len;
@@ -42,6 +43,7 @@ struct eap_pwd_data {
 
        u8 msk[EAP_MSK_LEN];
        u8 emsk[EAP_EMSK_LEN];
+       u8 session_id[1 + SHA256_MAC_LEN];
 
        BN_CTX *bnctx;
 };
@@ -57,6 +59,8 @@ static const char * eap_pwd_state_txt(int state)
                return "PWD-Commit-Req";
         case PWD_Confirm_Req:
                return "PWD-Confirm-Req";
+       case SUCCESS_ON_FRAG_COMPLETION:
+               return "SUCCESS_ON_FRAG_COMPLETION";
         case SUCCESS:
                return "SUCCESS";
         case FAILURE:
@@ -81,6 +85,7 @@ static void * eap_pwd_init(struct eap_sm *sm)
        struct eap_pwd_data *data;
        const u8 *identity, *password;
        size_t identity_len, password_len;
+       int fragment_size;
 
        password = eap_get_config_password(sm, &password_len);
        if (password == NULL) {
@@ -118,7 +123,7 @@ static void * eap_pwd_init(struct eap_sm *sm)
        if ((data->password = os_malloc(password_len)) == NULL) {
                wpa_printf(MSG_INFO, "EAP-PWD: memory allocation psk fail");
                BN_CTX_free(data->bnctx);
-               os_free(data->id_peer);
+               bin_clear_free(data->id_peer, data->id_peer_len);
                os_free(data);
                return NULL;
        }
@@ -127,7 +132,11 @@ static void * eap_pwd_init(struct eap_sm *sm)
 
        data->out_frag_pos = data->in_frag_pos = 0;
        data->inbuf = data->outbuf = NULL;
-       data->mtu = 1020; /* default from RFC 5931, make it configurable! */
+       fragment_size = eap_get_config_fragment_size(sm);
+       if (fragment_size <= 0)
+               data->mtu = 1020; /* default from RFC 5931 */
+       else
+               data->mtu = fragment_size;
 
        data->state = PWD_ID_Req;
 
@@ -139,24 +148,26 @@ static void eap_pwd_deinit(struct eap_sm *sm, void *priv)
 {
        struct eap_pwd_data *data = priv;
 
-       BN_free(data->private_value);
-       BN_free(data->server_scalar);
-       BN_free(data->my_scalar);
-       BN_free(data->k);
+       BN_clear_free(data->private_value);
+       BN_clear_free(data->server_scalar);
+       BN_clear_free(data->my_scalar);
+       BN_clear_free(data->k);
        BN_CTX_free(data->bnctx);
-       EC_POINT_free(data->my_element);
-       EC_POINT_free(data->server_element);
-       os_free(data->id_peer);
-       os_free(data->id_server);
-       os_free(data->password);
+       EC_POINT_clear_free(data->my_element);
+       EC_POINT_clear_free(data->server_element);
+       bin_clear_free(data->id_peer, data->id_peer_len);
+       bin_clear_free(data->id_server, data->id_server_len);
+       bin_clear_free(data->password, data->password_len);
        if (data->grp) {
                EC_GROUP_free(data->grp->group);
-               EC_POINT_free(data->grp->pwe);
-               BN_free(data->grp->order);
-               BN_free(data->grp->prime);
+               EC_POINT_clear_free(data->grp->pwe);
+               BN_clear_free(data->grp->order);
+               BN_clear_free(data->grp->prime);
                os_free(data->grp);
        }
-       os_free(data);
+       wpabuf_free(data->inbuf);
+       wpabuf_free(data->outbuf);
+       bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -179,6 +190,25 @@ static u8 * eap_pwd_getkey(struct eap_sm *sm, void *priv, size_t *len)
 }
 
 
+static u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_pwd_data *data = priv;
+       u8 *id;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       id = os_malloc(1 + SHA256_MAC_LEN);
+       if (id == NULL)
+               return NULL;
+
+       os_memcpy(id, data->session_id, 1 + SHA256_MAC_LEN);
+       *len = 1 + SHA256_MAC_LEN;
+
+       return id;
+}
+
+
 static void
 eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
                            struct eap_method_ret *ret,
@@ -222,8 +252,8 @@ eap_pwd_perform_id_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
        wpa_hexdump_ascii(MSG_INFO, "EAP-PWD (peer): server sent id of",
                          data->id_server, data->id_server_len);
 
-       if ((data->grp = (EAP_PWD_group *) os_malloc(sizeof(EAP_PWD_group))) ==
-           NULL) {
+       data->grp = os_zalloc(sizeof(EAP_PWD_group));
+       if (data->grp == NULL) {
                wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
                           "group");
                eap_pwd_state(data, FAILURE);
@@ -287,11 +317,15 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
                goto fin;
        }
 
-       BN_rand_range(data->private_value, data->grp->order);
-       BN_rand_range(mask, data->grp->order);
-       BN_add(data->my_scalar, data->private_value, mask);
-       BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
-              data->bnctx);
+       if (BN_rand_range(data->private_value, data->grp->order) != 1 ||
+           BN_rand_range(mask, data->grp->order) != 1 ||
+           BN_add(data->my_scalar, data->private_value, mask) != 1 ||
+           BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
+                  data->bnctx) != 1) {
+               wpa_printf(MSG_INFO,
+                          "EAP-pwd (peer): unable to get randomness");
+               goto fin;
+       }
 
        if (!EC_POINT_mul(data->grp->group, data->my_element, NULL,
                          data->grp->pwe, mask, data->bnctx)) {
@@ -306,7 +340,7 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
                wpa_printf(MSG_INFO, "EAP-PWD (peer): element inversion fail");
                goto fin;
        }
-       BN_free(mask);
+       BN_clear_free(mask);
 
        if (((x = BN_new()) == NULL) ||
            ((y = BN_new()) == NULL)) {
@@ -441,11 +475,11 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
 fin:
        os_free(scalar);
        os_free(element);
-       BN_free(x);
-       BN_free(y);
-       BN_free(cofactor);
-       EC_POINT_free(K);
-       EC_POINT_free(point);
+       BN_clear_free(x);
+       BN_clear_free(y);
+       BN_clear_free(cofactor);
+       EC_POINT_clear_free(K);
+       EC_POINT_clear_free(point);
        if (data->outbuf == NULL)
                eap_pwd_state(data, FAILURE);
        else
@@ -559,7 +593,7 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
        eap_pwd_h_final(hash, conf);
 
        ptr = (u8 *) payload;
-       if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) {
+       if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) {
                wpa_printf(MSG_INFO, "EAP-PWD (peer): confirm did not verify");
                goto fin;
        }
@@ -637,7 +671,7 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
 
        if (compute_keys(data->grp, data->bnctx, data->k,
                         data->my_scalar, data->server_scalar, conf, ptr,
-                        &cs, data->msk, data->emsk) < 0) {
+                        &cs, data->msk, data->emsk, data->session_id) < 0) {
                wpa_printf(MSG_INFO, "EAP-PWD (peer): unable to compute MSK | "
                           "EMSK");
                goto fin;
@@ -650,16 +684,15 @@ eap_pwd_perform_confirm_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
        wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
 
 fin:
-       os_free(cruft);
-       BN_free(x);
-       BN_free(y);
-       ret->methodState = METHOD_DONE;
+       bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
+       BN_clear_free(x);
+       BN_clear_free(y);
        if (data->outbuf == NULL) {
+               ret->methodState = METHOD_DONE;
                ret->decision = DECISION_FAIL;
                eap_pwd_state(data, FAILURE);
        } else {
-               ret->decision = DECISION_UNCOND_SUCC;
-               eap_pwd_state(data, SUCCESS);
+               eap_pwd_state(data, SUCCESS_ON_FRAG_COMPLETION);
        }
 }
 
@@ -736,6 +769,11 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret,
                wpa_printf(MSG_DEBUG, "EAP-pwd: Send %s fragment of %d bytes",
                           data->out_frag_pos == 0 ? "last" : "next",
                           (int) len);
+               if (data->state == SUCCESS_ON_FRAG_COMPLETION) {
+                       ret->methodState = METHOD_DONE;
+                       ret->decision = DECISION_UNCOND_SUCC;
+                       eap_pwd_state(data, SUCCESS);
+               }
                return resp;
        }
 
@@ -748,6 +786,8 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret,
                tot_len = WPA_GET_BE16(pos);
                wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments whose "
                           "total length = %d", tot_len);
+               if (tot_len > 15000)
+                       return NULL;
                data->inbuf = wpabuf_alloc(tot_len);
                if (data->inbuf == NULL) {
                        wpa_printf(MSG_INFO, "Out of memory to buffer "
@@ -768,6 +808,7 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret,
                                   (int) data->in_frag_pos,
                                   (int) wpabuf_len(data->inbuf));
                        wpabuf_free(data->inbuf);
+                       data->inbuf = NULL;
                        data->in_frag_pos = 0;
                        return NULL;
                }
@@ -819,11 +860,15 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret,
         */
        if (data->in_frag_pos) {
                wpabuf_free(data->inbuf);
+               data->inbuf = NULL;
                data->in_frag_pos = 0;
        }
 
-       if (data->outbuf == NULL)
+       if (data->outbuf == NULL) {
+               ret->methodState = METHOD_DONE;
+               ret->decision = DECISION_FAIL;
                return NULL;        /* generic failure */
+       }
 
        /*
         * we have output! Do we need to fragment it?
@@ -866,6 +911,11 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret,
                wpabuf_free(data->outbuf);
                data->outbuf = NULL;
                data->out_frag_pos = 0;
+               if (data->state == SUCCESS_ON_FRAG_COMPLETION) {
+                       ret->methodState = METHOD_DONE;
+                       ret->decision = DECISION_UNCOND_SUCC;
+                       eap_pwd_state(data, SUCCESS);
+               }
        }
 
        return resp;
@@ -902,7 +952,6 @@ int eap_peer_pwd_register(void)
        struct eap_method *eap;
        int ret;
 
-       EVP_add_digest(EVP_sha256());
        eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
                                    EAP_VENDOR_IETF, EAP_TYPE_PWD, "PWD");
        if (eap == NULL)
@@ -913,6 +962,7 @@ int eap_peer_pwd_register(void)
        eap->process = eap_pwd_process;
        eap->isKeyAvailable = eap_pwd_key_available;
        eap->getKey = eap_pwd_getkey;
+       eap->getSessionId = eap_pwd_get_session_id;
        eap->get_emsk = eap_pwd_get_emsk;
 
        ret = eap_peer_method_register(eap);
index 431519c..7d14907 100644 (file)
@@ -108,7 +108,7 @@ static void eap_sake_deinit(struct eap_sm *sm, void *priv)
        struct eap_sake_data *data = priv;
        os_free(data->serverid);
        os_free(data->peerid);
-       os_free(data);
+       bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -315,7 +315,7 @@ static struct wpabuf * eap_sake_process_confirm(struct eap_sm *sm,
                             data->peerid, data->peerid_len, 0,
                             wpabuf_head(reqData), wpabuf_len(reqData),
                             attr.mic_s, mic_s);
-       if (os_memcmp(attr.mic_s, mic_s, EAP_SAKE_MIC_LEN) != 0) {
+       if (os_memcmp_const(attr.mic_s, mic_s, EAP_SAKE_MIC_LEN) != 0) {
                wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_S");
                eap_sake_state(data, FAILURE);
                ret->methodState = METHOD_DONE;
index 82ea18d..bd06df7 100644 (file)
@@ -43,7 +43,7 @@ struct eap_sim_data {
        u8 *last_eap_identity;
        size_t last_eap_identity_len;
        enum {
-               CONTINUE, RESULT_SUCCESS, RESULT_FAILURE, SUCCESS, FAILURE
+               CONTINUE, RESULT_SUCCESS, SUCCESS, FAILURE
        } state;
        int result_ind, use_result_ind;
 };
@@ -57,8 +57,6 @@ static const char * eap_sim_state_txt(int state)
                return "CONTINUE";
        case RESULT_SUCCESS:
                return "RESULT_SUCCESS";
-       case RESULT_FAILURE:
-               return "RESULT_FAILURE";
        case SUCCESS:
                return "SUCCESS";
        case FAILURE:
@@ -132,6 +130,20 @@ static void * eap_sim_init(struct eap_sm *sm)
 }
 
 
+static void eap_sim_clear_keys(struct eap_sim_data *data, int reauth)
+{
+       if (!reauth) {
+               os_memset(data->mk, 0, EAP_SIM_MK_LEN);
+               os_memset(data->k_aut, 0, EAP_SIM_K_AUT_LEN);
+               os_memset(data->k_encr, 0, EAP_SIM_K_ENCR_LEN);
+       }
+       os_memset(data->kc, 0, 3 * EAP_SIM_KC_LEN);
+       os_memset(data->sres, 0, 3 * EAP_SIM_SRES_LEN);
+       os_memset(data->msk, 0, EAP_SIM_KEYING_DATA_LEN);
+       os_memset(data->emsk, 0, EAP_EMSK_LEN);
+}
+
+
 static void eap_sim_deinit(struct eap_sm *sm, void *priv)
 {
        struct eap_sim_data *data = priv;
@@ -140,11 +152,86 @@ static void eap_sim_deinit(struct eap_sm *sm, void *priv)
                os_free(data->pseudonym);
                os_free(data->reauth_id);
                os_free(data->last_eap_identity);
+               eap_sim_clear_keys(data, 0);
                os_free(data);
        }
 }
 
 
+static int eap_sim_ext_sim_req(struct eap_sm *sm, struct eap_sim_data *data)
+{
+       char req[200], *pos, *end;
+       size_t i;
+
+       wpa_printf(MSG_DEBUG, "EAP-SIM: Use external SIM processing");
+       pos = req;
+       end = pos + sizeof(req);
+       pos += os_snprintf(pos, end - pos, "GSM-AUTH");
+       for (i = 0; i < data->num_chal; i++) {
+               pos += os_snprintf(pos, end - pos, ":");
+               pos += wpa_snprintf_hex(pos, end - pos, data->rand[i],
+                                       GSM_RAND_LEN);
+       }
+
+       eap_sm_request_sim(sm, req);
+       return 1;
+}
+
+
+static int eap_sim_ext_sim_result(struct eap_sm *sm, struct eap_sim_data *data,
+                                 struct eap_peer_config *conf)
+{
+       char *resp, *pos;
+       size_t i;
+
+       wpa_printf(MSG_DEBUG,
+                  "EAP-SIM: Use result from external SIM processing");
+
+       resp = conf->external_sim_resp;
+       conf->external_sim_resp = NULL;
+
+       if (os_strncmp(resp, "GSM-AUTH:", 9) != 0) {
+               wpa_printf(MSG_DEBUG, "EAP-SIM: Unrecognized external SIM processing response");
+               os_free(resp);
+               return -1;
+       }
+
+       pos = resp + 9;
+       for (i = 0; i < data->num_chal; i++) {
+               wpa_hexdump(MSG_DEBUG, "EAP-SIM: RAND",
+                           data->rand[i], GSM_RAND_LEN);
+
+               if (hexstr2bin(pos, data->kc[i], EAP_SIM_KC_LEN) < 0)
+                       goto invalid;
+               wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Kc",
+                               data->kc[i], EAP_SIM_KC_LEN);
+               pos += EAP_SIM_KC_LEN * 2;
+               if (*pos != ':')
+                       goto invalid;
+               pos++;
+
+               if (hexstr2bin(pos, data->sres[i], EAP_SIM_SRES_LEN) < 0)
+                       goto invalid;
+               wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: SRES",
+                               data->sres[i], EAP_SIM_SRES_LEN);
+               pos += EAP_SIM_SRES_LEN * 2;
+               if (i + 1 < data->num_chal) {
+                       if (*pos != ':')
+                               goto invalid;
+                       pos++;
+               }
+       }
+
+       os_free(resp);
+       return 0;
+
+invalid:
+       wpa_printf(MSG_DEBUG, "EAP-SIM: Invalid external SIM processing GSM-AUTH response");
+       os_free(resp);
+       return -1;
+}
+
+
 static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data)
 {
        struct eap_peer_config *conf;
@@ -154,6 +241,14 @@ static int eap_sim_gsm_auth(struct eap_sm *sm, struct eap_sim_data *data)
        conf = eap_get_config(sm);
        if (conf == NULL)
                return -1;
+
+       if (sm->external_sim) {
+               if (conf->external_sim_resp)
+                       return eap_sim_ext_sim_result(sm, data, conf);
+               else
+                       return eap_sim_ext_sim_req(sm, data);
+       }
+
        if (conf->pcsc) {
                if (scard_gsm_auth(sm->scard_ctx, data->rand[0],
                                   data->sres[0], data->kc[0]) ||
@@ -369,7 +464,7 @@ static struct wpabuf * eap_sim_client_error(struct eap_sim_data *data, u8 id,
        msg = eap_sim_msg_init(EAP_CODE_RESPONSE, id, EAP_TYPE_SIM,
                               EAP_SIM_SUBTYPE_CLIENT_ERROR);
        eap_sim_msg_add(msg, EAP_SIM_AT_CLIENT_ERROR_CODE, err, NULL, 0);
-       return eap_sim_msg_finish(msg, NULL, NULL, 0);
+       return eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0);
 }
 
 
@@ -422,7 +517,7 @@ static struct wpabuf * eap_sim_response_start(struct eap_sm *sm,
                                identity, identity_len);
        }
 
-       return eap_sim_msg_finish(msg, NULL, NULL, 0);
+       return eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0);
 }
 
 
@@ -440,7 +535,8 @@ static struct wpabuf * eap_sim_response_challenge(struct eap_sim_data *data,
        }
        wpa_printf(MSG_DEBUG, "   AT_MAC");
        eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
-       return eap_sim_msg_finish(msg, data->k_aut, (u8 *) data->sres,
+       return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut,
+                                 (u8 *) data->sres,
                                  data->num_chal * EAP_SIM_SRES_LEN);
 }
 
@@ -482,7 +578,7 @@ static struct wpabuf * eap_sim_response_reauth(struct eap_sim_data *data,
        }
        wpa_printf(MSG_DEBUG, "   AT_MAC");
        eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
-       return eap_sim_msg_finish(msg, data->k_aut, nonce_s,
+       return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, nonce_s,
                                  EAP_SIM_NONCE_S_LEN);
 }
 
@@ -516,7 +612,7 @@ static struct wpabuf * eap_sim_response_notification(struct eap_sim_data *data,
                wpa_printf(MSG_DEBUG, "   AT_MAC");
                eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
        }
-       return eap_sim_msg_finish(msg, k_aut, (u8 *) "", 0);
+       return eap_sim_msg_finish(msg, EAP_TYPE_SIM, k_aut, (u8 *) "", 0);
 }
 
 
@@ -605,6 +701,7 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm,
        const u8 *identity;
        size_t identity_len;
        struct eap_sim_attrs eattr;
+       int res;
 
        wpa_printf(MSG_DEBUG, "EAP-SIM: subtype Challenge");
        data->reauth = 0;
@@ -648,8 +745,13 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm,
 
        os_memcpy(data->rand, attr->rand, attr->num_chal * GSM_RAND_LEN);
        data->num_chal = attr->num_chal;
-               
-       if (eap_sim_gsm_auth(sm, data)) {
+
+       res = eap_sim_gsm_auth(sm, data);
+       if (res > 0) {
+               wpa_printf(MSG_DEBUG, "EAP-SIM: Wait for external SIM processing");
+               return NULL;
+       }
+       if (res) {
                wpa_printf(MSG_WARNING, "EAP-SIM: GSM authentication failed");
                return eap_sim_client_error(data, id,
                                            EAP_SIM_UNABLE_TO_PROCESS_PACKET);
@@ -700,7 +802,7 @@ static struct wpabuf * eap_sim_process_challenge(struct eap_sm *sm,
        if (data->result_ind && attr->result_ind)
                data->use_result_ind = 1;
 
-       if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+       if (data->state != FAILURE) {
                eap_sim_state(data, data->use_result_ind ?
                              RESULT_SUCCESS : SUCCESS);
        }
@@ -864,9 +966,11 @@ static struct wpabuf * eap_sim_process_reauthentication(
        }
 
        if (eattr.counter < 0 || (size_t) eattr.counter <= data->counter) {
+               struct wpabuf *res;
                wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid counter "
                           "(%d <= %d)", eattr.counter, data->counter);
                data->counter_too_small = eattr.counter;
+
                /* Reply using Re-auth w/ AT_COUNTER_TOO_SMALL. The current
                 * reauth_id must not be used to start a new reauthentication.
                 * However, since it was used in the last EAP-Response-Identity
@@ -877,8 +981,11 @@ static struct wpabuf * eap_sim_process_reauthentication(
                data->last_eap_identity_len = data->reauth_id_len;
                data->reauth_id = NULL;
                data->reauth_id_len = 0;
+
+               res = eap_sim_response_reauth(data, id, 1, eattr.nonce_s);
                os_free(decrypted);
-               return eap_sim_response_reauth(data, id, 1, eattr.nonce_s);
+
+               return res;
        }
        data->counter = eattr.counter;
 
@@ -896,7 +1003,7 @@ static struct wpabuf * eap_sim_process_reauthentication(
        if (data->result_ind && attr->result_ind)
                data->use_result_ind = 1;
 
-       if (data->state != FAILURE && data->state != RESULT_FAILURE) {
+       if (data->state != FAILURE) {
                eap_sim_state(data, data->use_result_ind ?
                              RESULT_SUCCESS : SUCCESS);
        }
@@ -995,9 +1102,7 @@ done:
                        DECISION_UNCOND_SUCC : DECISION_COND_SUCC;
                ret->methodState = data->use_result_ind ?
                        METHOD_DONE : METHOD_MAY_CONT;
-       } else if (data->state == RESULT_FAILURE)
-               ret->methodState = METHOD_CONT;
-       else if (data->state == RESULT_SUCCESS)
+       } else if (data->state == RESULT_SUCCESS)
                ret->methodState = METHOD_CONT;
 
        if (ret->methodState == METHOD_DONE) {
@@ -1020,6 +1125,7 @@ static void eap_sim_deinit_for_reauth(struct eap_sm *sm, void *priv)
        struct eap_sim_data *data = priv;
        eap_sim_clear_identities(sm, data, CLEAR_EAP_ID);
        data->use_result_ind = 0;
+       eap_sim_clear_keys(data, 1);
 }
 
 
index d2066cd..5aa3fd5 100644 (file)
@@ -98,13 +98,49 @@ static void * eap_unauth_tls_init(struct eap_sm *sm)
 #endif /* EAP_UNAUTH_TLS */
 
 
+#ifdef CONFIG_HS20
+static void * eap_wfa_unauth_tls_init(struct eap_sm *sm)
+{
+       struct eap_tls_data *data;
+       struct eap_peer_config *config = eap_get_config(sm);
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+
+       data->ssl_ctx = sm->init_phase2 && sm->ssl_ctx2 ? sm->ssl_ctx2 :
+               sm->ssl_ctx;
+
+       if (eap_peer_tls_ssl_init(sm, &data->ssl, config,
+                                 EAP_WFA_UNAUTH_TLS_TYPE)) {
+               wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+               eap_tls_deinit(sm, data);
+               return NULL;
+       }
+
+       data->eap_type = EAP_WFA_UNAUTH_TLS_TYPE;
+
+       return data;
+}
+#endif /* CONFIG_HS20 */
+
+
+static void eap_tls_free_key(struct eap_tls_data *data)
+{
+       if (data->key_data) {
+               bin_clear_free(data->key_data, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
+               data->key_data = NULL;
+       }
+}
+
+
 static void eap_tls_deinit(struct eap_sm *sm, void *priv)
 {
        struct eap_tls_data *data = priv;
        if (data == NULL)
                return;
        eap_peer_tls_ssl_deinit(sm, &data->ssl);
-       os_free(data->key_data);
+       eap_tls_free_key(data);
        os_free(data->session_id);
        os_free(data);
 }
@@ -154,7 +190,7 @@ static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data,
        ret->methodState = METHOD_DONE;
        ret->decision = DECISION_UNCOND_SUCC;
 
-       os_free(data->key_data);
+       eap_tls_free_key(data);
        data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
                                                 "client EAP encryption",
                                                 EAP_TLS_KEY_LEN +
@@ -240,8 +276,7 @@ static void eap_tls_deinit_for_reauth(struct eap_sm *sm, void *priv)
 static void * eap_tls_init_for_reauth(struct eap_sm *sm, void *priv)
 {
        struct eap_tls_data *data = priv;
-       os_free(data->key_data);
-       data->key_data = NULL;
+       eap_tls_free_key(data);
        os_free(data->session_id);
        data->session_id = NULL;
        if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
@@ -382,3 +417,35 @@ int eap_peer_unauth_tls_register(void)
        return ret;
 }
 #endif /* EAP_UNAUTH_TLS */
+
+
+#ifdef CONFIG_HS20
+int eap_peer_wfa_unauth_tls_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_peer_method_alloc(EAP_PEER_METHOD_INTERFACE_VERSION,
+                                   EAP_VENDOR_WFA_NEW,
+                                   EAP_VENDOR_WFA_UNAUTH_TLS,
+                                   "WFA-UNAUTH-TLS");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_wfa_unauth_tls_init;
+       eap->deinit = eap_tls_deinit;
+       eap->process = eap_tls_process;
+       eap->isKeyAvailable = eap_tls_isKeyAvailable;
+       eap->getKey = eap_tls_getKey;
+       eap->get_status = eap_tls_get_status;
+       eap->has_reauth_data = eap_tls_has_reauth_data;
+       eap->deinit_for_reauth = eap_tls_deinit_for_reauth;
+       eap->init_for_reauth = eap_tls_init_for_reauth;
+       eap->get_emsk = eap_tls_get_emsk;
+
+       ret = eap_peer_method_register(eap);
+       if (ret)
+               eap_peer_method_free(eap);
+       return ret;
+}
+#endif /* CONFIG_HS20 */
index be8c301..8710781 100644 (file)
@@ -23,6 +23,10 @@ static struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
                return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS,
                                     EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len,
                                     code, identifier);
+       if (type == EAP_WFA_UNAUTH_TLS_TYPE)
+               return eap_msg_alloc(EAP_VENDOR_WFA_NEW,
+                                    EAP_VENDOR_WFA_UNAUTH_TLS, payload_len,
+                                    code, identifier);
        return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code,
                             identifier);
 }
@@ -64,6 +68,14 @@ static void eap_tls_params_flags(struct tls_connection_params *params,
                params->flags |= TLS_CONN_DISABLE_SESSION_TICKET;
        if (os_strstr(txt, "tls_disable_session_ticket=0"))
                params->flags &= ~TLS_CONN_DISABLE_SESSION_TICKET;
+       if (os_strstr(txt, "tls_disable_tlsv1_1=1"))
+               params->flags |= TLS_CONN_DISABLE_TLSv1_1;
+       if (os_strstr(txt, "tls_disable_tlsv1_1=0"))
+               params->flags &= ~TLS_CONN_DISABLE_TLSv1_1;
+       if (os_strstr(txt, "tls_disable_tlsv1_2=1"))
+               params->flags |= TLS_CONN_DISABLE_TLSv1_2;
+       if (os_strstr(txt, "tls_disable_tlsv1_2=0"))
+               params->flags &= ~TLS_CONN_DISABLE_TLSv1_2;
 }
 
 
@@ -78,6 +90,8 @@ static void eap_tls_params_from_conf1(struct tls_connection_params *params,
        params->dh_file = (char *) config->dh_file;
        params->subject_match = (char *) config->subject_match;
        params->altsubject_match = (char *) config->altsubject_match;
+       params->suffix_match = config->domain_suffix_match;
+       params->domain_match = config->domain_match;
        params->engine = config->engine;
        params->engine_id = config->engine_id;
        params->pin = config->pin;
@@ -99,6 +113,8 @@ static void eap_tls_params_from_conf2(struct tls_connection_params *params,
        params->dh_file = (char *) config->dh_file2;
        params->subject_match = (char *) config->subject_match2;
        params->altsubject_match = (char *) config->altsubject_match2;
+       params->suffix_match = config->domain_suffix_match2;
+       params->domain_match = config->domain_match2;
        params->engine = config->engine2;
        params->engine_id = config->engine2_id;
        params->pin = config->pin2;
@@ -133,6 +149,8 @@ static int eap_tls_params_from_conf(struct eap_sm *sm,
        } else {
                wpa_printf(MSG_DEBUG, "TLS: using phase1 config options");
                eap_tls_params_from_conf1(params, config);
+               if (data->eap_type == EAP_TYPE_FAST)
+                       params->flags |= TLS_CONN_EAP_FAST;
        }
 
        /*
@@ -153,6 +171,8 @@ static int eap_tls_params_from_conf(struct eap_sm *sm,
                return -1;
        }
 
+       params->openssl_ciphers = config->openssl_ciphers;
+
        return 0;
 }
 
@@ -363,15 +383,10 @@ u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm,
        struct tls_keys keys;
        u8 *out;
 
-       /*
-        * TLS library did not support session ID generation,
-        * so get the needed TLS session parameters
-        */
        if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
                return NULL;
 
-       if (keys.client_random == NULL || keys.server_random == NULL ||
-           keys.master_key == NULL)
+       if (keys.client_random == NULL || keys.server_random == NULL)
                return NULL;
 
        *len = 1 + keys.client_random_len + keys.server_random_len;
@@ -383,7 +398,7 @@ u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm,
        out[0] = eap_type;
        os_memcpy(out + 1, keys.client_random, keys.client_random_len);
        os_memcpy(out + 1 + keys.client_random_len, keys.server_random,
-                 keys.server_random_len);
+                 keys.server_random_len);
 
        return out;
 }
@@ -781,8 +796,11 @@ int eap_peer_tls_status(struct eap_sm *sm, struct eap_ssl_data *data,
        if (tls_get_cipher(data->ssl_ctx, data->conn, name, sizeof(name)) == 0)
        {
                ret = os_snprintf(buf + len, buflen - len,
-                                 "EAP TLS cipher=%s\n", name);
-               if (ret < 0 || (size_t) ret >= buflen - len)
+                                 "EAP TLS cipher=%s\n"
+                                 "tls_session_reused=%d\n",
+                                 name, tls_connection_resumed(data->ssl_ctx,
+                                                              data->conn));
+               if (os_snprintf_error(buflen - len, ret))
                        return len;
                len += ret;
        }
@@ -836,6 +854,10 @@ const u8 * eap_peer_tls_process_init(struct eap_sm *sm,
                pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
                                       EAP_VENDOR_TYPE_UNAUTH_TLS, reqData,
                                       &left);
+       else if (eap_type == EAP_WFA_UNAUTH_TLS_TYPE)
+               pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW,
+                                      EAP_VENDOR_WFA_UNAUTH_TLS, reqData,
+                                      &left);
        else
                pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, reqData,
                                       &left);
index 1a5e0f8..390c216 100644 (file)
@@ -87,6 +87,7 @@ struct eap_ssl_data {
 
 /* dummy type used as a flag for UNAUTH-TLS */
 #define EAP_UNAUTH_TLS_TYPE 255
+#define EAP_WFA_UNAUTH_TLS_TYPE 254
 
 
 int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
index bc13647..25b9f12 100644 (file)
@@ -243,7 +243,8 @@ static struct wpabuf * eap_tnc_process(struct eap_sm *sm, void *priv,
                message_length = WPA_GET_BE32(pos);
                pos += 4;
 
-               if (message_length < (u32) (end - pos)) {
+               if (message_length < (u32) (end - pos) ||
+                   message_length > 75000) {
                        wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message "
                                   "Length (%d; %ld remaining in this msg)",
                                   message_length, (long) (end - pos));
index 5091bf0..b5c028b 100644 (file)
@@ -133,6 +133,15 @@ static void eap_ttls_phase2_eap_deinit(struct eap_sm *sm,
 }
 
 
+static void eap_ttls_free_key(struct eap_ttls_data *data)
+{
+       if (data->key_data) {
+               bin_clear_free(data->key_data, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
+               data->key_data = NULL;
+       }
+}
+
+
 static void eap_ttls_deinit(struct eap_sm *sm, void *priv)
 {
        struct eap_ttls_data *data = priv;
@@ -141,7 +150,7 @@ static void eap_ttls_deinit(struct eap_sm *sm, void *priv)
        eap_ttls_phase2_eap_deinit(sm, data);
        os_free(data->phase2_eap_types);
        eap_peer_tls_ssl_deinit(sm, &data->ssl);
-       os_free(data->key_data);
+       eap_ttls_free_key(data);
        os_free(data->session_id);
        wpabuf_free(data->pending_phase2_req);
        os_free(data);
@@ -213,10 +222,11 @@ static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code,
 static int eap_ttls_v0_derive_key(struct eap_sm *sm,
                                  struct eap_ttls_data *data)
 {
-       os_free(data->key_data);
+       eap_ttls_free_key(data);
        data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
                                                 "ttls keying material",
-                                                EAP_TLS_KEY_LEN);
+                                                EAP_TLS_KEY_LEN +
+                                                EAP_EMSK_LEN);
        if (!data->key_data) {
                wpa_printf(MSG_INFO, "EAP-TTLS: Failed to derive key");
                return -1;
@@ -224,6 +234,9 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm,
 
        wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key",
                        data->key_data, EAP_TLS_KEY_LEN);
+       wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived EMSK",
+                       data->key_data + EAP_TLS_KEY_LEN,
+                       EAP_EMSK_LEN);
 
        os_free(data->session_id);
        data->session_id = eap_peer_tls_derive_session_id(sm, &data->ssl,
@@ -492,16 +505,6 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
        wpabuf_put(msg, pos - buf);
        *resp = msg;
 
-       if (sm->workaround) {
-               /* At least FreeRADIUS seems to be terminating
-                * EAP-TTLS/MSHCAPV2 without the expected MS-CHAP-v2 Success
-                * packet. */
-               wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: EAP workaround - "
-                          "allow success without tunneled response");
-               ret->methodState = METHOD_MAY_CONT;
-               ret->decision = DECISION_COND_SUCC;
-       }
-
        return 0;
 #else /* EAP_MSCHAPv2 */
        wpa_printf(MSG_ERROR, "EAP-TTLS: MSCHAPv2 not included in the build");
@@ -992,6 +995,7 @@ static int eap_ttls_encrypt_response(struct eap_sm *sm,
                                 resp, out_data)) {
                wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt a Phase 2 "
                           "frame");
+               wpabuf_free(resp);
                return -1;
        }
        wpabuf_free(resp);
@@ -1540,8 +1544,7 @@ static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv)
 static void * eap_ttls_init_for_reauth(struct eap_sm *sm, void *priv)
 {
        struct eap_ttls_data *data = priv;
-       os_free(data->key_data);
-       data->key_data = NULL;
+       eap_ttls_free_key(data);
        os_free(data->session_id);
        data->session_id = NULL;
        if (eap_peer_tls_reauth_init(sm, &data->ssl)) {
@@ -1569,7 +1572,7 @@ static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf,
        ret = os_snprintf(buf + len, buflen - len,
                          "EAP-TTLSv%d Phase2 method=",
                          data->ttls_version);
-       if (ret < 0 || (size_t) ret >= buflen - len)
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
        switch (data->phase2_type) {
@@ -1594,7 +1597,7 @@ static int eap_ttls_get_status(struct eap_sm *sm, void *priv, char *buf,
                ret = 0;
                break;
        }
-       if (ret < 0 || (size_t) ret >= buflen - len)
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
 
@@ -1647,6 +1650,25 @@ static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
 }
 
 
+static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_ttls_data *data = priv;
+       u8 *key;
+
+       if (data->key_data == NULL)
+               return NULL;
+
+       key = os_malloc(EAP_EMSK_LEN);
+       if (key == NULL)
+               return NULL;
+
+       *len = EAP_EMSK_LEN;
+       os_memcpy(key, data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
+
+       return key;
+}
+
+
 int eap_peer_ttls_register(void)
 {
        struct eap_method *eap;
@@ -1667,6 +1689,7 @@ int eap_peer_ttls_register(void)
        eap->has_reauth_data = eap_ttls_has_reauth_data;
        eap->deinit_for_reauth = eap_ttls_deinit_for_reauth;
        eap->init_for_reauth = eap_ttls_init_for_reauth;
+       eap->get_emsk = eap_ttls_get_emsk;
 
        ret = eap_peer_method_register(eap);
        if (ret)
index 040d1e7..b61057e 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * EAP peer method: Test method for vendor specific (expanded) EAP type
- * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 
 #include "common.h"
 #include "eap_i.h"
-#ifdef TEST_PENDING_REQUEST
 #include "eloop.h"
-#endif /* TEST_PENDING_REQUEST */
 
 
 #define EAP_VENDOR_ID EAP_VENDOR_HOSTAP
 #define EAP_VENDOR_TYPE 0xfcfbfaf9
 
 
-/* #define TEST_PENDING_REQUEST */
-
 struct eap_vendor_test_data {
        enum { INIT, CONFIRM, SUCCESS } state;
        int first_try;
+       int test_pending_req;
 };
 
 
 static void * eap_vendor_test_init(struct eap_sm *sm)
 {
        struct eap_vendor_test_data *data;
+       const u8 *password;
+       size_t password_len;
+
        data = os_zalloc(sizeof(*data));
        if (data == NULL)
                return NULL;
        data->state = INIT;
        data->first_try = 1;
+
+       password = eap_get_config_password(sm, &password_len);
+       data->test_pending_req = password && password_len == 7 &&
+               os_memcmp(password, "pending", 7) == 0;
+
        return data;
 }
 
@@ -50,7 +55,6 @@ static void eap_vendor_test_deinit(struct eap_sm *sm, void *priv)
 }
 
 
-#ifdef TEST_PENDING_REQUEST
 static void eap_vendor_ready(void *eloop_ctx, void *timeout_ctx)
 {
        struct eap_sm *sm = eloop_ctx;
@@ -58,7 +62,6 @@ static void eap_vendor_ready(void *eloop_ctx, void *timeout_ctx)
                   "request");
        eap_notify_pending(sm);
 }
-#endif /* TEST_PENDING_REQUEST */
 
 
 static struct wpabuf * eap_vendor_test_process(struct eap_sm *sm, void *priv,
@@ -98,8 +101,7 @@ static struct wpabuf * eap_vendor_test_process(struct eap_sm *sm, void *priv,
        }
 
        if (data->state == CONFIRM) {
-#ifdef TEST_PENDING_REQUEST
-               if (data->first_try) {
+               if (data->test_pending_req && data->first_try) {
                        data->first_try = 0;
                        wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: Testing "
                                   "pending request");
@@ -108,7 +110,6 @@ static struct wpabuf * eap_vendor_test_process(struct eap_sm *sm, void *priv,
                                               NULL);
                        return NULL;
                }
-#endif /* TEST_PENDING_REQUEST */
        }
 
        ret->ignore = FALSE;
index 8edb1ca..7ce0a53 100644 (file)
@@ -106,8 +106,10 @@ static int eap_wsc_new_ap_settings(struct wps_credential *cred,
        }
        if (os_strncmp(pos + 9, "NONE", 4) == 0)
                cred->encr_type = WPS_ENCR_NONE;
+#ifdef CONFIG_TESTING_OPTIONS
        else if (os_strncmp(pos + 9, "WEP", 3) == 0)
                cred->encr_type = WPS_ENCR_WEP;
+#endif /* CONFIG_TESTING_OPTIONS */
        else if (os_strncmp(pos + 9, "TKIP", 4) == 0)
                cred->encr_type = WPS_ENCR_TKIP;
        else if (os_strncmp(pos + 9, "CCMP", 4) == 0)
@@ -144,12 +146,13 @@ static void * eap_wsc_init(struct eap_sm *sm)
        size_t identity_len;
        int registrar;
        struct wps_config cfg;
-       const char *pos;
+       const char *pos, *end;
        const char *phase1;
        struct wps_context *wps;
        struct wps_credential new_ap_settings;
        int res;
        int nfc = 0;
+       u8 pkhash[WPS_OOB_PUBKEY_HASH_LEN];
 
        wps = sm->wps;
        if (wps == NULL) {
@@ -209,6 +212,15 @@ static void * eap_wsc_init(struct eap_sm *sm)
                        cfg.pbc = 1;
        }
 
+       pos = os_strstr(phase1, "dev_pw_id=");
+       if (pos) {
+               u16 id = atoi(pos + 10);
+               if (id == DEV_PW_NFC_CONNECTION_HANDOVER)
+                       nfc = 1;
+               if (cfg.pin || id == DEV_PW_NFC_CONNECTION_HANDOVER)
+                       cfg.dev_pw_id = id;
+       }
+
        if (cfg.pin == NULL && !cfg.pbc && !nfc) {
                wpa_printf(MSG_INFO, "EAP-WSC: PIN or PBC not set in phase1 "
                           "configuration data");
@@ -216,9 +228,23 @@ static void * eap_wsc_init(struct eap_sm *sm)
                return NULL;
        }
 
-       pos = os_strstr(phase1, "dev_pw_id=");
-       if (pos && cfg.pin)
-               cfg.dev_pw_id = atoi(pos + 10);
+       pos = os_strstr(phase1, " pkhash=");
+       if (pos) {
+               size_t len;
+               pos += 8;
+               end = os_strchr(pos, ' ');
+               if (end)
+                       len = end - pos;
+               else
+                       len = os_strlen(pos);
+               if (len != 2 * WPS_OOB_PUBKEY_HASH_LEN ||
+                   hexstr2bin(pos, pkhash, WPS_OOB_PUBKEY_HASH_LEN)) {
+                       wpa_printf(MSG_INFO, "EAP-WSC: Invalid pkhash");
+                       os_free(data);
+                       return NULL;
+               }
+               cfg.peer_pubkey_hash = pkhash;
+       }
 
        res = eap_wsc_new_ap_settings(&new_ap_settings, phase1);
        if (res < 0) {
@@ -436,7 +462,7 @@ static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv,
                message_length = WPA_GET_BE16(pos);
                pos += 2;
 
-               if (message_length < end - pos) {
+               if (message_length < end - pos || message_length > 50000) {
                        wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
                                   "Length");
                        ret->ignore = TRUE;
index fcf4712..55ab72a 100644 (file)
@@ -72,27 +72,10 @@ static int ikev2_derive_keys(struct ikev2_responder_data *data)
        os_memcpy(pos, data->i_spi, IKEV2_SPI_LEN);
        pos += IKEV2_SPI_LEN;
        os_memcpy(pos, data->r_spi, IKEV2_SPI_LEN);
-#ifdef CCNS_PL
-#if __BYTE_ORDER == __LITTLE_ENDIAN
-       {
-               int i;
-               u8 *tmp = pos - IKEV2_SPI_LEN;
-               /* Incorrect byte re-ordering on little endian hosts.. */
-               for (i = 0; i < IKEV2_SPI_LEN; i++)
-                       *tmp++ = data->i_spi[IKEV2_SPI_LEN - 1 - i];
-               for (i = 0; i < IKEV2_SPI_LEN; i++)
-                       *tmp++ = data->r_spi[IKEV2_SPI_LEN - 1 - i];
-       }
-#endif
-#endif /* CCNS_PL */
 
        /* SKEYSEED = prf(Ni | Nr, g^ir) */
        /* Use zero-padding per RFC 4306, Sect. 2.14 */
        pad_len = data->dh->prime_len - wpabuf_len(shared);
-#ifdef CCNS_PL
-       /* Shared secret is not zero-padded correctly */
-       pad_len = 0;
-#endif /* CCNS_PL */
        pad = os_zalloc(pad_len ? pad_len : 1);
        if (pad == NULL) {
                wpabuf_free(shared);
@@ -179,21 +162,12 @@ static int ikev2_parse_transform(struct ikev2_proposal_data *prop,
                                                   "Transform Attr for AES");
                                        break;
                                }
-#ifdef CCNS_PL
-                               if (WPA_GET_BE16(pos) != 0x001d /* ?? */) {
-                                       wpa_printf(MSG_DEBUG, "IKEV2: Not a "
-                                                  "Key Size attribute for "
-                                                  "AES");
-                                       break;
-                               }
-#else /* CCNS_PL */
                                if (WPA_GET_BE16(pos) != 0x800e) {
                                        wpa_printf(MSG_DEBUG, "IKEV2: Not a "
                                                   "Key Size attribute for "
                                                   "AES");
                                        break;
                                }
-#endif /* CCNS_PL */
                                if (WPA_GET_BE16(pos + 2) != 128) {
                                        wpa_printf(MSG_DEBUG, "IKEV2: "
                                                   "Unsupported AES key size "
@@ -239,7 +213,7 @@ static int ikev2_parse_proposal(struct ikev2_proposal_data *prop,
 
        p = (const struct ikev2_proposal *) pos;
        proposal_len = WPA_GET_BE16(p->proposal_length);
-       if (proposal_len < (int) sizeof(*p) || pos + proposal_len > end) {
+       if (proposal_len < (int) sizeof(*p) || proposal_len > end - pos) {
                wpa_printf(MSG_INFO, "IKEV2: Invalid proposal length %d",
                           proposal_len);
                return -1;
@@ -395,7 +369,7 @@ static int ikev2_process_kei(struct ikev2_responder_data *data,
        }
 
        if (kei_len < 4 + 96) {
-               wpa_printf(MSG_INFO, "IKEV2: Too show Key Exchange Payload");
+               wpa_printf(MSG_INFO, "IKEV2: Too short Key Exchange Payload");
                return -1;
        }
 
@@ -456,14 +430,6 @@ static int ikev2_process_ni(struct ikev2_responder_data *data,
                return -1;
        }
 
-#ifdef CCNS_PL
-       /* Zeros are removed incorrectly from the beginning of the nonces */
-       while (ni_len > 1 && *ni == 0) {
-               ni_len--;
-               ni++;
-       }
-#endif /* CCNS_PL */
-
        data->i_nonce_len = ni_len;
        os_memcpy(data->i_nonce, ni, ni_len);
        wpa_hexdump(MSG_MSGDUMP, "IKEV2: Ni",
@@ -599,7 +565,7 @@ static int ikev2_process_auth_secret(struct ikev2_responder_data *data,
                return -1;
 
        if (auth_len != prf->hash_len ||
-           os_memcmp(auth, auth_data, auth_len) != 0) {
+           os_memcmp_const(auth, auth_data, auth_len) != 0) {
                wpa_printf(MSG_INFO, "IKEV2: Invalid Authentication Data");
                wpa_hexdump(MSG_DEBUG, "IKEV2: Received Authentication Data",
                            auth, auth_len);
@@ -887,16 +853,7 @@ static int ikev2_build_sar1(struct ikev2_responder_data *data,
        phdr->flags = 0;
 
        p = wpabuf_put(msg, sizeof(*p));
-#ifdef CCNS_PL
-       /* Seems to require that the Proposal # is 1 even though RFC 4306
-        * Sect 3.3.1 has following requirement "When a proposal is accepted,
-        * all of the proposal numbers in the SA payload MUST be the same and
-        * MUST match the number on the proposal sent that was accepted.".
-        */
-       p->proposal_num = 1;
-#else /* CCNS_PL */
        p->proposal_num = data->proposal.proposal_num;
-#endif /* CCNS_PL */
        p->protocol_id = IKEV2_PROTOCOL_IKE;
        p->num_transforms = 4;
 
@@ -906,11 +863,7 @@ static int ikev2_build_sar1(struct ikev2_responder_data *data,
        WPA_PUT_BE16(t->transform_id, data->proposal.encr);
        if (data->proposal.encr == ENCR_AES_CBC) {
                /* Transform Attribute: Key Len = 128 bits */
-#ifdef CCNS_PL
-               wpabuf_put_be16(msg, 0x001d); /* ?? */
-#else /* CCNS_PL */
                wpabuf_put_be16(msg, 0x800e); /* AF=1, AttrType=14 */
-#endif /* CCNS_PL */
                wpabuf_put_be16(msg, 128); /* 128-bit key */
        }
        plen = (u8 *) wpabuf_put(msg, 0) - (u8 *) t;
@@ -1082,11 +1035,7 @@ static int ikev2_build_notification(struct ikev2_responder_data *data,
        phdr = wpabuf_put(msg, sizeof(*phdr));
        phdr->next_payload = next_payload;
        phdr->flags = 0;
-#ifdef CCNS_PL
-       wpabuf_put_u8(msg, 1); /* Protocol ID: IKE_SA notification */
-#else /* CCNS_PL */
        wpabuf_put_u8(msg, 0); /* Protocol ID: no existing SA */
-#endif /* CCNS_PL */
        wpabuf_put_u8(msg, 0); /* SPI Size */
        wpabuf_put_be16(msg, data->error_type);
 
@@ -1130,13 +1079,6 @@ static struct wpabuf * ikev2_build_sa_init(struct ikev2_responder_data *data)
        data->r_nonce_len = IKEV2_NONCE_MIN_LEN;
        if (random_get_bytes(data->r_nonce, data->r_nonce_len))
                return NULL;
-#ifdef CCNS_PL
-       /* Zeros are removed incorrectly from the beginning of the nonces in
-        * key derivation; as a workaround, make sure Nr does not start with
-        * zero.. */
-       if (data->r_nonce[0] == 0)
-               data->r_nonce[0] = 1;
-#endif /* CCNS_PL */
        wpa_hexdump(MSG_DEBUG, "IKEV2: Nr", data->r_nonce, data->r_nonce_len);
 
        msg = wpabuf_alloc(sizeof(struct ikev2_hdr) + data->IDr_len + 1500);
@@ -1257,6 +1199,7 @@ static struct wpabuf * ikev2_build_notify(struct ikev2_responder_data *data)
                        wpabuf_free(msg);
                        return NULL;
                }
+               wpabuf_free(plain);
                data->state = IKEV2_FAILED;
        } else {
                /* HDR, N */
index 37e6735..9bc7370 100644 (file)
@@ -117,8 +117,8 @@ int mschapv2_verify_auth_response(const u8 *auth_response,
            buf[0] != 'S' || buf[1] != '=' ||
            hexstr2bin((char *) (buf + 2), recv_response,
                       MSCHAPV2_AUTH_RESPONSE_LEN) ||
-           os_memcmp(auth_response, recv_response,
-                     MSCHAPV2_AUTH_RESPONSE_LEN) != 0)
+           os_memcmp_const(auth_response, recv_response,
+                           MSCHAPV2_AUTH_RESPONSE_LEN) != 0)
                return -1;
        return 0;
 }
index a3ec395..7ca956e 100644 (file)
@@ -13,6 +13,7 @@
 
 #include "common.h"
 #include "base64.h"
+#include "common/tnc.h"
 #include "tncc.h"
 #include "eap_common/eap_tlv_common.h"
 #include "eap_common/eap_defs.h"
@@ -25,7 +26,9 @@
 #endif /* UNICODE */
 
 
+#ifndef TNC_CONFIG_FILE
 #define TNC_CONFIG_FILE "/etc/tnc_config"
+#endif /* TNC_CONFIG_FILE */
 #define TNC_WINREG_PATH TEXT("SOFTWARE\\Trusted Computing Group\\TNC\\IMCs")
 #define IF_TNCCS_START \
 "<?xml version=\"1.0\"?>\n" \
 
 /* TNC IF-IMC */
 
-typedef unsigned long TNC_UInt32;
-typedef unsigned char *TNC_BufferReference;
-
-typedef TNC_UInt32 TNC_IMCID;
-typedef TNC_UInt32 TNC_ConnectionID;
-typedef TNC_UInt32 TNC_ConnectionState;
-typedef TNC_UInt32 TNC_RetryReason;
-typedef TNC_UInt32 TNC_MessageType;
-typedef TNC_MessageType *TNC_MessageTypeList;
-typedef TNC_UInt32 TNC_VendorID;
-typedef TNC_UInt32 TNC_MessageSubtype;
-typedef TNC_UInt32 TNC_Version;
-typedef TNC_UInt32 TNC_Result;
-
-typedef TNC_Result (*TNC_TNCC_BindFunctionPointer)(
-       TNC_IMCID imcID,
-       char *functionName,
-       void **pOutfunctionPointer);
-
-#define TNC_RESULT_SUCCESS 0
-#define TNC_RESULT_NOT_INITIALIZED 1
-#define TNC_RESULT_ALREADY_INITIALIZED 2
-#define TNC_RESULT_NO_COMMON_VERSION 3
-#define TNC_RESULT_CANT_RETRY 4
-#define TNC_RESULT_WONT_RETRY 5
-#define TNC_RESULT_INVALID_PARAMETER 6
-#define TNC_RESULT_CANT_RESPOND 7
-#define TNC_RESULT_ILLEGAL_OPERATION 8
-#define TNC_RESULT_OTHER 9
-#define TNC_RESULT_FATAL 10
-
-#define TNC_CONNECTION_STATE_CREATE 0
-#define TNC_CONNECTION_STATE_HANDSHAKE 1
-#define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2
-#define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3
-#define TNC_CONNECTION_STATE_ACCESS_NONE 4
-#define TNC_CONNECTION_STATE_DELETE 5
-
-#define TNC_IFIMC_VERSION_1 1
-
-#define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff)
-#define TNC_SUBTYPE_ANY ((TNC_MessageSubtype) 0xff)
-
-/* TNCC-TNCS Message Types */
-#define TNC_TNCCS_RECOMMENDATION               0x00000001
-#define TNC_TNCCS_ERROR                                0x00000002
-#define TNC_TNCCS_PREFERREDLANGUAGE            0x00000003
-#define TNC_TNCCS_REASONSTRINGS                        0x00000004
-
-
 /* IF-TNCCS-SOH - SSoH and SSoHR Attributes */
 enum {
        SSOH_MS_MACHINE_INVENTORY = 1,
@@ -1139,8 +1092,10 @@ static int tncc_read_config(struct tncc_data *tncc)
                        int error = 0;
 
                        imc = tncc_parse_imc(pos + 4, line_end, &error);
-                       if (error)
+                       if (error) {
+                               os_free(config);
                                return -1;
+                       }
                        if (imc) {
                                if (last == NULL)
                                        tncc->imc = imc;
index 9c41962..adfd3df 100644 (file)
@@ -2,7 +2,7 @@ all:
        @echo Nothing to be made.
 
 clean:
-       rm -f *~ *.o *.d
+       rm -f *~ *.o *.d *.gcno *.gcda *.gcov
 
 install:
        @echo Nothing to be made.
index f2a7cd7..9de6cb6 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / EAP Full Authenticator state machine (RFC 4137)
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +10,7 @@
 #define EAP_H
 
 #include "common/defs.h"
+#include "utils/list.h"
 #include "eap_common/eap_defs.h"
 #include "eap_server/eap_methods.h"
 #include "wpabuf.h"
@@ -32,8 +33,11 @@ struct eap_user {
                            * nt_password_hash() */
        int phase2;
        int force_version;
+       unsigned int remediation:1;
+       unsigned int macacl:1;
        int ttls_auth; /* bitfield of
                        * EAP_TTLS_AUTH_{PAP,CHAP,MSCHAP,MSCHAPV2} */
+       struct hostapd_radius_attr *accept_attr;
 };
 
 struct eap_eapol_interface {
@@ -55,6 +59,8 @@ struct eap_eapol_interface {
        struct wpabuf *eapReqData;
        u8 *eapKeyData;
        size_t eapKeyDataLen;
+       u8 *eapSessionId;
+       size_t eapSessionIdLen;
        Boolean eapKeyAvailable; /* called keyAvailable in IEEE 802.1X-2004 */
 
        /* AAA interface to full authenticator variables */
@@ -75,10 +81,27 @@ struct eap_eapol_interface {
        Boolean aaaTimeout;
 };
 
+struct eap_server_erp_key {
+       struct dl_list list;
+       size_t rRK_len;
+       size_t rIK_len;
+       u8 rRK[ERP_MAX_KEY_LEN];
+       u8 rIK[ERP_MAX_KEY_LEN];
+       u32 recv_seq;
+       u8 cryptosuite;
+       char keyname_nai[];
+};
+
 struct eapol_callbacks {
        int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
                            int phase2, struct eap_user *user);
        const char * (*get_eap_req_id_text)(void *ctx, size_t *len);
+       void (*log_msg)(void *ctx, const char *msg);
+       int (*get_erp_send_reauth_start)(void *ctx);
+       const char * (*get_erp_domain)(void *ctx);
+       struct eap_server_erp_key * (*erp_get_key)(void *ctx,
+                                                  const char *keyname);
+       int (*erp_add_key)(void *ctx, struct eap_server_erp_key *erp);
 };
 
 struct eap_config {
@@ -104,6 +127,14 @@ struct eap_config {
        int fragment_size;
 
        int pbc_in_m1;
+
+       const u8 *server_id;
+       size_t server_id_len;
+       int erp;
+
+#ifdef CONFIG_TESTING_OPTIONS
+       u32 tls_test_flags;
+#endif /* CONFIG_TESTING_OPTIONS */
 };
 
 
index f92704a..7d72309 100644 (file)
@@ -88,6 +88,19 @@ struct eap_method {
         * private data or this function may derive the key.
         */
        u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len);
+
+       /**
+        * getSessionId - Get EAP method specific Session-Id
+        * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+        * @priv: Pointer to private EAP method data from eap_method::init()
+        * @len: Pointer to a variable to store Session-Id length
+        * Returns: Session-Id or %NULL if not available
+        *
+        * This function can be used to get the Session-Id from the EAP method.
+        * The Session-Id may already be stored in the method-specific private
+        * data or this function may derive the Session-Id.
+        */
+       u8 * (*getSessionId)(struct eap_sm *sm, void *priv, size_t *len);
 };
 
 /**
@@ -103,7 +116,8 @@ struct eap_sm {
                EAP_INITIALIZE_PASSTHROUGH, EAP_IDLE2, EAP_RETRANSMIT2,
                EAP_RECEIVED2, EAP_DISCARD2, EAP_SEND_REQUEST2,
                EAP_AAA_REQUEST, EAP_AAA_RESPONSE, EAP_AAA_IDLE,
-               EAP_TIMEOUT_FAILURE2, EAP_FAILURE2, EAP_SUCCESS2
+               EAP_TIMEOUT_FAILURE2, EAP_FAILURE2, EAP_SUCCESS2,
+               EAP_INITIATE_REAUTH_START, EAP_INITIATE_RECEIVED
        } EAP_state;
 
        /* Constants */
@@ -125,6 +139,7 @@ struct eap_sm {
 
        /* Short-term (not maintained between packets) */
        Boolean rxResp;
+       Boolean rxInitiate;
        int respId;
        EapType respMethod;
        int respVendor;
@@ -132,7 +147,7 @@ struct eap_sm {
        Boolean ignore;
        enum {
                DECISION_SUCCESS, DECISION_FAILURE, DECISION_CONTINUE,
-               DECISION_PASSTHROUGH
+               DECISION_PASSTHROUGH, DECISION_INITIATE_REAUTH_START
        } decision;
 
        /* Miscellaneous variables */
@@ -188,10 +203,23 @@ struct eap_sm {
        int fragment_size;
 
        int pbc_in_m1;
+
+       const u8 *server_id;
+       size_t server_id_len;
+
+       Boolean initiate_reauth_start_sent;
+       Boolean try_initiate_reauth;
+       int erp;
+
+#ifdef CONFIG_TESTING_OPTIONS
+       u32 tls_test_flags;
+#endif /* CONFIG_TESTING_OPTIONS */
 };
 
 int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
                 int phase2);
+void eap_log_msg(struct eap_sm *sm, const char *fmt, ...)
+PRINTF_FORMAT(2, 3);
 void eap_sm_process_nak(struct eap_sm *sm, const u8 *nak_list, size_t len);
 
 #endif /* EAP_I_H */
index bc810a9..0baa327 100644 (file)
@@ -27,6 +27,7 @@ int eap_server_identity_register(void);
 int eap_server_md5_register(void);
 int eap_server_tls_register(void);
 int eap_server_unauth_tls_register(void);
+int eap_server_wfa_unauth_tls_register(void);
 int eap_server_mschapv2_register(void);
 int eap_server_peap_register(void);
 int eap_server_tlv_register(void);
@@ -45,5 +46,6 @@ int eap_server_wsc_register(void);
 int eap_server_ikev2_register(void);
 int eap_server_tnc_register(void);
 int eap_server_pwd_register(void);
+int eap_server_eke_register(void);
 
 #endif /* EAP_SERVER_METHODS_H */
index 15f7e22..bd919e5 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / EAP Full Authenticator state machine (RFC 4137)
- * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -15,6 +15,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/sha256.h"
 #include "eap_i.h"
 #include "state_machine.h"
 #include "common/wpa_ctrl.h"
@@ -44,6 +45,73 @@ static int eap_sm_Policy_getDecision(struct eap_sm *sm);
 static Boolean eap_sm_Policy_doPickUp(struct eap_sm *sm, EapType method);
 
 
+static int eap_get_erp_send_reauth_start(struct eap_sm *sm)
+{
+       if (sm->eapol_cb->get_erp_send_reauth_start)
+               return sm->eapol_cb->get_erp_send_reauth_start(sm->eapol_ctx);
+       return 0;
+}
+
+
+static const char * eap_get_erp_domain(struct eap_sm *sm)
+{
+       if (sm->eapol_cb->get_erp_domain)
+               return sm->eapol_cb->get_erp_domain(sm->eapol_ctx);
+       return NULL;
+}
+
+
+#ifdef CONFIG_ERP
+
+static struct eap_server_erp_key * eap_erp_get_key(struct eap_sm *sm,
+                                                  const char *keyname)
+{
+       if (sm->eapol_cb->erp_get_key)
+               return sm->eapol_cb->erp_get_key(sm->eapol_ctx, keyname);
+       return NULL;
+}
+
+
+static int eap_erp_add_key(struct eap_sm *sm, struct eap_server_erp_key *erp)
+{
+       if (sm->eapol_cb->erp_add_key)
+               return sm->eapol_cb->erp_add_key(sm->eapol_ctx, erp);
+       return -1;
+}
+
+#endif /* CONFIG_ERP */
+
+
+static struct wpabuf * eap_sm_buildInitiateReauthStart(struct eap_sm *sm,
+                                                      u8 id)
+{
+       const char *domain;
+       size_t plen = 1;
+       struct wpabuf *msg;
+       size_t domain_len = 0;
+
+       domain = eap_get_erp_domain(sm);
+       if (domain) {
+               domain_len = os_strlen(domain);
+               plen += 2 + domain_len;
+       }
+
+       msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH_START, plen,
+                           EAP_CODE_INITIATE, id);
+       if (msg == NULL)
+               return NULL;
+       wpabuf_put_u8(msg, 0); /* Reserved */
+       if (domain) {
+               /* Domain name TLV */
+               wpabuf_put_u8(msg, EAP_ERP_TLV_DOMAIN_NAME);
+               wpabuf_put_u8(msg, domain_len);
+               wpabuf_put_data(msg, domain, domain_len);
+       }
+
+       return msg;
+}
+
+
 static int eap_copy_buf(struct wpabuf **dst, const struct wpabuf *src)
 {
        if (src == NULL)
@@ -119,6 +187,32 @@ int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len,
 }
 
 
+void eap_log_msg(struct eap_sm *sm, const char *fmt, ...)
+{
+       va_list ap;
+       char *buf;
+       int buflen;
+
+       if (sm == NULL || sm->eapol_cb == NULL || sm->eapol_cb->log_msg == NULL)
+               return;
+
+       va_start(ap, fmt);
+       buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+       va_end(ap);
+
+       buf = os_malloc(buflen);
+       if (buf == NULL)
+               return;
+       va_start(ap, fmt);
+       vsnprintf(buf, buflen, fmt, ap);
+       va_end(ap);
+
+       sm->eapol_cb->log_msg(sm->eapol_ctx, buf);
+
+       os_free(buf);
+}
+
+
 SM_STATE(EAP, DISABLED)
 {
        SM_ENTRY(EAP, DISABLED);
@@ -138,13 +232,17 @@ SM_STATE(EAP, INITIALIZE)
                eap_server_clear_identity(sm);
        }
 
+       sm->try_initiate_reauth = FALSE;
        sm->currentId = -1;
        sm->eap_if.eapSuccess = FALSE;
        sm->eap_if.eapFail = FALSE;
        sm->eap_if.eapTimeout = FALSE;
-       os_free(sm->eap_if.eapKeyData);
+       bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
        sm->eap_if.eapKeyData = NULL;
        sm->eap_if.eapKeyDataLen = 0;
+       os_free(sm->eap_if.eapSessionId);
+       sm->eap_if.eapSessionId = NULL;
+       sm->eap_if.eapSessionIdLen = 0;
        sm->eap_if.eapKeyAvailable = FALSE;
        sm->eap_if.eapRestart = FALSE;
 
@@ -310,6 +408,95 @@ SM_STATE(EAP, METHOD_REQUEST)
 }
 
 
+static void eap_server_erp_init(struct eap_sm *sm)
+{
+#ifdef CONFIG_ERP
+       u8 *emsk = NULL;
+       size_t emsk_len = 0;
+       u8 EMSKname[EAP_EMSK_NAME_LEN];
+       u8 len[2];
+       const char *domain;
+       size_t domain_len, nai_buf_len;
+       struct eap_server_erp_key *erp = NULL;
+       int pos;
+
+       domain = eap_get_erp_domain(sm);
+       if (!domain)
+               return;
+
+       domain_len = os_strlen(domain);
+
+       nai_buf_len = 2 * EAP_EMSK_NAME_LEN + 1 + domain_len;
+       if (nai_buf_len > 253) {
+               /*
+                * keyName-NAI has a maximum length of 253 octet to fit in
+                * RADIUS attributes.
+                */
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Too long realm for ERP keyName-NAI maximum length");
+               return;
+       }
+       nai_buf_len++; /* null termination */
+       erp = os_zalloc(sizeof(*erp) + nai_buf_len);
+       if (erp == NULL)
+               goto fail;
+       erp->recv_seq = (u32) -1;
+
+       emsk = sm->m->get_emsk(sm, sm->eap_method_priv, &emsk_len);
+       if (!emsk || emsk_len == 0 || emsk_len > ERP_MAX_KEY_LEN) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: No suitable EMSK available for ERP");
+               goto fail;
+       }
+
+       wpa_hexdump_key(MSG_DEBUG, "EAP: EMSK", emsk, emsk_len);
+
+       WPA_PUT_BE16(len, 8);
+       if (hmac_sha256_kdf(sm->eap_if.eapSessionId, sm->eap_if.eapSessionIdLen,
+                           "EMSK", len, sizeof(len),
+                           EMSKname, EAP_EMSK_NAME_LEN) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP: Could not derive EMSKname");
+               goto fail;
+       }
+       wpa_hexdump(MSG_DEBUG, "EAP: EMSKname", EMSKname, EAP_EMSK_NAME_LEN);
+
+       pos = wpa_snprintf_hex(erp->keyname_nai, nai_buf_len,
+                              EMSKname, EAP_EMSK_NAME_LEN);
+       erp->keyname_nai[pos] = '@';
+       os_memcpy(&erp->keyname_nai[pos + 1], domain, domain_len);
+
+       WPA_PUT_BE16(len, emsk_len);
+       if (hmac_sha256_kdf(emsk, emsk_len,
+                           "EAP Re-authentication Root Key@ietf.org",
+                           len, sizeof(len), erp->rRK, emsk_len) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP: Could not derive rRK for ERP");
+               goto fail;
+       }
+       erp->rRK_len = emsk_len;
+       wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rRK", erp->rRK, erp->rRK_len);
+
+       if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
+                           "EAP Re-authentication Integrity Key@ietf.org",
+                           len, sizeof(len), erp->rIK, erp->rRK_len) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP: Could not derive rIK for ERP");
+               goto fail;
+       }
+       erp->rIK_len = erp->rRK_len;
+       wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rIK", erp->rIK, erp->rIK_len);
+
+       if (eap_erp_add_key(sm, erp) == 0) {
+               wpa_printf(MSG_DEBUG, "EAP: Stored ERP keys %s",
+                          erp->keyname_nai);
+               erp = NULL;
+       }
+
+fail:
+       bin_clear_free(emsk, emsk_len);
+       bin_clear_free(erp, sizeof(*erp));
+#endif /* CONFIG_ERP */
+}
+
+
 SM_STATE(EAP, METHOD_RESPONSE)
 {
        SM_ENTRY(EAP, METHOD_RESPONSE);
@@ -320,7 +507,7 @@ SM_STATE(EAP, METHOD_RESPONSE)
        sm->m->process(sm, sm->eap_method_priv, sm->eap_if.eapRespData);
        if (sm->m->isDone(sm, sm->eap_method_priv)) {
                eap_sm_Policy_update(sm, NULL, 0);
-               os_free(sm->eap_if.eapKeyData);
+               bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
                if (sm->m->getKey) {
                        sm->eap_if.eapKeyData = sm->m->getKey(
                                sm, sm->eap_method_priv,
@@ -329,6 +516,18 @@ SM_STATE(EAP, METHOD_RESPONSE)
                        sm->eap_if.eapKeyData = NULL;
                        sm->eap_if.eapKeyDataLen = 0;
                }
+               os_free(sm->eap_if.eapSessionId);
+               sm->eap_if.eapSessionId = NULL;
+               if (sm->m->getSessionId) {
+                       sm->eap_if.eapSessionId = sm->m->getSessionId(
+                               sm, sm->eap_method_priv,
+                               &sm->eap_if.eapSessionIdLen);
+                       wpa_hexdump(MSG_DEBUG, "EAP: Session-Id",
+                                   sm->eap_if.eapSessionId,
+                                   sm->eap_if.eapSessionIdLen);
+               }
+               if (sm->erp && sm->m->get_emsk && sm->eap_if.eapSessionId)
+                       eap_server_erp_init(sm);
                sm->methodState = METHOD_END;
        } else {
                sm->methodState = METHOD_CONTINUE;
@@ -343,6 +542,8 @@ SM_STATE(EAP, PROPOSE_METHOD)
 
        SM_ENTRY(EAP, PROPOSE_METHOD);
 
+       sm->try_initiate_reauth = FALSE;
+try_another_method:
        type = eap_sm_Policy_getNextMethod(sm, &vendor);
        if (vendor == EAP_VENDOR_IETF)
                sm->currentMethod = type;
@@ -360,8 +561,15 @@ SM_STATE(EAP, PROPOSE_METHOD)
                                   "method %d", sm->currentMethod);
                        sm->m = NULL;
                        sm->currentMethod = EAP_TYPE_NONE;
+                       goto try_another_method;
                }
        }
+       if (sm->m == NULL) {
+               wpa_printf(MSG_DEBUG, "EAP: Could not find suitable EAP method");
+               eap_log_msg(sm, "Could not find suitable EAP method");
+               sm->decision = DECISION_FAILURE;
+               return;
+       }
        if (sm->currentMethod == EAP_TYPE_IDENTITY ||
            sm->currentMethod == EAP_TYPE_NOTIFICATION)
                sm->methodState = METHOD_CONTINUE;
@@ -370,6 +578,8 @@ SM_STATE(EAP, PROPOSE_METHOD)
 
        wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD
                "vendor=%u method=%u", vendor, sm->currentMethod);
+       eap_log_msg(sm, "Propose EAP method vendor=%u method=%u",
+                   vendor, sm->currentMethod);
 }
 
 
@@ -456,12 +666,326 @@ SM_STATE(EAP, SUCCESS)
 }
 
 
+SM_STATE(EAP, INITIATE_REAUTH_START)
+{
+       SM_ENTRY(EAP, INITIATE_REAUTH_START);
+
+       sm->initiate_reauth_start_sent = TRUE;
+       sm->try_initiate_reauth = TRUE;
+       sm->currentId = eap_sm_nextId(sm, sm->currentId);
+       wpa_printf(MSG_DEBUG,
+                  "EAP: building EAP-Initiate-Re-auth-Start: Identifier %d",
+                  sm->currentId);
+       sm->lastId = sm->currentId;
+       wpabuf_free(sm->eap_if.eapReqData);
+       sm->eap_if.eapReqData = eap_sm_buildInitiateReauthStart(sm,
+                                                               sm->currentId);
+       wpabuf_free(sm->lastReqData);
+       sm->lastReqData = NULL;
+}
+
+
+#ifdef CONFIG_ERP
+
+static void erp_send_finish_reauth(struct eap_sm *sm,
+                                  struct eap_server_erp_key *erp, u8 id,
+                                  u8 flags, u16 seq, const char *nai)
+{
+       size_t plen;
+       struct wpabuf *msg;
+       u8 hash[SHA256_MAC_LEN];
+       size_t hash_len;
+       u8 seed[4];
+
+       if (erp) {
+               switch (erp->cryptosuite) {
+               case EAP_ERP_CS_HMAC_SHA256_256:
+                       hash_len = 32;
+                       break;
+               case EAP_ERP_CS_HMAC_SHA256_128:
+                       hash_len = 16;
+                       break;
+               default:
+                       return;
+               }
+       } else
+               hash_len = 0;
+
+       plen = 1 + 2 + 2 + os_strlen(nai);
+       if (hash_len)
+               plen += 1 + hash_len;
+       msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH, plen,
+                           EAP_CODE_FINISH, id);
+       if (msg == NULL)
+               return;
+       wpabuf_put_u8(msg, flags);
+       wpabuf_put_be16(msg, seq);
+
+       wpabuf_put_u8(msg, EAP_ERP_TLV_KEYNAME_NAI);
+       wpabuf_put_u8(msg, os_strlen(nai));
+       wpabuf_put_str(msg, nai);
+
+       if (erp) {
+               wpabuf_put_u8(msg, erp->cryptosuite);
+               if (hmac_sha256(erp->rIK, erp->rIK_len,
+                               wpabuf_head(msg), wpabuf_len(msg), hash) < 0) {
+                       wpabuf_free(msg);
+                       return;
+               }
+               wpabuf_put_data(msg, hash, hash_len);
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP: Send EAP-Finish/Re-auth (%s)",
+                  flags & 0x80 ? "failure" : "success");
+
+       sm->lastId = sm->currentId;
+       sm->currentId = id;
+       wpabuf_free(sm->eap_if.eapReqData);
+       sm->eap_if.eapReqData = msg;
+       wpabuf_free(sm->lastReqData);
+       sm->lastReqData = NULL;
+
+       if (flags & 0x80) {
+               sm->eap_if.eapFail = TRUE;
+               wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE
+                       MACSTR, MAC2STR(sm->peer_addr));
+               return;
+       }
+
+       bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
+       sm->eap_if.eapKeyDataLen = 0;
+       sm->eap_if.eapKeyData = os_malloc(erp->rRK_len);
+       if (!sm->eap_if.eapKeyData)
+               return;
+
+       WPA_PUT_BE16(seed, seq);
+       WPA_PUT_BE16(&seed[2], erp->rRK_len);
+       if (hmac_sha256_kdf(erp->rRK, erp->rRK_len,
+                           "Re-authentication Master Session Key@ietf.org",
+                           seed, sizeof(seed),
+                           sm->eap_if.eapKeyData, erp->rRK_len) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP: Could not derive rMSK for ERP");
+               bin_clear_free(sm->eap_if.eapKeyData, erp->rRK_len);
+               sm->eap_if.eapKeyData = NULL;
+               return;
+       }
+       sm->eap_if.eapKeyDataLen = erp->rRK_len;
+       sm->eap_if.eapKeyAvailable = TRUE;
+       wpa_hexdump_key(MSG_DEBUG, "EAP: ERP rMSK",
+                       sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
+       sm->eap_if.eapSuccess = TRUE;
+
+       wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS
+               MACSTR, MAC2STR(sm->peer_addr));
+}
+
+
+SM_STATE(EAP, INITIATE_RECEIVED)
+{
+       const u8 *pos, *end, *start, *tlvs, *hdr;
+       const struct eap_hdr *ehdr;
+       size_t len;
+       u8 flags;
+       u16 seq;
+       char nai[254];
+       struct eap_server_erp_key *erp;
+       int max_len;
+       u8 hash[SHA256_MAC_LEN];
+       size_t hash_len;
+       struct erp_tlvs parse;
+       u8 resp_flags = 0x80; /* default to failure; cleared on success */
+
+       SM_ENTRY(EAP, INITIATE_RECEIVED);
+
+       sm->rxInitiate = FALSE;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_ERP_TYPE_REAUTH,
+                              sm->eap_if.eapRespData, &len);
+       if (pos == NULL) {
+               wpa_printf(MSG_INFO, "EAP-Initiate: Invalid frame");
+               goto fail;
+       }
+       hdr = wpabuf_head(sm->eap_if.eapRespData);
+       ehdr = wpabuf_head(sm->eap_if.eapRespData);
+
+       wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-Auth", pos, len);
+       if (len < 4) {
+               wpa_printf(MSG_INFO, "EAP: Too short EAP-Initiate/Re-auth");
+               goto fail;
+       }
+       end = pos + len;
+
+       flags = *pos++;
+       seq = WPA_GET_BE16(pos);
+       pos += 2;
+       wpa_printf(MSG_DEBUG, "EAP: Flags=0x%x SEQ=%u", flags, seq);
+       tlvs = pos;
+
+       /*
+        * Parse TVs/TLVs. Since we do not yet know the length of the
+        * Authentication Tag, stop parsing if an unknown TV/TLV is seen and
+        * just try to find the keyName-NAI first so that we can check the
+        * Authentication Tag.
+        */
+       if (erp_parse_tlvs(tlvs, end, &parse, 1) < 0)
+               goto fail;
+
+       if (!parse.keyname) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: No keyName-NAI in EAP-Initiate/Re-auth Packet");
+               goto fail;
+       }
+
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP: EAP-Initiate/Re-auth - keyName-NAI",
+                         parse.keyname, parse.keyname_len);
+       if (parse.keyname_len > 253) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Too long keyName-NAI in EAP-Initiate/Re-auth");
+               goto fail;
+       }
+       os_memcpy(nai, parse.keyname, parse.keyname_len);
+       nai[parse.keyname_len] = '\0';
+
+       if (!sm->eap_server) {
+               /*
+                * In passthrough case, EAP-Initiate/Re-auth replaces
+                * EAP Identity exchange. Use keyName-NAI as the user identity
+                * and forward EAP-Initiate/Re-auth to the backend
+                * authentication server.
+                */
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Use keyName-NAI as user identity for backend authentication");
+               eap_server_clear_identity(sm);
+               sm->identity = (u8 *) dup_binstr(parse.keyname,
+                                                parse.keyname_len);
+               if (!sm->identity)
+                       goto fail;
+               sm->identity_len = parse.keyname_len;
+               return;
+       }
+
+       erp = eap_erp_get_key(sm, nai);
+       if (!erp) {
+               wpa_printf(MSG_DEBUG, "EAP: No matching ERP key found for %s",
+                          nai);
+               goto report_error;
+       }
+
+       if (erp->recv_seq != (u32) -1 && erp->recv_seq >= seq) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: SEQ=%u replayed (already received SEQ=%u)",
+                          seq, erp->recv_seq);
+               goto fail;
+       }
+
+       /* Is there enough room for Cryptosuite and Authentication Tag? */
+       start = parse.keyname + parse.keyname_len;
+       max_len = end - start;
+       if (max_len <
+           1 + (erp->cryptosuite == EAP_ERP_CS_HMAC_SHA256_256 ? 32 : 16)) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: Not enough room for Authentication Tag");
+               goto fail;
+       }
+
+       switch (erp->cryptosuite) {
+       case EAP_ERP_CS_HMAC_SHA256_256:
+               if (end[-33] != erp->cryptosuite) {
+                       wpa_printf(MSG_DEBUG,
+                                  "EAP: Different Cryptosuite used");
+                       goto fail;
+               }
+               hash_len = 32;
+               break;
+       case EAP_ERP_CS_HMAC_SHA256_128:
+               if (end[-17] != erp->cryptosuite) {
+                       wpa_printf(MSG_DEBUG,
+                                  "EAP: Different Cryptosuite used");
+                       goto fail;
+               }
+               hash_len = 16;
+               break;
+       default:
+               hash_len = 0;
+               break;
+       }
+
+       if (hash_len) {
+               if (hmac_sha256(erp->rIK, erp->rIK_len, hdr,
+                               end - hdr - hash_len, hash) < 0)
+                       goto fail;
+               if (os_memcmp(end - hash_len, hash, hash_len) != 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "EAP: Authentication Tag mismatch");
+                       goto fail;
+               }
+       }
+
+       /* Check if any supported CS results in matching tag */
+       if (!hash_len && max_len >= 1 + 32 &&
+           end[-33] == EAP_ERP_CS_HMAC_SHA256_256) {
+               if (hmac_sha256(erp->rIK, erp->rIK_len, hdr,
+                               end - hdr - 32, hash) < 0)
+                       goto fail;
+               if (os_memcmp(end - 32, hash, 32) == 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "EAP: Authentication Tag match using HMAC-SHA256-256");
+                       hash_len = 32;
+                       erp->cryptosuite = EAP_ERP_CS_HMAC_SHA256_256;
+               }
+       }
+
+       if (!hash_len && end[-17] == EAP_ERP_CS_HMAC_SHA256_128) {
+               if (hmac_sha256(erp->rIK, erp->rIK_len, hdr,
+                               end - hdr - 16, hash) < 0)
+                       goto fail;
+               if (os_memcmp(end - 16, hash, 16) == 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "EAP: Authentication Tag match using HMAC-SHA256-128");
+                       hash_len = 16;
+                       erp->cryptosuite = EAP_ERP_CS_HMAC_SHA256_128;
+               }
+       }
+
+       if (!hash_len) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: No supported cryptosuite matched Authentication Tag");
+               goto fail;
+       }
+       end -= 1 + hash_len;
+
+       /*
+        * Parse TVs/TLVs again now that we know the exact part of the buffer
+        * that contains them.
+        */
+       wpa_hexdump(MSG_DEBUG, "EAP: EAP-Initiate/Re-Auth TVs/TLVs",
+                   tlvs, end - tlvs);
+       if (erp_parse_tlvs(tlvs, end, &parse, 0) < 0)
+               goto fail;
+
+       wpa_printf(MSG_DEBUG, "EAP: ERP key %s SEQ updated to %u",
+                  erp->keyname_nai, seq);
+       erp->recv_seq = seq;
+       resp_flags &= ~0x80; /* R=0 - success */
+
+report_error:
+       erp_send_finish_reauth(sm, erp, ehdr->identifier, resp_flags, seq, nai);
+       return;
+
+fail:
+       sm->ignore = TRUE;
+}
+
+#endif /* CONFIG_ERP */
+
+
 SM_STATE(EAP, INITIALIZE_PASSTHROUGH)
 {
        SM_ENTRY(EAP, INITIALIZE_PASSTHROUGH);
 
        wpabuf_free(sm->eap_if.aaaEapRespData);
        sm->eap_if.aaaEapRespData = NULL;
+       sm->try_initiate_reauth = FALSE;
 }
 
 
@@ -596,7 +1120,7 @@ SM_STATE(EAP, SUCCESS2)
        if (sm->eap_if.aaaEapKeyAvailable) {
                EAP_COPY(&sm->eap_if.eapKeyData, sm->eap_if.aaaEapKeyData);
        } else {
-               os_free(sm->eap_if.eapKeyData);
+               bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
                sm->eap_if.eapKeyData = NULL;
                sm->eap_if.eapKeyDataLen = 0;
        }
@@ -655,9 +1179,14 @@ SM_STEP(EAP)
                        SM_ENTER(EAP, INITIALIZE);
                break;
        case EAP_IDLE:
-               if (sm->eap_if.retransWhile == 0)
-                       SM_ENTER(EAP, RETRANSMIT);
-               else if (sm->eap_if.eapResp)
+               if (sm->eap_if.retransWhile == 0) {
+                       if (sm->try_initiate_reauth) {
+                               sm->try_initiate_reauth = FALSE;
+                               SM_ENTER(EAP, SELECT_ACTION);
+                       } else {
+                               SM_ENTER(EAP, RETRANSMIT);
+                       }
+               } else if (sm->eap_if.eapResp)
                        SM_ENTER(EAP, RECEIVED);
                break;
        case EAP_RETRANSMIT:
@@ -680,12 +1209,17 @@ SM_STEP(EAP)
                           sm->respVendor == EAP_VENDOR_IETF &&
                           sm->respVendorMethod == sm->currentMethod)))
                        SM_ENTER(EAP, INTEGRITY_CHECK);
+#ifdef CONFIG_ERP
+               else if (sm->rxInitiate)
+                       SM_ENTER(EAP, INITIATE_RECEIVED);
+#endif /* CONFIG_ERP */
                else {
                        wpa_printf(MSG_DEBUG, "EAP: RECEIVED->DISCARD: "
                                   "rxResp=%d respId=%d currentId=%d "
                                   "respMethod=%d currentMethod=%d",
                                   sm->rxResp, sm->respId, sm->currentId,
                                   sm->respMethod, sm->currentMethod);
+                       eap_log_msg(sm, "Discard received EAP message");
                        SM_ENTER(EAP, DISCARD);
                }
                break;
@@ -702,6 +1236,15 @@ SM_STEP(EAP)
                        SM_ENTER(EAP, METHOD_RESPONSE);
                break;
        case EAP_METHOD_REQUEST:
+               if (sm->m == NULL) {
+                       /*
+                        * This transition is not mentioned in RFC 4137, but it
+                        * is needed to handle cleanly a case where EAP method
+                        * initialization fails.
+                        */
+                       SM_ENTER(EAP, FAILURE);
+                       break;
+               }
                SM_ENTER(EAP, SEND_REQUEST);
                break;
        case EAP_METHOD_RESPONSE:
@@ -758,9 +1301,22 @@ SM_STEP(EAP)
                        SM_ENTER(EAP, SUCCESS);
                else if (sm->decision == DECISION_PASSTHROUGH)
                        SM_ENTER(EAP, INITIALIZE_PASSTHROUGH);
+               else if (sm->decision == DECISION_INITIATE_REAUTH_START)
+                       SM_ENTER(EAP, INITIATE_REAUTH_START);
+#ifdef CONFIG_ERP
+               else if (sm->eap_server && sm->erp && sm->rxInitiate)
+                       SM_ENTER(EAP, INITIATE_RECEIVED);
+#endif /* CONFIG_ERP */
                else
                        SM_ENTER(EAP, PROPOSE_METHOD);
                break;
+       case EAP_INITIATE_REAUTH_START:
+               SM_ENTER(EAP, SEND_REQUEST);
+               break;
+       case EAP_INITIATE_RECEIVED:
+               if (!sm->eap_server)
+                       SM_ENTER(EAP, SELECT_ACTION);
+               break;
        case EAP_TIMEOUT_FAILURE:
                break;
        case EAP_FAILURE:
@@ -830,6 +1386,12 @@ static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
 {
        int rto, i;
 
+       if (sm->try_initiate_reauth) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: retransmit timeout 1 second for EAP-Initiate-Re-auth-Start");
+               return 1;
+       }
+
        if (methodTimeout) {
                /*
                 * EAP method (either internal or through AAA server, provided
@@ -883,6 +1445,7 @@ static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp)
 
        /* parse rxResp, respId, respMethod */
        sm->rxResp = FALSE;
+       sm->rxInitiate = FALSE;
        sm->respId = -1;
        sm->respMethod = EAP_TYPE_NONE;
        sm->respVendor = EAP_VENDOR_IETF;
@@ -909,6 +1472,8 @@ static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp)
 
        if (hdr->code == EAP_CODE_RESPONSE)
                sm->rxResp = TRUE;
+       else if (hdr->code == EAP_CODE_INITIATE)
+               sm->rxInitiate = TRUE;
 
        if (plen > sizeof(*hdr)) {
                u8 *pos = (u8 *) (hdr + 1);
@@ -926,10 +1491,10 @@ static void eap_sm_parseEapResp(struct eap_sm *sm, const struct wpabuf *resp)
                }
        }
 
-       wpa_printf(MSG_DEBUG, "EAP: parseEapResp: rxResp=%d respId=%d "
-                  "respMethod=%u respVendor=%u respVendorMethod=%u",
-                  sm->rxResp, sm->respId, sm->respMethod, sm->respVendor,
-                  sm->respVendorMethod);
+       wpa_printf(MSG_DEBUG,
+                  "EAP: parseEapResp: rxResp=%d rxInitiate=%d respId=%d respMethod=%u respVendor=%u respVendorMethod=%u",
+                  sm->rxResp, sm->rxInitiate, sm->respId, sm->respMethod,
+                  sm->respVendor, sm->respVendorMethod);
 }
 
 
@@ -1170,6 +1735,13 @@ static int eap_sm_Policy_getDecision(struct eap_sm *sm)
                return DECISION_CONTINUE;
        }
 
+       if (!sm->identity && eap_get_erp_send_reauth_start(sm) &&
+           !sm->initiate_reauth_start_sent) {
+               wpa_printf(MSG_DEBUG,
+                          "EAP: getDecision: send EAP-Initiate/Re-auth-Start");
+               return DECISION_INITIATE_REAUTH_START;
+       }
+
        if (sm->identity == NULL || sm->currentId == -1) {
                wpa_printf(MSG_DEBUG, "EAP: getDecision: no identity known "
                           "yet -> CONTINUE");
@@ -1214,7 +1786,7 @@ static void eap_user_free(struct eap_user *user)
 {
        if (user == NULL)
                return;
-       os_free(user->password);
+       bin_clear_free(user->password, user->password_len);
        user->password = NULL;
        os_free(user);
 }
@@ -1278,6 +1850,13 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx,
        sm->fragment_size = conf->fragment_size;
        sm->pwd_group = conf->pwd_group;
        sm->pbc_in_m1 = conf->pbc_in_m1;
+       sm->server_id = conf->server_id;
+       sm->server_id_len = conf->server_id_len;
+       sm->erp = conf->erp;
+
+#ifdef CONFIG_TESTING_OPTIONS
+       sm->tls_test_flags = conf->tls_test_flags;
+#endif /* CONFIG_TESTING_OPTIONS */
 
        wpa_printf(MSG_DEBUG, "EAP: Server state machine created");
 
@@ -1300,7 +1879,8 @@ void eap_server_sm_deinit(struct eap_sm *sm)
        if (sm->m && sm->eap_method_priv)
                sm->m->reset(sm, sm->eap_method_priv);
        wpabuf_free(sm->eap_if.eapReqData);
-       os_free(sm->eap_if.eapKeyData);
+       bin_clear_free(sm->eap_if.eapKeyData, sm->eap_if.eapKeyDataLen);
+       os_free(sm->eap_if.eapSessionId);
        wpabuf_free(sm->lastReqData);
        wpabuf_free(sm->eap_if.eapRespData);
        os_free(sm->identity);
@@ -1309,7 +1889,7 @@ void eap_server_sm_deinit(struct eap_sm *sm)
        os_free(sm->eap_fast_a_id_info);
        wpabuf_free(sm->eap_if.aaaEapReqData);
        wpabuf_free(sm->eap_if.aaaEapRespData);
-       os_free(sm->eap_if.aaaEapKeyData);
+       bin_clear_free(sm->eap_if.aaaEapKeyData, sm->eap_if.aaaEapKeyDataLen);
        eap_user_free(sm->user);
        wpabuf_free(sm->assoc_wps_ie);
        wpabuf_free(sm->assoc_p2p_ie);
index 469b9a0..db9b6aa 100644 (file)
@@ -241,7 +241,7 @@ static void eap_aka_reset(struct eap_sm *sm, void *priv)
        os_free(data->next_reauth_id);
        wpabuf_free(data->id_msgs);
        os_free(data->network_name);
-       os_free(data);
+       bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -336,7 +336,7 @@ static int eap_aka_verify_checkcode(struct eap_aka_data *data,
        else
                sha1_vector(1, &addr, &len, hash);
 
-       if (os_memcmp(hash, checkcode, hash_len) != 0) {
+       if (os_memcmp_const(hash, checkcode, hash_len) != 0) {
                wpa_printf(MSG_DEBUG, "EAP-AKA: Mismatch in AT_CHECKCODE");
                return -1;
        }
@@ -377,7 +377,7 @@ static struct wpabuf * eap_aka_build_identity(struct eap_sm *sm,
                wpa_printf(MSG_DEBUG, "   AT_PERMANENT_ID_REQ");
                eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0);
        }
-       buf = eap_sim_msg_finish(msg, NULL, NULL, 0);
+       buf = eap_sim_msg_finish(msg, data->eap_method, NULL, NULL, 0);
        if (eap_aka_add_id_msg(data, buf) < 0) {
                wpabuf_free(buf);
                return NULL;
@@ -534,7 +534,7 @@ static struct wpabuf * eap_aka_build_challenge(struct eap_sm *sm,
 
        wpa_printf(MSG_DEBUG, "   AT_MAC");
        eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
-       return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
+       return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0);
 }
 
 
@@ -581,7 +581,7 @@ static struct wpabuf * eap_aka_build_reauth(struct eap_sm *sm,
 
        wpa_printf(MSG_DEBUG, "   AT_MAC");
        eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
-       return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
+       return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0);
 }
 
 
@@ -620,7 +620,7 @@ static struct wpabuf * eap_aka_build_notification(struct eap_sm *sm,
                wpa_printf(MSG_DEBUG, "   AT_MAC");
                eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
        }
-       return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
+       return eap_sim_msg_finish(msg, data->eap_method, data->k_aut, NULL, 0);
 }
 
 
@@ -963,7 +963,7 @@ static void eap_aka_process_challenge(struct eap_sm *sm,
         */
        if (attr->res == NULL || attr->res_len < data->res_len ||
            attr->res_len_bits != data->res_len * 8 ||
-           os_memcmp(attr->res, data->res, data->res_len) != 0) {
+           os_memcmp_const(attr->res, data->res, data->res_len) != 0) {
                wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message did not "
                           "include valid AT_RES (attr len=%lu, res len=%lu "
                           "bits, expected %lu bits)",
@@ -1040,6 +1040,7 @@ static void eap_aka_process_sync_failure(struct eap_sm *sm,
        data->auts_reported = 1;
 
        /* Remain in CHALLENGE state to re-try after resynchronization */
+       eap_aka_fullauth(sm, data);
 }
 
 
@@ -1293,6 +1294,28 @@ static Boolean eap_aka_isSuccess(struct eap_sm *sm, void *priv)
 }
 
 
+static u8 * eap_aka_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_aka_data *data = priv;
+       u8 *id;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       *len = 1 + EAP_AKA_RAND_LEN + EAP_AKA_AUTN_LEN;
+       id = os_malloc(*len);
+       if (id == NULL)
+               return NULL;
+
+       id[0] = data->eap_method;
+       os_memcpy(id + 1, data->rand, EAP_AKA_RAND_LEN);
+       os_memcpy(id + 1 + EAP_AKA_RAND_LEN, data->autn, EAP_AKA_AUTN_LEN);
+       wpa_hexdump(MSG_DEBUG, "EAP-AKA: Derived Session-Id", id, *len);
+
+       return id;
+}
+
+
 int eap_server_aka_register(void)
 {
        struct eap_method *eap;
@@ -1312,6 +1335,7 @@ int eap_server_aka_register(void)
        eap->getKey = eap_aka_getKey;
        eap->isSuccess = eap_aka_isSuccess;
        eap->get_emsk = eap_aka_get_emsk;
+       eap->getSessionId = eap_aka_get_session_id;
 
        ret = eap_server_method_register(eap);
        if (ret)
@@ -1341,6 +1365,7 @@ int eap_server_aka_prime_register(void)
        eap->getKey = eap_aka_getKey;
        eap->isSuccess = eap_aka_isSuccess;
        eap->get_emsk = eap_aka_get_emsk;
+       eap->getSessionId = eap_aka_get_session_id;
 
        ret = eap_server_method_register(eap);
        if (ret)
diff --git a/src/eap_server/eap_server_eke.c b/src/eap_server/eap_server_eke.c
new file mode 100644 (file)
index 0000000..966f511
--- /dev/null
@@ -0,0 +1,793 @@
+/*
+ * hostapd / EAP-EKE (RFC 6124) server
+ * Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "crypto/random.h"
+#include "eap_server/eap_i.h"
+#include "eap_common/eap_eke_common.h"
+
+
+struct eap_eke_data {
+       enum {
+               IDENTITY, COMMIT, CONFIRM, FAILURE_REPORT, SUCCESS, FAILURE
+       } state;
+       u8 msk[EAP_MSK_LEN];
+       u8 emsk[EAP_EMSK_LEN];
+       u8 *peerid;
+       size_t peerid_len;
+       u8 peerid_type;
+       u8 serverid_type;
+       u8 dh_priv[EAP_EKE_MAX_DH_LEN];
+       u8 key[EAP_EKE_MAX_KEY_LEN];
+       struct eap_eke_session sess;
+       u8 nonce_p[EAP_EKE_MAX_NONCE_LEN];
+       u8 nonce_s[EAP_EKE_MAX_NONCE_LEN];
+       struct wpabuf *msgs;
+       int phase2;
+       u32 failure_code;
+};
+
+
+static const char * eap_eke_state_txt(int state)
+{
+       switch (state) {
+       case IDENTITY:
+               return "IDENTITY";
+       case COMMIT:
+               return "COMMIT";
+       case CONFIRM:
+               return "CONFIRM";
+       case FAILURE_REPORT:
+               return "FAILURE_REPORT";
+       case SUCCESS:
+               return "SUCCESS";
+       case FAILURE:
+               return "FAILURE";
+       default:
+               return "?";
+       }
+}
+
+
+static void eap_eke_state(struct eap_eke_data *data, int state)
+{
+       wpa_printf(MSG_DEBUG, "EAP-EKE: %s -> %s",
+                  eap_eke_state_txt(data->state),
+                  eap_eke_state_txt(state));
+       data->state = state;
+}
+
+
+static void eap_eke_fail(struct eap_eke_data *data, u32 code)
+{
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Failure - code 0x%x", code);
+       data->failure_code = code;
+       eap_eke_state(data, FAILURE_REPORT);
+}
+
+
+static void * eap_eke_init(struct eap_sm *sm)
+{
+       struct eap_eke_data *data;
+       size_t i;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       eap_eke_state(data, IDENTITY);
+
+       data->serverid_type = EAP_EKE_ID_OPAQUE;
+       for (i = 0; i < sm->server_id_len; i++) {
+               if (sm->server_id[i] == '.' &&
+                   data->serverid_type == EAP_EKE_ID_OPAQUE)
+                       data->serverid_type = EAP_EKE_ID_FQDN;
+               if (sm->server_id[i] == '@')
+                       data->serverid_type = EAP_EKE_ID_NAI;
+       }
+
+       data->phase2 = sm->init_phase2;
+
+       return data;
+}
+
+
+static void eap_eke_reset(struct eap_sm *sm, void *priv)
+{
+       struct eap_eke_data *data = priv;
+       eap_eke_session_clean(&data->sess);
+       os_free(data->peerid);
+       wpabuf_free(data->msgs);
+       bin_clear_free(data, sizeof(*data));
+}
+
+
+static struct wpabuf * eap_eke_build_msg(struct eap_eke_data *data,
+                                        u8 id, size_t length, u8 eke_exch)
+{
+       struct wpabuf *msg;
+       size_t plen;
+
+       plen = 1 + length;
+
+       msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_EKE, plen,
+                           EAP_CODE_REQUEST, id);
+       if (msg == NULL) {
+               wpa_printf(MSG_ERROR, "EAP-EKE: Failed to allocate memory");
+               return NULL;
+       }
+
+       wpabuf_put_u8(msg, eke_exch);
+
+       return msg;
+}
+
+
+static int supported_proposal(const u8 *pos)
+{
+       if (pos[0] == EAP_EKE_DHGROUP_EKE_16 &&
+           pos[1] == EAP_EKE_ENCR_AES128_CBC &&
+           pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 &&
+           pos[3] == EAP_EKE_MAC_HMAC_SHA2_256)
+               return 1;
+
+       if (pos[0] == EAP_EKE_DHGROUP_EKE_15 &&
+           pos[1] == EAP_EKE_ENCR_AES128_CBC &&
+           pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 &&
+           pos[3] == EAP_EKE_MAC_HMAC_SHA2_256)
+               return 1;
+
+       if (pos[0] == EAP_EKE_DHGROUP_EKE_14 &&
+           pos[1] == EAP_EKE_ENCR_AES128_CBC &&
+           pos[2] == EAP_EKE_PRF_HMAC_SHA2_256 &&
+           pos[3] == EAP_EKE_MAC_HMAC_SHA2_256)
+               return 1;
+
+       if (pos[0] == EAP_EKE_DHGROUP_EKE_14 &&
+           pos[1] == EAP_EKE_ENCR_AES128_CBC &&
+           pos[2] == EAP_EKE_PRF_HMAC_SHA1 &&
+           pos[3] == EAP_EKE_MAC_HMAC_SHA1)
+               return 1;
+
+       return 0;
+}
+
+
+static struct wpabuf * eap_eke_build_failure(struct eap_eke_data *data, u8 id)
+{
+       struct wpabuf *msg;
+
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Failure: Failure-Code=0x%x",
+                  data->failure_code);
+
+       msg = eap_eke_build_msg(data, id, 4, EAP_EKE_FAILURE);
+       if (msg == NULL) {
+               eap_eke_state(data, FAILURE);
+               return NULL;
+       }
+       wpabuf_put_be32(msg, data->failure_code);
+
+       return msg;
+}
+
+
+static struct wpabuf * eap_eke_build_identity(struct eap_sm *sm,
+                                             struct eap_eke_data *data,
+                                             u8 id)
+{
+       struct wpabuf *msg;
+       size_t plen;
+
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Identity");
+
+       plen = 2 + 4 * 4 + 1 + sm->server_id_len;
+       msg = eap_eke_build_msg(data, id, plen, EAP_EKE_ID);
+       if (msg == NULL)
+               return NULL;
+
+       wpabuf_put_u8(msg, 4); /* NumProposals */
+       wpabuf_put_u8(msg, 0); /* Reserved */
+
+       /* Proposal - DH Group 16 with AES128-CBC and SHA256 */
+       wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_16); /* Group Description */
+       wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */
+       wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */
+       wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */
+
+       /* Proposal - DH Group 15 with AES128-CBC and SHA256 */
+       wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_15); /* Group Description */
+       wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */
+       wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */
+       wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */
+
+       /* Proposal - DH Group 14 with AES128-CBC and SHA256 */
+       wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_14); /* Group Description */
+       wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */
+       wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA2_256); /* PRF */
+       wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA2_256); /* MAC */
+
+       /*
+        * Proposal - DH Group 14 with AES128-CBC and SHA1
+        * (mandatory to implement algorithms)
+        */
+       wpabuf_put_u8(msg, EAP_EKE_DHGROUP_EKE_14); /* Group Description */
+       wpabuf_put_u8(msg, EAP_EKE_ENCR_AES128_CBC); /* Encryption */
+       wpabuf_put_u8(msg, EAP_EKE_PRF_HMAC_SHA1); /* PRF */
+       wpabuf_put_u8(msg, EAP_EKE_MAC_HMAC_SHA1); /* MAC */
+
+       /* Server IDType + Identity */
+       wpabuf_put_u8(msg, data->serverid_type);
+       wpabuf_put_data(msg, sm->server_id, sm->server_id_len);
+
+       wpabuf_free(data->msgs);
+       data->msgs = wpabuf_dup(msg);
+       if (data->msgs == NULL) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       return msg;
+}
+
+
+static struct wpabuf * eap_eke_build_commit(struct eap_sm *sm,
+                                           struct eap_eke_data *data, u8 id)
+{
+       struct wpabuf *msg;
+       u8 pub[EAP_EKE_MAX_DH_LEN];
+
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Commit");
+
+       if (sm->user == NULL || sm->user->password == NULL) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Password with not configured");
+               eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND);
+               return eap_eke_build_failure(data, id);
+       }
+
+       if (eap_eke_derive_key(&data->sess, sm->user->password,
+                              sm->user->password_len,
+                              sm->server_id, sm->server_id_len,
+                              data->peerid, data->peerid_len, data->key) < 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive key");
+               eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+               return eap_eke_build_failure(data, id);
+       }
+
+       msg = eap_eke_build_msg(data, id, data->sess.dhcomp_len,
+                               EAP_EKE_COMMIT);
+       if (msg == NULL) {
+               eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+               return eap_eke_build_failure(data, id);
+       }
+
+       /*
+        * y_s = g ^ x_s (mod p)
+        * x_s = random number 2 .. p-1
+        * temp = prf(0+, password)
+        * key = prf+(temp, ID_S | ID_P)
+        * DHComponent_S = Encr(key, y_s)
+        */
+
+       if (eap_eke_dh_init(data->sess.dhgroup, data->dh_priv, pub) < 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Failed to initialize DH");
+               eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+               return eap_eke_build_failure(data, id);
+       }
+
+       if (eap_eke_dhcomp(&data->sess, data->key, pub,
+                          wpabuf_put(msg, data->sess.dhcomp_len))
+           < 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Failed to build DHComponent_S");
+               wpabuf_free(msg);
+               eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+               return eap_eke_build_failure(data, id);
+       }
+
+       if (wpabuf_resize(&data->msgs, wpabuf_len(msg)) < 0) {
+               wpabuf_free(msg);
+               eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+               return eap_eke_build_failure(data, id);
+       }
+       wpabuf_put_buf(data->msgs, msg);
+
+       return msg;
+}
+
+
+static struct wpabuf * eap_eke_build_confirm(struct eap_sm *sm,
+                                            struct eap_eke_data *data, u8 id)
+{
+       struct wpabuf *msg;
+       size_t plen, prot_len;
+       u8 nonces[2 * EAP_EKE_MAX_NONCE_LEN];
+       u8 *auth;
+
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Request/Confirm");
+
+       plen = data->sess.pnonce_ps_len + data->sess.prf_len;
+       msg = eap_eke_build_msg(data, id, plen, EAP_EKE_CONFIRM);
+       if (msg == NULL) {
+               eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+               return eap_eke_build_failure(data, id);
+       }
+
+       if (random_get_bytes(data->nonce_s, data->sess.nonce_len)) {
+               wpabuf_free(msg);
+               eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+               return eap_eke_build_failure(data, id);
+       }
+       wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_S",
+                       data->nonce_s, data->sess.nonce_len);
+
+       os_memcpy(nonces, data->nonce_p, data->sess.nonce_len);
+       os_memcpy(nonces + data->sess.nonce_len, data->nonce_s,
+                 data->sess.nonce_len);
+       prot_len = wpabuf_tailroom(msg);
+       if (eap_eke_prot(&data->sess, nonces, 2 * data->sess.nonce_len,
+                        wpabuf_put(msg, 0), &prot_len) < 0) {
+               wpabuf_free(msg);
+               eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+               return eap_eke_build_failure(data, id);
+       }
+       wpabuf_put(msg, prot_len);
+
+       if (eap_eke_derive_ka(&data->sess,
+                             sm->server_id, sm->server_id_len,
+                             data->peerid, data->peerid_len,
+                             data->nonce_p, data->nonce_s) < 0) {
+               wpabuf_free(msg);
+               eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+               return eap_eke_build_failure(data, id);
+       }
+
+       auth = wpabuf_put(msg, data->sess.prf_len);
+       if (eap_eke_auth(&data->sess, "EAP-EKE server", data->msgs, auth) < 0) {
+               wpabuf_free(msg);
+               eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+               return eap_eke_build_failure(data, id);
+       }
+       wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_S", auth, data->sess.prf_len);
+
+       return msg;
+}
+
+
+static struct wpabuf * eap_eke_buildReq(struct eap_sm *sm, void *priv, u8 id)
+{
+       struct eap_eke_data *data = priv;
+
+       switch (data->state) {
+       case IDENTITY:
+               return eap_eke_build_identity(sm, data, id);
+       case COMMIT:
+               return eap_eke_build_commit(sm, data, id);
+       case CONFIRM:
+               return eap_eke_build_confirm(sm, data, id);
+       case FAILURE_REPORT:
+               return eap_eke_build_failure(data, id);
+       default:
+               wpa_printf(MSG_DEBUG, "EAP-EKE: Unknown state %d in buildReq",
+                          data->state);
+               break;
+       }
+       return NULL;
+}
+
+
+static Boolean eap_eke_check(struct eap_sm *sm, void *priv,
+                            struct wpabuf *respData)
+{
+       struct eap_eke_data *data = priv;
+       size_t len;
+       const u8 *pos;
+       u8 eke_exch;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, respData, &len);
+       if (pos == NULL || len < 1) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Invalid frame");
+               return TRUE;
+       }
+
+       eke_exch = *pos;
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Received frame: EKE-Exch=%d", eke_exch);
+
+       if (data->state == IDENTITY && eke_exch == EAP_EKE_ID)
+               return FALSE;
+
+       if (data->state == COMMIT && eke_exch == EAP_EKE_COMMIT)
+               return FALSE;
+
+       if (data->state == CONFIRM && eke_exch == EAP_EKE_CONFIRM)
+               return FALSE;
+
+       if (eke_exch == EAP_EKE_FAILURE)
+               return FALSE;
+
+       wpa_printf(MSG_INFO, "EAP-EKE: Unexpected EKE-Exch=%d in state=%d",
+                  eke_exch, data->state);
+
+       return TRUE;
+}
+
+
+static void eap_eke_process_identity(struct eap_sm *sm,
+                                    struct eap_eke_data *data,
+                                    const struct wpabuf *respData,
+                                    const u8 *payload, size_t payloadlen)
+{
+       const u8 *pos, *end;
+       int i;
+
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Identity");
+
+       if (data->state != IDENTITY) {
+               eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
+               return;
+       }
+
+       pos = payload;
+       end = payload + payloadlen;
+
+       if (pos + 2 + 4 + 1 > end) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Too short EAP-EKE-ID payload");
+               eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
+               return;
+       }
+
+       if (*pos != 1) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Unexpected NumProposals %d (expected 1)",
+                          *pos);
+               eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
+               return;
+       }
+
+       pos += 2;
+
+       if (!supported_proposal(pos)) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Unexpected Proposal (%u:%u:%u:%u)",
+                          pos[0], pos[1], pos[2], pos[3]);
+               eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Selected Proposal (%u:%u:%u:%u)",
+                  pos[0], pos[1], pos[2], pos[3]);
+       if (eap_eke_session_init(&data->sess, pos[0], pos[1], pos[2], pos[3]) <
+           0) {
+               eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+               return;
+       }
+       pos += 4;
+
+       data->peerid_type = *pos++;
+       os_free(data->peerid);
+       data->peerid = os_malloc(end - pos);
+       if (data->peerid == NULL) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Failed to allocate memory for peerid");
+               eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+               return;
+       }
+       os_memcpy(data->peerid, pos, end - pos);
+       data->peerid_len = end - pos;
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Peer IDType %u", data->peerid_type);
+       wpa_hexdump_ascii(MSG_DEBUG, "EAP-EKE: Peer Identity",
+                         data->peerid, data->peerid_len);
+
+       if (eap_user_get(sm, data->peerid, data->peerid_len, data->phase2)) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Peer Identity not found from user database");
+               eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND);
+               return;
+       }
+
+       for (i = 0; i < EAP_MAX_METHODS; i++) {
+               if (sm->user->methods[i].vendor == EAP_VENDOR_IETF &&
+                   sm->user->methods[i].method == EAP_TYPE_EKE)
+                       break;
+       }
+       if (i == EAP_MAX_METHODS) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Matching user entry does not allow EAP-EKE");
+               eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND);
+               return;
+       }
+
+       if (sm->user->password == NULL || sm->user->password_len == 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: No password configured for peer");
+               eap_eke_fail(data, EAP_EKE_FAIL_PASSWD_NOT_FOUND);
+               return;
+       }
+
+       if (wpabuf_resize(&data->msgs, wpabuf_len(respData)) < 0) {
+               eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+               return;
+       }
+       wpabuf_put_buf(data->msgs, respData);
+
+       eap_eke_state(data, COMMIT);
+}
+
+
+static void eap_eke_process_commit(struct eap_sm *sm,
+                                  struct eap_eke_data *data,
+                                  const struct wpabuf *respData,
+                                  const u8 *payload, size_t payloadlen)
+{
+       const u8 *pos, *end, *dhcomp, *pnonce;
+       size_t decrypt_len;
+
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Commit");
+
+       if (data->state != COMMIT) {
+               eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
+               return;
+       }
+
+       pos = payload;
+       end = payload + payloadlen;
+
+       if (pos + data->sess.dhcomp_len + data->sess.pnonce_len > end) {
+               wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Commit");
+               eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
+               return;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "EAP-EKE: DHComponent_P",
+                   pos, data->sess.dhcomp_len);
+       dhcomp = pos;
+       pos += data->sess.dhcomp_len;
+       wpa_hexdump(MSG_DEBUG, "EAP-EKE: PNonce_P", pos, data->sess.pnonce_len);
+       pnonce = pos;
+       pos += data->sess.pnonce_len;
+       wpa_hexdump(MSG_DEBUG, "EAP-EKE: CBValue", pos, end - pos);
+
+       if (eap_eke_shared_secret(&data->sess, data->key, data->dh_priv, dhcomp)
+           < 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive shared secret");
+               eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+               return;
+       }
+
+       if (eap_eke_derive_ke_ki(&data->sess,
+                                sm->server_id, sm->server_id_len,
+                                data->peerid, data->peerid_len) < 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive Ke/Ki");
+               eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+               return;
+       }
+
+       decrypt_len = sizeof(data->nonce_p);
+       if (eap_eke_decrypt_prot(&data->sess, pnonce, data->sess.pnonce_len,
+                                data->nonce_p, &decrypt_len) < 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_P");
+               eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
+               return;
+       }
+       if (decrypt_len < (size_t) data->sess.nonce_len) {
+               wpa_printf(MSG_INFO, "EAP-EKE: PNonce_P protected data too short to include Nonce_P");
+               eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
+               return;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Nonce_P",
+                       data->nonce_p, data->sess.nonce_len);
+
+       if (wpabuf_resize(&data->msgs, wpabuf_len(respData)) < 0) {
+               eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+               return;
+       }
+       wpabuf_put_buf(data->msgs, respData);
+
+       eap_eke_state(data, CONFIRM);
+}
+
+
+static void eap_eke_process_confirm(struct eap_sm *sm,
+                                   struct eap_eke_data *data,
+                                   const struct wpabuf *respData,
+                                   const u8 *payload, size_t payloadlen)
+{
+       size_t decrypt_len;
+       u8 nonce[EAP_EKE_MAX_NONCE_LEN];
+       u8 auth_p[EAP_EKE_MAX_HASH_LEN];
+
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Confirm");
+
+       if (data->state != CONFIRM) {
+               eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Confirm");
+
+       if (payloadlen < (size_t) data->sess.pnonce_len + data->sess.prf_len) {
+               wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Confirm");
+               eap_eke_fail(data, EAP_EKE_FAIL_PROTO_ERROR);
+               return;
+       }
+
+       decrypt_len = sizeof(nonce);
+       if (eap_eke_decrypt_prot(&data->sess, payload, data->sess.pnonce_len,
+                                nonce, &decrypt_len) < 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Failed to decrypt PNonce_S");
+               eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
+               return;
+       }
+       if (decrypt_len < (size_t) data->sess.nonce_len) {
+               wpa_printf(MSG_INFO, "EAP-EKE: PNonce_S protected data too short to include Nonce_S");
+               eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
+               return;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "EAP-EKE: Received Nonce_S",
+                       nonce, data->sess.nonce_len);
+       if (os_memcmp(nonce, data->nonce_s, data->sess.nonce_len) != 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Received Nonce_S does not match previously sent Nonce_S");
+               eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
+               return;
+       }
+
+       if (eap_eke_auth(&data->sess, "EAP-EKE peer", data->msgs, auth_p) < 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Could not derive Auth_P");
+               eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+               return;
+       }
+       wpa_hexdump(MSG_DEBUG, "EAP-EKE: Auth_P", auth_p, data->sess.prf_len);
+       if (os_memcmp_const(auth_p, payload + data->sess.pnonce_len,
+                           data->sess.prf_len) != 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Auth_P does not match");
+               eap_eke_fail(data, EAP_EKE_FAIL_AUTHENTICATION_FAIL);
+               return;
+       }
+
+       if (eap_eke_derive_msk(&data->sess, sm->server_id, sm->server_id_len,
+                              data->peerid, data->peerid_len,
+                              data->nonce_s, data->nonce_p,
+                              data->msk, data->emsk) < 0) {
+               wpa_printf(MSG_INFO, "EAP-EKE: Failed to derive MSK/EMSK");
+               eap_eke_fail(data, EAP_EKE_FAIL_PRIVATE_INTERNAL_ERROR);
+               return;
+       }
+
+       os_memset(data->dh_priv, 0, sizeof(data->dh_priv));
+       os_memset(data->key, 0, sizeof(data->key));
+       eap_eke_session_clean(&data->sess);
+
+       eap_eke_state(data, SUCCESS);
+}
+
+
+static void eap_eke_process_failure(struct eap_sm *sm,
+                                   struct eap_eke_data *data,
+                                   const struct wpabuf *respData,
+                                   const u8 *payload, size_t payloadlen)
+{
+       u32 code;
+
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Received Response/Failure");
+
+       if (payloadlen < 4) {
+               wpa_printf(MSG_DEBUG, "EAP-EKE: Too short EAP-EKE-Failure");
+               eap_eke_state(data, FAILURE);
+               return;
+       }
+
+       code = WPA_GET_BE32(payload);
+       wpa_printf(MSG_DEBUG, "EAP-EKE: Peer reported failure code 0x%x", code);
+
+       eap_eke_state(data, FAILURE);
+}
+
+
+static void eap_eke_process(struct eap_sm *sm, void *priv,
+                            struct wpabuf *respData)
+{
+       struct eap_eke_data *data = priv;
+       u8 eke_exch;
+       size_t len;
+       const u8 *pos, *end;
+
+       pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_EKE, respData, &len);
+       if (pos == NULL || len < 1)
+               return;
+
+       eke_exch = *pos;
+       end = pos + len;
+       pos++;
+
+       wpa_hexdump(MSG_DEBUG, "EAP-EKE: Received payload", pos, end - pos);
+
+       switch (eke_exch) {
+       case EAP_EKE_ID:
+               eap_eke_process_identity(sm, data, respData, pos, end - pos);
+               break;
+       case EAP_EKE_COMMIT:
+               eap_eke_process_commit(sm, data, respData, pos, end - pos);
+               break;
+       case EAP_EKE_CONFIRM:
+               eap_eke_process_confirm(sm, data, respData, pos, end - pos);
+               break;
+       case EAP_EKE_FAILURE:
+               eap_eke_process_failure(sm, data, respData, pos, end - pos);
+               break;
+       }
+}
+
+
+static Boolean eap_eke_isDone(struct eap_sm *sm, void *priv)
+{
+       struct eap_eke_data *data = priv;
+       return data->state == SUCCESS || data->state == FAILURE;
+}
+
+
+static u8 * eap_eke_getKey(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_eke_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_MSK_LEN);
+       if (key == NULL)
+               return NULL;
+       os_memcpy(key, data->msk, EAP_MSK_LEN);
+       *len = EAP_MSK_LEN;
+
+       return key;
+}
+
+
+static u8 * eap_eke_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_eke_data *data = priv;
+       u8 *key;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       key = os_malloc(EAP_EMSK_LEN);
+       if (key == NULL)
+               return NULL;
+       os_memcpy(key, data->emsk, EAP_EMSK_LEN);
+       *len = EAP_EMSK_LEN;
+
+       return key;
+}
+
+
+static Boolean eap_eke_isSuccess(struct eap_sm *sm, void *priv)
+{
+       struct eap_eke_data *data = priv;
+       return data->state == SUCCESS;
+}
+
+
+int eap_server_eke_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_IETF, EAP_TYPE_EKE, "EKE");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_eke_init;
+       eap->reset = eap_eke_reset;
+       eap->buildReq = eap_eke_buildReq;
+       eap->check = eap_eke_check;
+       eap->process = eap_eke_process;
+       eap->isDone = eap_eke_isDone;
+       eap->getKey = eap_eke_getKey;
+       eap->isSuccess = eap_eke_isSuccess;
+       eap->get_emsk = eap_eke_get_emsk;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
index fcb80dc..6745100 100644 (file)
@@ -161,8 +161,8 @@ static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
                return 0;
        }
 
-       if (aes_unwrap(data->pac_opaque_encr, (pac_opaque_len - 8) / 8,
-                      pac_opaque, buf) < 0) {
+       if (aes_unwrap(data->pac_opaque_encr, sizeof(data->pac_opaque_encr),
+                      (pac_opaque_len - 8) / 8, pac_opaque, buf) < 0) {
                wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to decrypt "
                           "PAC-Opaque");
                os_free(buf);
@@ -186,8 +186,7 @@ static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
 
                switch (*pos) {
                case PAC_OPAQUE_TYPE_PAD:
-                       pos = end;
-                       break;
+                       goto done;
                case PAC_OPAQUE_TYPE_KEY:
                        if (pos[1] != EAP_FAST_PAC_KEY_LEN) {
                                wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid "
@@ -218,6 +217,7 @@ static int eap_fast_session_ticket_cb(void *ctx, const u8 *ticket, size_t len,
 
                pos += 2 + pos[1];
        }
+done:
 
        if (pac_key == NULL) {
                wpa_printf(MSG_DEBUG, "EAP-FAST: No PAC-Key included in "
@@ -511,7 +511,7 @@ static void eap_fast_reset(struct eap_sm *sm, void *priv)
        os_free(data->key_block_p);
        wpabuf_free(data->pending_phase2_resp);
        os_free(data->identity);
-       os_free(data);
+       bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -730,8 +730,8 @@ static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm,
                os_free(pac_buf);
                return NULL;
        }
-       if (aes_wrap(data->pac_opaque_encr, pac_len / 8, pac_buf,
-                    pac_opaque) < 0) {
+       if (aes_wrap(data->pac_opaque_encr, sizeof(data->pac_opaque_encr),
+                    pac_len / 8, pac_buf, pac_opaque) < 0) {
                os_free(pac_buf);
                os_free(pac_opaque);
                return NULL;
@@ -819,6 +819,9 @@ static int eap_fast_encrypt_phase2(struct eap_sm *sm,
        encr = eap_server_tls_encrypt(sm, &data->ssl, plain);
        wpabuf_free(plain);
 
+       if (!encr)
+               return -1;
+
        if (data->ssl.tls_out && piggyback) {
                wpa_printf(MSG_DEBUG, "EAP-FAST: Piggyback Phase 2 data "
                           "(len=%d) with last Phase 1 Message (len=%d "
@@ -1016,7 +1019,7 @@ static void eap_fast_process_phase2_response(struct eap_sm *sm,
        if (m->check(sm, priv, &buf)) {
                wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 check() asked to "
                           "ignore the packet");
-               next_type = eap_fast_req_failure(sm, data);
+               eap_fast_req_failure(sm, data);
                return;
        }
 
@@ -1123,7 +1126,8 @@ static void eap_fast_process_phase2_eap(struct eap_sm *sm,
 static int eap_fast_parse_tlvs(struct wpabuf *data,
                               struct eap_fast_tlv_parse *tlv)
 {
-       int mandatory, tlv_type, len, res;
+       int mandatory, tlv_type, res;
+       size_t len;
        u8 *pos, *end;
 
        os_memset(tlv, 0, sizeof(*tlv));
@@ -1136,13 +1140,14 @@ static int eap_fast_parse_tlvs(struct wpabuf *data,
                pos += 2;
                len = WPA_GET_BE16(pos);
                pos += 2;
-               if (pos + len > end) {
+               if (len > (size_t) (end - pos)) {
                        wpa_printf(MSG_INFO, "EAP-FAST: TLV overflow");
                        return -1;
                }
                wpa_printf(MSG_DEBUG, "EAP-FAST: Received Phase 2: "
-                          "TLV type %d length %d%s",
-                          tlv_type, len, mandatory ? " (mandatory)" : "");
+                          "TLV type %d length %u%s",
+                          tlv_type, (unsigned int) len,
+                          mandatory ? " (mandatory)" : "");
 
                res = eap_fast_parse_tlv(tlv, tlv_type, pos, len);
                if (res == -2)
@@ -1196,7 +1201,7 @@ static int eap_fast_validate_crypto_binding(
                return -1;
        }
 
-       if (os_memcmp(data->crypto_binding_nonce, b->nonce, 31) != 0 ||
+       if (os_memcmp_const(data->crypto_binding_nonce, b->nonce, 31) != 0 ||
            (data->crypto_binding_nonce[31] | 1) != b->nonce[31]) {
                wpa_printf(MSG_DEBUG, "EAP-FAST: Invalid nonce in "
                           "Crypto-Binding");
@@ -1210,7 +1215,7 @@ static int eap_fast_validate_crypto_binding(
                    (u8 *) b, bind_len);
        hmac_sha1(data->cmk, EAP_FAST_CMK_LEN, (u8 *) b, bind_len,
                  b->compound_mac);
-       if (os_memcmp(cmac, b->compound_mac, sizeof(cmac)) != 0) {
+       if (os_memcmp_const(cmac, b->compound_mac, sizeof(cmac)) != 0) {
                wpa_hexdump(MSG_MSGDUMP,
                            "EAP-FAST: Calculated Compound MAC",
                            b->compound_mac, sizeof(cmac));
@@ -1587,6 +1592,18 @@ static Boolean eap_fast_isSuccess(struct eap_sm *sm, void *priv)
 }
 
 
+static u8 * eap_fast_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_fast_data *data = priv;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_FAST,
+                                               len);
+}
+
+
 int eap_server_fast_register(void)
 {
        struct eap_method *eap;
@@ -1606,6 +1623,7 @@ int eap_server_fast_register(void)
        eap->getKey = eap_fast_getKey;
        eap->get_emsk = eap_fast_get_emsk;
        eap->isSuccess = eap_fast_isSuccess;
+       eap->getSessionId = eap_fast_get_session_id;
 
        ret = eap_server_method_register(eap);
        if (ret)
index 2853c48..50f15c3 100644 (file)
@@ -24,10 +24,10 @@ struct eap_gpsk_data {
        size_t sk_len;
        u8 pk[EAP_GPSK_MAX_PK_LEN];
        size_t pk_len;
+       u8 session_id[128];
+       size_t id_len;
        u8 *id_peer;
        size_t id_peer_len;
-       u8 *id_server;
-       size_t id_server_len;
 #define MAX_NUM_CSUITES 2
        struct eap_gpsk_csuite csuite_list[MAX_NUM_CSUITES];
        size_t csuite_count;
@@ -71,11 +71,6 @@ static void * eap_gpsk_init(struct eap_sm *sm)
                return NULL;
        data->state = GPSK_1;
 
-       /* TODO: add support for configuring ID_Server */
-       data->id_server = (u8 *) os_strdup("hostapd");
-       if (data->id_server)
-               data->id_server_len = os_strlen((char *) data->id_server);
-
        data->csuite_count = 0;
        if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF,
                                           EAP_GPSK_CIPHER_AES)) {
@@ -101,9 +96,8 @@ static void * eap_gpsk_init(struct eap_sm *sm)
 static void eap_gpsk_reset(struct eap_sm *sm, void *priv)
 {
        struct eap_gpsk_data *data = priv;
-       os_free(data->id_server);
        os_free(data->id_peer);
-       os_free(data);
+       bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -123,7 +117,7 @@ static struct wpabuf * eap_gpsk_build_gpsk_1(struct eap_sm *sm,
        wpa_hexdump(MSG_MSGDUMP, "EAP-GPSK: RAND_Server",
                    data->rand_server, EAP_GPSK_RAND_LEN);
 
-       len = 1 + 2 + data->id_server_len + EAP_GPSK_RAND_LEN + 2 +
+       len = 1 + 2 + sm->server_id_len + EAP_GPSK_RAND_LEN + 2 +
                data->csuite_count * sizeof(struct eap_gpsk_csuite);
        req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len,
                            EAP_CODE_REQUEST, id);
@@ -135,8 +129,8 @@ static struct wpabuf * eap_gpsk_build_gpsk_1(struct eap_sm *sm,
        }
 
        wpabuf_put_u8(req, EAP_GPSK_OPCODE_GPSK_1);
-       wpabuf_put_be16(req, data->id_server_len);
-       wpabuf_put_data(req, data->id_server, data->id_server_len);
+       wpabuf_put_be16(req, sm->server_id_len);
+       wpabuf_put_data(req, sm->server_id, sm->server_id_len);
        wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN);
        wpabuf_put_be16(req,
                        data->csuite_count * sizeof(struct eap_gpsk_csuite));
@@ -158,7 +152,7 @@ static struct wpabuf * eap_gpsk_build_gpsk_3(struct eap_sm *sm,
        wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-3");
 
        miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
-       len = 1 + 2 * EAP_GPSK_RAND_LEN + 2 + data->id_server_len +
+       len = 1 + 2 * EAP_GPSK_RAND_LEN + 2 + sm->server_id_len +
                sizeof(struct eap_gpsk_csuite) + 2 + miclen;
        req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, len,
                            EAP_CODE_REQUEST, id);
@@ -174,8 +168,8 @@ static struct wpabuf * eap_gpsk_build_gpsk_3(struct eap_sm *sm,
 
        wpabuf_put_data(req, data->rand_peer, EAP_GPSK_RAND_LEN);
        wpabuf_put_data(req, data->rand_server, EAP_GPSK_RAND_LEN);
-       wpabuf_put_be16(req, data->id_server_len);
-       wpabuf_put_data(req, data->id_server, data->id_server_len);
+       wpabuf_put_be16(req, sm->server_id_len);
+       wpabuf_put_data(req, sm->server_id, sm->server_id_len);
        csuite = wpabuf_put(req, sizeof(*csuite));
        WPA_PUT_BE32(csuite->vendor, data->vendor);
        WPA_PUT_BE16(csuite->specifier, data->specifier);
@@ -301,8 +295,8 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm,
                eap_gpsk_state(data, FAILURE);
                return;
        }
-       if (alen != data->id_server_len ||
-           os_memcmp(pos, data->id_server, alen) != 0) {
+       if (alen != sm->server_id_len ||
+           os_memcmp(pos, sm->server_id, alen) != 0) {
                wpa_printf(MSG_DEBUG, "EAP-GPSK: ID_Server in GPSK-1 and "
                           "GPSK-2 did not match");
                eap_gpsk_state(data, FAILURE);
@@ -416,7 +410,7 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm,
                                 data->vendor, data->specifier,
                                 data->rand_peer, data->rand_server,
                                 data->id_peer, data->id_peer_len,
-                                data->id_server, data->id_server_len,
+                                sm->server_id, sm->server_id_len,
                                 data->msk, data->emsk,
                                 data->sk, &data->sk_len,
                                 data->pk, &data->pk_len) < 0) {
@@ -425,6 +419,21 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm,
                return;
        }
 
+       if (eap_gpsk_derive_session_id(sm->user->password,
+                                      sm->user->password_len,
+                                      data->vendor, data->specifier,
+                                      data->rand_peer, data->rand_server,
+                                      data->id_peer, data->id_peer_len,
+                                      sm->server_id, sm->server_id_len,
+                                      EAP_TYPE_GPSK,
+                                      data->session_id, &data->id_len) < 0) {
+               wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to derive Session-Id");
+               eap_gpsk_state(data, FAILURE);
+               return;
+       }
+       wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Derived Session-Id",
+                   data->session_id, data->id_len);
+
        miclen = eap_gpsk_mic_len(data->vendor, data->specifier);
        if (end - pos < (int) miclen) {
                wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC "
@@ -441,7 +450,7 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm,
                eap_gpsk_state(data, FAILURE);
                return;
        }
-       if (os_memcmp(mic, pos, miclen) != 0) {
+       if (os_memcmp_const(mic, pos, miclen) != 0) {
                wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-2");
                wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen);
                wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen);
@@ -510,7 +519,7 @@ static void eap_gpsk_process_gpsk_4(struct eap_sm *sm,
                eap_gpsk_state(data, FAILURE);
                return;
        }
-       if (os_memcmp(mic, pos, miclen) != 0) {
+       if (os_memcmp_const(mic, pos, miclen) != 0) {
                wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-4");
                wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen);
                wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen);
@@ -601,6 +610,24 @@ static Boolean eap_gpsk_isSuccess(struct eap_sm *sm, void *priv)
 }
 
 
+static u8 * eap_gpsk_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_gpsk_data *data = priv;
+       u8 *sid;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       sid = os_malloc(data->id_len);
+       if (sid == NULL)
+               return NULL;
+       os_memcpy(sid, data->session_id, data->id_len);
+       *len = data->id_len;
+
+       return sid;
+}
+
+
 int eap_server_gpsk_register(void)
 {
        struct eap_method *eap;
@@ -620,6 +647,7 @@ int eap_server_gpsk_register(void)
        eap->getKey = eap_gpsk_getKey;
        eap->isSuccess = eap_gpsk_isSuccess;
        eap->get_emsk = eap_gpsk_get_emsk;
+       eap->getSessionId = eap_gpsk_get_session_id;
 
        ret = eap_server_method_register(eap);
        if (ret)
index f423106..98ac3c6 100644 (file)
@@ -175,7 +175,7 @@ static void eap_gtc_process(struct eap_sm *sm, void *priv,
        }
 
        if (rlen != sm->user->password_len ||
-           os_memcmp(pos, sm->user->password, rlen) != 0) {
+           os_memcmp_const(pos, sm->user->password, rlen) != 0) {
                wpa_printf(MSG_DEBUG, "EAP-GTC: Done - Failure");
                data->state = FAILURE;
        } else {
index 51dc4e8..4501533 100644 (file)
@@ -102,6 +102,7 @@ static void eap_identity_process(struct eap_sm *sm, void *priv,
        struct eap_identity_data *data = priv;
        const u8 *pos;
        size_t len;
+       char *buf;
 
        if (data->pick_up) {
                if (eap_identity_check(sm, data, respData)) {
@@ -119,6 +120,12 @@ static void eap_identity_process(struct eap_sm *sm, void *priv,
                return; /* Should not happen - frame already validated */
 
        wpa_hexdump_ascii(MSG_DEBUG, "EAP-Identity: Peer identity", pos, len);
+       buf = os_malloc(len * 4 + 1);
+       if (buf) {
+               printf_encode(buf, len * 4 + 1, pos, len);
+               eap_log_msg(sm, "EAP-Response/Identity '%s'", buf);
+               os_free(buf);
+       }
        if (sm->identity)
                sm->update_user = TRUE;
        os_free(sm->identity);
index 42aaca2..16e6276 100644 (file)
@@ -103,8 +103,11 @@ static void * eap_ikev2_init(struct eap_sm *sm)
        data->ikev2.proposal.encr = ENCR_AES_CBC;
        data->ikev2.proposal.dh = DH_GROUP2_1024BIT_MODP;
 
-       data->ikev2.IDi = (u8 *) os_strdup("hostapd");
-       data->ikev2.IDi_len = 7;
+       data->ikev2.IDi = os_malloc(sm->server_id_len);
+       if (data->ikev2.IDi == NULL)
+               goto failed;
+       os_memcpy(data->ikev2.IDi, sm->server_id, sm->server_id_len);
+       data->ikev2.IDi_len = sm->server_id_len;
 
        data->ikev2.get_shared_secret = eap_ikev2_get_shared_secret;
        data->ikev2.cb_ctx = sm;
@@ -124,7 +127,7 @@ static void eap_ikev2_reset(struct eap_sm *sm, void *priv)
        wpabuf_free(data->in_buf);
        wpabuf_free(data->out_buf);
        ikev2_initiator_deinit(&data->ikev2);
-       os_free(data);
+       bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -253,7 +256,8 @@ static Boolean eap_ikev2_check(struct eap_sm *sm, void *priv,
 
 static int eap_ikev2_process_icv(struct eap_ikev2_data *data,
                                 const struct wpabuf *respData,
-                                u8 flags, const u8 *pos, const u8 **end)
+                                u8 flags, const u8 *pos, const u8 **end,
+                                int frag_ack)
 {
        if (flags & IKEV2_FLAGS_ICV_INCLUDED) {
                int icv_len = eap_ikev2_validate_icv(
@@ -263,7 +267,7 @@ static int eap_ikev2_process_icv(struct eap_ikev2_data *data,
                        return -1;
                /* Hide Integrity Checksum Data from further processing */
                *end -= icv_len;
-       } else if (data->keys_ready) {
+       } else if (data->keys_ready && !frag_ack) {
                wpa_printf(MSG_INFO, "EAP-IKEV2: The message should have "
                           "included integrity checksum");
                return -1;
@@ -305,6 +309,12 @@ static int eap_ikev2_process_fragment(struct eap_ikev2_data *data,
 
        if (data->in_buf == NULL) {
                /* First fragment of the message */
+               if (message_length > 50000) {
+                       /* Limit maximum memory allocation */
+                       wpa_printf(MSG_DEBUG,
+                                  "EAP-IKEV2: Ignore too long message");
+                       return -1;
+               }
                data->in_buf = wpabuf_alloc(message_length);
                if (data->in_buf == NULL) {
                        wpa_printf(MSG_DEBUG, "EAP-IKEV2: No memory for "
@@ -362,7 +372,9 @@ static void eap_ikev2_process(struct eap_sm *sm, void *priv,
        } else
                flags = *pos++;
 
-       if (eap_ikev2_process_icv(data, respData, flags, pos, &end) < 0) {
+       if (eap_ikev2_process_icv(data, respData, flags, pos, &end,
+                                 data->state == WAIT_FRAG_ACK && len == 0) < 0)
+       {
                eap_ikev2_state(data, FAIL);
                return;
        }
@@ -505,6 +517,36 @@ static u8 * eap_ikev2_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
 }
 
 
+static u8 * eap_ikev2_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_ikev2_data *data = priv;
+       u8 *sid;
+       size_t sid_len;
+       size_t offset;
+
+       if (data->state != DONE || !data->keymat_ok)
+               return NULL;
+
+       sid_len = 1 + data->ikev2.i_nonce_len + data->ikev2.r_nonce_len;
+       sid = os_malloc(sid_len);
+       if (sid) {
+               offset = 0;
+               sid[offset] = EAP_TYPE_IKEV2;
+               offset++;
+               os_memcpy(sid + offset, data->ikev2.i_nonce,
+                         data->ikev2.i_nonce_len);
+               offset += data->ikev2.i_nonce_len;
+               os_memcpy(sid + offset, data->ikev2.r_nonce,
+                         data->ikev2.r_nonce_len);
+               *len = sid_len;
+               wpa_hexdump(MSG_DEBUG, "EAP-IKEV2: Derived Session-Id",
+                           sid, sid_len);
+       }
+
+       return sid;
+}
+
+
 int eap_server_ikev2_register(void)
 {
        struct eap_method *eap;
@@ -525,6 +567,7 @@ int eap_server_ikev2_register(void)
        eap->getKey = eap_ikev2_getKey;
        eap->isSuccess = eap_ikev2_isSuccess;
        eap->get_emsk = eap_ikev2_get_emsk;
+       eap->getSessionId = eap_ikev2_get_session_id;
 
        ret = eap_server_method_register(eap);
        if (ret)
index 5a5e290..71e8d59 100644 (file)
@@ -126,7 +126,7 @@ static void eap_md5_process(struct eap_sm *sm, void *priv,
                return;
        }
 
-       if (os_memcmp(hash, pos, CHAP_MD5_LEN) == 0) {
+       if (os_memcmp_const(hash, pos, CHAP_MD5_LEN) == 0) {
                wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Success");
                data->state = SUCCESS;
        } else {
index 0209fad..9e9dc93 100644 (file)
@@ -153,7 +153,7 @@ void eap_server_unregister_methods(void)
  * eap_server_get_name - Get EAP method name for the given EAP type
  * @vendor: EAP Vendor-Id (0 = IETF)
  * @type: EAP method type
- * Returns: EAP method name, e.g., TLS, or %NULL if not found
+ * Returns: EAP method name, e.g., TLS, or "unknown" if not found
  *
  * This function maps EAP type numbers into EAP type names based on the list of
  * EAP methods included in the build.
@@ -167,5 +167,5 @@ const char * eap_server_get_name(int vendor, EapType type)
                if (m->vendor == vendor && m->method == type)
                        return m->name;
        }
-       return NULL;
+       return "unknown";
 }
index 8d3dd52..05848d2 100644 (file)
@@ -91,7 +91,7 @@ static void eap_mschapv2_reset(struct eap_sm *sm, void *priv)
                return;
 
        os_free(data->peer_challenge);
-       os_free(data);
+       bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -100,7 +100,6 @@ static struct wpabuf * eap_mschapv2_build_challenge(
 {
        struct wpabuf *req;
        struct eap_mschapv2_hdr *ms;
-       char *name = "hostapd"; /* TODO: make this configurable */
        size_t ms_len;
 
        if (!data->auth_challenge_from_tls &&
@@ -111,7 +110,7 @@ static struct wpabuf * eap_mschapv2_build_challenge(
                return NULL;
        }
 
-       ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + os_strlen(name);
+       ms_len = sizeof(*ms) + 1 + CHALLENGE_LEN + sm->server_id_len;
        req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, ms_len,
                            EAP_CODE_REQUEST, id);
        if (req == NULL) {
@@ -133,7 +132,7 @@ static struct wpabuf * eap_mschapv2_build_challenge(
                wpabuf_put(req, CHALLENGE_LEN);
        wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Challenge",
                    data->auth_challenge, CHALLENGE_LEN);
-       wpabuf_put_data(req, name, os_strlen(name));
+       wpabuf_put_data(req, sm->server_id, sm->server_id_len);
 
        return req;
 }
@@ -291,6 +290,7 @@ static void eap_mschapv2_process_response(struct eap_sm *sm,
        const u8 *username, *user;
        size_t username_len, user_len;
        int res;
+       char *buf;
 
        pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData,
                               &len);
@@ -330,6 +330,13 @@ static void eap_mschapv2_process_response(struct eap_sm *sm,
        wpa_printf(MSG_MSGDUMP, "EAP-MSCHAPV2: Flags 0x%x", flags);
        wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Name", name, name_len);
 
+       buf = os_malloc(name_len * 4 + 1);
+       if (buf) {
+               printf_encode(buf, name_len * 4 + 1, name, name_len);
+               eap_log_msg(sm, "EAP-MSCHAPV2 Name '%s'", buf);
+               os_free(buf);
+       }
+
        /* MSCHAPv2 does not include optional domain name in the
         * challenge-response calculation, so remove domain prefix
         * (if present). */
@@ -386,7 +393,7 @@ static void eap_mschapv2_process_response(struct eap_sm *sm,
                return;
        }
 
-       if (os_memcmp(nt_response, expected, 24) == 0) {
+       if (os_memcmp_const(nt_response, expected, 24) == 0) {
                const u8 *pw_hash;
                u8 pw_hash_buf[16], pw_hash_hash[16];
 
@@ -407,13 +414,16 @@ static void eap_mschapv2_process_response(struct eap_sm *sm,
                        }
                        pw_hash = pw_hash_buf;
                }
-               generate_authenticator_response_pwhash(
-                       pw_hash, peer_challenge, data->auth_challenge,
-                       username, username_len, nt_response,
-                       data->auth_response);
-
-               hash_nt_password_hash(pw_hash, pw_hash_hash);
-               get_master_key(pw_hash_hash, nt_response, data->master_key);
+               if (generate_authenticator_response_pwhash(
+                           pw_hash, peer_challenge, data->auth_challenge,
+                           username, username_len, nt_response,
+                           data->auth_response) < 0 ||
+                   hash_nt_password_hash(pw_hash, pw_hash_hash) < 0 ||
+                   get_master_key(pw_hash_hash, nt_response,
+                                  data->master_key)) {
+                       data->state = FAILURE;
+                       return;
+               }
                data->master_key_valid = 1;
                wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key",
                                data->master_key, MSCHAPV2_KEY_LEN);
index 35a42ad..0e6b4a0 100644 (file)
@@ -36,6 +36,7 @@ struct eap_pax_data {
        u8 mk[EAP_PAX_MK_LEN];
        u8 ck[EAP_PAX_CK_LEN];
        u8 ick[EAP_PAX_ICK_LEN];
+       u8 mid[EAP_PAX_MID_LEN];
        int keys_set;
        char *cid;
        size_t cid_len;
@@ -64,7 +65,7 @@ static void eap_pax_reset(struct eap_sm *sm, void *priv)
 {
        struct eap_pax_data *data = priv;
        os_free(data->cid);
-       os_free(data);
+       bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -148,7 +149,6 @@ static struct wpabuf * eap_pax_build_std_3(struct eap_sm *sm,
                    (u8 *) data->cid, data->cid_len, NULL, 0, pos);
        wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
                    pos, EAP_PAX_MAC_LEN);
-       pos += EAP_PAX_MAC_LEN;
 
        /* Optional ADE could be added here, if needed */
 
@@ -268,7 +268,7 @@ static Boolean eap_pax_check(struct eap_sm *sm, void *priv,
                            wpabuf_mhead(respData),
                            wpabuf_len(respData) - EAP_PAX_ICV_LEN,
                            NULL, 0, NULL, 0, icvbuf);
-               if (os_memcmp(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) {
+               if (os_memcmp_const(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) {
                        wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV");
                        wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
                                    icvbuf, EAP_PAX_ICV_LEN);
@@ -287,7 +287,7 @@ static void eap_pax_process_std_2(struct eap_sm *sm,
        struct eap_pax_hdr *resp;
        u8 mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN];
        const u8 *pos;
-       size_t len, left;
+       size_t len, left, cid_len;
        int i;
 
        if (data->state != PAX_STD_1)
@@ -320,7 +320,12 @@ static void eap_pax_process_std_2(struct eap_sm *sm,
                wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)");
                return;
        }
-       data->cid_len = WPA_GET_BE16(pos);
+       cid_len = WPA_GET_BE16(pos);
+       if (cid_len > 1500) {
+               wpa_printf(MSG_INFO, "EAP-PAX: Too long CID");
+               return;
+       }
+       data->cid_len = cid_len;
        os_free(data->cid);
        data->cid = os_malloc(data->cid_len);
        if (data->cid == NULL) {
@@ -383,7 +388,7 @@ static void eap_pax_process_std_2(struct eap_sm *sm,
 
        if (eap_pax_initial_key_derivation(data->mac_id, data->ak,
                                           data->rand.e, data->mk, data->ck,
-                                          data->ick) < 0) {
+                                          data->ick, data->mid) < 0) {
                wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial "
                           "key derivation");
                data->state = FAILURE;
@@ -395,7 +400,7 @@ static void eap_pax_process_std_2(struct eap_sm *sm,
                    data->rand.r.x, EAP_PAX_RAND_LEN,
                    data->rand.r.y, EAP_PAX_RAND_LEN,
                    (u8 *) data->cid, data->cid_len, mac);
-       if (os_memcmp(mac, pos, EAP_PAX_MAC_LEN) != 0) {
+       if (os_memcmp_const(mac, pos, EAP_PAX_MAC_LEN) != 0) {
                wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in "
                           "PAX_STD-2");
                wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)",
@@ -417,7 +422,7 @@ static void eap_pax_process_std_2(struct eap_sm *sm,
                    wpabuf_head(respData),
                    wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0,
                    icvbuf);
-       if (os_memcmp(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) {
+       if (os_memcmp_const(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) {
                wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2");
                wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
                            icvbuf, EAP_PAX_ICV_LEN);
@@ -537,6 +542,26 @@ static Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv)
 }
 
 
+static u8 * eap_pax_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_pax_data *data = priv;
+       u8 *sid;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       sid = os_malloc(1 + EAP_PAX_MID_LEN);
+       if (sid == NULL)
+               return NULL;
+
+       *len = 1 + EAP_PAX_MID_LEN;
+       sid[0] = EAP_TYPE_PAX;
+       os_memcpy(sid + 1, data->mid, EAP_PAX_MID_LEN);
+
+       return sid;
+}
+
+
 int eap_server_pax_register(void)
 {
        struct eap_method *eap;
@@ -556,6 +581,7 @@ int eap_server_pax_register(void)
        eap->getKey = eap_pax_getKey;
        eap->isSuccess = eap_pax_isSuccess;
        eap->get_emsk = eap_pax_get_emsk;
+       eap->getSessionId = eap_pax_get_session_id;
 
        ret = eap_server_method_register(eap);
        if (ret)
index 68253c4..faa0fd2 100644 (file)
@@ -22,7 +22,6 @@
 /* Maximum supported PEAP version
  * 0 = Microsoft's PEAP version 0; draft-kamath-pppext-peapv0-00.txt
  * 1 = draft-josefsson-ppext-eap-tls-eap-05.txt
- * 2 = draft-josefsson-ppext-eap-tls-eap-10.txt
  */
 #define EAP_PEAP_VERSION 1
 
@@ -99,33 +98,6 @@ static void eap_peap_state(struct eap_peap_data *data, int state)
 }
 
 
-static struct wpabuf * eap_peapv2_tlv_eap_payload(struct wpabuf *buf)
-{
-       struct wpabuf *e;
-       struct eap_tlv_hdr *tlv;
-
-       if (buf == NULL)
-               return NULL;
-
-       /* Encapsulate EAP packet in EAP-Payload TLV */
-       wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Add EAP-Payload TLV");
-       e = wpabuf_alloc(sizeof(*tlv) + wpabuf_len(buf));
-       if (e == NULL) {
-               wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Failed to allocate memory "
-                          "for TLV encapsulation");
-               wpabuf_free(buf);
-               return NULL;
-       }
-       tlv = wpabuf_put(e, sizeof(*tlv));
-       tlv->tlv_type = host_to_be16(EAP_TLV_TYPE_MANDATORY |
-                                    EAP_TLV_EAP_PAYLOAD_TLV);
-       tlv->length = host_to_be16(wpabuf_len(buf));
-       wpabuf_put_buf(e, buf);
-       wpabuf_free(buf);
-       return e;
-}
-
-
 static void eap_peap_req_success(struct eap_sm *sm,
                                 struct eap_peap_data *data)
 {
@@ -200,7 +172,7 @@ static void eap_peap_reset(struct eap_sm *sm, void *priv)
        wpabuf_free(data->pending_phase2_resp);
        os_free(data->phase2_key);
        wpabuf_free(data->soh_response);
-       os_free(data);
+       bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -239,8 +211,6 @@ static struct wpabuf * eap_peap_build_phase2_req(struct eap_sm *sm,
                return NULL;
        }
        buf = data->phase2_method->buildReq(sm, data->phase2_priv, id);
-       if (data->peap_version >= 2 && buf)
-               buf = eap_peapv2_tlv_eap_payload(buf);
        if (buf == NULL)
                return NULL;
 
@@ -374,12 +344,14 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm,
        size_t mlen;
 
        mlen = 6; /* Result TLV */
-       if (data->crypto_binding != NO_BINDING)
+       if (data->peap_version == 0 && data->tlv_request == TLV_REQ_SUCCESS &&
+           data->crypto_binding != NO_BINDING) {
                mlen += 60; /* Cryptobinding TLV */
 #ifdef EAP_SERVER_TNC
-       if (data->soh_response)
-               mlen += wpabuf_len(data->soh_response);
+               if (data->soh_response)
+                       mlen += wpabuf_len(data->soh_response);
 #endif /* EAP_SERVER_TNC */
+       }
 
        buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, mlen,
                            EAP_CODE_REQUEST, id);
@@ -425,8 +397,6 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm,
                len[1] = 1;
 
                tlv_type = EAP_TLV_CRYPTO_BINDING_TLV;
-               if (data->peap_version >= 2)
-                       tlv_type |= EAP_TLV_TYPE_MANDATORY;
                wpabuf_put_be16(buf, tlv_type);
                wpabuf_put_be16(buf, 56);
 
@@ -505,8 +475,7 @@ static struct wpabuf * eap_peap_buildReq(struct eap_sm *sm, void *priv, u8 id)
                return eap_peap_build_start(sm, data, id);
        case PHASE1:
        case PHASE1_ID2:
-               if (data->peap_version < 2 &&
-                   tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
+               if (tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
                        wpa_printf(MSG_DEBUG, "EAP-PEAP: Phase1 done, "
                                   "starting Phase2");
                        eap_peap_state(data, PHASE2_START);
@@ -626,7 +595,7 @@ static int eap_tlv_validate_cryptobinding(struct eap_sm *sm,
        buf[60] = EAP_TYPE_PEAP;
        hmac_sha1(data->cmk, 20, buf, sizeof(buf), mac);
 
-       if (os_memcmp(mac, pos, SHA1_MAC_LEN) != 0) {
+       if (os_memcmp_const(mac, pos, SHA1_MAC_LEN) != 0) {
                wpa_printf(MSG_DEBUG, "EAP-PEAP: Invalid Compound_MAC in "
                           "cryptobinding TLV");
                wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK", data->cmk, 20);
@@ -1079,47 +1048,6 @@ static void eap_peap_process_phase2(struct eap_sm *sm,
                wpabuf_free(in_decrypted);
 
                in_decrypted = nbuf;
-       } else if (data->peap_version >= 2) {
-               struct eap_tlv_hdr *tlv;
-               struct wpabuf *nmsg;
-
-               if (wpabuf_len(in_decrypted) < sizeof(*tlv) + sizeof(*hdr)) {
-                       wpa_printf(MSG_INFO, "EAP-PEAPv2: Too short Phase 2 "
-                                  "EAP TLV");
-                       wpabuf_free(in_decrypted);
-                       return;
-               }
-               tlv = wpabuf_mhead(in_decrypted);
-               if ((be_to_host16(tlv->tlv_type) & EAP_TLV_TYPE_MASK) !=
-                   EAP_TLV_EAP_PAYLOAD_TLV) {
-                       wpa_printf(MSG_INFO, "EAP-PEAPv2: Not an EAP TLV");
-                       wpabuf_free(in_decrypted);
-                       return;
-               }
-               if (sizeof(*tlv) + be_to_host16(tlv->length) >
-                   wpabuf_len(in_decrypted)) {
-                       wpa_printf(MSG_INFO, "EAP-PEAPv2: Invalid EAP TLV "
-                                  "length");
-                       wpabuf_free(in_decrypted);
-                       return;
-               }
-               hdr = (struct eap_hdr *) (tlv + 1);
-               if (be_to_host16(hdr->length) > be_to_host16(tlv->length)) {
-                       wpa_printf(MSG_INFO, "EAP-PEAPv2: No room for full "
-                                  "EAP packet in EAP TLV");
-                       wpabuf_free(in_decrypted);
-                       return;
-               }
-
-               nmsg = wpabuf_alloc(be_to_host16(hdr->length));
-               if (nmsg == NULL) {
-                       wpabuf_free(in_decrypted);
-                       return;
-               }
-
-               wpabuf_put_data(nmsg, hdr, be_to_host16(hdr->length));
-               wpabuf_free(in_decrypted);
-               in_decrypted = nmsg;
        }
 
        hdr = wpabuf_head(in_decrypted);
@@ -1168,53 +1096,6 @@ static void eap_peap_process_phase2(struct eap_sm *sm,
 }
 
 
-static int eap_peapv2_start_phase2(struct eap_sm *sm,
-                                  struct eap_peap_data *data)
-{
-       struct wpabuf *buf, *buf2;
-
-       wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Phase1 done, include first Phase2 "
-                  "payload in the same message");
-       eap_peap_state(data, PHASE1_ID2);
-       if (eap_peap_phase2_init(sm, data, EAP_TYPE_IDENTITY))
-               return -1;
-
-       /* TODO: which Id to use here? */
-       buf = data->phase2_method->buildReq(sm, data->phase2_priv, 6);
-       if (buf == NULL)
-               return -1;
-
-       buf2 = eap_peapv2_tlv_eap_payload(buf);
-       if (buf2 == NULL)
-               return -1;
-
-       wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAPv2: Identity Request", buf2);
-
-       buf = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn,
-                                    buf2);
-       wpabuf_free(buf2);
-
-       if (buf == NULL) {
-               wpa_printf(MSG_INFO, "EAP-PEAPv2: Failed to encrypt Phase 2 "
-                          "data");
-               return -1;
-       }
-
-       wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAPv2: Encrypted Identity Request",
-                       buf);
-
-       /* Append TLS data into the pending buffer after the Server Finished */
-       if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(buf)) < 0) {
-               wpabuf_free(buf);
-               return -1;
-       }
-       wpabuf_put_buf(data->ssl.tls_out, buf);
-       wpabuf_free(buf);
-
-       return 0;
-}
-
-
 static int eap_peap_process_version(struct eap_sm *sm, void *priv,
                                    int peer_version)
 {
@@ -1249,14 +1130,6 @@ static void eap_peap_process_msg(struct eap_sm *sm, void *priv,
                        eap_peap_state(data, FAILURE);
                        break;
                }
-
-               if (data->peap_version >= 2 &&
-                   tls_connection_established(sm->ssl_ctx, data->ssl.conn)) {
-                       if (eap_peapv2_start_phase2(sm, data)) {
-                               eap_peap_state(data, FAILURE);
-                               break;
-                       }
-               }
                break;
        case PHASE2_START:
                eap_peap_state(data, PHASE2_ID);
@@ -1358,6 +1231,18 @@ static Boolean eap_peap_isSuccess(struct eap_sm *sm, void *priv)
 }
 
 
+static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_peap_data *data = priv;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_PEAP,
+                                               len);
+}
+
+
 int eap_server_peap_register(void)
 {
        struct eap_method *eap;
@@ -1376,6 +1261,7 @@ int eap_server_peap_register(void)
        eap->isDone = eap_peap_isDone;
        eap->getKey = eap_peap_getKey;
        eap->isSuccess = eap_peap_isSuccess;
+       eap->getSessionId = eap_peap_get_session_id;
 
        ret = eap_server_method_register(eap);
        if (ret)
index 0cd9799..12b5d25 100644 (file)
@@ -22,8 +22,8 @@ struct eap_psk_data {
        enum { PSK_1, PSK_3, SUCCESS, FAILURE } state;
        u8 rand_s[EAP_PSK_RAND_LEN];
        u8 rand_p[EAP_PSK_RAND_LEN];
-       u8 *id_p, *id_s;
-       size_t id_p_len, id_s_len;
+       u8 *id_p;
+       size_t id_p_len;
        u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN];
        u8 msk[EAP_MSK_LEN];
        u8 emsk[EAP_EMSK_LEN];
@@ -38,8 +38,6 @@ static void * eap_psk_init(struct eap_sm *sm)
        if (data == NULL)
                return NULL;
        data->state = PSK_1;
-       data->id_s = (u8 *) "hostapd";
-       data->id_s_len = 7;
 
        return data;
 }
@@ -49,7 +47,7 @@ static void eap_psk_reset(struct eap_sm *sm, void *priv)
 {
        struct eap_psk_data *data = priv;
        os_free(data->id_p);
-       os_free(data);
+       bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -70,7 +68,7 @@ static struct wpabuf * eap_psk_build_1(struct eap_sm *sm,
                    data->rand_s, EAP_PSK_RAND_LEN);
 
        req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_PSK,
-                           sizeof(*psk) + data->id_s_len,
+                           sizeof(*psk) + sm->server_id_len,
                            EAP_CODE_REQUEST, id);
        if (req == NULL) {
                wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory "
@@ -82,7 +80,7 @@ static struct wpabuf * eap_psk_build_1(struct eap_sm *sm,
        psk = wpabuf_put(req, sizeof(*psk));
        psk->flags = EAP_PSK_FLAGS_SET_T(0); /* T=0 */
        os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN);
-       wpabuf_put_data(req, data->id_s, data->id_s_len);
+       wpabuf_put_data(req, sm->server_id, sm->server_id_len);
 
        return req;
 }
@@ -112,13 +110,13 @@ static struct wpabuf * eap_psk_build_3(struct eap_sm *sm,
        os_memcpy(psk->rand_s, data->rand_s, EAP_PSK_RAND_LEN);
 
        /* MAC_S = OMAC1-AES-128(AK, ID_S||RAND_P) */
-       buflen = data->id_s_len + EAP_PSK_RAND_LEN;
+       buflen = sm->server_id_len + EAP_PSK_RAND_LEN;
        buf = os_malloc(buflen);
        if (buf == NULL)
                goto fail;
 
-       os_memcpy(buf, data->id_s, data->id_s_len);
-       os_memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN);
+       os_memcpy(buf, sm->server_id, sm->server_id_len);
+       os_memcpy(buf + sm->server_id_len, data->rand_p, EAP_PSK_RAND_LEN);
        if (omac1_aes_128(data->ak, buf, buflen, psk->mac_s)) {
                os_free(buf);
                goto fail;
@@ -296,7 +294,7 @@ static void eap_psk_process_2(struct eap_sm *sm,
        os_memcpy(data->rand_p, resp->rand_p, EAP_PSK_RAND_LEN);
 
        /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */
-       buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN;
+       buflen = data->id_p_len + sm->server_id_len + 2 * EAP_PSK_RAND_LEN;
        buf = os_malloc(buflen);
        if (buf == NULL) {
                data->state = FAILURE;
@@ -304,8 +302,8 @@ static void eap_psk_process_2(struct eap_sm *sm,
        }
        os_memcpy(buf, data->id_p, data->id_p_len);
        pos = buf + data->id_p_len;
-       os_memcpy(pos, data->id_s, data->id_s_len);
-       pos += data->id_s_len;
+       os_memcpy(pos, sm->server_id, sm->server_id_len);
+       pos += sm->server_id_len;
        os_memcpy(pos, data->rand_s, EAP_PSK_RAND_LEN);
        pos += EAP_PSK_RAND_LEN;
        os_memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN);
@@ -316,7 +314,7 @@ static void eap_psk_process_2(struct eap_sm *sm,
        }
        os_free(buf);
        wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", resp->mac_p, EAP_PSK_MAC_LEN);
-       if (os_memcmp(mac, resp->mac_p, EAP_PSK_MAC_LEN) != 0) {
+       if (os_memcmp_const(mac, resp->mac_p, EAP_PSK_MAC_LEN) != 0) {
                wpa_printf(MSG_INFO, "EAP-PSK: Invalid MAC_P");
                wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: Expected MAC_P",
                            mac, EAP_PSK_MAC_LEN);
@@ -487,6 +485,28 @@ static Boolean eap_psk_isSuccess(struct eap_sm *sm, void *priv)
 }
 
 
+static u8 * eap_psk_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_psk_data *data = priv;
+       u8 *id;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       *len = 1 + 2 * EAP_PSK_RAND_LEN;
+       id = os_malloc(*len);
+       if (id == NULL)
+               return NULL;
+
+       id[0] = EAP_TYPE_PSK;
+       os_memcpy(id + 1, data->rand_p, EAP_PSK_RAND_LEN);
+       os_memcpy(id + 1 + EAP_PSK_RAND_LEN, data->rand_s, EAP_PSK_RAND_LEN);
+       wpa_hexdump(MSG_DEBUG, "EAP-PSK: Derived Session-Id", id, *len);
+
+       return id;
+}
+
+
 int eap_server_psk_register(void)
 {
        struct eap_method *eap;
@@ -506,6 +526,7 @@ int eap_server_psk_register(void)
        eap->getKey = eap_psk_getKey;
        eap->isSuccess = eap_psk_isSuccess;
        eap->get_emsk = eap_psk_get_emsk;
+       eap->getSessionId = eap_psk_get_session_id;
 
        ret = eap_server_method_register(eap);
        if (ret)
index b61061b..943af0d 100644 (file)
@@ -45,6 +45,7 @@ struct eap_pwd_data {
 
        u8 msk[EAP_MSK_LEN];
        u8 emsk[EAP_EMSK_LEN];
+       u8 session_id[1 + SHA256_MAC_LEN];
 
        BN_CTX *bnctx;
 };
@@ -105,7 +106,7 @@ static void * eap_pwd_init(struct eap_sm *sm)
        if (data->password == NULL) {
                wpa_printf(MSG_INFO, "EAP-PWD: Memory allocation password "
                           "fail");
-               os_free(data->id_server);
+               bin_clear_free(data->id_server, data->id_server_len);
                os_free(data);
                return NULL;
        }
@@ -115,15 +116,16 @@ static void * eap_pwd_init(struct eap_sm *sm)
        data->bnctx = BN_CTX_new();
        if (data->bnctx == NULL) {
                wpa_printf(MSG_INFO, "EAP-PWD: bn context allocation fail");
-               os_free(data->password);
-               os_free(data->id_server);
+               bin_clear_free(data->password, data->password_len);
+               bin_clear_free(data->id_server, data->id_server_len);
                os_free(data);
                return NULL;
        }
 
        data->in_frag_pos = data->out_frag_pos = 0;
        data->inbuf = data->outbuf = NULL;
-       data->mtu = 1020; /* default from RFC 5931, make it configurable! */
+       /* use default MTU from RFC 5931 if not configured otherwise */
+       data->mtu = sm->fragment_size > 0 ? sm->fragment_size : 1020;
 
        return data;
 }
@@ -133,24 +135,26 @@ static void eap_pwd_reset(struct eap_sm *sm, void *priv)
 {
        struct eap_pwd_data *data = priv;
 
-       BN_free(data->private_value);
-       BN_free(data->peer_scalar);
-       BN_free(data->my_scalar);
-       BN_free(data->k);
+       BN_clear_free(data->private_value);
+       BN_clear_free(data->peer_scalar);
+       BN_clear_free(data->my_scalar);
+       BN_clear_free(data->k);
        BN_CTX_free(data->bnctx);
-       EC_POINT_free(data->my_element);
-       EC_POINT_free(data->peer_element);
-       os_free(data->id_peer);
-       os_free(data->id_server);
-       os_free(data->password);
+       EC_POINT_clear_free(data->my_element);
+       EC_POINT_clear_free(data->peer_element);
+       bin_clear_free(data->id_peer, data->id_peer_len);
+       bin_clear_free(data->id_server, data->id_server_len);
+       bin_clear_free(data->password, data->password_len);
        if (data->grp) {
                EC_GROUP_free(data->grp->group);
-               EC_POINT_free(data->grp->pwe);
-               BN_free(data->grp->order);
-               BN_free(data->grp->prime);
+               EC_POINT_clear_free(data->grp->pwe);
+               BN_clear_free(data->grp->order);
+               BN_clear_free(data->grp->prime);
                os_free(data->grp);
        }
-       os_free(data);
+       wpabuf_free(data->inbuf);
+       wpabuf_free(data->outbuf);
+       bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -206,11 +210,15 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm,
                goto fin;
        }
 
-       BN_rand_range(data->private_value, data->grp->order);
-       BN_rand_range(mask, data->grp->order);
-       BN_add(data->my_scalar, data->private_value, mask);
-       BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
-              data->bnctx);
+       if (BN_rand_range(data->private_value, data->grp->order) != 1 ||
+           BN_rand_range(mask, data->grp->order) != 1 ||
+           BN_add(data->my_scalar, data->private_value, mask) != 1 ||
+           BN_mod(data->my_scalar, data->my_scalar, data->grp->order,
+                  data->bnctx) != 1) {
+               wpa_printf(MSG_INFO,
+                          "EAP-pwd (server): unable to get randomness");
+               goto fin;
+       }
 
        if (!EC_POINT_mul(data->grp->group, data->my_element, NULL,
                          data->grp->pwe, mask, data->bnctx)) {
@@ -226,7 +234,7 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm,
                           "fail");
                goto fin;
        }
-       BN_free(mask);
+       BN_clear_free(mask);
 
        if (((x = BN_new()) == NULL) ||
            ((y = BN_new()) == NULL)) {
@@ -278,8 +286,8 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm,
 fin:
        os_free(scalar);
        os_free(element);
-       BN_free(x);
-       BN_free(y);
+       BN_clear_free(x);
+       BN_clear_free(y);
        if (data->outbuf == NULL)
                eap_pwd_state(data, FAILURE);
 }
@@ -402,9 +410,9 @@ static void eap_pwd_build_confirm_req(struct eap_sm *sm,
        wpabuf_put_data(data->outbuf, conf, SHA256_MAC_LEN);
 
 fin:
-       os_free(cruft);
-       BN_free(x);
-       BN_free(y);
+       bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
+       BN_clear_free(x);
+       BN_clear_free(y);
        if (data->outbuf == NULL)
                eap_pwd_state(data, FAILURE);
 }
@@ -523,6 +531,7 @@ eap_pwd_build_req(struct eap_sm *sm, void *priv, u8 id)
         */
        if (data->out_frag_pos >= wpabuf_len(data->outbuf)) {
                wpabuf_free(data->outbuf);
+               data->outbuf = NULL;
                data->out_frag_pos = 0;
        }
 
@@ -595,7 +604,8 @@ static void eap_pwd_process_id_resp(struct eap_sm *sm,
        wpa_hexdump_ascii(MSG_DEBUG, "EAP-PWD (server): peer sent id of",
                          data->id_peer, data->id_peer_len);
 
-       if ((data->grp = os_malloc(sizeof(EAP_PWD_group))) == NULL) {
+       data->grp = os_zalloc(sizeof(EAP_PWD_group));
+       if (data->grp == NULL) {
                wpa_printf(MSG_INFO, "EAP-PWD: failed to allocate memory for "
                           "group");
                return;
@@ -718,11 +728,11 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
        res = 1;
 
 fin:
-       EC_POINT_free(K);
-       EC_POINT_free(point);
-       BN_free(cofactor);
-       BN_free(x);
-       BN_free(y);
+       EC_POINT_clear_free(K);
+       EC_POINT_clear_free(point);
+       BN_clear_free(cofactor);
+       BN_clear_free(x);
+       BN_clear_free(y);
 
        if (res)
                eap_pwd_state(data, PWD_Confirm_Req);
@@ -829,7 +839,7 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data,
        eap_pwd_h_final(hash, conf);
 
        ptr = (u8 *) payload;
-       if (os_memcmp(conf, ptr, SHA256_MAC_LEN)) {
+       if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) {
                wpa_printf(MSG_INFO, "EAP-PWD (server): confirm did not "
                           "verify");
                goto fin;
@@ -838,15 +848,16 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data,
        wpa_printf(MSG_DEBUG, "EAP-pwd (server): confirm verified");
        if (compute_keys(data->grp, data->bnctx, data->k,
                         data->peer_scalar, data->my_scalar, conf,
-                        data->my_confirm, &cs, data->msk, data->emsk) < 0)
+                        data->my_confirm, &cs, data->msk, data->emsk,
+                        data->session_id) < 0)
                eap_pwd_state(data, FAILURE);
        else
                eap_pwd_state(data, SUCCESS);
 
 fin:
-       os_free(cruft);
-       BN_free(x);
-       BN_free(y);
+       bin_clear_free(cruft, BN_num_bytes(data->grp->prime));
+       BN_clear_free(x);
+       BN_clear_free(y);
 }
 
 
@@ -893,6 +904,8 @@ static void eap_pwd_process(struct eap_sm *sm, void *priv,
                tot_len = WPA_GET_BE16(pos);
                wpa_printf(MSG_DEBUG, "EAP-pwd: Incoming fragments, total "
                           "length = %d", tot_len);
+               if (tot_len > 15000)
+                       return;
                data->inbuf = wpabuf_alloc(tot_len);
                if (data->inbuf == NULL) {
                        wpa_printf(MSG_INFO, "EAP-pwd: Out of memory to "
@@ -949,6 +962,7 @@ static void eap_pwd_process(struct eap_sm *sm, void *priv,
         */
        if (data->in_frag_pos) {
                wpabuf_free(data->inbuf);
+               data->inbuf = NULL;
                data->in_frag_pos = 0;
        }
 }
@@ -1006,6 +1020,25 @@ static Boolean eap_pwd_is_done(struct eap_sm *sm, void *priv)
 }
 
 
+static u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_pwd_data *data = priv;
+       u8 *id;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       id = os_malloc(1 + SHA256_MAC_LEN);
+       if (id == NULL)
+               return NULL;
+
+       os_memcpy(id, data->session_id, 1 + SHA256_MAC_LEN);
+       *len = 1 + SHA256_MAC_LEN;
+
+       return id;
+}
+
+
 int eap_server_pwd_register(void)
 {
        struct eap_method *eap;
@@ -1014,8 +1047,6 @@ int eap_server_pwd_register(void)
        struct timezone tz;
        u32 sr;
 
-       EVP_add_digest(EVP_sha256());
-
        sr = 0xdeaddada;
        (void) gettimeofday(&tp, &tz);
        sr ^= (tp.tv_sec ^ tp.tv_usec);
@@ -1036,6 +1067,7 @@ int eap_server_pwd_register(void)
        eap->getKey = eap_pwd_getkey;
        eap->get_emsk = eap_pwd_get_emsk;
        eap->isSuccess = eap_pwd_is_success;
+       eap->getSessionId = eap_pwd_get_session_id;
 
        ret = eap_server_method_register(eap);
        if (ret)
index f72e1bf..de70777 100644 (file)
@@ -27,8 +27,6 @@ struct eap_sake_data {
        u8 session_id;
        u8 *peerid;
        size_t peerid_len;
-       u8 *serverid;
-       size_t serverid_len;
 };
 
 
@@ -77,11 +75,6 @@ static void * eap_sake_init(struct eap_sm *sm)
        wpa_printf(MSG_DEBUG, "EAP-SAKE: Initialized Session ID %d",
                   data->session_id);
 
-       /* TODO: add support for configuring SERVERID */
-       data->serverid = (u8 *) os_strdup("hostapd");
-       if (data->serverid)
-               data->serverid_len = os_strlen((char *) data->serverid);
-
        return data;
 }
 
@@ -89,9 +82,8 @@ static void * eap_sake_init(struct eap_sm *sm)
 static void eap_sake_reset(struct eap_sm *sm, void *priv)
 {
        struct eap_sake_data *data = priv;
-       os_free(data->serverid);
        os_free(data->peerid);
-       os_free(data);
+       bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -131,8 +123,7 @@ static struct wpabuf * eap_sake_build_identity(struct eap_sm *sm,
        wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Identity");
 
        plen = 4;
-       if (data->serverid)
-               plen += 2 + data->serverid_len;
+       plen += 2 + sm->server_id_len;
        msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_IDENTITY);
        if (msg == NULL) {
                data->state = FAILURE;
@@ -142,11 +133,9 @@ static struct wpabuf * eap_sake_build_identity(struct eap_sm *sm,
        wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_PERM_ID_REQ");
        eap_sake_add_attr(msg, EAP_SAKE_AT_PERM_ID_REQ, NULL, 2);
 
-       if (data->serverid) {
-               wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
-               eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
-                                 data->serverid, data->serverid_len);
-       }
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
+       eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
+                         sm->server_id, sm->server_id_len);
 
        return msg;
 }
@@ -169,9 +158,7 @@ static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm,
        wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)",
                    data->rand_s, EAP_SAKE_RAND_LEN);
 
-       plen = 2 + EAP_SAKE_RAND_LEN;
-       if (data->serverid)
-               plen += 2 + data->serverid_len;
+       plen = 2 + EAP_SAKE_RAND_LEN + 2 + sm->server_id_len;
        msg = eap_sake_build_msg(data, id, plen, EAP_SAKE_SUBTYPE_CHALLENGE);
        if (msg == NULL) {
                data->state = FAILURE;
@@ -182,11 +169,9 @@ static struct wpabuf * eap_sake_build_challenge(struct eap_sm *sm,
        eap_sake_add_attr(msg, EAP_SAKE_AT_RAND_S,
                          data->rand_s, EAP_SAKE_RAND_LEN);
 
-       if (data->serverid) {
-               wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
-               eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
-                                 data->serverid, data->serverid_len);
-       }
+       wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_SERVERID");
+       eap_sake_add_attr(msg, EAP_SAKE_AT_SERVERID,
+                         sm->server_id, sm->server_id_len);
 
        return msg;
 }
@@ -213,7 +198,7 @@ static struct wpabuf * eap_sake_build_confirm(struct eap_sm *sm,
        wpabuf_put_u8(msg, 2 + EAP_SAKE_MIC_LEN);
        mic = wpabuf_put(msg, EAP_SAKE_MIC_LEN);
        if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
-                                data->serverid, data->serverid_len,
+                                sm->server_id, sm->server_id_len,
                                 data->peerid, data->peerid_len, 0,
                                 wpabuf_head(msg), wpabuf_len(msg), mic, mic))
        {
@@ -362,11 +347,11 @@ static void eap_sake_process_challenge(struct eap_sm *sm,
                             (u8 *) &data->tek, data->msk, data->emsk);
 
        eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
-                            data->serverid, data->serverid_len,
+                            sm->server_id, sm->server_id_len,
                             data->peerid, data->peerid_len, 1,
                             wpabuf_head(respData), wpabuf_len(respData),
                             attr.mic_p, mic_p);
-       if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
+       if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
                wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
                eap_sake_state(data, FAILURE);
                return;
@@ -399,11 +384,11 @@ static void eap_sake_process_confirm(struct eap_sm *sm,
        }
 
        eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
-                            data->serverid, data->serverid_len,
+                            sm->server_id, sm->server_id_len,
                             data->peerid, data->peerid_len, 1,
                             wpabuf_head(respData), wpabuf_len(respData),
                             attr.mic_p, mic_p);
-       if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
+       if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
                wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
                eap_sake_state(data, FAILURE);
        } else
@@ -510,6 +495,28 @@ static Boolean eap_sake_isSuccess(struct eap_sm *sm, void *priv)
 }
 
 
+static u8 * eap_sake_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_sake_data *data = priv;
+       u8 *id;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       *len = 1 + 2 * EAP_SAKE_RAND_LEN;
+       id = os_malloc(*len);
+       if (id == NULL)
+               return NULL;
+
+       id[0] = EAP_TYPE_SAKE;
+       os_memcpy(id + 1, data->rand_s, EAP_SAKE_RAND_LEN);
+       os_memcpy(id + 1 + EAP_SAKE_RAND_LEN, data->rand_s, EAP_SAKE_RAND_LEN);
+       wpa_hexdump(MSG_DEBUG, "EAP-SAKE: Derived Session-Id", id, *len);
+
+       return id;
+}
+
+
 int eap_server_sake_register(void)
 {
        struct eap_method *eap;
@@ -529,6 +536,7 @@ int eap_server_sake_register(void)
        eap->getKey = eap_sake_getKey;
        eap->isSuccess = eap_sake_isSuccess;
        eap->get_emsk = eap_sake_get_emsk;
+       eap->getSessionId = eap_sake_get_session_id;
 
        ret = eap_server_method_register(eap);
        if (ret)
index b531241..ddfb71c 100644 (file)
@@ -94,7 +94,7 @@ static void eap_sim_reset(struct eap_sm *sm, void *priv)
        struct eap_sim_data *data = priv;
        os_free(data->next_pseudonym);
        os_free(data->next_reauth_id);
-       os_free(data);
+       bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -140,7 +140,7 @@ static struct wpabuf * eap_sim_build_start(struct eap_sm *sm,
        ver[1] = EAP_SIM_VERSION;
        eap_sim_msg_add(msg, EAP_SIM_AT_VERSION_LIST, sizeof(ver),
                        ver, sizeof(ver));
-       return eap_sim_msg_finish(msg, NULL, NULL, 0);
+       return eap_sim_msg_finish(msg, EAP_TYPE_SIM, NULL, NULL, 0);
 }
 
 
@@ -240,8 +240,8 @@ static struct wpabuf * eap_sim_build_challenge(struct eap_sm *sm,
 
        wpa_printf(MSG_DEBUG, "   AT_MAC");
        eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
-       return eap_sim_msg_finish(msg, data->k_aut, data->nonce_mt,
-                                 EAP_SIM_NONCE_MT_LEN);
+       return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut,
+                                 data->nonce_mt, EAP_SIM_NONCE_MT_LEN);
 }
 
 
@@ -278,7 +278,7 @@ static struct wpabuf * eap_sim_build_reauth(struct eap_sm *sm,
 
        wpa_printf(MSG_DEBUG, "   AT_MAC");
        eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
-       return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
+       return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, NULL, 0);
 }
 
 
@@ -317,7 +317,7 @@ static struct wpabuf * eap_sim_build_notification(struct eap_sm *sm,
                wpa_printf(MSG_DEBUG, "   AT_MAC");
                eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC);
        }
-       return eap_sim_msg_finish(msg, data->k_aut, NULL, 0);
+       return eap_sim_msg_finish(msg, EAP_TYPE_SIM, data->k_aut, NULL, 0);
 }
 
 
@@ -820,6 +820,29 @@ static Boolean eap_sim_isSuccess(struct eap_sm *sm, void *priv)
 }
 
 
+static u8 * eap_sim_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_sim_data *data = priv;
+       u8 *id;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       *len = 1 + data->num_chal * GSM_RAND_LEN + EAP_SIM_NONCE_MT_LEN;
+       id = os_malloc(*len);
+       if (id == NULL)
+               return NULL;
+
+       id[0] = EAP_TYPE_SIM;
+       os_memcpy(id + 1, data->rand, data->num_chal * GSM_RAND_LEN);
+       os_memcpy(id + 1 + data->num_chal * GSM_RAND_LEN, data->nonce_mt,
+                 EAP_SIM_NONCE_MT_LEN);
+       wpa_hexdump(MSG_DEBUG, "EAP-SIM: Derived Session-Id", id, *len);
+
+       return id;
+}
+
+
 int eap_server_sim_register(void)
 {
        struct eap_method *eap;
@@ -839,6 +862,7 @@ int eap_server_sim_register(void)
        eap->getKey = eap_sim_getKey;
        eap->isSuccess = eap_sim_isSuccess;
        eap->get_emsk = eap_sim_get_emsk;
+       eap->getSessionId = eap_sim_get_session_id;
 
        ret = eap_server_method_register(eap);
        if (ret)
index 447f47c..58cfe8a 100644 (file)
@@ -94,6 +94,28 @@ static void * eap_unauth_tls_init(struct eap_sm *sm)
 #endif /* EAP_SERVER_UNAUTH_TLS */
 
 
+#ifdef CONFIG_HS20
+static void * eap_wfa_unauth_tls_init(struct eap_sm *sm)
+{
+       struct eap_tls_data *data;
+
+       data = os_zalloc(sizeof(*data));
+       if (data == NULL)
+               return NULL;
+       data->state = START;
+
+       if (eap_server_tls_ssl_init(sm, &data->ssl, 0)) {
+               wpa_printf(MSG_INFO, "EAP-TLS: Failed to initialize SSL.");
+               eap_tls_reset(sm, data);
+               return NULL;
+       }
+
+       data->eap_type = EAP_WFA_UNAUTH_TLS_TYPE;
+       return data;
+}
+#endif /* CONFIG_HS20 */
+
+
 static void eap_tls_reset(struct eap_sm *sm, void *priv)
 {
        struct eap_tls_data *data = priv;
@@ -178,6 +200,10 @@ static Boolean eap_tls_check(struct eap_sm *sm, void *priv,
                pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
                                       EAP_VENDOR_TYPE_UNAUTH_TLS, respData,
                                       &len);
+       else if (data->eap_type == EAP_WFA_UNAUTH_TLS_TYPE)
+               pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW,
+                                      EAP_VENDOR_WFA_UNAUTH_TLS, respData,
+                                      &len);
        else
                pos = eap_hdr_validate(EAP_VENDOR_IETF, data->eap_type,
                                       respData, &len);
@@ -261,7 +287,7 @@ static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
                if (emsk)
                        os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN,
                                  EAP_EMSK_LEN);
-               os_free(eapKeyData);
+               bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
        } else
                emsk = NULL;
 
@@ -284,6 +310,18 @@ static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv)
 }
 
 
+static u8 * eap_tls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_tls_data *data = priv;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_TLS,
+                                               len);
+}
+
+
 int eap_server_tls_register(void)
 {
        struct eap_method *eap;
@@ -303,6 +341,7 @@ int eap_server_tls_register(void)
        eap->getKey = eap_tls_getKey;
        eap->isSuccess = eap_tls_isSuccess;
        eap->get_emsk = eap_tls_get_emsk;
+       eap->getSessionId = eap_tls_get_session_id;
 
        ret = eap_server_method_register(eap);
        if (ret)
@@ -340,3 +379,34 @@ int eap_server_unauth_tls_register(void)
        return ret;
 }
 #endif /* EAP_SERVER_UNAUTH_TLS */
+
+
+#ifdef CONFIG_HS20
+int eap_server_wfa_unauth_tls_register(void)
+{
+       struct eap_method *eap;
+       int ret;
+
+       eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
+                                     EAP_VENDOR_WFA_NEW,
+                                     EAP_VENDOR_WFA_UNAUTH_TLS,
+                                     "WFA-UNAUTH-TLS");
+       if (eap == NULL)
+               return -1;
+
+       eap->init = eap_wfa_unauth_tls_init;
+       eap->reset = eap_tls_reset;
+       eap->buildReq = eap_tls_buildReq;
+       eap->check = eap_tls_check;
+       eap->process = eap_tls_process;
+       eap->isDone = eap_tls_isDone;
+       eap->getKey = eap_tls_getKey;
+       eap->isSuccess = eap_tls_isSuccess;
+       eap->get_emsk = eap_tls_get_emsk;
+
+       ret = eap_server_method_register(eap);
+       if (ret)
+               eap_server_method_free(eap);
+       return ret;
+}
+#endif /* CONFIG_HS20 */
index 9efb5b2..56916c4 100644 (file)
@@ -25,14 +25,32 @@ struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
                return eap_msg_alloc(EAP_VENDOR_UNAUTH_TLS,
                                     EAP_VENDOR_TYPE_UNAUTH_TLS, payload_len,
                                     code, identifier);
+       else if (type == EAP_WFA_UNAUTH_TLS_TYPE)
+               return eap_msg_alloc(EAP_VENDOR_WFA_NEW,
+                                    EAP_VENDOR_WFA_UNAUTH_TLS, payload_len,
+                                    code, identifier);
        return eap_msg_alloc(EAP_VENDOR_IETF, type, payload_len, code,
                             identifier);
 }
 
 
+#ifdef CONFIG_TLS_INTERNAL
+static void eap_server_tls_log_cb(void *ctx, const char *msg)
+{
+       struct eap_sm *sm = ctx;
+       eap_log_msg(sm, "TLS: %s", msg);
+}
+#endif /* CONFIG_TLS_INTERNAL */
+
+
 int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
                            int verify_peer)
 {
+       if (sm->ssl_ctx == NULL) {
+               wpa_printf(MSG_ERROR, "TLS context not initialized - cannot use TLS-based EAP method");
+               return -1;
+       }
+
        data->eap = sm;
        data->phase2 = sm->init_phase2;
 
@@ -43,6 +61,13 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
                return -1;
        }
 
+#ifdef CONFIG_TLS_INTERNAL
+       tls_connection_set_log_cb(data->conn, eap_server_tls_log_cb, sm);
+#ifdef CONFIG_TESTING_OPTIONS
+       tls_connection_set_test_flags(data->conn, sm->tls_test_flags);
+#endif /* CONFIG_TESTING_OPTIONS */
+#endif /* CONFIG_TLS_INTERNAL */
+
        if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) {
                wpa_printf(MSG_INFO, "SSL: Failed to configure verification "
                           "of TLS peer certificate");
@@ -115,6 +140,47 @@ fail:
 }
 
 
+/**
+ * eap_server_tls_derive_session_id - Derive a Session-Id based on TLS data
+ * @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
+ * @data: Data for TLS processing
+ * @eap_type: EAP method used in Phase 1 (EAP_TYPE_TLS/PEAP/TTLS/FAST)
+ * @len: Pointer to length of the session ID generated
+ * Returns: Pointer to allocated Session-Id on success or %NULL on failure
+ *
+ * This function derive the Session-Id based on the TLS session data
+ * (client/server random and method type).
+ *
+ * The caller is responsible for freeing the returned buffer.
+ */
+u8 * eap_server_tls_derive_session_id(struct eap_sm *sm,
+                                     struct eap_ssl_data *data, u8 eap_type,
+                                     size_t *len)
+{
+       struct tls_keys keys;
+       u8 *out;
+
+       if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys))
+               return NULL;
+
+       if (keys.client_random == NULL || keys.server_random == NULL)
+               return NULL;
+
+       *len = 1 + keys.client_random_len + keys.server_random_len;
+       out = os_malloc(*len);
+       if (out == NULL)
+               return NULL;
+
+       /* Session-Id = EAP type || client.random || server.random */
+       out[0] = eap_type;
+       os_memcpy(out + 1, keys.client_random, keys.client_random_len);
+       os_memcpy(out + 1 + keys.client_random_len, keys.server_random,
+                 keys.server_random_len);
+
+       return out;
+}
+
+
 struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data,
                                         int eap_type, int version, u8 id)
 {
@@ -388,6 +454,10 @@ int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data,
                pos = eap_hdr_validate(EAP_VENDOR_UNAUTH_TLS,
                                       EAP_VENDOR_TYPE_UNAUTH_TLS, respData,
                                       &left);
+       else if (eap_type == EAP_WFA_UNAUTH_TLS_TYPE)
+               pos = eap_hdr_validate(EAP_VENDOR_WFA_NEW,
+                                      EAP_VENDOR_WFA_UNAUTH_TLS, respData,
+                                      &left);
        else
                pos = eap_hdr_validate(EAP_VENDOR_IETF, eap_type, respData,
                                       &left);
index 67a3dfa..21bd26f 100644 (file)
@@ -480,7 +480,8 @@ static void eap_tnc_process(struct eap_sm *sm, void *priv,
                message_length = WPA_GET_BE32(pos);
                pos += 4;
 
-               if (message_length < (u32) (end - pos)) {
+               if (message_length < (u32) (end - pos) ||
+                   message_length > 75000) {
                        wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message "
                                   "Length (%d; %ld remaining in this msg)",
                                   message_length, (long) (end - pos));
index 647bd2f..12a31b0 100644 (file)
@@ -336,7 +336,7 @@ static void eap_ttls_reset(struct eap_sm *sm, void *priv)
                data->phase2_method->reset(sm, data->phase2_priv);
        eap_server_tls_ssl_deinit(sm, &data->ssl);
        wpabuf_free(data->pending_phase2_eap_resp);
-       os_free(data);
+       bin_clear_free(data, sizeof(*data));
 }
 
 
@@ -409,7 +409,7 @@ static struct wpabuf * eap_ttls_build_phase2_mschapv2(
                                       RADIUS_VENDOR_ID_MICROSOFT, 1, 43);
                *pos++ = data->mschapv2_ident;
                ret = os_snprintf((char *) pos, end - pos, "S=");
-               if (ret >= 0 && ret < end - pos)
+               if (!os_snprintf_error(end - pos, ret))
                        pos += ret;
                pos += wpa_snprintf_hex_uppercase(
                        (char *) pos, end - pos, data->mschapv2_auth_response,
@@ -509,8 +509,8 @@ static void eap_ttls_process_phase2_pap(struct eap_sm *sm,
        }
 
        if (sm->user->password_len != user_password_len ||
-           os_memcmp(sm->user->password, user_password, user_password_len) !=
-           0) {
+           os_memcmp_const(sm->user->password, user_password,
+                           user_password_len) != 0) {
                wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Invalid user password");
                eap_ttls_state(data, FAILURE);
                return;
@@ -558,7 +558,8 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm,
                return;
        }
 
-       if (os_memcmp(challenge, chal, EAP_TTLS_CHAP_CHALLENGE_LEN) != 0 ||
+       if (os_memcmp_const(challenge, chal, EAP_TTLS_CHAP_CHALLENGE_LEN)
+           != 0 ||
            password[0] != chal[EAP_TTLS_CHAP_CHALLENGE_LEN]) {
                wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Challenge mismatch");
                os_free(chal);
@@ -571,7 +572,8 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm,
        chap_md5(password[0], sm->user->password, sm->user->password_len,
                 challenge, challenge_len, hash);
 
-       if (os_memcmp(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) == 0) {
+       if (os_memcmp_const(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) ==
+           0) {
                wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password");
                eap_ttls_state(data, SUCCESS);
        } else {
@@ -616,7 +618,8 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm,
                return;
        }
 
-       if (os_memcmp(challenge, chal, EAP_TTLS_MSCHAP_CHALLENGE_LEN) != 0 ||
+       if (os_memcmp_const(challenge, chal, EAP_TTLS_MSCHAP_CHALLENGE_LEN)
+           != 0 ||
            response[0] != chal[EAP_TTLS_MSCHAP_CHALLENGE_LEN]) {
                wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Challenge mismatch");
                os_free(chal);
@@ -631,7 +634,7 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm,
                nt_challenge_response(challenge, sm->user->password,
                                      sm->user->password_len, nt_response);
 
-       if (os_memcmp(nt_response, response + 2 + 24, 24) == 0) {
+       if (os_memcmp_const(nt_response, response + 2 + 24, 24) == 0) {
                wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response");
                eap_ttls_state(data, SUCCESS);
        } else {
@@ -703,7 +706,8 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
                return;
        }
 
-       if (os_memcmp(challenge, chal, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) != 0 ||
+       if (os_memcmp_const(challenge, chal, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN)
+           != 0 ||
            response[0] != chal[EAP_TTLS_MSCHAPV2_CHALLENGE_LEN]) {
                wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Challenge mismatch");
                os_free(chal);
@@ -736,7 +740,7 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm,
        }
 
        rx_resp = response + 2 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 8;
-       if (os_memcmp(nt_response, rx_resp, 24) == 0) {
+       if (os_memcmp_const(nt_response, rx_resp, 24) == 0) {
                wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct "
                           "NT-Response");
                data->mschapv2_resp_ok = 1;
@@ -984,6 +988,16 @@ static void eap_ttls_process_phase2(struct eap_sm *sm,
        }
 
        if (parse.user_name) {
+               char *nbuf;
+               nbuf = os_malloc(parse.user_name_len * 4 + 1);
+               if (nbuf) {
+                       printf_encode(nbuf, parse.user_name_len * 4 + 1,
+                                     parse.user_name,
+                                     parse.user_name_len);
+                       eap_log_msg(sm, "TTLS-User-Name '%s'", nbuf);
+                       os_free(nbuf);
+               }
+
                os_free(sm->identity);
                sm->identity = os_malloc(parse.user_name_len);
                if (sm->identity == NULL) {
@@ -1167,6 +1181,50 @@ static Boolean eap_ttls_isSuccess(struct eap_sm *sm, void *priv)
 }
 
 
+static u8 * eap_ttls_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_ttls_data *data = priv;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       return eap_server_tls_derive_session_id(sm, &data->ssl, EAP_TYPE_TTLS,
+                                               len);
+}
+
+
+static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+       struct eap_ttls_data *data = priv;
+       u8 *eapKeyData, *emsk;
+
+       if (data->state != SUCCESS)
+               return NULL;
+
+       eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
+                                              "ttls keying material",
+                                              EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
+       if (eapKeyData) {
+               emsk = os_malloc(EAP_EMSK_LEN);
+               if (emsk)
+                       os_memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN,
+                                 EAP_EMSK_LEN);
+               bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
+       } else
+               emsk = NULL;
+
+       if (emsk) {
+               *len = EAP_EMSK_LEN;
+               wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived EMSK",
+                           emsk, EAP_EMSK_LEN);
+       } else {
+               wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive EMSK");
+       }
+
+       return emsk;
+}
+
+
 int eap_server_ttls_register(void)
 {
        struct eap_method *eap;
@@ -1185,6 +1243,8 @@ int eap_server_ttls_register(void)
        eap->isDone = eap_ttls_isDone;
        eap->getKey = eap_ttls_getKey;
        eap->isSuccess = eap_ttls_isSuccess;
+       eap->getSessionId = eap_ttls_get_session_id;
+       eap->get_emsk = eap_ttls_get_emsk;
 
        ret = eap_server_method_register(eap);
        if (ret)
index 97ec0c0..9d9c28d 100644 (file)
@@ -380,7 +380,7 @@ static void eap_wsc_process(struct eap_sm *sm, void *priv,
                message_length = WPA_GET_BE16(pos);
                pos += 2;
 
-               if (message_length < end - pos) {
+               if (message_length < end - pos || message_length > 50000) {
                        wpa_printf(MSG_DEBUG, "EAP-WSC: Invalid Message "
                                   "Length");
                        return;
index 1b9d701..acf5435 100644 (file)
@@ -38,7 +38,6 @@ struct eap_sim_db_pending {
        char imsi[20];
        enum { PENDING, SUCCESS, FAILURE } state;
        void *cb_session_ctx;
-       struct os_time timestamp;
        int aka;
        union {
                struct {
@@ -574,16 +573,14 @@ static void eap_sim_db_receive(int sock, void *eloop_ctx, void *sock_ctx)
        char buf[1000], *pos, *cmd, *imsi;
        int res;
 
-       res = recv(sock, buf, sizeof(buf), 0);
+       res = recv(sock, buf, sizeof(buf) - 1, 0);
        if (res < 0)
                return;
+       buf[res] = '\0';
        wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-SIM DB: Received from an "
                              "external source", (u8 *) buf, res);
        if (res == 0)
                return;
-       if (res >= (int) sizeof(buf))
-               res = sizeof(buf) - 1;
-       buf[res] = '\0';
 
        if (data->get_complete_cb == NULL) {
                wpa_printf(MSG_DEBUG, "EAP-SIM DB: No get_complete_cb "
@@ -630,7 +627,7 @@ static int eap_sim_db_open_socket(struct eap_sim_db_data *data)
 
        data->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
        if (data->sock < 0) {
-               perror("socket(eap_sim_db)");
+               wpa_printf(MSG_INFO, "socket(eap_sim_db): %s", strerror(errno));
                return -1;
        }
 
@@ -640,8 +637,13 @@ static int eap_sim_db_open_socket(struct eap_sim_db_data *data)
                    "/tmp/eap_sim_db_%d-%d", getpid(), counter++);
        os_free(data->local_sock);
        data->local_sock = os_strdup(addr.sun_path);
+       if (data->local_sock == NULL) {
+               close(data->sock);
+               data->sock = -1;
+               return -1;
+       }
        if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-               perror("bind(eap_sim_db)");
+               wpa_printf(MSG_INFO, "bind(eap_sim_db): %s", strerror(errno));
                close(data->sock);
                data->sock = -1;
                return -1;
@@ -651,12 +653,16 @@ static int eap_sim_db_open_socket(struct eap_sim_db_data *data)
        addr.sun_family = AF_UNIX;
        os_strlcpy(addr.sun_path, data->fname + 5, sizeof(addr.sun_path));
        if (connect(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-               perror("connect(eap_sim_db)");
+               wpa_printf(MSG_INFO, "connect(eap_sim_db): %s",
+                          strerror(errno));
                wpa_hexdump_ascii(MSG_INFO, "HLR/AuC GW socket",
                                  (u8 *) addr.sun_path,
                                  os_strlen(addr.sun_path));
                close(data->sock);
                data->sock = -1;
+               unlink(data->local_sock);
+               os_free(data->local_sock);
+               data->local_sock = NULL;
                return -1;
        }
 
@@ -804,7 +810,8 @@ static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg,
 
        if (send(data->sock, msg, len, 0) < 0) {
                _errno = errno;
-               perror("send[EAP-SIM DB UNIX]");
+               wpa_printf(MSG_INFO, "send[EAP-SIM DB UNIX]: %s",
+                          strerror(errno));
        }
 
        if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
@@ -816,7 +823,8 @@ static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg,
                wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the "
                           "external server");
                if (send(data->sock, msg, len, 0) < 0) {
-                       perror("send[EAP-SIM DB UNIX]");
+                       wpa_printf(MSG_INFO, "send[EAP-SIM DB UNIX]: %s",
+                                  strerror(errno));
                        return -1;
                }
        }
@@ -914,12 +922,13 @@ int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data,
 
        imsi_len = os_strlen(imsi);
        len = os_snprintf(msg, sizeof(msg), "SIM-REQ-AUTH ");
-       if (len < 0 || len + imsi_len >= sizeof(msg))
+       if (os_snprintf_error(sizeof(msg), len) ||
+           len + imsi_len >= sizeof(msg))
                return EAP_SIM_DB_FAILURE;
        os_memcpy(msg + len, imsi, imsi_len);
        len += imsi_len;
        ret = os_snprintf(msg + len, sizeof(msg) - len, " %d", max_chal);
-       if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
+       if (os_snprintf_error(sizeof(msg) - len, ret))
                return EAP_SIM_DB_FAILURE;
        len += ret;
 
@@ -932,7 +941,6 @@ int eap_sim_db_get_gsm_triplets(struct eap_sim_db_data *data,
        if (entry == NULL)
                return EAP_SIM_DB_FAILURE;
 
-       os_get_time(&entry->timestamp);
        os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi));
        entry->cb_session_ctx = cb_session_ctx;
        entry->state = PENDING;
@@ -957,7 +965,7 @@ static char * eap_sim_db_get_next(struct eap_sim_db_data *data, char prefix)
        pos = id;
        end = id + sizeof(buf) * 2 + 2;
        *pos++ = prefix;
-       pos += wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf));
+       wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf));
        
        return id;
 }
@@ -1378,7 +1386,8 @@ int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username,
 
        imsi_len = os_strlen(imsi);
        len = os_snprintf(msg, sizeof(msg), "AKA-REQ-AUTH ");
-       if (len < 0 || len + imsi_len >= sizeof(msg))
+       if (os_snprintf_error(sizeof(msg), len) ||
+           len + imsi_len >= sizeof(msg))
                return EAP_SIM_DB_FAILURE;
        os_memcpy(msg + len, imsi, imsi_len);
        len += imsi_len;
@@ -1392,7 +1401,6 @@ int eap_sim_db_get_aka_auth(struct eap_sim_db_data *data, const char *username,
        if (entry == NULL)
                return EAP_SIM_DB_FAILURE;
 
-       os_get_time(&entry->timestamp);
        entry->aka = 1;
        os_strlcpy(entry->imsi, imsi, sizeof(entry->imsi));
        entry->cb_session_ctx = cb_session_ctx;
@@ -1443,19 +1451,20 @@ int eap_sim_db_resynchronize(struct eap_sim_db_data *data,
 
                imsi_len = os_strlen(imsi);
                len = os_snprintf(msg, sizeof(msg), "AKA-AUTS ");
-               if (len < 0 || len + imsi_len >= sizeof(msg))
+               if (os_snprintf_error(sizeof(msg), len) ||
+                   len + imsi_len >= sizeof(msg))
                        return -1;
                os_memcpy(msg + len, imsi, imsi_len);
                len += imsi_len;
 
                ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
-               if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
+               if (os_snprintf_error(sizeof(msg) - len, ret))
                        return -1;
                len += ret;
                len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
                                        auts, EAP_AKA_AUTS_LEN);
                ret = os_snprintf(msg + len, sizeof(msg) - len, " ");
-               if (ret < 0 || (size_t) ret >= sizeof(msg) - len)
+               if (os_snprintf_error(sizeof(msg) - len, ret))
                        return -1;
                len += ret;
                len += wpa_snprintf_hex(msg + len, sizeof(msg) - len,
index 11f5827..ddf90b8 100644 (file)
@@ -64,6 +64,7 @@ struct eap_ssl_data {
 
 /* dummy type used as a flag for UNAUTH-TLS */
 #define EAP_UNAUTH_TLS_TYPE 255
+#define EAP_WFA_UNAUTH_TLS_TYPE 254
 
 
 struct wpabuf * eap_tls_msg_alloc(EapType type, size_t payload_len,
@@ -73,6 +74,9 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
 void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
 u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
                               char *label, size_t len);
+u8 * eap_server_tls_derive_session_id(struct eap_sm *sm,
+                                     struct eap_ssl_data *data, u8 eap_type,
+                                     size_t *len);
 struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data,
                                         int eap_type, int version, u8 id);
 struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version);
index 512ba30..632598f 100644 (file)
@@ -633,7 +633,7 @@ static int ikev2_process_auth_secret(struct ikev2_initiator_data *data,
                return -1;
 
        if (auth_len != prf->hash_len ||
-           os_memcmp(auth, auth_data, auth_len) != 0) {
+           os_memcmp_const(auth, auth_data, auth_len) != 0) {
                wpa_printf(MSG_INFO, "IKEV2: Invalid Authentication Data");
                wpa_hexdump(MSG_DEBUG, "IKEV2: Received Authentication Data",
                            auth, auth_len);
index e429f1e..dc6f689 100644 (file)
@@ -11,6 +11,7 @@
 
 #include "common.h"
 #include "base64.h"
+#include "common/tnc.h"
 #include "tncs.h"
 #include "eap_common/eap_tlv_common.h"
 #include "eap_common/eap_defs.h"
@@ -19,7 +20,9 @@
 /* TODO: TNCS must be thread-safe; review the code and add locking etc. if
  * needed.. */
 
+#ifndef TNC_CONFIG_FILE
 #define TNC_CONFIG_FILE "/etc/tnc_config"
+#endif /* TNC_CONFIG_FILE */
 #define IF_TNCCS_START \
 "<?xml version=\"1.0\"?>\n" \
 "<TNCCS-Batch BatchId=\"%d\" Recipient=\"TNCS\" " \
 
 /* TNC IF-IMV */
 
-typedef unsigned long TNC_UInt32;
-typedef unsigned char *TNC_BufferReference;
-
-typedef TNC_UInt32 TNC_IMVID;
-typedef TNC_UInt32 TNC_ConnectionID;
-typedef TNC_UInt32 TNC_ConnectionState;
-typedef TNC_UInt32 TNC_RetryReason;
-typedef TNC_UInt32 TNC_IMV_Action_Recommendation;
-typedef TNC_UInt32 TNC_IMV_Evaluation_Result;
-typedef TNC_UInt32 TNC_MessageType;
-typedef TNC_MessageType *TNC_MessageTypeList;
-typedef TNC_UInt32 TNC_VendorID;
-typedef TNC_UInt32 TNC_Subtype;
-typedef TNC_UInt32 TNC_Version;
-typedef TNC_UInt32 TNC_Result;
-typedef TNC_UInt32 TNC_AttributeID;
-
-typedef TNC_Result (*TNC_TNCS_BindFunctionPointer)(
-       TNC_IMVID imvID,
-       char *functionName,
-       void **pOutfunctionPointer);
-
-#define TNC_RESULT_SUCCESS 0
-#define TNC_RESULT_NOT_INITIALIZED 1
-#define TNC_RESULT_ALREADY_INITIALIZED 2
-#define TNC_RESULT_NO_COMMON_VERSION 3
-#define TNC_RESULT_CANT_RETRY 4
-#define TNC_RESULT_WONT_RETRY 5
-#define TNC_RESULT_INVALID_PARAMETER 6
-#define TNC_RESULT_CANT_RESPOND 7
-#define TNC_RESULT_ILLEGAL_OPERATION 8
-#define TNC_RESULT_OTHER 9
-#define TNC_RESULT_FATAL 10
-
-#define TNC_CONNECTION_STATE_CREATE 0
-#define TNC_CONNECTION_STATE_HANDSHAKE 1
-#define TNC_CONNECTION_STATE_ACCESS_ALLOWED 2
-#define TNC_CONNECTION_STATE_ACCESS_ISOLATED 3
-#define TNC_CONNECTION_STATE_ACCESS_NONE 4
-#define TNC_CONNECTION_STATE_DELETE 5
-
-#define TNC_IFIMV_VERSION_1 1
-
-#define TNC_VENDORID_ANY ((TNC_VendorID) 0xffffff)
-#define TNC_SUBTYPE_ANY ((TNC_Subtype) 0xff)
-
-/* TNCC-TNCS Message Types */
-#define TNC_TNCCS_RECOMMENDATION               0x00000001
-#define TNC_TNCCS_ERROR                                0x00000002
-#define TNC_TNCCS_PREFERREDLANGUAGE            0x00000003
-#define TNC_TNCCS_REASONSTRINGS                        0x00000004
-
-/* Possible TNC_IMV_Action_Recommendation values: */
-enum IMV_Action_Recommendation {
-       TNC_IMV_ACTION_RECOMMENDATION_ALLOW,
-       TNC_IMV_ACTION_RECOMMENDATION_NO_ACCESS,
-       TNC_IMV_ACTION_RECOMMENDATION_ISOLATE,
-       TNC_IMV_ACTION_RECOMMENDATION_NO_RECOMMENDATION
-};
-
-/* Possible TNC_IMV_Evaluation_Result values: */
-enum IMV_Evaluation_Result {
-       TNC_IMV_EVALUATION_RESULT_COMPLIANT,
-       TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MINOR,
-       TNC_IMV_EVALUATION_RESULT_NONCOMPLIANT_MAJOR,
-       TNC_IMV_EVALUATION_RESULT_ERROR,
-       TNC_IMV_EVALUATION_RESULT_DONT_KNOW
-};
-
 struct tnc_if_imv {
        struct tnc_if_imv *next;
        char *name;
@@ -1181,6 +1115,9 @@ int tncs_global_init(void)
 {
        struct tnc_if_imv *imv;
 
+       if (tncs_global_data)
+               return 0;
+
        tncs_global_data = os_zalloc(sizeof(*tncs_global_data));
        if (tncs_global_data == NULL)
                return -1;
index 9c41962..adfd3df 100644 (file)
@@ -2,7 +2,7 @@ all:
        @echo Nothing to be made.
 
 clean:
-       rm -f *~ *.o *.d
+       rm -f *~ *.o *.d *.gcno *.gcda *.gcov
 
 install:
        @echo Nothing to be made.
index b6e0b13..5579582 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.1X-2004 Authenticator - State dump
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -118,108 +118,172 @@ static inline const char * ctrl_dir_state_txt(int s)
 }
 
 
-void eapol_auth_dump_state(FILE *f, const char *prefix,
-                          struct eapol_state_machine *sm)
+int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf,
+                         size_t buflen)
 {
-       fprintf(f, "%sEAPOL state machine:\n", prefix);
-       fprintf(f, "%s  aWhile=%d quietWhile=%d reAuthWhen=%d\n", prefix,
-               sm->aWhile, sm->quietWhile, sm->reAuthWhen);
+       char *pos, *end;
+       int ret;
+
+       pos = buf;
+       end = pos + buflen;
+
+       ret = os_snprintf(pos, end - pos, "aWhile=%d\nquietWhile=%d\n"
+                         "reAuthWhen=%d\n",
+                         sm->aWhile, sm->quietWhile, sm->reAuthWhen);
+       if (os_snprintf_error(end - pos, ret))
+               return pos - buf;
+       pos += ret;
+
 #define _SB(b) ((b) ? "TRUE" : "FALSE")
-       fprintf(f,
-               "%s  authAbort=%s authFail=%s authPortStatus=%s authStart=%s\n"
-               "%s  authTimeout=%s authSuccess=%s eapFail=%s eapolEap=%s\n"
-               "%s  eapSuccess=%s eapTimeout=%s initialize=%s "
-               "keyAvailable=%s\n"
-               "%s  keyDone=%s keyRun=%s keyTxEnabled=%s portControl=%s\n"
-               "%s  portEnabled=%s portValid=%s reAuthenticate=%s\n",
-               prefix, _SB(sm->authAbort), _SB(sm->authFail),
-               port_state_txt(sm->authPortStatus), _SB(sm->authStart),
-               prefix, _SB(sm->authTimeout), _SB(sm->authSuccess),
-               _SB(sm->eap_if->eapFail), _SB(sm->eapolEap),
-               prefix, _SB(sm->eap_if->eapSuccess),
-               _SB(sm->eap_if->eapTimeout),
-               _SB(sm->initialize), _SB(sm->eap_if->eapKeyAvailable),
-               prefix, _SB(sm->keyDone), _SB(sm->keyRun),
-               _SB(sm->keyTxEnabled), port_type_txt(sm->portControl),
-               prefix, _SB(sm->eap_if->portEnabled), _SB(sm->portValid),
-               _SB(sm->reAuthenticate));
-
-       fprintf(f, "%s  Authenticator PAE:\n"
-               "%s    state=%s\n"
-               "%s    eapolLogoff=%s eapolStart=%s eapRestart=%s\n"
-               "%s    portMode=%s reAuthCount=%d\n"
-               "%s    quietPeriod=%d reAuthMax=%d\n"
-               "%s    authEntersConnecting=%d\n"
-               "%s    authEapLogoffsWhileConnecting=%d\n"
-               "%s    authEntersAuthenticating=%d\n"
-               "%s    authAuthSuccessesWhileAuthenticating=%d\n"
-               "%s    authAuthTimeoutsWhileAuthenticating=%d\n"
-               "%s    authAuthFailWhileAuthenticating=%d\n"
-               "%s    authAuthEapStartsWhileAuthenticating=%d\n"
-               "%s    authAuthEapLogoffWhileAuthenticating=%d\n"
-               "%s    authAuthReauthsWhileAuthenticated=%d\n"
-               "%s    authAuthEapStartsWhileAuthenticated=%d\n"
-               "%s    authAuthEapLogoffWhileAuthenticated=%d\n",
-               prefix, prefix, auth_pae_state_txt(sm->auth_pae_state), prefix,
-               _SB(sm->eapolLogoff), _SB(sm->eapolStart),
-               _SB(sm->eap_if->eapRestart),
-               prefix, port_type_txt(sm->portMode), sm->reAuthCount,
-               prefix, sm->quietPeriod, sm->reAuthMax,
-               prefix, sm->authEntersConnecting,
-               prefix, sm->authEapLogoffsWhileConnecting,
-               prefix, sm->authEntersAuthenticating,
-               prefix, sm->authAuthSuccessesWhileAuthenticating,
-               prefix, sm->authAuthTimeoutsWhileAuthenticating,
-               prefix, sm->authAuthFailWhileAuthenticating,
-               prefix, sm->authAuthEapStartsWhileAuthenticating,
-               prefix, sm->authAuthEapLogoffWhileAuthenticating,
-               prefix, sm->authAuthReauthsWhileAuthenticated,
-               prefix, sm->authAuthEapStartsWhileAuthenticated,
-               prefix, sm->authAuthEapLogoffWhileAuthenticated);
-
-       fprintf(f, "%s  Backend Authentication:\n"
-               "%s    state=%s\n"
-               "%s    eapNoReq=%s eapReq=%s eapResp=%s\n"
-               "%s    serverTimeout=%d\n"
-               "%s    backendResponses=%d\n"
-               "%s    backendAccessChallenges=%d\n"
-               "%s    backendOtherRequestsToSupplicant=%d\n"
-               "%s    backendAuthSuccesses=%d\n"
-               "%s    backendAuthFails=%d\n",
-               prefix, prefix,
-               be_auth_state_txt(sm->be_auth_state),
-               prefix, _SB(sm->eap_if->eapNoReq), _SB(sm->eap_if->eapReq),
-               _SB(sm->eap_if->eapResp),
-               prefix, sm->serverTimeout,
-               prefix, sm->backendResponses,
-               prefix, sm->backendAccessChallenges,
-               prefix, sm->backendOtherRequestsToSupplicant,
-               prefix, sm->backendAuthSuccesses,
-               prefix, sm->backendAuthFails);
-
-       fprintf(f, "%s  Reauthentication Timer:\n"
-               "%s    state=%s\n"
-               "%s    reAuthPeriod=%d reAuthEnabled=%s\n", prefix, prefix,
-               reauth_timer_state_txt(sm->reauth_timer_state), prefix,
-               sm->reAuthPeriod, _SB(sm->reAuthEnabled));
-
-       fprintf(f, "%s  Authenticator Key Transmit:\n"
-               "%s    state=%s\n", prefix, prefix,
-               auth_key_tx_state_txt(sm->auth_key_tx_state));
-
-       fprintf(f, "%s  Key Receive:\n"
-               "%s    state=%s\n"
-               "%s    rxKey=%s\n", prefix, prefix,
-               key_rx_state_txt(sm->key_rx_state), prefix, _SB(sm->rxKey));
-
-       fprintf(f, "%s  Controlled Directions:\n"
-               "%s    state=%s\n"
-               "%s    adminControlledDirections=%s "
-               "operControlledDirections=%s\n"
-               "%s    operEdge=%s\n", prefix, prefix,
-               ctrl_dir_state_txt(sm->ctrl_dir_state),
-               prefix, ctrl_dir_txt(sm->adminControlledDirections),
-               ctrl_dir_txt(sm->operControlledDirections),
-               prefix, _SB(sm->operEdge));
+       ret = os_snprintf(pos, end - pos,
+                         "authAbort=%s\n"
+                         "authFail=%s\n"
+                         "authPortStatus=%s\n"
+                         "authStart=%s\n"
+                         "authTimeout=%s\n"
+                         "authSuccess=%s\n"
+                         "eapFail=%s\n"
+                         "eapolEap=%s\n"
+                         "eapSuccess=%s\n"
+                         "eapTimeout=%s\n"
+                         "initialize=%s\n"
+                         "keyAvailable=%s\n"
+                         "keyDone=%s\n"
+                         "keyRun=%s\n"
+                         "keyTxEnabled=%s\n"
+                         "portControl=%s\n"
+                         "portEnabled=%s\n"
+                         "portValid=%s\n"
+                         "reAuthenticate=%s\n",
+                         _SB(sm->authAbort),
+                         _SB(sm->authFail),
+                         port_state_txt(sm->authPortStatus),
+                         _SB(sm->authStart),
+                         _SB(sm->authTimeout),
+                         _SB(sm->authSuccess),
+                         _SB(sm->eap_if->eapFail),
+                         _SB(sm->eapolEap),
+                         _SB(sm->eap_if->eapSuccess),
+                         _SB(sm->eap_if->eapTimeout),
+                         _SB(sm->initialize),
+                         _SB(sm->eap_if->eapKeyAvailable),
+                         _SB(sm->keyDone), _SB(sm->keyRun),
+                         _SB(sm->keyTxEnabled),
+                         port_type_txt(sm->portControl),
+                         _SB(sm->eap_if->portEnabled),
+                         _SB(sm->portValid),
+                         _SB(sm->reAuthenticate));
+       if (os_snprintf_error(end - pos, ret))
+               return pos - buf;
+       pos += ret;
+
+       ret = os_snprintf(pos, end - pos,
+                         "auth_pae_state=%s\n"
+                         "eapolLogoff=%s\n"
+                         "eapolStart=%s\n"
+                         "eapRestart=%s\n"
+                         "portMode=%s\n"
+                         "reAuthCount=%d\n"
+                         "quietPeriod=%d\n"
+                         "reAuthMax=%d\n"
+                         "authEntersConnecting=%d\n"
+                         "authEapLogoffsWhileConnecting=%d\n"
+                         "authEntersAuthenticating=%d\n"
+                         "authAuthSuccessesWhileAuthenticating=%d\n"
+                         "authAuthTimeoutsWhileAuthenticating=%d\n"
+                         "authAuthFailWhileAuthenticating=%d\n"
+                         "authAuthEapStartsWhileAuthenticating=%d\n"
+                         "authAuthEapLogoffWhileAuthenticating=%d\n"
+                         "authAuthReauthsWhileAuthenticated=%d\n"
+                         "authAuthEapStartsWhileAuthenticated=%d\n"
+                         "authAuthEapLogoffWhileAuthenticated=%d\n",
+                         auth_pae_state_txt(sm->auth_pae_state),
+                         _SB(sm->eapolLogoff),
+                         _SB(sm->eapolStart),
+                         _SB(sm->eap_if->eapRestart),
+                         port_type_txt(sm->portMode),
+                         sm->reAuthCount,
+                         sm->quietPeriod, sm->reAuthMax,
+                         sm->authEntersConnecting,
+                         sm->authEapLogoffsWhileConnecting,
+                         sm->authEntersAuthenticating,
+                         sm->authAuthSuccessesWhileAuthenticating,
+                         sm->authAuthTimeoutsWhileAuthenticating,
+                         sm->authAuthFailWhileAuthenticating,
+                         sm->authAuthEapStartsWhileAuthenticating,
+                         sm->authAuthEapLogoffWhileAuthenticating,
+                         sm->authAuthReauthsWhileAuthenticated,
+                         sm->authAuthEapStartsWhileAuthenticated,
+                         sm->authAuthEapLogoffWhileAuthenticated);
+       if (os_snprintf_error(end - pos, ret))
+               return pos - buf;
+       pos += ret;
+
+       ret = os_snprintf(pos, end - pos,
+                         "be_auth_state=%s\n"
+                         "eapNoReq=%s\n"
+                         "eapReq=%s\n"
+                         "eapResp=%s\n"
+                         "serverTimeout=%d\n"
+                         "backendResponses=%d\n"
+                         "backendAccessChallenges=%d\n"
+                         "backendOtherRequestsToSupplicant=%d\n"
+                         "backendAuthSuccesses=%d\n"
+                         "backendAuthFails=%d\n",
+                         be_auth_state_txt(sm->be_auth_state),
+                         _SB(sm->eap_if->eapNoReq),
+                         _SB(sm->eap_if->eapReq),
+                         _SB(sm->eap_if->eapResp),
+                         sm->serverTimeout,
+                         sm->backendResponses,
+                         sm->backendAccessChallenges,
+                         sm->backendOtherRequestsToSupplicant,
+                         sm->backendAuthSuccesses,
+                         sm->backendAuthFails);
+       if (os_snprintf_error(end - pos, ret))
+               return pos - buf;
+       pos += ret;
+
+       ret = os_snprintf(pos, end - pos,
+                         "reauth_timer_state=%s\n"
+                         "reAuthPeriod=%d\n"
+                         "reAuthEnabled=%s\n",
+                         reauth_timer_state_txt(sm->reauth_timer_state),
+                         sm->reAuthPeriod,
+                         _SB(sm->reAuthEnabled));
+       if (os_snprintf_error(end - pos, ret))
+               return pos - buf;
+       pos += ret;
+
+       ret = os_snprintf(pos, end - pos,
+                         "auth_key_tx_state=%s\n",
+                         auth_key_tx_state_txt(sm->auth_key_tx_state));
+       if (os_snprintf_error(end - pos, ret))
+               return pos - buf;
+       pos += ret;
+
+       ret = os_snprintf(pos, end - pos,
+                         "key_rx_state=%s\n"
+                         "rxKey=%s\n",
+                         key_rx_state_txt(sm->key_rx_state),
+                         _SB(sm->rxKey));
+       if (os_snprintf_error(end - pos, ret))
+               return pos - buf;
+       pos += ret;
+
+       ret = os_snprintf(pos, end - pos,
+                         "ctrl_dir_state=%s\n"
+                         "adminControlledDirections=%s\n"
+                         "operControlledDirections=%s\n"
+                         "operEdge=%s\n",
+                         ctrl_dir_state_txt(sm->ctrl_dir_state),
+                         ctrl_dir_txt(sm->adminControlledDirections),
+                         ctrl_dir_txt(sm->operControlledDirections),
+                         _SB(sm->operEdge));
+       if (os_snprintf_error(end - pos, ret))
+               return pos - buf;
+       pos += ret;
 #undef _SB
+
+       return pos - buf;
 }
index c3ccb46..0df6eb5 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * IEEE 802.1X-2004 Authenticator - EAPOL state machine
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -43,6 +43,7 @@ sm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 0)
 static void eapol_sm_step_run(struct eapol_state_machine *sm);
 static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx);
 static void eapol_auth_initialize(struct eapol_state_machine *sm);
+static void eapol_auth_conf_free(struct eapol_auth_config *conf);
 
 
 static void eapol_auth_logger(struct eapol_authenticator *eapol,
@@ -219,7 +220,8 @@ SM_STATE(AUTH_PAE, DISCONNECTED)
        sm->eapolLogoff = FALSE;
        if (!from_initialize) {
                sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0,
-                                      sm->flags & EAPOL_SM_PREAUTH);
+                                      sm->flags & EAPOL_SM_PREAUTH,
+                                      sm->remediation);
        }
 }
 
@@ -276,7 +278,7 @@ SM_STATE(AUTH_PAE, HELD)
                                   eap_server_get_name(0, sm->eap_type_supp));
        }
        sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0,
-                              sm->flags & EAPOL_SM_PREAUTH);
+                              sm->flags & EAPOL_SM_PREAUTH, sm->remediation);
 }
 
 
@@ -302,7 +304,7 @@ SM_STATE(AUTH_PAE, AUTHENTICATED)
                           eap_server_get_name(0, sm->eap_type_authsrv),
                           extra);
        sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 1,
-                              sm->flags & EAPOL_SM_PREAUTH);
+                              sm->flags & EAPOL_SM_PREAUTH, sm->remediation);
 }
 
 
@@ -830,6 +832,9 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
        eap_conf.fragment_size = eapol->conf.fragment_size;
        eap_conf.pwd_group = eapol->conf.pwd_group;
        eap_conf.pbc_in_m1 = eapol->conf.pbc_in_m1;
+       eap_conf.server_id = eapol->conf.server_id;
+       eap_conf.server_id_len = eapol->conf.server_id_len;
+       eap_conf.erp = eapol->conf.erp;
        sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf);
        if (sm->eap == NULL) {
                eapol_auth_free(sm);
@@ -848,6 +853,11 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
                sm->radius_cui = wpabuf_alloc_copy(radius_cui,
                                                   os_strlen(radius_cui));
 
+       sm->acct_multi_session_id_lo = eapol->acct_multi_session_id_lo++;
+       if (eapol->acct_multi_session_id_lo == 0)
+               eapol->acct_multi_session_id_hi++;
+       sm->acct_multi_session_id_hi = eapol->acct_multi_session_id_hi;
+
        return sm;
 }
 
@@ -999,8 +1009,13 @@ static int eapol_sm_get_eap_user(void *ctx, const u8 *identity,
                                 struct eap_user *user)
 {
        struct eapol_state_machine *sm = ctx;
-       return sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity,
-                                         identity_len, phase2, user);
+       int ret;
+
+       ret = sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity,
+                                        identity_len, phase2, user);
+       if (user->remediation)
+               sm->remediation = 1;
+       return ret;
 }
 
 
@@ -1012,10 +1027,44 @@ static const char * eapol_sm_get_eap_req_id_text(void *ctx, size_t *len)
 }
 
 
+static int eapol_sm_get_erp_send_reauth_start(void *ctx)
+{
+       struct eapol_state_machine *sm = ctx;
+       return sm->eapol->conf.erp_send_reauth_start;
+}
+
+
+static const char * eapol_sm_get_erp_domain(void *ctx)
+{
+       struct eapol_state_machine *sm = ctx;
+       return sm->eapol->conf.erp_domain;
+}
+
+
+static struct eap_server_erp_key * eapol_sm_erp_get_key(void *ctx,
+                                                       const char *keyname)
+{
+       struct eapol_state_machine *sm = ctx;
+       return sm->eapol->cb.erp_get_key(sm->eapol->conf.ctx, keyname);
+}
+
+
+static int eapol_sm_erp_add_key(void *ctx, struct eap_server_erp_key *erp)
+{
+       struct eapol_state_machine *sm = ctx;
+       return sm->eapol->cb.erp_add_key(sm->eapol->conf.ctx, erp);
+}
+
+
 static struct eapol_callbacks eapol_cb =
 {
        eapol_sm_get_eap_user,
-       eapol_sm_get_eap_req_id_text
+       eapol_sm_get_eap_req_id_text,
+       NULL,
+       eapol_sm_get_erp_send_reauth_start,
+       eapol_sm_get_erp_domain,
+       eapol_sm_erp_get_key,
+       eapol_sm_erp_add_key,
 };
 
 
@@ -1045,6 +1094,8 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst,
        os_free(dst->eap_req_id_text);
        dst->pwd_group = src->pwd_group;
        dst->pbc_in_m1 = src->pbc_in_m1;
+       dst->server_id = src->server_id;
+       dst->server_id_len = src->server_id_len;
        if (src->eap_req_id_text) {
                dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len);
                if (dst->eap_req_id_text == NULL)
@@ -1058,16 +1109,16 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst,
        }
        if (src->pac_opaque_encr_key) {
                dst->pac_opaque_encr_key = os_malloc(16);
+               if (dst->pac_opaque_encr_key == NULL)
+                       goto fail;
                os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key,
                          16);
        } else
                dst->pac_opaque_encr_key = NULL;
        if (src->eap_fast_a_id) {
                dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len);
-               if (dst->eap_fast_a_id == NULL) {
-                       os_free(dst->eap_req_id_text);
-                       return -1;
-               }
+               if (dst->eap_fast_a_id == NULL)
+                       goto fail;
                os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id,
                          src->eap_fast_a_id_len);
                dst->eap_fast_a_id_len = src->eap_fast_a_id_len;
@@ -1075,11 +1126,8 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst,
                dst->eap_fast_a_id = NULL;
        if (src->eap_fast_a_id_info) {
                dst->eap_fast_a_id_info = os_strdup(src->eap_fast_a_id_info);
-               if (dst->eap_fast_a_id_info == NULL) {
-                       os_free(dst->eap_req_id_text);
-                       os_free(dst->eap_fast_a_id);
-                       return -1;
-               }
+               if (dst->eap_fast_a_id_info == NULL)
+                       goto fail;
        } else
                dst->eap_fast_a_id_info = NULL;
        dst->eap_fast_prov = src->eap_fast_prov;
@@ -1089,7 +1137,23 @@ static int eapol_auth_conf_clone(struct eapol_auth_config *dst,
        dst->tnc = src->tnc;
        dst->wps = src->wps;
        dst->fragment_size = src->fragment_size;
+
+       os_free(dst->erp_domain);
+       if (src->erp_domain) {
+               dst->erp_domain = os_strdup(src->erp_domain);
+               if (dst->erp_domain == NULL)
+                       goto fail;
+       } else {
+               dst->erp_domain = NULL;
+       }
+       dst->erp_send_reauth_start = src->erp_send_reauth_start;
+       dst->erp = src->erp;
+
        return 0;
+
+fail:
+       eapol_auth_conf_free(dst);
+       return -1;
 }
 
 
@@ -1103,6 +1167,8 @@ static void eapol_auth_conf_free(struct eapol_auth_config *conf)
        conf->eap_fast_a_id = NULL;
        os_free(conf->eap_fast_a_id_info);
        conf->eap_fast_a_id_info = NULL;
+       os_free(conf->erp_domain);
+       conf->erp_domain = NULL;
 }
 
 
@@ -1110,6 +1176,7 @@ struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf,
                                             struct eapol_auth_cb *cb)
 {
        struct eapol_authenticator *eapol;
+       struct os_time now;
 
        eapol = os_zalloc(sizeof(*eapol));
        if (eapol == NULL)
@@ -1135,6 +1202,14 @@ struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf,
        eapol->cb.abort_auth = cb->abort_auth;
        eapol->cb.tx_key = cb->tx_key;
        eapol->cb.eapol_event = cb->eapol_event;
+       eapol->cb.erp_get_key = cb->erp_get_key;
+       eapol->cb.erp_add_key = cb->erp_add_key;
+
+       /* Acct-Multi-Session-Id should be unique over reboots. If reliable
+        * clock is not available, this could be replaced with reboot counter,
+        * etc. */
+       os_get_time(&now);
+       eapol->acct_multi_session_id_hi = now.sec;
 
        return eapol;
 }
index b50bbdd..ebed19a 100644 (file)
@@ -24,6 +24,9 @@ struct eapol_auth_config {
        void *eap_sim_db_priv;
        char *eap_req_id_text; /* a copy of this will be allocated */
        size_t eap_req_id_text_len;
+       int erp_send_reauth_start;
+       char *erp_domain; /* a copy of this will be allocated */
+       int erp; /* Whether ERP is enabled on authentication server */
        u8 *pac_opaque_encr_key;
        u8 *eap_fast_a_id;
        size_t eap_fast_a_id_len;
@@ -37,12 +40,15 @@ struct eapol_auth_config {
        int fragment_size;
        u16 pwd_group;
        int pbc_in_m1;
+       const u8 *server_id;
+       size_t server_id_len;
 
        /* Opaque context pointer to owner data for callback functions */
        void *ctx;
 };
 
 struct eap_user;
+struct eap_server_erp_key;
 
 typedef enum {
        EAPOL_LOGGER_DEBUG, EAPOL_LOGGER_INFO, EAPOL_LOGGER_WARNING
@@ -58,7 +64,8 @@ struct eapol_auth_cb {
                           size_t datalen);
        void (*aaa_send)(void *ctx, void *sta_ctx, const u8 *data,
                         size_t datalen);
-       void (*finished)(void *ctx, void *sta_ctx, int success, int preauth);
+       void (*finished)(void *ctx, void *sta_ctx, int success, int preauth,
+                        int remediation);
        int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len,
                            int phase2, struct eap_user *user);
        int (*sta_entry_alive)(void *ctx, const u8 *addr);
@@ -68,6 +75,9 @@ struct eapol_auth_cb {
        void (*abort_auth)(void *ctx, void *sta_ctx);
        void (*tx_key)(void *ctx, void *sta_ctx);
        void (*eapol_event)(void *ctx, void *sta_ctx, enum eapol_event type);
+       struct eap_server_erp_key * (*erp_get_key)(void *ctx,
+                                                  const char *keyname);
+       int (*erp_add_key)(void *ctx, struct eap_server_erp_key *erp);
 };
 
 
@@ -81,8 +91,8 @@ eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr,
                 const char *identity, const char *radius_cui);
 void eapol_auth_free(struct eapol_state_machine *sm);
 void eapol_auth_step(struct eapol_state_machine *sm);
-void eapol_auth_dump_state(FILE *f, const char *prefix,
-                          struct eapol_state_machine *sm);
+int eapol_auth_dump_state(struct eapol_state_machine *sm, char *buf,
+                         size_t buflen);
 int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx);
 
 #endif /* EAPOL_AUTH_SM_H */
index d7f893a..a29b49c 100644 (file)
@@ -30,6 +30,9 @@ struct eapol_authenticator {
 
        u8 *default_wep_key;
        u8 default_wep_key_idx;
+
+       u32 acct_multi_session_id_hi;
+       u32 acct_multi_session_id_lo;
 };
 
 
@@ -173,6 +176,11 @@ struct eapol_state_machine {
        struct eapol_authenticator *eapol;
 
        void *sta; /* station context pointer to use in callbacks */
+
+       int remediation;
+
+       u32 acct_multi_session_id_hi;
+       u32 acct_multi_session_id_lo;
 };
 
 #endif /* EAPOL_AUTH_SM_I_H */
index 9c41962..adfd3df 100644 (file)
@@ -2,7 +2,7 @@ all:
        @echo Nothing to be made.
 
 clean:
-       rm -f *~ *.o *.d
+       rm -f *~ *.o *.d *.gcno *.gcda *.gcov
 
 install:
        @echo Nothing to be made.
index 9b054fc..9cc234a 100644 (file)
@@ -128,6 +128,7 @@ struct eapol_sm {
        struct wpabuf *eapReqData; /* for EAP */
        Boolean altAccept; /* for EAP */
        Boolean altReject; /* for EAP */
+       Boolean eapTriggerStart;
        Boolean replay_counter_valid;
        u8 last_replay_counter[16];
        struct eapol_config conf;
@@ -137,6 +138,9 @@ struct eapol_sm {
        Boolean cached_pmk;
 
        Boolean unicast_key_received, broadcast_key_received;
+
+       Boolean force_authorized_update;
+
 #ifdef CONFIG_EAP_PROXY
        Boolean use_eap_proxy;
        struct eap_proxy_sm *eap_proxy;
@@ -210,7 +214,6 @@ SM_STATE(SUPP_PAE, LOGOFF)
        SM_ENTRY(SUPP_PAE, LOGOFF);
        eapol_sm_txLogoff(sm);
        sm->logoffSent = TRUE;
-       sm->suppPortStatus = Unauthorized;
        eapol_sm_set_port_unauthorized(sm);
 }
 
@@ -220,8 +223,8 @@ SM_STATE(SUPP_PAE, DISCONNECTED)
        SM_ENTRY(SUPP_PAE, DISCONNECTED);
        sm->sPortMode = Auto;
        sm->startCount = 0;
+       sm->eapTriggerStart = FALSE;
        sm->logoffSent = FALSE;
-       sm->suppPortStatus = Unauthorized;
        eapol_sm_set_port_unauthorized(sm);
        sm->suppAbort = TRUE;
 
@@ -243,6 +246,11 @@ SM_STATE(SUPP_PAE, CONNECTING)
 {
        int send_start = sm->SUPP_PAE_state == SUPP_PAE_CONNECTING;
        SM_ENTRY(SUPP_PAE, CONNECTING);
+
+       if (sm->eapTriggerStart)
+               send_start = 1;
+       sm->eapTriggerStart = FALSE;
+
        if (send_start) {
                sm->startWhen = sm->startPeriod;
                sm->startCount++;
@@ -254,12 +262,14 @@ SM_STATE(SUPP_PAE, CONNECTING)
                 * delay authentication. Use a short timeout to send the first
                 * EAPOL-Start if Authenticator does not start authentication.
                 */
-#ifdef CONFIG_WPS
-               /* Reduce latency on starting WPS negotiation. */
-               sm->startWhen = 1;
-#else /* CONFIG_WPS */
-               sm->startWhen = 3;
-#endif /* CONFIG_WPS */
+               if (sm->conf.wps && !(sm->conf.wps & EAPOL_PEER_IS_WPS20_AP)) {
+                       /* Reduce latency on starting WPS negotiation. */
+                       wpa_printf(MSG_DEBUG,
+                                  "EAPOL: Using shorter startWhen for WPS");
+                       sm->startWhen = 1;
+               } else {
+                       sm->startWhen = 2;
+               }
        }
        eapol_enable_timer_tick(sm);
        sm->eapolEap = FALSE;
@@ -286,7 +296,6 @@ SM_STATE(SUPP_PAE, HELD)
        SM_ENTRY(SUPP_PAE, HELD);
        sm->heldWhile = sm->heldPeriod;
        eapol_enable_timer_tick(sm);
-       sm->suppPortStatus = Unauthorized;
        eapol_sm_set_port_unauthorized(sm);
        sm->cb_status = EAPOL_CB_FAILURE;
 }
@@ -295,7 +304,6 @@ SM_STATE(SUPP_PAE, HELD)
 SM_STATE(SUPP_PAE, AUTHENTICATED)
 {
        SM_ENTRY(SUPP_PAE, AUTHENTICATED);
-       sm->suppPortStatus = Authorized;
        eapol_sm_set_port_authorized(sm);
        sm->cb_status = EAPOL_CB_SUCCESS;
 }
@@ -311,7 +319,6 @@ SM_STATE(SUPP_PAE, RESTART)
 SM_STATE(SUPP_PAE, S_FORCE_AUTH)
 {
        SM_ENTRY(SUPP_PAE, S_FORCE_AUTH);
-       sm->suppPortStatus = Authorized;
        eapol_sm_set_port_authorized(sm);
        sm->sPortMode = ForceAuthorized;
 }
@@ -320,7 +327,6 @@ SM_STATE(SUPP_PAE, S_FORCE_AUTH)
 SM_STATE(SUPP_PAE, S_FORCE_UNAUTH)
 {
        SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH);
-       sm->suppPortStatus = Unauthorized;
        eapol_sm_set_port_unauthorized(sm);
        sm->sPortMode = ForceUnauthorized;
        eapol_sm_txLogoff(sm);
@@ -387,6 +393,8 @@ SM_STEP(SUPP_PAE)
                        SM_ENTER(SUPP_PAE, HELD);
                else if (sm->suppTimeout)
                        SM_ENTER(SUPP_PAE, CONNECTING);
+               else if (sm->eapTriggerStart)
+                       SM_ENTER(SUPP_PAE, CONNECTING);
                break;
        case SUPP_PAE_HELD:
                if (sm->heldWhile == 0)
@@ -722,8 +730,8 @@ static void eapol_sm_processKey(struct eapol_sm *sm)
        hmac_md5(keydata.sign_key, sign_key_len,
                 sm->last_rx_key, sizeof(*hdr) + be_to_host16(hdr->length),
                 key->key_signature);
-       if (os_memcmp(orig_key_sign, key->key_signature,
-                     IEEE8021X_KEY_SIGN_LEN) != 0) {
+       if (os_memcmp_const(orig_key_sign, key->key_signature,
+                           IEEE8021X_KEY_SIGN_LEN) != 0) {
                wpa_printf(MSG_DEBUG, "EAPOL: Invalid key signature in "
                           "EAPOL-Key packet");
                os_memcpy(key->key_signature, orig_key_sign,
@@ -879,14 +887,24 @@ static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx)
 
 static void eapol_sm_set_port_authorized(struct eapol_sm *sm)
 {
-       if (sm->ctx->port_cb)
+       int cb;
+
+       cb = sm->suppPortStatus != Authorized || sm->force_authorized_update;
+       sm->force_authorized_update = FALSE;
+       sm->suppPortStatus = Authorized;
+       if (cb && sm->ctx->port_cb)
                sm->ctx->port_cb(sm->ctx->ctx, 1);
 }
 
 
 static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm)
 {
-       if (sm->ctx->port_cb)
+       int cb;
+
+       cb = sm->suppPortStatus != Unauthorized || sm->force_authorized_update;
+       sm->force_authorized_update = FALSE;
+       sm->suppPortStatus = Unauthorized;
+       if (cb && sm->ctx->port_cb)
                sm->ctx->port_cb(sm->ctx->ctx, 0);
 }
 
@@ -933,9 +951,15 @@ void eapol_sm_step(struct eapol_sm *sm)
        }
 
        if (sm->ctx->cb && sm->cb_status != EAPOL_CB_IN_PROGRESS) {
-               int success = sm->cb_status == EAPOL_CB_SUCCESS ? 1 : 0;
+               enum eapol_supp_result result;
+               if (sm->cb_status == EAPOL_CB_SUCCESS)
+                       result = EAPOL_SUPP_RESULT_SUCCESS;
+               else if (eap_peer_was_failure_expected(sm->eap))
+                       result = EAPOL_SUPP_RESULT_EXPECTED_FAILURE;
+               else
+                       result = EAPOL_SUPP_RESULT_FAILURE;
                sm->cb_status = EAPOL_CB_IN_PROGRESS;
-               sm->ctx->cb(sm, success, sm->ctx->cb_ctx);
+               sm->ctx->cb(sm, result, sm->ctx->cb_ctx);
        }
 }
 
@@ -1084,7 +1108,7 @@ int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen,
                          "suppPortStatus=%s\n",
                          eapol_supp_pae_state(sm->SUPP_PAE_state),
                          eapol_port_status(sm->suppPortStatus));
-       if (len < 0 || (size_t) len >= buflen)
+       if (os_snprintf_error(buflen, len))
                return 0;
 
        if (verbose) {
@@ -1101,7 +1125,7 @@ int eapol_sm_get_status(struct eapol_sm *sm, char *buf, size_t buflen,
                                  sm->maxStart,
                                  eapol_port_control(sm->portControl),
                                  eapol_supp_be_state(sm->SUPP_BE_state));
-               if (ret < 0 || (size_t) ret >= buflen - len)
+               if (os_snprintf_error(buflen - len, ret))
                        return len;
                len += ret;
        }
@@ -1155,7 +1179,7 @@ int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen)
                          "Authorized" : "Unauthorized",
                          sm->SUPP_BE_state);
 
-       if (ret < 0 || (size_t) ret >= buflen)
+       if (os_snprintf_error(buflen, ret))
                return 0;
        len = ret;
 
@@ -1183,7 +1207,7 @@ int eapol_sm_get_mib(struct eapol_sm *sm, char *buf, size_t buflen)
                          sm->dot1xSuppLastEapolFrameVersion,
                          MAC2STR(sm->dot1xSuppLastEapolFrameSource));
 
-       if (ret < 0 || (size_t) ret >= buflen - len)
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
 
@@ -1229,7 +1253,7 @@ int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
                return 0;
        }
 #ifdef CONFIG_WPS
-       if (sm->conf.workaround &&
+       if (sm->conf.wps && sm->conf.workaround &&
            plen < len - sizeof(*hdr) &&
            hdr->type == IEEE802_1X_TYPE_EAP_PACKET &&
            len - sizeof(*hdr) > sizeof(struct eap_hdr)) {
@@ -1257,6 +1281,24 @@ int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
 
        switch (hdr->type) {
        case IEEE802_1X_TYPE_EAP_PACKET:
+               if (sm->conf.workaround) {
+                       /*
+                        * An AP has been reported to send out EAP message with
+                        * undocumented code 10 at some point near the
+                        * completion of EAP authentication. This can result in
+                        * issues with the unexpected EAP message triggering
+                        * restart of EAPOL authentication. Avoid this by
+                        * skipping the message without advancing the state
+                        * machine.
+                        */
+                       const struct eap_hdr *ehdr =
+                               (const struct eap_hdr *) (hdr + 1);
+                       if (plen >= sizeof(*ehdr) && ehdr->code == 10) {
+                               wpa_printf(MSG_DEBUG, "EAPOL: Ignore EAP packet with unknown code 10");
+                               break;
+                       }
+               }
+
                if (sm->cached_pmk) {
                        /* Trying to use PMKSA caching, but Authenticator did
                         * not seem to have a matching entry. Need to restart
@@ -1314,6 +1356,13 @@ int eapol_sm_rx_eapol(struct eapol_sm *sm, const u8 *src, const u8 *buf,
                        eapol_sm_step(sm);
                }
                break;
+#ifdef CONFIG_MACSEC
+       case IEEE802_1X_TYPE_EAPOL_MKA:
+               wpa_printf(MSG_EXCESSIVE,
+                          "EAPOL type %d will be handled by MKA",
+                          hdr->type);
+               break;
+#endif /* CONFIG_MACSEC */
        default:
                wpa_printf(MSG_DEBUG, "EAPOL: Received unknown EAPOL type %d",
                           hdr->type);
@@ -1352,6 +1401,8 @@ void eapol_sm_notify_portEnabled(struct eapol_sm *sm, Boolean enabled)
                return;
        wpa_printf(MSG_DEBUG, "EAPOL: External notification - "
                   "portEnabled=%d", enabled);
+       if (sm->portEnabled != enabled)
+               sm->force_authorized_update = TRUE;
        sm->portEnabled = enabled;
        eapol_sm_step(sm);
 }
@@ -1451,6 +1502,7 @@ void eapol_sm_notify_config(struct eapol_sm *sm,
        sm->conf.required_keys = conf->required_keys;
        sm->conf.fast_reauth = conf->fast_reauth;
        sm->conf.workaround = conf->workaround;
+       sm->conf.wps = conf->wps;
 #ifdef CONFIG_EAP_PROXY
        if (sm->use_eap_proxy) {
                /* Using EAP Proxy, so skip EAP state machine update */
@@ -1461,6 +1513,7 @@ void eapol_sm_notify_config(struct eapol_sm *sm,
                eap_set_fast_reauth(sm->eap, conf->fast_reauth);
                eap_set_workaround(sm->eap, conf->workaround);
                eap_set_force_disabled(sm->eap, conf->eap_disabled);
+               eap_set_external_sim(sm->eap, conf->external_sim);
        }
 }
 
@@ -1482,7 +1535,7 @@ int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len)
        size_t eap_len;
 
 #ifdef CONFIG_EAP_PROXY
-       if (sm->use_eap_proxy) {
+       if (sm && sm->use_eap_proxy) {
                /* Get key from EAP proxy */
                if (sm == NULL || !eap_proxy_key_available(sm->eap_proxy)) {
                        wpa_printf(MSG_DEBUG, "EAPOL: EAP key not available");
@@ -1523,6 +1576,24 @@ key_fetched:
 
 
 /**
+ * eapol_sm_get_session_id - Get EAP Session-Id
+ * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
+ * @len: Pointer to variable that will be set to number of bytes in the session
+ * Returns: Pointer to the EAP Session-Id or %NULL on failure
+ *
+ * The Session-Id is available only after a successful authentication.
+ */
+const u8 * eapol_sm_get_session_id(struct eapol_sm *sm, size_t *len)
+{
+       if (sm == NULL || !eap_key_available(sm->eap)) {
+               wpa_printf(MSG_DEBUG, "EAPOL: EAP Session-Id not available");
+               return NULL;
+       }
+       return eap_get_eapSessionId(sm->eap, len);
+}
+
+
+/**
  * eapol_sm_notify_logoff - Notification of logon/logoff commands
  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
  * @logoff: Whether command was logoff
@@ -1563,21 +1634,15 @@ void eapol_sm_notify_cached(struct eapol_sm *sm)
 /**
  * eapol_sm_notify_pmkid_attempt - Notification of PMKSA caching
  * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init()
- * @attempt: Whether PMKSA caching is tried
  *
- * Notify EAPOL state machines whether PMKSA caching is used.
+ * Notify EAPOL state machines if PMKSA caching is used.
  */
-void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt)
+void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm)
 {
        if (sm == NULL)
                return;
-       if (attempt) {
-               wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA");
-               sm->cached_pmk = TRUE;
-       } else {
-               wpa_printf(MSG_DEBUG, "RSN: Do not try to use cached PMKSA");
-               sm->cached_pmk = FALSE;
-       }
+       wpa_printf(MSG_DEBUG, "RSN: Trying to use cached PMKSA");
+       sm->cached_pmk = TRUE;
 }
 
 
@@ -1589,7 +1654,6 @@ static void eapol_sm_abort_cached(struct eapol_sm *sm)
                return;
        sm->cached_pmk = FALSE;
        sm->SUPP_PAE_state = SUPP_PAE_CONNECTING;
-       sm->suppPortStatus = Unauthorized;
        eapol_sm_set_port_unauthorized(sm);
 
        /* Make sure we do not start sending EAPOL-Start frames first, but
@@ -1761,6 +1825,8 @@ static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable)
                return sm->altAccept;
        case EAPOL_altReject:
                return sm->altReject;
+       case EAPOL_eapTriggerStart:
+               return sm->eapTriggerStart;
        }
        return FALSE;
 }
@@ -1800,6 +1866,9 @@ static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable,
        case EAPOL_altReject:
                sm->altReject = value;
                break;
+       case EAPOL_eapTriggerStart:
+               sm->eapTriggerStart = value;
+               break;
        }
 }
 
@@ -1887,13 +1956,14 @@ static void eapol_sm_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field,
 #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 
 static void eapol_sm_notify_cert(void *ctx, int depth, const char *subject,
-                                const char *cert_hash,
+                                const char *altsubject[],
+                                int num_altsubject, const char *cert_hash,
                                 const struct wpabuf *cert)
 {
        struct eapol_sm *sm = ctx;
        if (sm->ctx->cert_cb)
-               sm->ctx->cert_cb(sm->ctx->ctx, depth, subject,
-                                cert_hash, cert);
+               sm->ctx->cert_cb(sm->ctx->ctx, depth, subject, altsubject,
+                                num_altsubject, cert_hash, cert);
 }
 
 
@@ -1907,6 +1977,17 @@ static void eapol_sm_notify_status(void *ctx, const char *status,
 }
 
 
+#ifdef CONFIG_EAP_PROXY
+static void eapol_sm_eap_proxy_cb(void *ctx)
+{
+       struct eapol_sm *sm = ctx;
+
+       if (sm->ctx->eap_proxy_cb)
+               sm->ctx->eap_proxy_cb(sm->ctx->ctx);
+}
+#endif /* CONFIG_EAP_PROXY */
+
+
 static void eapol_sm_set_anon_id(void *ctx, const u8 *id, size_t len)
 {
        struct eapol_sm *sm = ctx;
@@ -1930,6 +2011,9 @@ static struct eapol_callbacks eapol_cb =
        eapol_sm_eap_param_needed,
        eapol_sm_notify_cert,
        eapol_sm_notify_status,
+#ifdef CONFIG_EAP_PROXY
+       eapol_sm_eap_proxy_cb,
+#endif /* CONFIG_EAP_PROXY */
        eapol_sm_set_anon_id
 };
 
@@ -1965,6 +2049,7 @@ struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
        conf.opensc_engine_path = ctx->opensc_engine_path;
        conf.pkcs11_engine_path = ctx->pkcs11_engine_path;
        conf.pkcs11_module_path = ctx->pkcs11_module_path;
+       conf.openssl_ciphers = ctx->openssl_ciphers;
        conf.wps = ctx->wps;
        conf.cert_in_cb = ctx->cert_in_cb;
 
@@ -1983,6 +2068,7 @@ struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
 #endif /* CONFIG_EAP_PROXY */
 
        /* Initialize EAPOL state machines */
+       sm->force_authorized_update = TRUE;
        sm->initialize = TRUE;
        eapol_sm_step(sm);
        sm->initialize = FALSE;
@@ -2032,3 +2118,22 @@ int eapol_sm_failed(struct eapol_sm *sm)
                return 0;
        return !sm->eapSuccess && sm->eapFail;
 }
+
+
+int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len)
+{
+#ifdef CONFIG_EAP_PROXY
+       if (sm->eap_proxy == NULL)
+               return -1;
+       return eap_proxy_get_imsi(sm->eap_proxy, imsi, len);
+#else /* CONFIG_EAP_PROXY */
+       return -1;
+#endif /* CONFIG_EAP_PROXY */
+}
+
+
+void eapol_sm_erp_flush(struct eapol_sm *sm)
+{
+       if (sm)
+               eap_peer_erp_free_keys(sm->eap);
+}
index c4b87da..1309ff7 100644 (file)
@@ -53,11 +53,29 @@ struct eapol_config {
         * eap_disabled - Whether EAP is disabled
         */
        int eap_disabled;
+
+       /**
+        * external_sim - Use external processing for SIM/USIM operations
+        */
+       int external_sim;
+
+#define EAPOL_LOCAL_WPS_IN_USE BIT(0)
+#define EAPOL_PEER_IS_WPS20_AP BIT(1)
+       /**
+        * wps - Whether this connection is used for WPS
+        */
+       int wps;
 };
 
 struct eapol_sm;
 struct wpa_config_blob;
 
+enum eapol_supp_result {
+       EAPOL_SUPP_RESULT_FAILURE,
+       EAPOL_SUPP_RESULT_SUCCESS,
+       EAPOL_SUPP_RESULT_EXPECTED_FAILURE
+};
+
 /**
  * struct eapol_ctx - Global (for all networks) EAPOL state machine context
  */
@@ -78,7 +96,7 @@ struct eapol_ctx {
        /**
         * cb - Function to be called when EAPOL negotiation has been completed
         * @eapol: Pointer to EAPOL state machine data
-        * @success: Whether the authentication was completed successfully
+        * @result: Whether the authentication was completed successfully
         * @ctx: Pointer to context data (cb_ctx)
         *
         * This optional callback function will be called when the EAPOL
@@ -86,7 +104,8 @@ struct eapol_ctx {
         * EAPOL state machine to process the key and terminate the EAPOL state
         * machine. Currently, this is used only in RSN pre-authentication.
         */
-       void (*cb)(struct eapol_sm *eapol, int success, void *ctx);
+       void (*cb)(struct eapol_sm *eapol, enum eapol_supp_result result,
+                  void *ctx);
 
        /**
         * cb_ctx - Callback context for cb()
@@ -193,6 +212,15 @@ struct eapol_ctx {
        const char *pkcs11_module_path;
 
        /**
+        * openssl_ciphers - OpenSSL cipher string
+        *
+        * This is an OpenSSL specific configuration option for configuring the
+        * default ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the
+        * default.
+        */
+       const char *openssl_ciphers;
+
+       /**
         * wps - WPS context data
         *
         * This is only used by EAP-WSC and can be left %NULL if not available.
@@ -220,10 +248,13 @@ struct eapol_ctx {
         * @ctx: Callback context (ctx)
         * @depth: Depth in certificate chain (0 = server)
         * @subject: Subject of the peer certificate
+        * @altsubject: Select fields from AltSubject of the peer certificate
+        * @num_altsubject: Number of altsubject values
         * @cert_hash: SHA-256 hash of the certificate
         * @cert: Peer certificate
         */
        void (*cert_cb)(void *ctx, int depth, const char *subject,
+                       const char *altsubject[], int num_altsubject,
                        const char *cert_hash, const struct wpabuf *cert);
 
        /**
@@ -240,6 +271,14 @@ struct eapol_ctx {
        void (*status_cb)(void *ctx, const char *status,
                          const char *parameter);
 
+#ifdef CONFIG_EAP_PROXY
+       /**
+        * eap_proxy_cb - Callback signifying any updates from eap_proxy
+        * @ctx: eapol_ctx from eap_peer_sm_init() call
+        */
+       void (*eap_proxy_cb)(void *ctx);
+#endif /* CONFIG_EAP_PROXY */
+
        /**
         * set_anon_id - Set or add anonymous identity
         * @ctx: eapol_ctx from eap_peer_sm_init() call
@@ -273,9 +312,10 @@ void eapol_sm_notify_config(struct eapol_sm *sm,
                            struct eap_peer_config *config,
                            const struct eapol_config *conf);
 int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len);
+const u8 * eapol_sm_get_session_id(struct eapol_sm *sm, size_t *len);
 void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff);
 void eapol_sm_notify_cached(struct eapol_sm *sm);
-void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm, int attempt);
+void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm);
 void eapol_sm_register_scard_ctx(struct eapol_sm *sm, void *ctx);
 void eapol_sm_notify_portControl(struct eapol_sm *sm, PortControl portControl);
 void eapol_sm_notify_ctrl_attached(struct eapol_sm *sm);
@@ -287,6 +327,8 @@ const char * eapol_sm_get_method_name(struct eapol_sm *sm);
 void eapol_sm_set_ext_pw_ctx(struct eapol_sm *sm,
                             struct ext_password_data *ext);
 int eapol_sm_failed(struct eapol_sm *sm);
+void eapol_sm_erp_flush(struct eapol_sm *sm);
+int eapol_sm_get_eap_proxy_imsi(struct eapol_sm *sm, char *imsi, size_t *len);
 #else /* IEEE8021X_EAPOL */
 static inline struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
 {
@@ -346,13 +388,20 @@ static inline int eapol_sm_get_key(struct eapol_sm *sm, u8 *key, size_t len)
 {
        return -1;
 }
+static inline const u8 *
+eapol_sm_get_session_id(struct eapol_sm *sm, size_t *len)
+{
+       return NULL;
+}
 static inline void eapol_sm_notify_logoff(struct eapol_sm *sm, Boolean logoff)
 {
 }
 static inline void eapol_sm_notify_cached(struct eapol_sm *sm)
 {
 }
-#define eapol_sm_notify_pmkid_attempt(sm, attempt) do { } while (0)
+static inline void eapol_sm_notify_pmkid_attempt(struct eapol_sm *sm)
+{
+}
 #define eapol_sm_register_scard_ctx(sm, ctx) do { } while (0)
 static inline void eapol_sm_notify_portControl(struct eapol_sm *sm,
                                               PortControl portControl)
@@ -386,6 +435,9 @@ static inline int eapol_sm_failed(struct eapol_sm *sm)
 {
        return 0;
 }
+static inline void eapol_sm_erp_flush(struct eapol_sm *sm)
+{
+}
 #endif /* IEEE8021X_EAPOL */
 
 #endif /* EAPOL_SUPP_SM_H */
index 9c41962..adfd3df 100644 (file)
@@ -2,7 +2,7 @@ all:
        @echo Nothing to be made.
 
 clean:
-       rm -f *~ *.o *.d
+       rm -f *~ *.o *.d *.gcno *.gcda *.gcov
 
 install:
        @echo Nothing to be made.
index dd825b5..2a45245 100644 (file)
@@ -39,6 +39,11 @@ struct l2_ethhdr {
 #pragma pack(pop)
 #endif /* _MSC_VER */
 
+enum l2_packet_filter_type {
+       L2_PACKET_FILTER_DHCP,
+       L2_PACKET_FILTER_NDISC,
+};
+
 /**
  * l2_packet_init - Initialize l2_packet interface
  * @ifname: Interface name
@@ -63,6 +68,19 @@ struct l2_packet_data * l2_packet_init(
        void *rx_callback_ctx, int l2_hdr);
 
 /**
+ * l2_packet_init_bridge - Like l2_packet_init() but with bridge workaround
+ *
+ * This version of l2_packet_init() can be used to enable a workaround for Linux
+ * packet socket in case of a station interface in a bridge.
+ */
+struct l2_packet_data * l2_packet_init_bridge(
+       const char *br_ifname, const char *ifname, const u8 *own_addr,
+       unsigned short protocol,
+       void (*rx_callback)(void *ctx, const u8 *src_addr,
+                           const u8 *buf, size_t len),
+       void *rx_callback_ctx, int l2_hdr);
+
+/**
  * l2_packet_deinit - Deinitialize l2_packet interface
  * @l2: Pointer to internal l2_packet data from l2_packet_init()
  */
@@ -121,4 +139,16 @@ int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len);
  */
 void l2_packet_notify_auth_start(struct l2_packet_data *l2);
 
+/**
+ * l2_packet_set_packet_filter - Set socket filter for l2_packet
+ * @l2: Pointer to internal l2_packet data from l2_packet_init()
+ * @type: enum l2_packet_filter_type, type of filter
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to set the socket filter for l2_packet socket.
+ *
+ */
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+                               enum l2_packet_filter_type type);
+
 #endif /* L2_PACKET_H */
index 2e9a04c..aa83648 100644 (file)
@@ -256,6 +256,18 @@ struct l2_packet_data * l2_packet_init(
 }
 
 
+struct l2_packet_data * l2_packet_init_bridge(
+       const char *br_ifname, const char *ifname, const u8 *own_addr,
+       unsigned short protocol,
+       void (*rx_callback)(void *ctx, const u8 *src_addr,
+                           const u8 *buf, size_t len),
+       void *rx_callback_ctx, int l2_hdr)
+{
+       return l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
+                             rx_callback_ctx, l2_hdr);
+}
+
+
 void l2_packet_deinit(struct l2_packet_data *l2)
 {
        if (l2 != NULL) {
@@ -308,3 +320,10 @@ int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
 void l2_packet_notify_auth_start(struct l2_packet_data *l2)
 {
 }
+
+
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+                               enum l2_packet_filter_type type)
+{
+       return -1;
+}
index 1419830..41de2f8 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - Layer2 packet handling with Linux packet sockets
- * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 #include <sys/ioctl.h>
 #include <netpacket/packet.h>
 #include <net/if.h>
+#include <linux/filter.h>
 
 #include "common.h"
 #include "eloop.h"
+#include "crypto/sha1.h"
+#include "crypto/crypto.h"
 #include "l2_packet.h"
 
 
@@ -26,6 +29,56 @@ struct l2_packet_data {
        void *rx_callback_ctx;
        int l2_hdr; /* whether to include layer 2 (Ethernet) header data
                     * buffers */
+
+       /* For working around Linux packet socket behavior and regression. */
+       int fd_br_rx;
+       int last_from_br;
+       u8 last_hash[SHA1_MAC_LEN];
+       unsigned int num_rx, num_rx_br;
+};
+
+/* Generated by 'sudo tcpdump -s 3000 -dd greater 278 and ip and udp and
+ * src port bootps and dst port bootpc'
+ */
+static struct sock_filter dhcp_sock_filter_insns[] = {
+       { 0x80, 0, 0, 0x00000000 },
+       { 0x35, 0, 12, 0x00000116 },
+       { 0x28, 0, 0, 0x0000000c },
+       { 0x15, 0, 10, 0x00000800 },
+       { 0x30, 0, 0, 0x00000017 },
+       { 0x15, 0, 8, 0x00000011 },
+       { 0x28, 0, 0, 0x00000014 },
+       { 0x45, 6, 0, 0x00001fff },
+       { 0xb1, 0, 0, 0x0000000e },
+       { 0x48, 0, 0, 0x0000000e },
+       { 0x15, 0, 3, 0x00000043 },
+       { 0x48, 0, 0, 0x00000010 },
+       { 0x15, 0, 1, 0x00000044 },
+       { 0x6, 0, 0, 0x00000bb8 },
+       { 0x6, 0, 0, 0x00000000 },
+};
+
+static const struct sock_fprog dhcp_sock_filter = {
+       .len = ARRAY_SIZE(dhcp_sock_filter_insns),
+       .filter = dhcp_sock_filter_insns,
+};
+
+
+/* Generated by 'sudo tcpdump -dd -s 1500 multicast and ip6[6]=58' */
+static struct sock_filter ndisc_sock_filter_insns[] = {
+       { 0x30, 0, 0, 0x00000000 },
+       { 0x45, 0, 5, 0x00000001 },
+       { 0x28, 0, 0, 0x0000000c },
+       { 0x15, 0, 3, 0x000086dd },
+       { 0x30, 0, 0, 0x00000014 },
+       { 0x15, 0, 1, 0x0000003a },
+       { 0x6, 0, 0, 0x000005dc },
+       { 0x6, 0, 0, 0x00000000 },
+};
+
+static const struct sock_fprog ndisc_sock_filter = {
+       .len = ARRAY_SIZE(ndisc_sock_filter_insns),
+       .filter = ndisc_sock_filter_insns,
 };
 
 
@@ -74,6 +127,7 @@ static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
        struct sockaddr_ll ll;
        socklen_t fromlen;
 
+       l2->num_rx++;
        os_memset(&ll, 0, sizeof(ll));
        fromlen = sizeof(ll);
        res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll,
@@ -84,6 +138,80 @@ static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
                return;
        }
 
+       wpa_printf(MSG_DEBUG, "%s: src=" MACSTR " len=%d",
+                  __func__, MAC2STR(ll.sll_addr), (int) res);
+
+       if (l2->fd_br_rx >= 0) {
+               u8 hash[SHA1_MAC_LEN];
+               const u8 *addr[1];
+               size_t len[1];
+
+               /*
+                * Close the workaround socket if the kernel version seems to be
+                * able to deliver packets through the packet socket before
+                * authorization has been completed (in dormant state).
+                */
+               if (l2->num_rx_br <= 1) {
+                       wpa_printf(MSG_DEBUG,
+                                  "l2_packet_receive: Main packet socket for %s seems to have working RX - close workaround bridge socket",
+                                  l2->ifname);
+                       eloop_unregister_read_sock(l2->fd_br_rx);
+                       close(l2->fd_br_rx);
+                       l2->fd_br_rx = -1;
+               }
+
+               addr[0] = buf;
+               len[0] = res;
+               sha1_vector(1, addr, len, hash);
+               if (l2->last_from_br &&
+                   os_memcmp(hash, l2->last_hash, SHA1_MAC_LEN) == 0) {
+                       wpa_printf(MSG_DEBUG, "%s: Drop duplicate RX",
+                                  __func__);
+                       return;
+               }
+               os_memcpy(l2->last_hash, hash, SHA1_MAC_LEN);
+       }
+
+       l2->last_from_br = 0;
+       l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
+}
+
+
+static void l2_packet_receive_br(int sock, void *eloop_ctx, void *sock_ctx)
+{
+       struct l2_packet_data *l2 = eloop_ctx;
+       u8 buf[2300];
+       int res;
+       struct sockaddr_ll ll;
+       socklen_t fromlen;
+       u8 hash[SHA1_MAC_LEN];
+       const u8 *addr[1];
+       size_t len[1];
+
+       l2->num_rx_br++;
+       os_memset(&ll, 0, sizeof(ll));
+       fromlen = sizeof(ll);
+       res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &ll,
+                      &fromlen);
+       if (res < 0) {
+               wpa_printf(MSG_DEBUG, "l2_packet_receive_br - recvfrom: %s",
+                          strerror(errno));
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "%s: src=" MACSTR " len=%d",
+                  __func__, MAC2STR(ll.sll_addr), (int) res);
+
+       addr[0] = buf;
+       len[0] = res;
+       sha1_vector(1, addr, len, hash);
+       if (!l2->last_from_br &&
+           os_memcmp(hash, l2->last_hash, SHA1_MAC_LEN) == 0) {
+               wpa_printf(MSG_DEBUG, "%s: Drop duplicate RX", __func__);
+               return;
+       }
+       l2->last_from_br = 1;
+       os_memcpy(l2->last_hash, hash, SHA1_MAC_LEN);
        l2->rx_callback(l2->rx_callback_ctx, ll.sll_addr, buf, res);
 }
 
@@ -105,6 +233,7 @@ struct l2_packet_data * l2_packet_init(
        l2->rx_callback = rx_callback;
        l2->rx_callback_ctx = rx_callback_ctx;
        l2->l2_hdr = l2_hdr;
+       l2->fd_br_rx = -1;
 
        l2->fd = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,
                        htons(protocol));
@@ -152,6 +281,87 @@ struct l2_packet_data * l2_packet_init(
 }
 
 
+struct l2_packet_data * l2_packet_init_bridge(
+       const char *br_ifname, const char *ifname, const u8 *own_addr,
+       unsigned short protocol,
+       void (*rx_callback)(void *ctx, const u8 *src_addr,
+                           const u8 *buf, size_t len),
+       void *rx_callback_ctx, int l2_hdr)
+{
+       struct l2_packet_data *l2;
+       struct sock_filter ethertype_sock_filter_insns[] = {
+               /* Load ethertype */
+               BPF_STMT(BPF_LD | BPF_H | BPF_ABS, 2 * ETH_ALEN),
+               /* Jump over next statement if ethertype does not match */
+               BPF_JUMP(BPF_JMP | BPF_JEQ | BPF_K, protocol, 0, 1),
+               /* Ethertype match - return all */
+               BPF_STMT(BPF_RET | BPF_K, ~0),
+               /* No match - drop */
+               BPF_STMT(BPF_RET | BPF_K, 0)
+       };
+       const struct sock_fprog ethertype_sock_filter = {
+               .len = ARRAY_SIZE(ethertype_sock_filter_insns),
+               .filter = ethertype_sock_filter_insns,
+       };
+       struct sockaddr_ll ll;
+
+       l2 = l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
+                           rx_callback_ctx, l2_hdr);
+       if (!l2)
+               return NULL;
+
+       /*
+        * The Linux packet socket behavior has changed over the years and there
+        * is an inconvenient regression in it that breaks RX for a specific
+        * protocol from interfaces in a bridge when that interface is not in
+        * fully operation state (i.e., when in station mode and not completed
+        * authorization). To work around this, register ETH_P_ALL version of
+        * the packet socket bound to the real netdev and use socket filter to
+        * match the ethertype value. This version is less efficient, but
+        * required for functionality with many kernel version. If the main
+        * packet socket is found to be working, this less efficient version
+        * gets closed automatically.
+        */
+
+       l2->fd_br_rx = socket(PF_PACKET, l2_hdr ? SOCK_RAW : SOCK_DGRAM,
+                             htons(ETH_P_ALL));
+       if (l2->fd_br_rx < 0) {
+               wpa_printf(MSG_DEBUG, "%s: socket(PF_PACKET-fd_br_rx): %s",
+                          __func__, strerror(errno));
+               /* try to continue without the workaround RX socket */
+               return l2;
+       }
+
+       os_memset(&ll, 0, sizeof(ll));
+       ll.sll_family = PF_PACKET;
+       ll.sll_ifindex = if_nametoindex(ifname);
+       ll.sll_protocol = htons(ETH_P_ALL);
+       if (bind(l2->fd_br_rx, (struct sockaddr *) &ll, sizeof(ll)) < 0) {
+               wpa_printf(MSG_DEBUG, "%s: bind[PF_PACKET-fd_br_rx]: %s",
+                          __func__, strerror(errno));
+               /* try to continue without the workaround RX socket */
+               close(l2->fd_br_rx);
+               l2->fd_br_rx = -1;
+               return l2;
+       }
+
+       if (setsockopt(l2->fd_br_rx, SOL_SOCKET, SO_ATTACH_FILTER,
+                      &ethertype_sock_filter, sizeof(struct sock_fprog))) {
+               wpa_printf(MSG_DEBUG,
+                          "l2_packet_linux: setsockopt(SO_ATTACH_FILTER) failed: %s",
+                          strerror(errno));
+               /* try to continue without the workaround RX socket */
+               close(l2->fd_br_rx);
+               l2->fd_br_rx = -1;
+               return l2;
+       }
+
+       eloop_register_read_sock(l2->fd_br_rx, l2_packet_receive_br, l2, NULL);
+
+       return l2;
+}
+
+
 void l2_packet_deinit(struct l2_packet_data *l2)
 {
        if (l2 == NULL)
@@ -161,7 +371,12 @@ void l2_packet_deinit(struct l2_packet_data *l2)
                eloop_unregister_read_sock(l2->fd);
                close(l2->fd);
        }
-               
+
+       if (l2->fd_br_rx >= 0) {
+               eloop_unregister_read_sock(l2->fd_br_rx);
+               close(l2->fd_br_rx);
+       }
+
        os_free(l2);
 }
 
@@ -202,3 +417,31 @@ int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
 void l2_packet_notify_auth_start(struct l2_packet_data *l2)
 {
 }
+
+
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+                               enum l2_packet_filter_type type)
+{
+       const struct sock_fprog *sock_filter;
+
+       switch (type) {
+       case L2_PACKET_FILTER_DHCP:
+               sock_filter = &dhcp_sock_filter;
+               break;
+       case L2_PACKET_FILTER_NDISC:
+               sock_filter = &ndisc_sock_filter;
+               break;
+       default:
+               return -1;
+       }
+
+       if (setsockopt(l2->fd, SOL_SOCKET, SO_ATTACH_FILTER,
+                      sock_filter, sizeof(struct sock_fprog))) {
+               wpa_printf(MSG_ERROR,
+                          "l2_packet_linux: setsockopt(SO_ATTACH_FILTER) failed: %s",
+                          strerror(errno));
+               return -1;
+       }
+
+       return 0;
+}
index 23b8ddc..7167781 100644 (file)
@@ -450,6 +450,18 @@ struct l2_packet_data * l2_packet_init(
 }
 
 
+struct l2_packet_data * l2_packet_init_bridge(
+       const char *br_ifname, const char *ifname, const u8 *own_addr,
+       unsigned short protocol,
+       void (*rx_callback)(void *ctx, const u8 *src_addr,
+                           const u8 *buf, size_t len),
+       void *rx_callback_ctx, int l2_hdr)
+{
+       return l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
+                             rx_callback_ctx, l2_hdr);
+}
+
+
 void l2_packet_deinit(struct l2_packet_data *l2)
 {
        if (l2 == NULL)
@@ -514,3 +526,10 @@ int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len)
 void l2_packet_notify_auth_start(struct l2_packet_data *l2)
 {
 }
+
+
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+                               enum l2_packet_filter_type type)
+{
+       return -1;
+}
index b01e830..307fc6d 100644 (file)
@@ -84,12 +84,25 @@ struct l2_packet_data * l2_packet_init(
         * TODO: open connection for receiving frames
         */
        l2->fd = -1;
-       eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
+       if (l2->fd >= 0)
+               eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL);
 
        return l2;
 }
 
 
+struct l2_packet_data * l2_packet_init_bridge(
+       const char *br_ifname, const char *ifname, const u8 *own_addr,
+       unsigned short protocol,
+       void (*rx_callback)(void *ctx, const u8 *src_addr,
+                           const u8 *buf, size_t len),
+       void *rx_callback_ctx, int l2_hdr)
+{
+       return l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
+                             rx_callback_ctx, l2_hdr);
+}
+
+
 void l2_packet_deinit(struct l2_packet_data *l2)
 {
        if (l2 == NULL)
@@ -115,3 +128,10 @@ void l2_packet_notify_auth_start(struct l2_packet_data *l2)
 {
        /* This function can be left empty */
 }
+
+
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+                               enum l2_packet_filter_type type)
+{
+       return -1;
+}
index 45aef56..bb4f4a3 100644 (file)
@@ -54,15 +54,16 @@ static int l2_packet_init_libdnet(struct l2_packet_data *l2)
 
        l2->eth = eth_open(l2->ifname);
        if (!l2->eth) {
-               printf("Failed to open interface '%s'.\n", l2->ifname);
-               perror("eth_open");
+               wpa_printf(MSG_ERROR,
+                          "Failed to open interface '%s' - eth_open: %s",
+                          l2->ifname, strerror(errno));
                return -1;
        }
 
        if (eth_get(l2->eth, &own_addr) < 0) {
-               printf("Failed to get own hw address from interface '%s'.\n",
-                      l2->ifname);
-               perror("eth_get");
+               wpa_printf(MSG_ERROR,
+                          "Failed to get own hw address from interface '%s' - eth_get: %s",
+                          l2->ifname, strerror(errno));
                eth_close(l2->eth);
                l2->eth = NULL;
                return -1;
@@ -378,3 +379,10 @@ void l2_packet_notify_auth_start(struct l2_packet_data *l2)
                               l2, l2->pcap);
 #endif /* CONFIG_WINPCAP */
 }
+
+
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+                               enum l2_packet_filter_type type)
+{
+       return -1;
+}
index 6b117ca..e26ca20 100644 (file)
@@ -44,7 +44,7 @@ static int wpa_priv_cmd(struct l2_packet_data *l2, int cmd,
        msg.msg_namelen = sizeof(l2->priv_addr);
 
        if (sendmsg(l2->fd, &msg, 0) < 0) {
-               perror("L2: sendmsg(cmd)");
+               wpa_printf(MSG_ERROR, "L2: sendmsg(cmd): %s", strerror(errno));
                return -1;
        }
 
@@ -82,7 +82,8 @@ int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto,
        msg.msg_namelen = sizeof(l2->priv_addr);
 
        if (sendmsg(l2->fd, &msg, 0) < 0) {
-               perror("L2: sendmsg(packet_send)");
+               wpa_printf(MSG_ERROR, "L2: sendmsg(packet_send): %s",
+                          strerror(errno));
                return -1;
        }
 
@@ -102,7 +103,8 @@ static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx)
        res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from,
                       &fromlen);
        if (res < 0) {
-               perror("l2_packet_receive - recvfrom");
+               wpa_printf(MSG_ERROR, "l2_packet_receive - recvfrom: %s",
+                          strerror(errno));
                return;
        }
        if (res < ETH_ALEN) {
@@ -162,7 +164,7 @@ struct l2_packet_data * l2_packet_init(
 
        l2->fd = socket(PF_UNIX, SOCK_DGRAM, 0);
        if (l2->fd < 0) {
-               perror("socket(PF_UNIX)");
+               wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
                os_free(l2->own_socket_path);
                l2->own_socket_path = NULL;
                os_free(l2);
@@ -173,7 +175,8 @@ struct l2_packet_data * l2_packet_init(
        addr.sun_family = AF_UNIX;
        os_strlcpy(addr.sun_path, l2->own_socket_path, sizeof(addr.sun_path));
        if (bind(l2->fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-               perror("l2-pkt-privsep: bind(PF_UNIX)");
+               wpa_printf(MSG_ERROR, "l2-pkt-privsep: bind(PF_UNIX): %s",
+                          strerror(errno));
                goto fail;
        }
 
@@ -191,14 +194,14 @@ struct l2_packet_data * l2_packet_init(
        tv.tv_usec = 0;
        res = select(l2->fd + 1, &rfds, NULL, NULL, &tv);
        if (res < 0 && errno != EINTR) {
-               perror("select");
+               wpa_printf(MSG_ERROR, "select: %s", strerror(errno));
                goto fail;
        }
 
        if (FD_ISSET(l2->fd, &rfds)) {
                res = recv(l2->fd, reply, sizeof(reply), 0);
                if (res < 0) {
-                       perror("recv");
+                       wpa_printf(MSG_ERROR, "recv: %s", strerror(errno));
                        goto fail;
                }
        } else {
@@ -228,6 +231,18 @@ fail:
 }
 
 
+struct l2_packet_data * l2_packet_init_bridge(
+       const char *br_ifname, const char *ifname, const u8 *own_addr,
+       unsigned short protocol,
+       void (*rx_callback)(void *ctx, const u8 *src_addr,
+                           const u8 *buf, size_t len),
+       void *rx_callback_ctx, int l2_hdr)
+{
+       return l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
+                             rx_callback_ctx, l2_hdr);
+}
+
+
 void l2_packet_deinit(struct l2_packet_data *l2)
 {
        if (l2 == NULL)
@@ -259,3 +274,10 @@ void l2_packet_notify_auth_start(struct l2_packet_data *l2)
 {
        wpa_priv_cmd(l2, PRIVSEP_CMD_L2_NOTIFY_AUTH_START, NULL, 0);
 }
+
+
+int l2_packet_set_packet_filter(struct l2_packet_data *l2,
+                               enum l2_packet_filter_type type)
+{
+       return -1;
+}
index b6e5088..74085a3 100644 (file)
@@ -248,6 +248,18 @@ struct l2_packet_data * l2_packet_init(
 }
 
 
+struct l2_packet_data * l2_packet_init_bridge(
+       const char *br_ifname, const char *ifname, const u8 *own_addr,
+       unsigned short protocol,
+       void (*rx_callback)(void *ctx, const u8 *src_addr,
+                           const u8 *buf, size_t len),
+       void *rx_callback_ctx, int l2_hdr)
+{
+       return l2_packet_init(br_ifname, own_addr, protocol, rx_callback,
+                             rx_callback_ctx, l2_hdr);
+}
+
+
 static void l2_packet_deinit_timeout(void *eloop_ctx, void *timeout_ctx)
 {
        struct l2_packet_data *l2 = eloop_ctx;
index b260d25..0c79d99 100644 (file)
@@ -15,6 +15,10 @@ ifeq ($(V), 1)
 Q=
 E=true
 endif
+ifeq ($(QUIET), 1)
+Q=@
+E=true
+endif
 
 %.o: %.c
        $(Q)$(CC) -c -o $@ $(CFLAGS) $<
index cffba62..adfd3df 100644 (file)
@@ -2,8 +2,7 @@ all:
        @echo Nothing to be made.
 
 clean:
-       for d in $(SUBDIRS); do make -C $$d clean; done
-       rm -f *~ *.o *.d
+       rm -f *~ *.o *.d *.gcno *.gcda *.gcov
 
 install:
        @echo Nothing to be made.
old mode 100644 (file)
new mode 100755 (executable)
index 242297e..09084a7
@@ -12,6 +12,9 @@
 #include "eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
+#include "crypto/sha256.h"
+#include "crypto/crypto.h"
 #include "wps/wps_i.h"
 #include "p2p_i.h"
 #include "p2p.h"
@@ -41,21 +44,32 @@ static void p2p_scan_timeout(void *eloop_ctx, void *timeout_ctx);
  * P2P_PEER_EXPIRATION_AGE - Number of seconds after which inactive peer
  * entries will be removed
  */
-#define P2P_PEER_EXPIRATION_AGE 300
+#ifndef P2P_PEER_EXPIRATION_AGE
+#define P2P_PEER_EXPIRATION_AGE 60
+#endif /* P2P_PEER_EXPIRATION_AGE */
 
 #define P2P_PEER_EXPIRATION_INTERVAL (P2P_PEER_EXPIRATION_AGE / 2)
 
 static void p2p_expire_peers(struct p2p_data *p2p)
 {
        struct p2p_device *dev, *n;
-       struct os_time now;
+       struct os_reltime now;
        size_t i;
 
-       os_get_time(&now);
+       os_get_reltime(&now);
        dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) {
                if (dev->last_seen.sec + P2P_PEER_EXPIRATION_AGE >= now.sec)
                        continue;
 
+               if (dev == p2p->go_neg_peer) {
+                       /*
+                        * GO Negotiation is in progress with the peer, so
+                        * don't expire the peer entry until GO Negotiation
+                        * fails or times out.
+                        */
+                       continue;
+               }
+
                if (p2p->cfg->go_connected &&
                    p2p->cfg->go_connected(p2p->cfg->cb_ctx,
                                           dev->info.p2p_device_addr)) {
@@ -63,7 +77,7 @@ static void p2p_expire_peers(struct p2p_data *p2p)
                         * We are connected as a client to a group in which the
                         * peer is the GO, so do not expire the peer entry.
                         */
-                       os_get_time(&dev->last_seen);
+                       os_get_reltime(&dev->last_seen);
                        continue;
                }
 
@@ -77,7 +91,7 @@ static void p2p_expire_peers(struct p2p_data *p2p)
                         * The peer is connected as a client in a group where
                         * we are the GO, so do not expire the peer entry.
                         */
-                       os_get_time(&dev->last_seen);
+                       os_get_reltime(&dev->last_seen);
                        continue;
                }
 
@@ -127,16 +141,31 @@ static const char * p2p_state_txt(int state)
                return "INVITE";
        case P2P_INVITE_LISTEN:
                return "INVITE_LISTEN";
-       case P2P_SEARCH_WHEN_READY:
-               return "SEARCH_WHEN_READY";
-       case P2P_CONTINUE_SEARCH_WHEN_READY:
-               return "CONTINUE_SEARCH_WHEN_READY";
        default:
                return "?";
        }
 }
 
 
+const char * p2p_get_state_txt(struct p2p_data *p2p)
+{
+       return p2p_state_txt(p2p->state);
+}
+
+
+struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p)
+{
+       return p2p ? p2p->p2ps_adv_list : NULL;
+}
+
+
+void p2p_set_intended_addr(struct p2p_data *p2p, const u8 *intended_addr)
+{
+       if (p2p && intended_addr)
+               os_memcpy(p2p->intended_addr, intended_addr, ETH_ALEN);
+}
+
+
 u16 p2p_get_provisioning_info(struct p2p_data *p2p, const u8 *addr)
 {
        struct p2p_device *dev = NULL;
@@ -170,6 +199,14 @@ void p2p_set_state(struct p2p_data *p2p, int new_state)
        p2p_dbg(p2p, "State %s -> %s",
                p2p_state_txt(p2p->state), p2p_state_txt(new_state));
        p2p->state = new_state;
+
+       if (new_state == P2P_IDLE && p2p->pending_channel) {
+               p2p_dbg(p2p, "Apply change in listen channel");
+               p2p->cfg->reg_class = p2p->pending_reg_class;
+               p2p->cfg->channel = p2p->pending_channel;
+               p2p->pending_reg_class = 0;
+               p2p->pending_channel = 0;
+       }
 }
 
 
@@ -189,26 +226,35 @@ void p2p_clear_timeout(struct p2p_data *p2p)
 }
 
 
-void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer,
-                      int status)
+void p2p_go_neg_failed(struct p2p_data *p2p, int status)
 {
        struct p2p_go_neg_results res;
-       p2p_clear_timeout(p2p);
-       p2p_set_state(p2p, P2P_IDLE);
-       if (p2p->go_neg_peer) {
-               p2p->go_neg_peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE;
-               p2p->go_neg_peer->wps_method = WPS_NOT_READY;
+       struct p2p_device *peer = p2p->go_neg_peer;
+
+       if (!peer)
+               return;
+
+       eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
+       if (p2p->state != P2P_SEARCH) {
+               /*
+                * Clear timeouts related to GO Negotiation if no new p2p_find
+                * has been started.
+                */
+               p2p_clear_timeout(p2p);
+               p2p_set_state(p2p, P2P_IDLE);
        }
+
+       peer->flags &= ~P2P_DEV_PEER_WAITING_RESPONSE;
+       peer->wps_method = WPS_NOT_READY;
+       peer->oob_pw_id = 0;
+       wpabuf_free(peer->go_neg_conf);
+       peer->go_neg_conf = NULL;
        p2p->go_neg_peer = NULL;
 
        os_memset(&res, 0, sizeof(res));
        res.status = status;
-       if (peer) {
-               os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr,
-                         ETH_ALEN);
-               os_memcpy(res.peer_interface_addr, peer->intended_addr,
-                         ETH_ALEN);
-       }
+       os_memcpy(res.peer_device_addr, peer->info.p2p_device_addr, ETH_ALEN);
+       os_memcpy(res.peer_interface_addr, peer->intended_addr, ETH_ALEN);
        p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
 }
 
@@ -222,13 +268,20 @@ static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc)
        p2p_dbg(p2p, "Starting short listen state (state=%s)",
                p2p_state_txt(p2p->state));
 
+       if (p2p->pending_listen_freq) {
+               /* We have a pending p2p_listen request */
+               p2p_dbg(p2p, "p2p_listen command pending already");
+               return;
+       }
+
        freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel);
        if (freq < 0) {
                p2p_dbg(p2p, "Unknown regulatory class/channel");
                return;
        }
 
-       os_get_random((u8 *) &r, sizeof(r));
+       if (os_get_random((u8 *) &r, sizeof(r)) < 0)
+               r = 0;
        tu = (r % ((p2p->max_disc_int - p2p->min_disc_int) + 1) +
              p2p->min_disc_int) * 100;
        if (p2p->max_disc_tu >= 0 && tu > (unsigned int) p2p->max_disc_tu)
@@ -244,14 +297,14 @@ static void p2p_listen_in_find(struct p2p_data *p2p, int dev_disc)
                return;
        }
 
-       p2p->pending_listen_freq = freq;
-       p2p->pending_listen_sec = 0;
-       p2p->pending_listen_usec = 1024 * tu;
-
        ies = p2p_build_probe_resp_ies(p2p);
        if (ies == NULL)
                return;
 
+       p2p->pending_listen_freq = freq;
+       p2p->pending_listen_sec = 0;
+       p2p->pending_listen_usec = 1024 * tu;
+
        if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, 1024 * tu / 1000,
                    ies) < 0) {
                p2p_dbg(p2p, "Failed to start listen mode");
@@ -268,13 +321,18 @@ int p2p_listen(struct p2p_data *p2p, unsigned int timeout)
 
        p2p_dbg(p2p, "Going to listen(only) state");
 
+       if (p2p->pending_listen_freq) {
+               /* We have a pending p2p_listen request */
+               p2p_dbg(p2p, "p2p_listen command pending already");
+               return -1;
+       }
+
        freq = p2p_channel_to_freq(p2p->cfg->reg_class, p2p->cfg->channel);
        if (freq < 0) {
                p2p_dbg(p2p, "Unknown regulatory class/channel");
                return -1;
        }
 
-       p2p->pending_listen_freq = freq;
        p2p->pending_listen_sec = timeout / 1000;
        p2p->pending_listen_usec = (timeout % 1000) * 1000;
 
@@ -292,6 +350,8 @@ int p2p_listen(struct p2p_data *p2p, unsigned int timeout)
        if (ies == NULL)
                return -1;
 
+       p2p->pending_listen_freq = freq;
+
        if (p2p->cfg->start_listen(p2p->cfg->cb_ctx, freq, timeout, ies) < 0) {
                p2p_dbg(p2p, "Failed to start listen mode");
                p2p->pending_listen_freq = 0;
@@ -309,8 +369,10 @@ int p2p_listen(struct p2p_data *p2p, unsigned int timeout)
 static void p2p_device_clear_reported(struct p2p_data *p2p)
 {
        struct p2p_device *dev;
-       dl_list_for_each(dev, &p2p->devices, struct p2p_device, list)
+       dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
                dev->flags &= ~P2P_DEV_REPORTED;
+               dev->sd_reqs = 0;
+       }
 }
 
 
@@ -371,7 +433,7 @@ static struct p2p_device * p2p_create_device(struct p2p_data *p2p,
        dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
                count++;
                if (oldest == NULL ||
-                   os_time_before(&dev->last_seen, &oldest->last_seen))
+                   os_reltime_before(&dev->last_seen, &oldest->last_seen))
                        oldest = dev;
        }
        if (count + 1 > p2p->cfg->max_peers && oldest) {
@@ -474,7 +536,7 @@ static int p2p_add_group_clients(struct p2p_data *p2p, const u8 *go_dev_addr,
 
                os_memcpy(dev->interface_addr, cli->p2p_interface_addr,
                          ETH_ALEN);
-               os_get_time(&dev->last_seen);
+               os_get_reltime(&dev->last_seen);
                os_memcpy(dev->member_in_go_dev, go_dev_addr, ETH_ALEN);
                os_memcpy(dev->member_in_go_iface, go_interface_addr,
                          ETH_ALEN);
@@ -571,6 +633,64 @@ static void p2p_copy_wps_info(struct p2p_data *p2p, struct p2p_device *dev,
 }
 
 
+static void p2p_update_peer_vendor_elems(struct p2p_device *dev, const u8 *ies,
+                                        size_t ies_len)
+{
+       const u8 *pos, *end;
+       u8 id, len;
+
+       wpabuf_free(dev->info.vendor_elems);
+       dev->info.vendor_elems = NULL;
+
+       end = ies + ies_len;
+
+       for (pos = ies; pos + 1 < end; pos += len) {
+               id = *pos++;
+               len = *pos++;
+
+               if (pos + len > end)
+                       break;
+
+               if (id != WLAN_EID_VENDOR_SPECIFIC || len < 3)
+                       continue;
+
+               if (len >= 4) {
+                       u32 type = WPA_GET_BE32(pos);
+
+                       if (type == WPA_IE_VENDOR_TYPE ||
+                           type == WMM_IE_VENDOR_TYPE ||
+                           type == WPS_IE_VENDOR_TYPE ||
+                           type == P2P_IE_VENDOR_TYPE ||
+                           type == WFD_IE_VENDOR_TYPE)
+                               continue;
+               }
+
+               /* Unknown vendor element - make raw IE data available */
+               if (wpabuf_resize(&dev->info.vendor_elems, 2 + len) < 0)
+                       break;
+               wpabuf_put_data(dev->info.vendor_elems, pos - 2, 2 + len);
+       }
+}
+
+
+static int p2p_compare_wfd_info(struct p2p_device *dev,
+                             const struct p2p_message *msg)
+{
+       if (dev->info.wfd_subelems && msg->wfd_subelems) {
+               if (dev->info.wfd_subelems->used != msg->wfd_subelems->used)
+                       return 1;
+
+               return os_memcmp(dev->info.wfd_subelems->buf,
+                                msg->wfd_subelems->buf,
+                                dev->info.wfd_subelems->used);
+       }
+       if (dev->info.wfd_subelems || msg->wfd_subelems)
+               return 1;
+
+       return 0;
+}
+
+
 /**
  * p2p_add_device - Add peer entries based on scan results or P2P frames
  * @p2p: P2P module context from p2p_init()
@@ -590,14 +710,15 @@ static void p2p_copy_wps_info(struct p2p_data *p2p, struct p2p_device *dev,
  * Info attributes.
  */
 int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
-                  struct os_time *rx_time, int level, const u8 *ies,
+                  struct os_reltime *rx_time, int level, const u8 *ies,
                   size_t ies_len, int scan_res)
 {
        struct p2p_device *dev;
        struct p2p_message msg;
        const u8 *p2p_dev_addr;
+       int wfd_changed;
        int i;
-       struct os_time time_now;
+       struct os_reltime time_now;
 
        os_memset(&msg, 0, sizeof(msg));
        if (p2p_parse_ies(ies, ies_len, &msg)) {
@@ -631,7 +752,7 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
        }
 
        if (rx_time == NULL) {
-               os_get_time(&time_now);
+               os_get_reltime(&time_now);
                rx_time = &time_now;
        }
 
@@ -640,7 +761,7 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
         * entry is newer than the one previously stored.
         */
        if (dev->last_seen.sec > 0 &&
-           os_time_before(rx_time, &dev->last_seen)) {
+           os_reltime_before(rx_time, &dev->last_seen)) {
                p2p_dbg(p2p, "Do not update peer entry based on old frame (rx_time=%u.%06u last_seen=%u.%06u)",
                        (unsigned int) rx_time->sec,
                        (unsigned int) rx_time->usec,
@@ -650,7 +771,7 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
                return -1;
        }
 
-       os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_time));
+       os_memcpy(&dev->last_seen, rx_time, sizeof(struct os_reltime));
 
        dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
 
@@ -664,6 +785,12 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
                dev->oper_ssid_len = msg.ssid[1];
        }
 
+       if (msg.adv_service_instance && msg.adv_service_instance_len) {
+               wpabuf_free(dev->info.p2ps_instance);
+               dev->info.p2ps_instance = wpabuf_alloc_copy(
+                       msg.adv_service_instance, msg.adv_service_instance_len);
+       }
+
        if (freq >= 2412 && freq <= 2484 && msg.ds_params &&
            *msg.ds_params >= 1 && *msg.ds_params <= 14) {
                int ds_freq;
@@ -707,6 +834,8 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
                        break;
        }
 
+       wfd_changed = p2p_compare_wfd_info(dev, &msg);
+
        if (msg.wfd_subelems) {
                wpabuf_free(dev->info.wfd_subelems);
                dev->info.wfd_subelems = wpabuf_dup(msg.wfd_subelems);
@@ -719,10 +848,11 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
 
        p2p_parse_free(&msg);
 
-       if (p2p_pending_sd_req(p2p, dev))
-               dev->flags |= P2P_DEV_SD_SCHEDULE;
+       p2p_update_peer_vendor_elems(dev, ies, ies_len);
 
-       if (dev->flags & P2P_DEV_REPORTED)
+       if (dev->flags & P2P_DEV_REPORTED && !wfd_changed &&
+           (!msg.adv_service_instance ||
+            (dev->flags & P2P_DEV_P2PS_REPORTED)))
                return 0;
 
        p2p_dbg(p2p, "Peer found with Listen frequency %d MHz (rx_time=%u.%06u)",
@@ -758,6 +888,9 @@ int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
                            !(dev->flags & P2P_DEV_REPORTED_ONCE));
        dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
 
+       if (msg.adv_service_instance)
+               dev->flags |= P2P_DEV_P2PS_REPORTED;
+
        return 0;
 }
 
@@ -770,8 +903,7 @@ static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev)
                /*
                 * If GO Negotiation is in progress, report that it has failed.
                 */
-               p2p_go_neg_failed(p2p, dev, -1);
-               p2p->go_neg_peer = NULL;
+               p2p_go_neg_failed(p2p, -1);
        }
        if (p2p->invite_peer == dev)
                p2p->invite_peer = NULL;
@@ -791,6 +923,9 @@ static void p2p_device_free(struct p2p_data *p2p, struct p2p_device *dev)
        }
 
        wpabuf_free(dev->info.wfd_subelems);
+       wpabuf_free(dev->info.vendor_elems);
+       wpabuf_free(dev->go_neg_conf);
+       wpabuf_free(dev->info.p2ps_instance);
 
        os_free(dev);
 }
@@ -876,17 +1011,8 @@ static void p2p_search(struct p2p_data *p2p)
                                 p2p->num_req_dev_types, p2p->req_dev_types,
                                 p2p->find_dev_id, pw_id);
        if (res < 0) {
-               p2p_dbg(p2p, "Scan request failed");
+               p2p_dbg(p2p, "Scan request schedule failed");
                p2p_continue_find(p2p);
-       } else if (res == 1) {
-               p2p_dbg(p2p, "Could not start p2p_scan at this point - will try again after previous scan completes");
-               p2p_set_state(p2p, P2P_CONTINUE_SEARCH_WHEN_READY);
-       } else {
-               p2p_dbg(p2p, "Running p2p_scan");
-               p2p->p2p_scan_running = 1;
-               eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
-               eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
-                                      p2p, NULL);
        }
 }
 
@@ -899,6 +1025,22 @@ static void p2p_find_timeout(void *eloop_ctx, void *timeout_ctx)
 }
 
 
+void p2p_notify_scan_trigger_status(struct p2p_data *p2p, int status)
+{
+       if (status != 0) {
+               p2p_dbg(p2p, "Scan request failed");
+               /* Do continue find even for the first p2p_find_scan */
+               p2p_continue_find(p2p);
+       } else {
+               p2p_dbg(p2p, "Running p2p_scan");
+               p2p->p2p_scan_running = 1;
+               eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
+               eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
+                                      p2p, NULL);
+       }
+}
+
+
 static int p2p_run_after_scan(struct p2p_data *p2p)
 {
        struct p2p_device *dev;
@@ -968,15 +1110,49 @@ static void p2p_free_req_dev_types(struct p2p_data *p2p)
 }
 
 
+static int p2ps_gen_hash(struct p2p_data *p2p, const char *str, u8 *hash)
+{
+       u8 buf[SHA256_MAC_LEN];
+       char str_buf[256];
+       const u8 *adv_array;
+       size_t i, adv_len;
+
+       if (!str || !hash)
+               return 0;
+
+       if (!str[0]) {
+               os_memcpy(hash, p2p->wild_card_hash, P2PS_HASH_LEN);
+               return 1;
+       }
+
+       adv_array = (u8 *) str_buf;
+       adv_len = os_strlen(str);
+
+       for (i = 0; str[i] && i < adv_len; i++) {
+               if (str[i] >= 'A' && str[i] <= 'Z')
+                       str_buf[i] = str[i] - 'A' + 'a';
+               else
+                       str_buf[i] = str[i];
+       }
+
+       if (sha256_vector(1, &adv_array, &adv_len, buf))
+               return 0;
+
+       os_memcpy(hash, buf, P2PS_HASH_LEN);
+       return 1;
+}
+
+
 int p2p_find(struct p2p_data *p2p, unsigned int timeout,
             enum p2p_discovery_type type,
             unsigned int num_req_dev_types, const u8 *req_dev_types,
-            const u8 *dev_id, unsigned int search_delay)
+            const u8 *dev_id, unsigned int search_delay,
+            u8 seek_count, const char **seek, int freq)
 {
        int res;
 
        p2p_dbg(p2p, "Starting find (type=%d)", type);
-       os_get_time(&p2p->find_start);
+       os_get_reltime(&p2p->find_start);
        if (p2p->p2p_scan_running) {
                p2p_dbg(p2p, "p2p_scan is already running");
        }
@@ -998,6 +1174,47 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
        } else
                p2p->find_dev_id = NULL;
 
+       if (seek_count == 0 || !seek) {
+               /* Not an ASP search */
+               p2p->p2ps_seek = 0;
+       } else if (seek_count == 1 && seek && (!seek[0] || !seek[0][0])) {
+               /*
+                * An empty seek string means no hash values, but still an ASP
+                * search.
+                */
+               p2p->p2ps_seek_count = 0;
+               p2p->p2ps_seek = 1;
+       } else if (seek && seek_count <= P2P_MAX_QUERY_HASH) {
+               u8 buf[P2PS_HASH_LEN];
+               int i;
+
+               p2p->p2ps_seek_count = seek_count;
+               for (i = 0; i < seek_count; i++) {
+                       if (!p2ps_gen_hash(p2p, seek[i], buf))
+                               continue;
+
+                       /* If asking for wildcard, don't do others */
+                       if (os_memcmp(buf, p2p->wild_card_hash,
+                                     P2PS_HASH_LEN) == 0) {
+                               p2p->p2ps_seek_count = 0;
+                               break;
+                       }
+
+                       os_memcpy(&p2p->query_hash[i * P2PS_HASH_LEN], buf,
+                                 P2PS_HASH_LEN);
+               }
+               p2p->p2ps_seek = 1;
+       } else {
+               p2p->p2ps_seek_count = 0;
+               p2p->p2ps_seek = 1;
+       }
+
+       /* Special case to perform wildcard search */
+       if (p2p->p2ps_seek_count == 0 && p2p->p2ps_seek) {
+               p2p->p2ps_seek_count = 1;
+               os_memcpy(&p2p->query_hash, p2p->wild_card_hash, P2PS_HASH_LEN);
+       }
+
        p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
        p2p_clear_timeout(p2p);
        p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
@@ -1013,6 +1230,19 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
                                       p2p, NULL);
        switch (type) {
        case P2P_FIND_START_WITH_FULL:
+               if (freq > 0) {
+                       /*
+                        * Start with the specified channel and then move to
+                        * social channels only scans.
+                        */
+                       res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx,
+                                                P2P_SCAN_SPECIFIC, freq,
+                                                p2p->num_req_dev_types,
+                                                p2p->req_dev_types, dev_id,
+                                                DEV_PW_DEFAULT);
+                       break;
+               }
+               /* fall through */
        case P2P_FIND_PROGRESSIVE:
                res = p2p->cfg->p2p_scan(p2p->cfg->cb_ctx, P2P_SCAN_FULL, 0,
                                         p2p->num_req_dev_types,
@@ -1029,18 +1259,11 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
                return -1;
        }
 
-       if (res == 0) {
-               p2p_dbg(p2p, "Running p2p_scan");
-               p2p->p2p_scan_running = 1;
-               eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
-               eloop_register_timeout(P2P_SCAN_TIMEOUT, 0, p2p_scan_timeout,
-                                      p2p, NULL);
-       } else if (res == 1) {
-               p2p_dbg(p2p, "Could not start p2p_scan at this point - will try again after previous scan completes");
-               res = 0;
-               p2p_set_state(p2p, P2P_SEARCH_WHEN_READY);
-               eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
-       } else {
+       if (res != 0 && p2p->p2p_scan_running) {
+               p2p_dbg(p2p, "Failed to start p2p_scan - another p2p_scan was already running");
+               /* wait for the previous p2p_scan to complete */
+               res = 0; /* do not report failure */
+       } else if (res != 0) {
                p2p_dbg(p2p, "Failed to start p2p_scan");
                p2p_set_state(p2p, P2P_IDLE);
                eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
@@ -1050,35 +1273,16 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
 }
 
 
-int p2p_other_scan_completed(struct p2p_data *p2p)
-{
-       if (p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY) {
-               p2p_set_state(p2p, P2P_SEARCH);
-               p2p_search(p2p);
-               return 1;
-       }
-       if (p2p->state != P2P_SEARCH_WHEN_READY)
-               return 0;
-       p2p_dbg(p2p, "Starting pending P2P find now that previous scan was completed");
-       if (p2p_find(p2p, p2p->last_p2p_find_timeout, p2p->find_type,
-                    p2p->num_req_dev_types, p2p->req_dev_types,
-                    p2p->find_dev_id, p2p->search_delay) < 0) {
-               p2p->cfg->find_stopped(p2p->cfg->cb_ctx);
-               return 0;
-       }
-       return 1;
-}
-
-
 void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq)
 {
        p2p_dbg(p2p, "Stopping find");
        eloop_cancel_timeout(p2p_find_timeout, p2p, NULL);
        p2p_clear_timeout(p2p);
-       if (p2p->state == P2P_SEARCH ||
-           p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY ||
-           p2p->state == P2P_SEARCH_WHEN_READY)
+       if (p2p->state == P2P_SEARCH || p2p->state == P2P_SD_DURING_FIND)
                p2p->cfg->find_stopped(p2p->cfg->cb_ctx);
+
+       p2p->p2ps_seek_count = 0;
+
        p2p_set_state(p2p, P2P_IDLE);
        p2p_free_req_dev_types(p2p);
        p2p->start_after_scan = P2P_AFTER_SCAN_NOTHING;
@@ -1088,6 +1292,7 @@ void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq)
        p2p->sd_peer = NULL;
        p2p->invite_peer = NULL;
        p2p_stop_listen_for_freq(p2p, freq);
+       p2p->send_action_in_progress = 0;
 }
 
 
@@ -1114,25 +1319,42 @@ void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq)
 }
 
 
+void p2p_stop_listen(struct p2p_data *p2p)
+{
+       if (p2p->state != P2P_LISTEN_ONLY) {
+               p2p_dbg(p2p, "Skip stop_listen since not in listen_only state.");
+               return;
+       }
+
+       p2p_stop_listen_for_freq(p2p, 0);
+       p2p_set_state(p2p, P2P_IDLE);
+}
+
+
 void p2p_stop_find(struct p2p_data *p2p)
 {
+       p2p->pending_listen_freq = 0;
        p2p_stop_find_for_freq(p2p, 0);
 }
 
 
 static int p2p_prepare_channel_pref(struct p2p_data *p2p,
                                    unsigned int force_freq,
-                                   unsigned int pref_freq)
+                                   unsigned int pref_freq, int go)
 {
        u8 op_class, op_channel;
        unsigned int freq = force_freq ? force_freq : pref_freq;
 
+       p2p_dbg(p2p, "Prepare channel pref - force_freq=%u pref_freq=%u go=%d",
+               force_freq, pref_freq, go);
        if (p2p_freq_to_channel(freq, &op_class, &op_channel) < 0) {
                p2p_dbg(p2p, "Unsupported frequency %u MHz", freq);
                return -1;
        }
 
-       if (!p2p_channels_includes(&p2p->cfg->channels, op_class, op_channel)) {
+       if (!p2p_channels_includes(&p2p->cfg->channels, op_class, op_channel) &&
+           (go || !p2p_channels_includes(&p2p->cfg->cli_channels, op_class,
+                                         op_channel))) {
                p2p_dbg(p2p, "Frequency %u MHz (oper_class %u channel %u) not allowed for P2P",
                        freq, op_class, op_channel);
                return -1;
@@ -1158,6 +1380,11 @@ static int p2p_prepare_channel_pref(struct p2p_data *p2p,
 static void p2p_prepare_channel_best(struct p2p_data *p2p)
 {
        u8 op_class, op_channel;
+       const int op_classes_5ghz[] = { 124, 115, 0 };
+       const int op_classes_ht40[] = { 126, 127, 116, 117, 0 };
+       const int op_classes_vht[] = { 128, 0 };
+
+       p2p_dbg(p2p, "Prepare channel best");
 
        if (!p2p->cfg->cfg_op_channel && p2p->best_freq_overall > 0 &&
            p2p_supported_freq(p2p, p2p->best_freq_overall) &&
@@ -1180,9 +1407,47 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p)
                p2p_dbg(p2p, "Select best 2.4 GHz channel as operating channel preference");
                p2p->op_reg_class = op_class;
                p2p->op_channel = op_channel;
-       } else {
+       } else if (p2p->cfg->num_pref_chan > 0 &&
+                  p2p_channels_includes(&p2p->cfg->channels,
+                                        p2p->cfg->pref_chan[0].op_class,
+                                        p2p->cfg->pref_chan[0].chan)) {
+               p2p_dbg(p2p, "Select first pref_chan entry as operating channel preference");
+               p2p->op_reg_class = p2p->cfg->pref_chan[0].op_class;
+               p2p->op_channel = p2p->cfg->pref_chan[0].chan;
+       } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_vht,
+                                     &p2p->op_reg_class, &p2p->op_channel) ==
+                  0) {
+               p2p_dbg(p2p, "Select possible VHT channel (op_class %u channel %u) as operating channel preference",
+                       p2p->op_reg_class, p2p->op_channel);
+       } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_ht40,
+                                     &p2p->op_reg_class, &p2p->op_channel) ==
+                  0) {
+               p2p_dbg(p2p, "Select possible HT40 channel (op_class %u channel %u) as operating channel preference",
+                       p2p->op_reg_class, p2p->op_channel);
+       } else if (p2p_channel_select(&p2p->cfg->channels, op_classes_5ghz,
+                                     &p2p->op_reg_class, &p2p->op_channel) ==
+                  0) {
+               p2p_dbg(p2p, "Select possible 5 GHz channel (op_class %u channel %u) as operating channel preference",
+                       p2p->op_reg_class, p2p->op_channel);
+       } else if (p2p_channels_includes(&p2p->cfg->channels,
+                                        p2p->cfg->op_reg_class,
+                                        p2p->cfg->op_channel)) {
+               p2p_dbg(p2p, "Select pre-configured channel as operating channel preference");
                p2p->op_reg_class = p2p->cfg->op_reg_class;
                p2p->op_channel = p2p->cfg->op_channel;
+       } else if (p2p_channel_random_social(&p2p->cfg->channels,
+                                            &p2p->op_reg_class,
+                                            &p2p->op_channel) == 0) {
+               p2p_dbg(p2p, "Select random available social channel (op_class %u channel %u) as operating channel preference",
+                       p2p->op_reg_class, p2p->op_channel);
+       } else {
+               /* Select any random available channel from the first available
+                * operating class */
+               p2p_channel_select(&p2p->cfg->channels, NULL,
+                                  &p2p->op_reg_class,
+                                  &p2p->op_channel);
+               p2p_dbg(p2p, "Select random available channel %d from operating class %d as operating channel preference",
+                       p2p->op_channel, p2p->op_reg_class);
        }
 
        os_memcpy(&p2p->channels, &p2p->cfg->channels,
@@ -1196,6 +1461,7 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p)
  * @dev: Selected peer device
  * @force_freq: Forced frequency in MHz or 0 if not forced
  * @pref_freq: Preferred frequency in MHz or 0 if no preference
+ * @go: Whether the local end will be forced to be GO
  * Returns: 0 on success, -1 on failure (channel not supported for P2P)
  *
  * This function is used to do initial operating channel selection for GO
@@ -1204,14 +1470,25 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p)
  * is available.
  */
 int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
-                       unsigned int force_freq, unsigned int pref_freq)
+                       unsigned int force_freq, unsigned int pref_freq, int go)
 {
+       p2p_dbg(p2p, "Prepare channel - force_freq=%u pref_freq=%u go=%d",
+               force_freq, pref_freq, go);
        if (force_freq || pref_freq) {
-               if (p2p_prepare_channel_pref(p2p, force_freq, pref_freq) < 0)
+               if (p2p_prepare_channel_pref(p2p, force_freq, pref_freq, go) <
+                   0)
                        return -1;
        } else {
                p2p_prepare_channel_best(p2p);
        }
+       p2p_channels_dump(p2p, "prepared channels", &p2p->channels);
+       if (go)
+               p2p_channels_remove_freqs(&p2p->channels, &p2p->no_go_freq);
+       else if (!force_freq)
+               p2p_channels_union_inplace(&p2p->channels,
+                                          &p2p->cfg->cli_channels);
+       p2p_channels_dump(p2p, "after go/cli filter/add", &p2p->channels);
+
        p2p_dbg(p2p, "Own preference for operation channel: Operating Class %u Channel %u%s",
                p2p->op_reg_class, p2p->op_channel,
                force_freq ? " (forced)" : "");
@@ -1250,15 +1527,16 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
                int go_intent, const u8 *own_interface_addr,
                unsigned int force_freq, int persistent_group,
                const u8 *force_ssid, size_t force_ssid_len,
-               int pd_before_go_neg, unsigned int pref_freq)
+               int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id)
 {
        struct p2p_device *dev;
 
        p2p_dbg(p2p, "Request to start group negotiation - peer=" MACSTR
                "  GO Intent=%d  Intended Interface Address=" MACSTR
-               " wps_method=%d persistent_group=%d pd_before_go_neg=%d",
+               " wps_method=%d persistent_group=%d pd_before_go_neg=%d "
+               "oob_pw_id=%u",
                MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
-               wps_method, persistent_group, pd_before_go_neg);
+               wps_method, persistent_group, pd_before_go_neg, oob_pw_id);
 
        dev = p2p_get_device(p2p, peer_addr);
        if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
@@ -1267,7 +1545,8 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
                return -1;
        }
 
-       if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0)
+       if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq,
+                               go_intent == 15) < 0)
                return -1;
 
        if (dev->flags & P2P_DEV_GROUP_CLIENT_ONLY) {
@@ -1341,6 +1620,7 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
        }
 
        dev->wps_method = wps_method;
+       dev->oob_pw_id = oob_pw_id;
        dev->status = P2P_SC_SUCCESS;
 
        if (p2p->p2p_scan_running) {
@@ -1360,15 +1640,15 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
                  int go_intent, const u8 *own_interface_addr,
                  unsigned int force_freq, int persistent_group,
                  const u8 *force_ssid, size_t force_ssid_len,
-                 unsigned int pref_freq)
+                 unsigned int pref_freq, u16 oob_pw_id)
 {
        struct p2p_device *dev;
 
        p2p_dbg(p2p, "Request to authorize group negotiation - peer=" MACSTR
                "  GO Intent=%d  Intended Interface Address=" MACSTR
-               " wps_method=%d  persistent_group=%d",
+               " wps_method=%d  persistent_group=%d oob_pw_id=%u",
                MAC2STR(peer_addr), go_intent, MAC2STR(own_interface_addr),
-               wps_method, persistent_group);
+               wps_method, persistent_group, oob_pw_id);
 
        dev = p2p_get_device(p2p, peer_addr);
        if (dev == NULL) {
@@ -1377,7 +1657,8 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
                return -1;
        }
 
-       if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0)
+       if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq, go_intent ==
+                               15) < 0)
                return -1;
 
        p2p->ssid_set = 0;
@@ -1398,6 +1679,7 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
        os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
 
        dev->wps_method = wps_method;
+       dev->oob_pw_id = oob_pw_id;
        dev->status = P2P_SC_SUCCESS;
 
        return 0;
@@ -1407,7 +1689,7 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
 void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
                      struct p2p_device *dev, struct p2p_message *msg)
 {
-       os_get_time(&dev->last_seen);
+       os_get_reltime(&dev->last_seen);
 
        p2p_copy_wps_info(p2p, dev, 0, msg);
 
@@ -1474,8 +1756,15 @@ void p2p_build_ssid(struct p2p_data *p2p, u8 *ssid, size_t *ssid_len)
 
 int p2p_go_params(struct p2p_data *p2p, struct p2p_go_neg_results *params)
 {
-       p2p_build_ssid(p2p, params->ssid, &params->ssid_len);
-       p2p_random(params->passphrase, 8);
+       if (p2p->ssid_set) {
+               os_memcpy(params->ssid, p2p->ssid, p2p->ssid_len);
+               params->ssid_len = p2p->ssid_len;
+       } else {
+               p2p_build_ssid(p2p, params->ssid, &params->ssid_len);
+       }
+       p2p->ssid_set = 0;
+
+       p2p_random(params->passphrase, p2p->cfg->passphrase_len);
        return 0;
 }
 
@@ -1485,8 +1774,6 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
        struct p2p_go_neg_results res;
        int go = peer->go_state == LOCAL_GO;
        struct p2p_channels intersection;
-       int freqs;
-       size_t i, j;
 
        p2p_dbg(p2p, "GO Negotiation with " MACSTR " completed (%s will be GO)",
                MAC2STR(peer->info.p2p_device_addr), go ? "local end" : "peer");
@@ -1509,7 +1796,7 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
                                               p2p->op_channel);
                os_memcpy(res.ssid, p2p->ssid, p2p->ssid_len);
                res.ssid_len = p2p->ssid_len;
-               p2p_random(res.passphrase, 8);
+               p2p_random(res.passphrase, p2p->cfg->passphrase_len);
        } else {
                res.freq = peer->oper_freq;
                if (p2p->ssid_len) {
@@ -1518,30 +1805,28 @@ void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer)
                }
        }
 
+       p2p_channels_dump(p2p, "own channels", &p2p->channels);
+       p2p_channels_dump(p2p, "peer channels", &peer->channels);
        p2p_channels_intersect(&p2p->channels, &peer->channels,
                               &intersection);
-       freqs = 0;
-       for (i = 0; i < intersection.reg_classes; i++) {
-               struct p2p_reg_class *c = &intersection.reg_class[i];
-               if (freqs + 1 == P2P_MAX_CHANNELS)
-                       break;
-               for (j = 0; j < c->channels; j++) {
-                       int freq;
-                       if (freqs + 1 == P2P_MAX_CHANNELS)
-                               break;
-                       freq = p2p_channel_to_freq(c->reg_class, c->channel[j]);
-                       if (freq < 0)
-                               continue;
-                       res.freq_list[freqs++] = freq;
-               }
+       if (go) {
+               p2p_channels_remove_freqs(&intersection, &p2p->no_go_freq);
+               p2p_channels_dump(p2p, "intersection after no-GO removal",
+                                 &intersection);
        }
 
+       p2p_channels_to_freqs(&intersection, res.freq_list,
+                             P2P_MAX_CHANNELS);
+
        res.peer_config_timeout = go ? peer->client_timeout : peer->go_timeout;
 
        p2p_clear_timeout(p2p);
        p2p->ssid_set = 0;
        peer->go_neg_req_sent = 0;
        peer->wps_method = WPS_NOT_READY;
+       peer->oob_pw_id = 0;
+       wpabuf_free(peer->go_neg_conf);
+       peer->go_neg_conf = NULL;
 
        p2p_set_state(p2p, P2P_PROVISIONING);
        p2p->cfg->go_neg_completed(p2p->cfg->cb_ctx, &res);
@@ -1605,20 +1890,15 @@ static void p2p_rx_action_public(struct p2p_data *p2p, const u8 *da,
        case WLAN_PA_VENDOR_SPECIFIC:
                data++;
                len--;
-               if (len < 3)
-                       return;
-               if (WPA_GET_BE24(data) != OUI_WFA)
+               if (len < 4)
                        return;
-
-               data += 3;
-               len -= 3;
-               if (len < 1)
+               if (WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE)
                        return;
 
-               if (*data != P2P_OUI_TYPE)
-                       return;
+               data += 4;
+               len -= 4;
 
-               p2p_rx_p2p_action(p2p, sa, data + 1, len - 1, freq);
+               p2p_rx_p2p_action(p2p, sa, data, len, freq);
                break;
        case WLAN_PA_GAS_INITIAL_REQ:
                p2p_rx_gas_initial_req(p2p, sa, data + 1, len - 1, freq);
@@ -1651,15 +1931,10 @@ void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa,
        if (len < 4)
                return;
 
-       if (WPA_GET_BE24(data) != OUI_WFA)
-               return;
-       data += 3;
-       len -= 3;
-
-       if (*data != P2P_OUI_TYPE)
+       if (WPA_GET_BE32(data) != P2P_IE_VENDOR_TYPE)
                return;
-       data++;
-       len--;
+       data += 4;
+       len -= 4;
 
        /* P2P action frame */
        p2p_dbg(p2p, "RX P2P Action from " MACSTR, MAC2STR(sa));
@@ -1693,8 +1968,17 @@ static void p2p_go_neg_start(void *eloop_ctx, void *timeout_ctx)
        struct p2p_data *p2p = eloop_ctx;
        if (p2p->go_neg_peer == NULL)
                return;
+       if (p2p->pending_listen_freq) {
+               p2p_dbg(p2p, "Clear pending_listen_freq for p2p_go_neg_start");
+               p2p->pending_listen_freq = 0;
+       }
        p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
        p2p->go_neg_peer->status = P2P_SC_SUCCESS;
+       /*
+        * Set new timeout to make sure a previously set one does not expire
+        * too quickly while waiting for the GO Negotiation to complete.
+        */
+       p2p_set_timeout(p2p, 0, 500000);
        p2p_connect_send(p2p, p2p->go_neg_peer);
 }
 
@@ -1704,8 +1988,13 @@ static void p2p_invite_start(void *eloop_ctx, void *timeout_ctx)
        struct p2p_data *p2p = eloop_ctx;
        if (p2p->invite_peer == NULL)
                return;
+       if (p2p->pending_listen_freq) {
+               p2p_dbg(p2p, "Clear pending_listen_freq for p2p_invite_start");
+               p2p->pending_listen_freq = 0;
+       }
        p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
-       p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr);
+       p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr,
+                       p2p->invite_dev_pw_id);
 }
 
 
@@ -1738,7 +2027,7 @@ static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr,
        if (dev) {
                if (dev->country[0] == 0 && msg.listen_channel)
                        os_memcpy(dev->country, msg.listen_channel, 3);
-               os_get_time(&dev->last_seen);
+               os_get_reltime(&dev->last_seen);
                p2p_parse_free(&msg);
                return; /* already known */
        }
@@ -1749,7 +2038,7 @@ static void p2p_add_dev_from_probe_req(struct p2p_data *p2p, const u8 *addr,
                return;
        }
 
-       os_get_time(&dev->last_seen);
+       os_get_reltime(&dev->last_seen);
        dev->flags |= P2P_DEV_PROBE_REQ_ONLY;
 
        if (msg.listen_channel) {
@@ -1783,7 +2072,7 @@ struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p,
 
        dev = p2p_get_device(p2p, addr);
        if (dev) {
-               os_get_time(&dev->last_seen);
+               os_get_reltime(&dev->last_seen);
                return dev; /* already known */
        }
 
@@ -1846,11 +2135,12 @@ int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps)
                                attr.num_req_dev_type))
                return 1; /* Own Primary Device Type matches */
 
-       for (i = 0; i < p2p->cfg->num_sec_dev_types; i++)
+       for (i = 0; i < p2p->cfg->num_sec_dev_types; i++) {
                if (dev_type_list_match(p2p->cfg->sec_dev_type[i],
                                        attr.req_dev_type,
                                        attr.num_req_dev_type))
-               return 1; /* Own Secondary Device Type matches */
+                       return 1; /* Own Secondary Device Type matches */
+       }
 
        /* No matching device type found */
        return 0;
@@ -1869,6 +2159,12 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p)
                extra = wpabuf_len(p2p->wfd_ie_probe_resp);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+       if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P])
+               extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]);
+
+       if (p2p->query_count)
+               extra += MAX_SVC_ADV_IE_LEN;
+
        buf = wpabuf_alloc(1000 + extra);
        if (buf == NULL)
                return NULL;
@@ -1878,13 +2174,21 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p)
                pw_id = p2p_wps_method_pw_id(p2p->go_neg_peer->wps_method);
        }
 
-       p2p_build_wps_ie(p2p, buf, pw_id, 1);
+       if (p2p_build_wps_ie(p2p, buf, pw_id, 1) < 0) {
+               p2p_dbg(p2p, "Failed to build WPS IE for Probe Response");
+               wpabuf_free(buf);
+               return NULL;
+       }
 
 #ifdef CONFIG_WIFI_DISPLAY
        if (p2p->wfd_ie_probe_resp)
                wpabuf_put_buf(buf, p2p->wfd_ie_probe_resp);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+       if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P])
+               wpabuf_put_buf(buf,
+                              p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P]);
+
        /* P2P IE */
        len = p2p_buf_add_ie_hdr(buf);
        p2p_buf_add_capability(buf, p2p->dev_capab &
@@ -1895,10 +2199,38 @@ struct wpabuf * p2p_build_probe_resp_ies(struct p2p_data *p2p)
        p2p_buf_add_device_info(buf, p2p, NULL);
        p2p_buf_update_ie_hdr(buf, len);
 
+       if (p2p->query_count) {
+               p2p_buf_add_service_instance(buf, p2p, p2p->query_count,
+                                            p2p->query_hash,
+                                            p2p->p2ps_adv_list);
+       }
+
        return buf;
 }
 
 
+static int p2p_service_find_asp(struct p2p_data *p2p, const u8 *hash)
+{
+       struct p2ps_advertisement *adv_data;
+
+       p2p_dbg(p2p, "ASP find - ASP list: %p", p2p->p2ps_adv_list);
+
+       /* Wildcard always matches if we have actual services */
+       if (os_memcmp(hash, p2p->wild_card_hash, P2PS_HASH_LEN) == 0)
+               return p2p->p2ps_adv_list != NULL;
+
+       adv_data = p2p->p2ps_adv_list;
+       while (adv_data) {
+               p2p_dbg(p2p, "ASP hash: %x =? %x", hash[0], adv_data->hash[0]);
+               if (os_memcmp(hash, adv_data->hash, P2PS_HASH_LEN) == 0)
+                       return 1;
+               adv_data = adv_data->next;
+       }
+
+       return 0;
+}
+
+
 static enum p2p_probe_req_status
 p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
                const u8 *bssid, const u8 *ie, size_t ie_len)
@@ -1909,19 +2241,16 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
        struct p2p_message msg;
        struct wpabuf *ies;
 
-       if (!p2p->in_listen || !p2p->drv_in_listen) {
-               /* not in Listen state - ignore Probe Request */
-               return P2P_PREQ_NOT_LISTEN;
-       }
-
        if (ieee802_11_parse_elems((u8 *) ie, ie_len, &elems, 0) ==
            ParseFailed) {
                /* Ignore invalid Probe Request frames */
+               p2p_dbg(p2p, "Could not parse Probe Request frame - ignore it");
                return P2P_PREQ_MALFORMED;
        }
 
        if (elems.p2p == NULL) {
                /* not a P2P probe - ignore it */
+               p2p_dbg(p2p, "Not a P2P probe - ignore it");
                return P2P_PREQ_NOT_P2P;
        }
 
@@ -1929,11 +2258,15 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
            os_memcmp(dst, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
                /* Not sent to the broadcast address or our P2P Device Address
                 */
+               p2p_dbg(p2p, "Probe Req DA " MACSTR " not ours - ignore it",
+                       MAC2STR(dst));
                return P2P_PREQ_NOT_PROCESSED;
        }
 
        if (bssid && !is_broadcast_ether_addr(bssid)) {
                /* Not sent to the Wildcard BSSID */
+               p2p_dbg(p2p, "Probe Req BSSID " MACSTR " not wildcard - ignore it",
+                       MAC2STR(bssid));
                return P2P_PREQ_NOT_PROCESSED;
        }
 
@@ -1941,23 +2274,86 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
            os_memcmp(elems.ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) !=
            0) {
                /* not using P2P Wildcard SSID - ignore */
+               p2p_dbg(p2p, "Probe Req not using P2P Wildcard SSID - ignore it");
                return P2P_PREQ_NOT_PROCESSED;
        }
 
        if (supp_rates_11b_only(&elems)) {
                /* Indicates support for 11b rates only */
+               p2p_dbg(p2p, "Probe Req with 11b rates only supported - ignore it");
                return P2P_PREQ_NOT_P2P;
        }
 
        os_memset(&msg, 0, sizeof(msg));
        if (p2p_parse_ies(ie, ie_len, &msg) < 0) {
                /* Could not parse P2P attributes */
+               p2p_dbg(p2p, "Could not parse P2P attributes in Probe Req - ignore it");
                return P2P_PREQ_NOT_P2P;
        }
 
+       p2p->p2ps_svc_found = 0;
+
+       if (msg.service_hash && msg.service_hash_count) {
+               const u8 *hash = msg.service_hash;
+               u8 *dest = p2p->query_hash;
+               u8 i;
+
+               p2p->query_count = 0;
+               for (i = 0; i < msg.service_hash_count; i++) {
+                       if (p2p_service_find_asp(p2p, hash)) {
+                               p2p->p2ps_svc_found = 1;
+
+                               if (!os_memcmp(hash, p2p->wild_card_hash,
+                                              P2PS_HASH_LEN)) {
+                                       /* We found match(es) but wildcard
+                                        * will return all */
+                                       p2p->query_count = 1;
+                                       os_memcpy(p2p->query_hash, hash,
+                                                 P2PS_HASH_LEN);
+                                       break;
+                               }
+
+                               /* Save each matching hash */
+                               if (p2p->query_count < P2P_MAX_QUERY_HASH) {
+                                       os_memcpy(dest, hash, P2PS_HASH_LEN);
+                                       dest += P2PS_HASH_LEN;
+                                       p2p->query_count++;
+                               } else {
+                                       /* We found match(es) but too many to
+                                        * return all */
+                                       p2p->query_count = 0;
+                                       break;
+                               }
+                       }
+                       hash += P2PS_HASH_LEN;
+               }
+
+               p2p_dbg(p2p, "ASP adv found: %d", p2p->p2ps_svc_found);
+
+               /* Probed hash unknown */
+               if (!p2p->p2ps_svc_found) {
+                       p2p_parse_free(&msg);
+                       return P2P_PREQ_NOT_PROCESSED;
+               }
+       } else {
+               /* This is not a P2PS Probe Request */
+               p2p->query_count = 0;
+               p2p_dbg(p2p, "No P2PS Hash in Probe Request");
+
+               if (!p2p->in_listen || !p2p->drv_in_listen) {
+                       /* not in Listen state - ignore Probe Request */
+                       p2p_dbg(p2p, "Not in Listen state (in_listen=%d drv_in_listen=%d) - ignore Probe Request",
+                               p2p->in_listen, p2p->drv_in_listen);
+                       p2p_parse_free(&msg);
+                       return P2P_PREQ_NOT_LISTEN;
+               }
+       }
+
        if (msg.device_id &&
            os_memcmp(msg.device_id, p2p->cfg->dev_addr, ETH_ALEN) != 0) {
                /* Device ID did not match */
+               p2p_dbg(p2p, "Probe Req requested Device ID " MACSTR " did not match - ignore it",
+                       MAC2STR(msg.device_id));
                p2p_parse_free(&msg);
                return P2P_PREQ_NOT_PROCESSED;
        }
@@ -1966,6 +2362,7 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
        if (msg.wps_attributes &&
            !p2p_match_dev_type(p2p, msg.wps_attributes)) {
                /* No match with Requested Device Type */
+               p2p_dbg(p2p, "Probe Req requestred Device Type did not match - ignore it");
                p2p_parse_free(&msg);
                return P2P_PREQ_NOT_PROCESSED;
        }
@@ -1973,6 +2370,7 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
 
        if (!p2p->cfg->send_probe_resp) {
                /* Response generated elsewhere */
+               p2p_dbg(p2p, "Probe Resp generated elsewhere - do not generate additional response");
                return P2P_PREQ_NOT_PROCESSED;
        }
 
@@ -2000,7 +2398,7 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
        resp->frame_control = host_to_le16((WLAN_FC_TYPE_MGMT << 2) |
                                           (WLAN_FC_STYPE_PROBE_RESP << 4));
        os_memcpy(resp->da, addr, ETH_ALEN);
-#ifdef TIZEN_EXT_P2P
+#ifdef BCM_DRIVER_V115
        os_memcpy(resp->sa, p2p->cfg->own_addr, ETH_ALEN);
        os_memcpy(resp->bssid, p2p->cfg->own_addr, ETH_ALEN);
 #else
@@ -2057,6 +2455,7 @@ p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
        p2p_add_dev_from_probe_req(p2p, addr, ie, ie_len);
 
        res = p2p_reply_probe(p2p, addr, dst, bssid, ie, ie_len);
+       p2p->query_count = 0;
 
        if ((p2p->state == P2P_CONNECT || p2p->state == P2P_CONNECT_LISTEN) &&
            p2p->go_neg_peer &&
@@ -2072,10 +2471,12 @@ p2p_probe_req_rx(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
 
        if ((p2p->state == P2P_INVITE || p2p->state == P2P_INVITE_LISTEN) &&
            p2p->invite_peer &&
+           (p2p->invite_peer->flags & P2P_DEV_WAIT_INV_REQ_ACK) &&
            os_memcmp(addr, p2p->invite_peer->info.p2p_device_addr, ETH_ALEN)
            == 0) {
                /* Received a Probe Request from Invite peer */
                p2p_dbg(p2p, "Found Invite peer - try to start Invite from timeout");
+               eloop_cancel_timeout(p2p_invite_start, p2p, NULL);
                eloop_register_timeout(0, 0, p2p_invite_start, p2p, NULL);
                return P2P_PREQ_PROCESSED;
        }
@@ -2152,6 +2553,9 @@ int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf,
                extra = wpabuf_len(p2p->wfd_ie_assoc_req);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+       if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ])
+               extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ]);
+
        /*
         * (Re)Association Request - P2P IE
         * P2P Capability attribute (shall be present)
@@ -2167,6 +2571,10 @@ int p2p_assoc_req_ie(struct p2p_data *p2p, const u8 *bssid, u8 *buf,
                wpabuf_put_buf(tmp, p2p->wfd_ie_assoc_req);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+       if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ])
+               wpabuf_put_buf(tmp,
+                              p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_REQ]);
+
        peer = bssid ? p2p_get_device(p2p, bssid) : NULL;
 
        lpos = p2p_buf_add_ie_hdr(tmp);
@@ -2205,38 +2613,164 @@ int p2p_scan_result_text(const u8 *ies, size_t ies_len, char *buf, char *end)
 }
 
 
-int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr)
+struct p2ps_advertisement *
+p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id)
 {
-       struct p2p_message msg;
+       struct p2ps_advertisement *adv_data;
 
-       os_memset(&msg, 0, sizeof(msg));
-       if (p2p_parse_p2p_ie(p2p_ie, &msg))
-               return -1;
+       if (!p2p)
+               return NULL;
 
-       if (msg.p2p_device_addr) {
-               os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN);
-               return 0;
-       } else if (msg.device_id) {
-               os_memcpy(dev_addr, msg.device_id, ETH_ALEN);
-               return 0;
+       adv_data = p2p->p2ps_adv_list;
+       while (adv_data) {
+               if (adv_data->id == adv_id)
+                       return adv_data;
+               adv_data = adv_data->next;
        }
-       return -1;
+
+       return NULL;
 }
 
 
-int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr)
+int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id)
 {
-       struct wpabuf *p2p_ie;
-       int ret;
+       struct p2ps_advertisement *adv_data;
+       struct p2ps_advertisement **prior;
 
-       p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
-                                            P2P_IE_VENDOR_TYPE);
-       if (p2p_ie == NULL)
+       if (!p2p)
                return -1;
-       ret = p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr);
-       wpabuf_free(p2p_ie);
-       return ret;
-}
+
+       adv_data = p2p->p2ps_adv_list;
+       prior = &p2p->p2ps_adv_list;
+       while (adv_data) {
+               if (adv_data->id == adv_id) {
+                       p2p_dbg(p2p, "Delete ASP adv_id=0x%x", adv_id);
+                       *prior = adv_data->next;
+                       os_free(adv_data);
+                       return 0;
+               }
+               prior = &adv_data->next;
+               adv_data = adv_data->next;
+       }
+
+       return -1;
+}
+
+
+int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id,
+                       const char *adv_str, u8 svc_state, u16 config_methods,
+                       const char *svc_info)
+{
+       struct p2ps_advertisement *adv_data, *tmp, **prev;
+       u8 buf[P2PS_HASH_LEN];
+       size_t adv_data_len, adv_len, info_len = 0;
+
+       if (!p2p || !adv_str || !adv_str[0])
+               return -1;
+
+       if (!(config_methods & p2p->cfg->config_methods)) {
+               p2p_dbg(p2p, "Config methods not supported svc: 0x%x dev: 0x%x",
+                       config_methods, p2p->cfg->config_methods);
+               return -1;
+       }
+
+       if (!p2ps_gen_hash(p2p, adv_str, buf))
+               return -1;
+
+       if (svc_info)
+               info_len = os_strlen(svc_info);
+       adv_len = os_strlen(adv_str);
+       adv_data_len = sizeof(struct p2ps_advertisement) + adv_len + 1 +
+               info_len + 1;
+
+       adv_data = os_zalloc(adv_data_len);
+       if (!adv_data)
+               return -1;
+
+       os_memcpy(adv_data->hash, buf, P2PS_HASH_LEN);
+       adv_data->id = adv_id;
+       adv_data->state = svc_state;
+       adv_data->config_methods = config_methods & p2p->cfg->config_methods;
+       adv_data->auto_accept = (u8) auto_accept;
+       os_memcpy(adv_data->svc_name, adv_str, adv_len);
+
+       if (svc_info && info_len) {
+               adv_data->svc_info = &adv_data->svc_name[adv_len + 1];
+               os_memcpy(adv_data->svc_info, svc_info, info_len);
+       }
+
+       /*
+        * Group Advertisements by service string. They do not need to be
+        * sorted, but groups allow easier Probe Response instance grouping
+        */
+       tmp = p2p->p2ps_adv_list;
+       prev = &p2p->p2ps_adv_list;
+       while (tmp) {
+               if (tmp->id == adv_data->id) {
+                       if (os_strcmp(tmp->svc_name, adv_data->svc_name) != 0) {
+                               os_free(adv_data);
+                               return -1;
+                       }
+                       adv_data->next = tmp->next;
+                       *prev = adv_data;
+                       os_free(tmp);
+                       goto inserted;
+               } else {
+                       if (os_strcmp(tmp->svc_name, adv_data->svc_name) == 0) {
+                               adv_data->next = tmp->next;
+                               tmp->next = adv_data;
+                               goto inserted;
+                       }
+               }
+               prev = &tmp->next;
+               tmp = tmp->next;
+       }
+
+       /* No svc_name match found */
+       adv_data->next = p2p->p2ps_adv_list;
+       p2p->p2ps_adv_list = adv_data;
+
+inserted:
+       p2p_dbg(p2p,
+               "Added ASP advertisement adv_id=0x%x config_methods=0x%x svc_state=0x%x adv_str='%s'",
+               adv_id, adv_data->config_methods, svc_state, adv_str);
+
+       return 0;
+}
+
+
+int p2p_parse_dev_addr_in_p2p_ie(struct wpabuf *p2p_ie, u8 *dev_addr)
+{
+       struct p2p_message msg;
+
+       os_memset(&msg, 0, sizeof(msg));
+       if (p2p_parse_p2p_ie(p2p_ie, &msg))
+               return -1;
+
+       if (msg.p2p_device_addr) {
+               os_memcpy(dev_addr, msg.p2p_device_addr, ETH_ALEN);
+               return 0;
+       } else if (msg.device_id) {
+               os_memcpy(dev_addr, msg.device_id, ETH_ALEN);
+               return 0;
+       }
+       return -1;
+}
+
+
+int p2p_parse_dev_addr(const u8 *ies, size_t ies_len, u8 *dev_addr)
+{
+       struct wpabuf *p2p_ie;
+       int ret;
+
+       p2p_ie = ieee802_11_vendor_ie_concat(ies, ies_len,
+                                            P2P_IE_VENDOR_TYPE);
+       if (p2p_ie == NULL)
+               return -1;
+       ret = p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr);
+       wpabuf_free(p2p_ie);
+       return ret;
+}
 
 
 static void p2p_clear_go_neg(struct p2p_data *p2p)
@@ -2288,7 +2822,8 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg)
 {
        struct p2p_data *p2p;
 
-       if (cfg->max_peers < 1)
+       if (cfg->max_peers < 1 ||
+           cfg->passphrase_len < 8 || cfg->passphrase_len > 63)
                return NULL;
 
        p2p = os_zalloc(sizeof(*p2p) + sizeof(*cfg));
@@ -2317,11 +2852,14 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg)
                        p2p->cfg->num_pref_chan = 0;
        }
 
+       p2ps_gen_hash(p2p, P2PS_WILD_HASH_STR, p2p->wild_card_hash);
+
        p2p->min_disc_int = 1;
        p2p->max_disc_int = 3;
        p2p->max_disc_tu = -1;
 
-       os_get_random(&p2p->next_tie_breaker, 1);
+       if (os_get_random(&p2p->next_tie_breaker, 1) < 0)
+               p2p->next_tie_breaker = 0;
        p2p->next_tie_breaker &= 0x01;
        if (cfg->sd_request)
                p2p->dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
@@ -2337,6 +2875,11 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg)
 
        p2p->go_timeout = 100;
        p2p->client_timeout = 20;
+       p2p->num_p2p_sd_queries = 0;
+
+       p2p_dbg(p2p, "initialized");
+       p2p_channels_dump(p2p, "channels", &p2p->cfg->channels);
+       p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels);
 
        return p2p;
 }
@@ -2344,10 +2887,8 @@ struct p2p_data * p2p_init(const struct p2p_config *cfg)
 
 void p2p_deinit(struct p2p_data *p2p)
 {
-#if defined TIZEN_EXT_P2P
-       if (p2p == NULL)
-               return;
-#endif
+       struct p2ps_advertisement *adv, *prev;
+
 #ifdef CONFIG_WIFI_DISPLAY
        wpabuf_free(p2p->wfd_ie_beacon);
        wpabuf_free(p2p->wfd_ie_probe_req);
@@ -2366,6 +2907,7 @@ void p2p_deinit(struct p2p_data *p2p)
        eloop_cancel_timeout(p2p_ext_listen_timeout, p2p, NULL);
        eloop_cancel_timeout(p2p_scan_timeout, p2p, NULL);
        eloop_cancel_timeout(p2p_go_neg_start, p2p, NULL);
+       eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
        p2p_flush(p2p);
        p2p_free_req_dev_types(p2p);
        os_free(p2p->cfg->dev_name);
@@ -2375,9 +2917,19 @@ void p2p_deinit(struct p2p_data *p2p)
        os_free(p2p->cfg->serial_number);
        os_free(p2p->cfg->pref_chan);
        os_free(p2p->groups);
+       os_free(p2p->p2ps_prov);
        wpabuf_free(p2p->sd_resp);
        os_free(p2p->after_scan_tx);
        p2p_remove_wps_vendor_extensions(p2p);
+       os_free(p2p->no_go_freq.range);
+
+       adv = p2p->p2ps_adv_list;
+       while (adv) {
+               prev = adv;
+               adv = adv->next;
+               os_free(prev);
+       }
+
        os_free(p2p);
 }
 
@@ -2407,10 +2959,13 @@ int p2p_unauthorize(struct p2p_data *p2p, const u8 *addr)
 
        p2p_dbg(p2p, "Unauthorizing " MACSTR, MAC2STR(addr));
 
-       if (p2p->go_neg_peer == dev)
+       if (p2p->go_neg_peer == dev) {
+               eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
                p2p->go_neg_peer = NULL;
+       }
 
        dev->wps_method = WPS_NOT_READY;
+       dev->oob_pw_id = 0;
        dev->flags &= ~P2P_DEV_WAIT_GO_NEG_RESPONSE;
        dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
 
@@ -2565,27 +3120,66 @@ int p2p_set_country(struct p2p_data *p2p, const char *country)
 }
 
 
+static int p2p_pre_find_operation(struct p2p_data *p2p, struct p2p_device *dev)
+{
+       if (dev->sd_pending_bcast_queries == 0) {
+               /* Initialize with total number of registered broadcast
+                * SD queries. */
+               dev->sd_pending_bcast_queries = p2p->num_p2p_sd_queries;
+       }
+
+       if (p2p_start_sd(p2p, dev) == 0)
+               return 1;
+
+       if (dev->req_config_methods &&
+           !(dev->flags & P2P_DEV_PD_FOR_JOIN)) {
+               p2p_dbg(p2p, "Send pending Provision Discovery Request to "
+                       MACSTR " (config methods 0x%x)",
+                       MAC2STR(dev->info.p2p_device_addr),
+                       dev->req_config_methods);
+               if (p2p_send_prov_disc_req(p2p, dev, 0, 0) == 0)
+                       return 1;
+       }
+
+       return 0;
+}
+
+
 void p2p_continue_find(struct p2p_data *p2p)
 {
        struct p2p_device *dev;
+       int found;
+
        p2p_set_state(p2p, P2P_SEARCH);
+
+       /* Continue from the device following the last iteration */
+       found = 0;
        dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
-               if (dev->flags & P2P_DEV_SD_SCHEDULE) {
-                       if (p2p_start_sd(p2p, dev) == 0)
-                               return;
-                       else
-                               break;
-               } else if (dev->req_config_methods &&
-                          !(dev->flags & P2P_DEV_PD_FOR_JOIN)) {
-                       p2p_dbg(p2p, "Send pending Provision Discovery Request to "
-                               MACSTR " (config methods 0x%x)",
-                               MAC2STR(dev->info.p2p_device_addr),
-                               dev->req_config_methods);
-                       if (p2p_send_prov_disc_req(p2p, dev, 0, 0) == 0)
-                               return;
+               if (dev == p2p->last_p2p_find_oper) {
+                       found = 1;
+                       continue;
+               }
+               if (!found)
+                       continue;
+               if (p2p_pre_find_operation(p2p, dev) > 0) {
+                       p2p->last_p2p_find_oper = dev;
+                       return;
                }
        }
 
+       /*
+        * Wrap around to the beginning of the list and continue until the last
+        * iteration device.
+        */
+       dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+               if (p2p_pre_find_operation(p2p, dev) > 0) {
+                       p2p->last_p2p_find_oper = dev;
+                       return;
+               }
+               if (dev == p2p->last_p2p_find_oper)
+                       break;
+       }
+
        p2p_listen_in_find(p2p, 1);
 }
 
@@ -2597,20 +3191,34 @@ static void p2p_sd_cb(struct p2p_data *p2p, int success)
        p2p->pending_action_state = P2P_NO_PENDING_ACTION;
 
        if (!success) {
-               if (p2p->sd_peer) {
-                       p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
-                       p2p->sd_peer = NULL;
-               }
-               p2p_continue_find(p2p);
+               if (p2p->sd_peer)
+                       p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+               p2p->sd_peer = NULL;
+               if (p2p->state != P2P_IDLE)
+                       p2p_continue_find(p2p);
                return;
        }
 
        if (p2p->sd_peer == NULL) {
                p2p_dbg(p2p, "No SD peer entry known");
-               p2p_continue_find(p2p);
+               if (p2p->state != P2P_IDLE)
+                       p2p_continue_find(p2p);
                return;
        }
 
+       if (p2p->sd_query && p2p->sd_query->for_all_peers) {
+               /* Update the pending broadcast SD query count for this device
+                */
+               p2p->sd_peer->sd_pending_bcast_queries--;
+
+               /*
+                * If there are no pending broadcast queries for this device,
+                * mark it as done (-1).
+                */
+               if (p2p->sd_peer->sd_pending_bcast_queries == 0)
+                       p2p->sd_peer->sd_pending_bcast_queries = -1;
+       }
+
        /* Wait for response from the peer */
        p2p_set_state(p2p, P2P_SD_DURING_FIND);
        p2p_set_timeout(p2p, 0, 200000);
@@ -2625,9 +3233,6 @@ static void p2p_retry_pd(struct p2p_data *p2p)
 {
        struct p2p_device *dev;
 
-       if (p2p->state != P2P_IDLE)
-               return;
-
        /*
         * Retry the prov disc req attempt only for the peer that the user had
         * requested.
@@ -2645,7 +3250,8 @@ static void p2p_retry_pd(struct p2p_data *p2p)
                        MAC2STR(dev->info.p2p_device_addr),
                        dev->req_config_methods);
                p2p_send_prov_disc_req(p2p, dev,
-                                      dev->flags & P2P_DEV_PD_FOR_JOIN, 0);
+                                      dev->flags & P2P_DEV_PD_FOR_JOIN,
+                                      p2p->pd_force_freq);
                return;
        }
 }
@@ -2700,11 +3306,56 @@ static void p2p_prov_disc_cb(struct p2p_data *p2p, int success)
 }
 
 
+static int p2p_check_after_scan_tx_continuation(struct p2p_data *p2p)
+{
+       if (p2p->after_scan_tx_in_progress) {
+               p2p->after_scan_tx_in_progress = 0;
+               if (p2p->start_after_scan != P2P_AFTER_SCAN_NOTHING &&
+                   p2p_run_after_scan(p2p))
+                       return 1;
+               if (p2p->state == P2P_SEARCH) {
+                       p2p_dbg(p2p, "Continue find after after_scan_tx completion");
+                       p2p_continue_find(p2p);
+               }
+       }
+
+       return 0;
+}
+
+
+static void p2p_prov_disc_resp_cb(struct p2p_data *p2p, int success)
+{
+       p2p_dbg(p2p, "Provision Discovery Response TX callback: success=%d",
+               success);
+
+       if (p2p->send_action_in_progress) {
+               p2p->send_action_in_progress = 0;
+               p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+       }
+
+       p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+
+       if (!success)
+               goto continue_search;
+
+       if (!p2p->cfg->prov_disc_resp_cb ||
+           p2p->cfg->prov_disc_resp_cb(p2p->cfg->cb_ctx) < 1)
+               goto continue_search;
+
+       p2p_dbg(p2p,
+               "Post-Provision Discovery operations started - do not try to continue other P2P operations");
+       return;
+
+continue_search:
+       p2p_check_after_scan_tx_continuation(p2p);
+}
+
+
 int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
-                        struct os_time *rx_time, int level, const u8 *ies,
+                        struct os_reltime *rx_time, int level, const u8 *ies,
                         size_t ies_len)
 {
-       if (os_time_before(rx_time, &p2p->find_start)) {
+       if (os_reltime_before(rx_time, &p2p->find_start)) {
                /*
                 * The driver may have cached (e.g., in cfg80211 BSS table) the
                 * scan results for relatively long time. To avoid reporting
@@ -2742,6 +3393,7 @@ void p2p_scan_res_handled(struct p2p_data *p2p)
 
 void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id)
 {
+       u8 dev_capab;
        u8 *len;
 
 #ifdef CONFIG_WIFI_DISPLAY
@@ -2749,9 +3401,20 @@ void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id)
                wpabuf_put_buf(ies, p2p->wfd_ie_probe_req);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+       if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P])
+               wpabuf_put_buf(ies,
+                              p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]);
+
        len = p2p_buf_add_ie_hdr(ies);
-       p2p_buf_add_capability(ies, p2p->dev_capab &
-                              ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
+
+       dev_capab = p2p->dev_capab & ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY;
+
+       /* P2PS requires Probe Request frames to include SD bit */
+       if (p2p->p2ps_seek && p2p->p2ps_seek_count)
+               dev_capab |= P2P_DEV_CAPAB_SERVICE_DISCOVERY;
+
+       p2p_buf_add_capability(ies, dev_capab, 0);
+
        if (dev_id)
                p2p_buf_add_device_id(ies, dev_id);
        if (p2p->cfg->reg_class && p2p->cfg->channel)
@@ -2761,6 +3424,10 @@ void p2p_scan_ie(struct p2p_data *p2p, struct wpabuf *ies, const u8 *dev_id)
        if (p2p->ext_listen_interval)
                p2p_buf_add_ext_listen_timing(ies, p2p->ext_listen_period,
                                              p2p->ext_listen_interval);
+
+       if (p2p->p2ps_seek && p2p->p2ps_seek_count)
+               p2p_buf_add_service_hash(ies, p2p);
+
        /* TODO: p2p_buf_add_operating_channel() if GO */
        p2p_buf_update_ie_hdr(ies, len);
 }
@@ -2775,6 +3442,10 @@ size_t p2p_scan_ie_buf_len(struct p2p_data *p2p)
                len += wpabuf_len(p2p->wfd_ie_probe_req);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+       if (p2p && p2p->vendor_elem &&
+           p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P])
+               len += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_PROBE_REQ_P2P]);
+
        return len;
 }
 
@@ -2832,7 +3503,8 @@ static void p2p_go_neg_req_cb(struct p2p_data *p2p, int success)
                 * make it less likely to hit cases where we could end up in
                 * sync with peer not listening.
                 */
-               os_get_random((u8 *) &r, sizeof(r));
+               if (os_get_random((u8 *) &r, sizeof(r)) < 0)
+                       r = 0;
                timeout += r % 100000;
        }
        p2p_set_timeout(p2p, 0, timeout);
@@ -2857,15 +3529,20 @@ static void p2p_go_neg_resp_failure_cb(struct p2p_data *p2p, int success,
 {
        p2p_dbg(p2p, "GO Negotiation Response (failure) TX callback: success=%d", success);
        if (p2p->go_neg_peer && p2p->go_neg_peer->status != P2P_SC_SUCCESS) {
-               p2p_go_neg_failed(p2p, p2p->go_neg_peer,
-                                 p2p->go_neg_peer->status);
-       } else if (success) {
+               p2p_go_neg_failed(p2p, p2p->go_neg_peer->status);
+               return;
+       }
+
+       if (success) {
                struct p2p_device *dev;
                dev = p2p_get_device(p2p, addr);
                if (dev &&
                    dev->status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
                        dev->flags |= P2P_DEV_PEER_WAITING_RESPONSE;
        }
+
+       if (p2p->state == P2P_SEARCH || p2p->state == P2P_SD_DURING_FIND)
+               p2p_continue_find(p2p);
 }
 
 
@@ -2875,13 +3552,44 @@ static void p2p_go_neg_conf_cb(struct p2p_data *p2p,
        struct p2p_device *dev;
 
        p2p_dbg(p2p, "GO Negotiation Confirm TX callback: result=%d", result);
-       p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
        if (result == P2P_SEND_ACTION_FAILED) {
-               p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+               p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+               p2p_go_neg_failed(p2p, -1);
                return;
        }
+
+       dev = p2p->go_neg_peer;
+
        if (result == P2P_SEND_ACTION_NO_ACK) {
                /*
+                * Retry GO Negotiation Confirmation
+                * P2P_GO_NEG_CNF_MAX_RETRY_COUNT times if we did not receive
+                * ACK for confirmation.
+                */
+               if (dev && dev->go_neg_conf &&
+                   dev->go_neg_conf_sent <= P2P_GO_NEG_CNF_MAX_RETRY_COUNT) {
+                       p2p_dbg(p2p, "GO Negotiation Confirm retry %d",
+                               dev->go_neg_conf_sent);
+                       p2p->pending_action_state = P2P_PENDING_GO_NEG_CONFIRM;
+                       if (p2p_send_action(p2p, dev->go_neg_conf_freq,
+                                           dev->info.p2p_device_addr,
+                                           p2p->cfg->dev_addr,
+                                           dev->info.p2p_device_addr,
+                                           wpabuf_head(dev->go_neg_conf),
+                                           wpabuf_len(dev->go_neg_conf), 0) >=
+                           0) {
+                               dev->go_neg_conf_sent++;
+                               return;
+                       }
+                       p2p_dbg(p2p, "Failed to re-send Action frame");
+
+                       /*
+                        * Continue with the assumption that the first attempt
+                        * went through and just the ACK frame was lost.
+                        */
+               }
+
+               /*
                 * It looks like the TX status for GO Negotiation Confirm is
                 * often showing failure even when the peer has actually
                 * received the frame. Since the peer may change channels
@@ -2894,7 +3602,8 @@ static void p2p_go_neg_conf_cb(struct p2p_data *p2p,
                p2p_dbg(p2p, "Assume GO Negotiation Confirm TX was actually received by the peer even though Ack was not reported");
        }
 
-       dev = p2p->go_neg_peer;
+       p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+
        if (dev == NULL)
                return;
 
@@ -2910,24 +3619,19 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
        int success;
 
        p2p_dbg(p2p, "Action frame TX callback (state=%d freq=%u dst=" MACSTR
-               " src=" MACSTR " bssid=" MACSTR " result=%d",
+               " src=" MACSTR " bssid=" MACSTR " result=%d p2p_state=%s)",
                p2p->pending_action_state, freq, MAC2STR(dst), MAC2STR(src),
-               MAC2STR(bssid), result);
+               MAC2STR(bssid), result, p2p_state_txt(p2p->state));
        success = result == P2P_SEND_ACTION_SUCCESS;
        state = p2p->pending_action_state;
        p2p->pending_action_state = P2P_NO_PENDING_ACTION;
        switch (state) {
        case P2P_NO_PENDING_ACTION:
-               if (p2p->after_scan_tx_in_progress) {
-                       p2p->after_scan_tx_in_progress = 0;
-                       if (p2p->start_after_scan != P2P_AFTER_SCAN_NOTHING &&
-                           p2p_run_after_scan(p2p))
-                               break;
-                       if (p2p->state == P2P_SEARCH) {
-                               p2p_dbg(p2p, "Continue find after after_scan_tx completion");
-                               p2p_continue_find(p2p);
-                       }
+               if (p2p->send_action_in_progress) {
+                       p2p->send_action_in_progress = 0;
+                       p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
                }
+               p2p_check_after_scan_tx_continuation(p2p);
                break;
        case P2P_PENDING_GO_NEG_REQUEST:
                p2p_go_neg_req_cb(p2p, success);
@@ -2947,6 +3651,9 @@ void p2p_send_action_cb(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
        case P2P_PENDING_PD:
                p2p_prov_disc_cb(p2p, success);
                break;
+       case P2P_PENDING_PD_RESPONSE:
+               p2p_prov_disc_resp_cb(p2p, success);
+               break;
        case P2P_PENDING_INVITATION_REQUEST:
                p2p_invitation_req_cb(p2p, success);
                break;
@@ -3012,7 +3719,7 @@ int p2p_listen_end(struct p2p_data *p2p, unsigned int freq)
        if (p2p->state == P2P_CONNECT_LISTEN && p2p->go_neg_peer) {
                if (p2p->go_neg_peer->connect_reqs >= 120) {
                        p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response");
-                       p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+                       p2p_go_neg_failed(p2p, -1);
                        return 0;
                }
 
@@ -3063,7 +3770,7 @@ static void p2p_timeout_connect(struct p2p_data *p2p)
        if (p2p->go_neg_peer &&
            (p2p->go_neg_peer->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) {
                p2p_dbg(p2p, "Wait for GO Negotiation Confirm timed out - assume GO Negotiation failed");
-               p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+               p2p_go_neg_failed(p2p, -1);
                return;
        }
        if (p2p->go_neg_peer &&
@@ -3073,7 +3780,12 @@ static void p2p_timeout_connect(struct p2p_data *p2p)
                p2p_connect_send(p2p, p2p->go_neg_peer);
                return;
        }
-
+       if (p2p->go_neg_peer && p2p->go_neg_peer->oob_go_neg_freq > 0) {
+               p2p_dbg(p2p, "Skip connect-listen since GO Neg channel known (OOB)");
+               p2p_set_state(p2p, P2P_CONNECT_LISTEN);
+               p2p_set_timeout(p2p, 0, 30000);
+               return;
+       }
        p2p_set_state(p2p, P2P_CONNECT_LISTEN);
        p2p_listen_in_find(p2p, 0);
 }
@@ -3089,7 +3801,7 @@ static void p2p_timeout_connect_listen(struct p2p_data *p2p)
 
                if (p2p->go_neg_peer->connect_reqs >= 120) {
                        p2p_dbg(p2p, "Timeout on sending GO Negotiation Request without getting response");
-                       p2p_go_neg_failed(p2p, p2p->go_neg_peer, -1);
+                       p2p_go_neg_failed(p2p, -1);
                        return;
                }
 
@@ -3102,13 +3814,13 @@ static void p2p_timeout_connect_listen(struct p2p_data *p2p)
 
 static void p2p_timeout_wait_peer_connect(struct p2p_data *p2p)
 {
-       /*
-        * TODO: could remain constantly in Listen state for some time if there
-        * are no other concurrent uses for the radio. For now, go to listen
-        * state once per second to give other uses a chance to use the radio.
-        */
        p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
-       p2p_set_timeout(p2p, 0, 500000);
+
+       if (p2p->cfg->is_concurrent_session_active &&
+           p2p->cfg->is_concurrent_session_active(p2p->cfg->cb_ctx))
+               p2p_set_timeout(p2p, 0, 500000);
+       else
+               p2p_set_timeout(p2p, 0, 200000);
 }
 
 
@@ -3121,13 +3833,6 @@ static void p2p_timeout_wait_peer_idle(struct p2p_data *p2p)
                return;
        }
 
-       dev->wait_count++;
-       if (dev->wait_count >= 120) {
-               p2p_dbg(p2p, "Timeout on waiting peer to become ready for GO Negotiation");
-               p2p_go_neg_failed(p2p, dev, -1);
-               return;
-       }
-
        p2p_dbg(p2p, "Go to Listen state while waiting for the peer to become ready for GO Negotiation");
        p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT);
        p2p_listen_in_find(p2p, 0);
@@ -3139,7 +3844,6 @@ static void p2p_timeout_sd_during_find(struct p2p_data *p2p)
        p2p_dbg(p2p, "Service Discovery Query timeout");
        if (p2p->sd_peer) {
                p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
-               p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
                p2p->sd_peer = NULL;
        }
        p2p_continue_find(p2p);
@@ -3156,6 +3860,9 @@ static void p2p_timeout_prov_disc_during_find(struct p2p_data *p2p)
 
 static void p2p_timeout_prov_disc_req(struct p2p_data *p2p)
 {
+       u32 adv_id = 0;
+       u8 *adv_mac = NULL;
+
        p2p->pending_action_state = P2P_NO_PENDING_ACTION;
 
        /*
@@ -3184,12 +3891,18 @@ static void p2p_timeout_prov_disc_req(struct p2p_data *p2p)
                                for_join = 1;
                }
 
+               if (p2p->p2ps_prov) {
+                       adv_id = p2p->p2ps_prov->adv_id;
+                       adv_mac = p2p->p2ps_prov->adv_mac;
+               }
+
                if (p2p->cfg->prov_disc_fail)
                        p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx,
                                                 p2p->pending_pd_devaddr,
                                                 for_join ?
                                                 P2P_PROV_DISC_TIMEOUT_JOIN :
-                                                P2P_PROV_DISC_TIMEOUT);
+                                                P2P_PROV_DISC_TIMEOUT,
+                                                adv_id, adv_mac, NULL);
                p2p_reset_pending_pd(p2p);
        }
 }
@@ -3217,14 +3930,15 @@ static void p2p_timeout_invite_listen(struct p2p_data *p2p)
        if (p2p->invite_peer && p2p->invite_peer->invitation_reqs < 100) {
                p2p_set_state(p2p, P2P_INVITE);
                p2p_invite_send(p2p, p2p->invite_peer,
-                               p2p->invite_go_dev_addr);
+                               p2p->invite_go_dev_addr, p2p->invite_dev_pw_id);
        } else {
                if (p2p->invite_peer) {
                        p2p_dbg(p2p, "Invitation Request retry limit reached");
                        if (p2p->cfg->invitation_result)
                                p2p->cfg->invitation_result(
                                        p2p->cfg->cb_ctx, -1, NULL, NULL,
-                                       p2p->invite_peer->info.p2p_device_addr);
+                                       p2p->invite_peer->info.p2p_device_addr,
+                                       0, 0);
                }
                p2p_set_state(p2p, P2P_IDLE);
        }
@@ -3238,6 +3952,10 @@ static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx)
        p2p_dbg(p2p, "Timeout (state=%s)", p2p_state_txt(p2p->state));
 
        p2p->in_listen = 0;
+       if (p2p->drv_in_listen) {
+               p2p_dbg(p2p, "Driver is still in listen state - stop it");
+               p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+       }
 
        switch (p2p->state) {
        case P2P_IDLE:
@@ -3299,10 +4017,6 @@ static void p2p_state_timeout(void *eloop_ctx, void *timeout_ctx)
        case P2P_INVITE_LISTEN:
                p2p_timeout_invite_listen(p2p);
                break;
-       case P2P_SEARCH_WHEN_READY:
-               break;
-       case P2P_CONTINUE_SEARCH_WHEN_READY:
-               break;
        }
 }
 
@@ -3323,24 +4037,6 @@ int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr)
        return 0;
 }
 
-#ifdef TIZEN_EXT_P2P
-int p2p_reject_connection(struct p2p_data *p2p, const u8 *peer_addr)
-{
-       struct p2p_device *dev;
-
-       dev = p2p_get_device(p2p, peer_addr);
-       p2p_dbg(p2p, "P2P: Local request to reject connection attempts by peer "
-      MACSTR, MAC2STR(peer_addr));
-       if (dev == NULL) {
-               p2p_dbg(p2p, "P2P: Peer " MACSTR " unknown", MAC2STR(peer_addr));
-               return -1;
-       }
-
-       return p2p_reject_send(p2p, dev);
-}
-#endif
-
-
 
 const char * p2p_wps_method_text(enum p2p_wps_method method)
 {
@@ -3353,6 +4049,10 @@ const char * p2p_wps_method_text(enum p2p_wps_method method)
                return "Keypad";
        case WPS_PBC:
                return "PBC";
+       case WPS_NFC:
+               return "NFC";
+       case WPS_P2PS:
+               return "P2PS";
        }
 
        return "??";
@@ -3403,7 +4103,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
        struct p2p_device *dev;
        int res;
        char *pos, *end;
-       struct os_time now;
+       struct os_reltime now;
 
        if (info == NULL)
                return -1;
@@ -3414,7 +4114,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
        pos = buf;
        end = buf + buflen;
 
-       os_get_time(&now);
+       os_get_reltime(&now);
        res = os_snprintf(pos, end - pos,
                          "age=%d\n"
                          "listen_freq=%d\n"
@@ -3429,9 +4129,8 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
                          "country=%c%c\n"
                          "oper_freq=%d\n"
                          "req_config_methods=0x%x\n"
-                         "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
+                         "flags=%s%s%s%s%s%s%s%s%s%s%s%s%s%s\n"
                          "status=%d\n"
-                         "wait_count=%u\n"
                          "invitation_reqs=%u\n",
                          (int) (now.sec - dev->last_seen.sec),
                          dev->listen_freq,
@@ -3452,13 +4151,12 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
                          dev->flags & P2P_DEV_REPORTED ? "[REPORTED]" : "",
                          dev->flags & P2P_DEV_NOT_YET_READY ?
                          "[NOT_YET_READY]" : "",
-                         dev->flags & P2P_DEV_SD_INFO ? "[SD_INFO]" : "",
-                         dev->flags & P2P_DEV_SD_SCHEDULE ? "[SD_SCHEDULE]" :
-                         "",
                          dev->flags & P2P_DEV_PD_PEER_DISPLAY ?
                          "[PD_PEER_DISPLAY]" : "",
                          dev->flags & P2P_DEV_PD_PEER_KEYPAD ?
                          "[PD_PEER_KEYPAD]" : "",
+                         dev->flags & P2P_DEV_PD_PEER_P2PS ?
+                         "[PD_PEER_P2PS]" : "",
                          dev->flags & P2P_DEV_USER_REJECTED ?
                          "[USER_REJECTED]" : "",
                          dev->flags & P2P_DEV_PEER_WAITING_RESPONSE ?
@@ -3476,9 +4174,8 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
                          dev->flags & P2P_DEV_PD_FOR_JOIN ?
                          "[PD_FOR_JOIN]" : "",
                          dev->status,
-                         dev->wait_count,
                          dev->invitation_reqs);
-       if (res < 0 || res >= end - pos)
+       if (os_snprintf_error(end - pos, res))
                return pos - buf;
        pos += res;
 
@@ -3488,7 +4185,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
                                  "ext_listen_interval=%u\n",
                                  dev->ext_listen_period,
                                  dev->ext_listen_interval);
-               if (res < 0 || res >= end - pos)
+               if (os_snprintf_error(end - pos, res))
                        return pos - buf;
                pos += res;
        }
@@ -3498,7 +4195,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
                                  "oper_ssid=%s\n",
                                  wpa_ssid_txt(dev->oper_ssid,
                                               dev->oper_ssid_len));
-               if (res < 0 || res >= end - pos)
+               if (os_snprintf_error(end - pos, res))
                        return pos - buf;
                pos += res;
        }
@@ -3506,7 +4203,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
 #ifdef CONFIG_WIFI_DISPLAY
        if (dev->info.wfd_subelems) {
                res = os_snprintf(pos, end - pos, "wfd_subelems=");
-               if (res < 0 || res >= end - pos)
+               if (os_snprintf_error(end - pos, res))
                        return pos - buf;
                pos += res;
 
@@ -3515,7 +4212,7 @@ int p2p_get_peer_info_txt(const struct p2p_peer_info *info,
                                        wpabuf_len(dev->info.wfd_subelems));
 
                res = os_snprintf(pos, end - pos, "\n");
-               if (res < 0 || res >= end - pos)
+               if (os_snprintf_error(end - pos, res))
                        return pos - buf;
                pos += res;
        }
@@ -3718,6 +4415,11 @@ static void p2p_process_presence_resp(struct p2p_data *p2p, const u8 *da,
                return;
        }
 
+       if (p2p->cfg->presence_resp) {
+               p2p->cfg->presence_resp(p2p->cfg->cb_ctx, sa, *msg.status,
+                                       msg.noa, msg.noa_len);
+       }
+
        if (*msg.status) {
                p2p_dbg(p2p, "P2P Presence Request was rejected: status %u",
                        *msg.status);
@@ -3744,6 +4446,15 @@ static void p2p_ext_listen_timeout(void *eloop_ctx, void *timeout_ctx)
                                       p2p_ext_listen_timeout, p2p, NULL);
        }
 
+       if ((p2p->cfg->is_p2p_in_progress &&
+            p2p->cfg->is_p2p_in_progress(p2p->cfg->cb_ctx)) ||
+           (p2p->pending_action_state == P2P_PENDING_PD &&
+            p2p->pd_retries > 0)) {
+               p2p_dbg(p2p, "Operation in progress - skip Extended Listen timeout (%s)",
+                       p2p_state_txt(p2p->state));
+               return;
+       }
+
        if (p2p->state == P2P_LISTEN_ONLY && p2p->ext_listen_only) {
                /*
                 * This should not really happen, but it looks like the Listen
@@ -3815,8 +4526,10 @@ void p2p_deauth_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
        os_memset(&msg, 0, sizeof(msg));
        if (p2p_parse_ies(ie, ie_len, &msg))
                return;
-       if (msg.minor_reason_code == NULL)
+       if (msg.minor_reason_code == NULL) {
+               p2p_parse_free(&msg);
                return;
+       }
 
        p2p_dbg(p2p, "Deauthentication notification BSSID " MACSTR
                " reason_code=%u minor_reason_code=%u",
@@ -3837,8 +4550,10 @@ void p2p_disassoc_notif(struct p2p_data *p2p, const u8 *bssid, u16 reason_code,
        os_memset(&msg, 0, sizeof(msg));
        if (p2p_parse_ies(ie, ie_len, &msg))
                return;
-       if (msg.minor_reason_code == NULL)
+       if (msg.minor_reason_code == NULL) {
+               p2p_parse_free(&msg);
                return;
+       }
 
        p2p_dbg(p2p, "Disassociation notification BSSID " MACSTR
                " reason_code=%u minor_reason_code=%u",
@@ -3860,20 +4575,53 @@ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled)
 }
 
 
-int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel)
+int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class,
+                                u8 *op_channel)
+{
+       return p2p_channel_random_social(&p2p->channels, op_class, op_channel);
+}
+
+
+int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel,
+                          u8 forced)
 {
        if (p2p_channel_to_freq(reg_class, channel) < 0)
                return -1;
 
+       /*
+        * Listen channel was set in configuration or set by control interface;
+        * cannot override it.
+        */
+       if (p2p->cfg->channel_forced && forced == 0) {
+               p2p_dbg(p2p,
+                       "Listen channel was previously configured - do not override based on optimization");
+               return -1;
+       }
+
        p2p_dbg(p2p, "Set Listen channel: reg_class %u channel %u",
                reg_class, channel);
-       p2p->cfg->reg_class = reg_class;
-       p2p->cfg->channel = channel;
+
+       if (p2p->state == P2P_IDLE) {
+               p2p->cfg->reg_class = reg_class;
+               p2p->cfg->channel = channel;
+               p2p->cfg->channel_forced = forced;
+       } else {
+               p2p_dbg(p2p, "Defer setting listen channel");
+               p2p->pending_reg_class = reg_class;
+               p2p->pending_channel = channel;
+               p2p->pending_channel_forced = forced;
+       }
 
        return 0;
 }
 
 
+u8 p2p_get_listen_channel(struct p2p_data *p2p)
+{
+       return p2p->cfg->channel;
+}
+
+
 int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len)
 {
        p2p_dbg(p2p, "New SSID postfix: %s", wpa_ssid_txt(postfix, len));
@@ -3926,6 +4674,43 @@ int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
 }
 
 
+int p2p_set_no_go_freq(struct p2p_data *p2p,
+                      const struct wpa_freq_range_list *list)
+{
+       struct wpa_freq_range *tmp;
+
+       if (list == NULL || list->num == 0) {
+               os_free(p2p->no_go_freq.range);
+               p2p->no_go_freq.range = NULL;
+               p2p->no_go_freq.num = 0;
+               return 0;
+       }
+
+       tmp = os_calloc(list->num, sizeof(struct wpa_freq_range));
+       if (tmp == NULL)
+               return -1;
+       os_memcpy(tmp, list->range, list->num * sizeof(struct wpa_freq_range));
+       os_free(p2p->no_go_freq.range);
+       p2p->no_go_freq.range = tmp;
+       p2p->no_go_freq.num = list->num;
+       p2p_dbg(p2p, "Updated no GO chan list");
+
+       return 0;
+}
+
+
+int p2p_get_member_in_go_dev(struct p2p_data *p2p, const u8 *dev_addr,
+                          u8 *member_in_go_dev)
+{
+
+       struct p2p_device *dev = p2p_get_device(p2p, dev_addr);
+       if (dev == NULL || is_zero_ether_addr(dev->member_in_go_dev))
+               return -1;
+       os_memcpy(member_in_go_dev, dev->member_in_go_dev, ETH_ALEN);
+       return 0;
+}
+
+
 int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
                           u8 *iface_addr)
 {
@@ -3988,10 +4773,16 @@ void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled)
 }
 
 
-void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan)
+void p2p_update_channel_list(struct p2p_data *p2p,
+                            const struct p2p_channels *chan,
+                            const struct p2p_channels *cli_chan)
 {
        p2p_dbg(p2p, "Update channel list");
        os_memcpy(&p2p->cfg->channels, chan, sizeof(struct p2p_channels));
+       p2p_channels_dump(p2p, "channels", &p2p->cfg->channels);
+       os_memcpy(&p2p->cfg->cli_channels, cli_chan,
+                 sizeof(struct p2p_channels));
+       p2p_channels_dump(p2p, "cli_channels", &p2p->cfg->cli_channels);
 }
 
 
@@ -4070,7 +4861,7 @@ p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next)
                                dev = dl_list_first(&dev->list,
                                                    struct p2p_device,
                                                    list);
-                               if (&dev->list == &p2p->devices)
+                               if (!dev || &dev->list == &p2p->devices)
                                        return NULL;
                        } while (dev->flags & P2P_DEV_PROBE_REQ_ONLY);
                }
@@ -4082,7 +4873,7 @@ p2p_get_peer_found(struct p2p_data *p2p, const u8 *addr, int next)
                        dev = dl_list_first(&dev->list,
                                            struct p2p_device,
                                            list);
-                       if (&dev->list == &p2p->devices)
+                       if (!dev || &dev->list == &p2p->devices)
                                return NULL;
                }
        }
@@ -4095,8 +4886,7 @@ int p2p_in_progress(struct p2p_data *p2p)
 {
        if (p2p == NULL)
                return 0;
-       if (p2p->state == P2P_SEARCH || p2p->state == P2P_SEARCH_WHEN_READY ||
-           p2p->state == P2P_CONTINUE_SEARCH_WHEN_READY)
+       if (p2p->state == P2P_SEARCH)
                return 2;
        return p2p->state != P2P_IDLE && p2p->state != P2P_PROVISIONING;
 }
@@ -4112,13 +4902,6 @@ void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout,
 }
 
 
-void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay)
-{
-       if (p2p && p2p->search_delay < delay)
-               p2p->search_delay = delay;
-}
-
-
 #ifdef CONFIG_WIFI_DISPLAY
 
 static void p2p_update_wfd_ie_groups(struct p2p_data *p2p)
@@ -4128,7 +4911,7 @@ static void p2p_update_wfd_ie_groups(struct p2p_data *p2p)
 
        for (g = 0; g < p2p->num_groups; g++) {
                group = p2p->groups[g];
-               p2p_group_update_ies(group);
+               p2p_group_force_beacon_update_ies(group);
        }
 }
 
@@ -4306,3 +5089,269 @@ void p2p_err(struct p2p_data *p2p, const char *fmt, ...)
        va_end(ap);
        p2p->cfg->debug_print(p2p->cfg->cb_ctx, MSG_ERROR, buf);
 }
+
+
+void p2p_loop_on_known_peers(struct p2p_data *p2p,
+                            void (*peer_callback)(struct p2p_peer_info *peer,
+                                                  void *user_data),
+                            void *user_data)
+{
+       struct p2p_device *dev, *n;
+
+       dl_list_for_each_safe(dev, n, &p2p->devices, struct p2p_device, list) {
+               peer_callback(&dev->info, user_data);
+       }
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+static struct wpabuf * p2p_build_nfc_handover(struct p2p_data *p2p,
+                                             int client_freq,
+                                             const u8 *go_dev_addr,
+                                             const u8 *ssid, size_t ssid_len)
+{
+       struct wpabuf *buf;
+       u8 op_class, channel;
+       enum p2p_role_indication role = P2P_DEVICE_NOT_IN_GROUP;
+
+       buf = wpabuf_alloc(1000);
+       if (buf == NULL)
+               return NULL;
+
+       op_class = p2p->cfg->reg_class;
+       channel = p2p->cfg->channel;
+
+       p2p_buf_add_capability(buf, p2p->dev_capab &
+                              ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
+       p2p_buf_add_device_info(buf, p2p, NULL);
+
+       if (p2p->num_groups > 0) {
+               int freq = p2p_group_get_freq(p2p->groups[0]);
+               role = P2P_GO_IN_A_GROUP;
+               if (p2p_freq_to_channel(freq, &op_class, &channel) < 0) {
+                       p2p_dbg(p2p,
+                               "Unknown GO operating frequency %d MHz for NFC handover",
+                               freq);
+                       wpabuf_free(buf);
+                       return NULL;
+               }
+       } else if (client_freq > 0) {
+               role = P2P_CLIENT_IN_A_GROUP;
+               if (p2p_freq_to_channel(client_freq, &op_class, &channel) < 0) {
+                       p2p_dbg(p2p,
+                               "Unknown client operating frequency %d MHz for NFC handover",
+                               client_freq);
+                       wpabuf_free(buf);
+                       return NULL;
+               }
+       }
+
+       p2p_buf_add_oob_go_neg_channel(buf, p2p->cfg->country, op_class,
+                                      channel, role);
+
+       if (p2p->num_groups > 0) {
+               /* Limit number of clients to avoid very long message */
+               p2p_buf_add_group_info(p2p->groups[0], buf, 5);
+               p2p_group_buf_add_id(p2p->groups[0], buf);
+       } else if (client_freq > 0 &&
+                  go_dev_addr && !is_zero_ether_addr(go_dev_addr) &&
+                  ssid && ssid_len > 0) {
+               /*
+                * Add the optional P2P Group ID to indicate in which group this
+                * device is a P2P Client.
+                */
+               p2p_buf_add_group_id(buf, go_dev_addr, ssid, ssid_len);
+       }
+
+       return buf;
+}
+
+
+struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p,
+                                          int client_freq,
+                                          const u8 *go_dev_addr,
+                                          const u8 *ssid, size_t ssid_len)
+{
+       return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid,
+                                     ssid_len);
+}
+
+
+struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p,
+                                          int client_freq,
+                                          const u8 *go_dev_addr,
+                                          const u8 *ssid, size_t ssid_len)
+{
+       return p2p_build_nfc_handover(p2p, client_freq, go_dev_addr, ssid,
+                                     ssid_len);
+}
+
+
+int p2p_process_nfc_connection_handover(struct p2p_data *p2p,
+                                       struct p2p_nfc_params *params)
+{
+       struct p2p_message msg;
+       struct p2p_device *dev;
+       const u8 *p2p_dev_addr;
+       int freq;
+       enum p2p_role_indication role;
+
+       params->next_step = NO_ACTION;
+
+       if (p2p_parse_ies_separate(params->wsc_attr, params->wsc_len,
+                                  params->p2p_attr, params->p2p_len, &msg)) {
+               p2p_dbg(p2p, "Failed to parse WSC/P2P attributes from NFC");
+               p2p_parse_free(&msg);
+               return -1;
+       }
+
+       if (msg.p2p_device_addr)
+               p2p_dev_addr = msg.p2p_device_addr;
+       else if (msg.device_id)
+               p2p_dev_addr = msg.device_id;
+       else {
+               p2p_dbg(p2p, "Ignore scan data without P2P Device Info or P2P Device Id");
+               p2p_parse_free(&msg);
+               return -1;
+       }
+
+       if (msg.oob_dev_password) {
+               os_memcpy(params->oob_dev_pw, msg.oob_dev_password,
+                         msg.oob_dev_password_len);
+               params->oob_dev_pw_len = msg.oob_dev_password_len;
+       }
+
+       dev = p2p_create_device(p2p, p2p_dev_addr);
+       if (dev == NULL) {
+               p2p_parse_free(&msg);
+               return -1;
+       }
+
+       params->peer = &dev->info;
+
+       os_get_reltime(&dev->last_seen);
+       dev->flags &= ~(P2P_DEV_PROBE_REQ_ONLY | P2P_DEV_GROUP_CLIENT_ONLY);
+       p2p_copy_wps_info(p2p, dev, 0, &msg);
+
+       if (!msg.oob_go_neg_channel) {
+               p2p_dbg(p2p, "OOB GO Negotiation Channel attribute not included");
+               return -1;
+       }
+
+       if (msg.oob_go_neg_channel[3] == 0 &&
+           msg.oob_go_neg_channel[4] == 0)
+               freq = 0;
+       else
+               freq = p2p_channel_to_freq(msg.oob_go_neg_channel[3],
+                                          msg.oob_go_neg_channel[4]);
+       if (freq < 0) {
+               p2p_dbg(p2p, "Unknown peer OOB GO Neg channel");
+               return -1;
+       }
+       role = msg.oob_go_neg_channel[5];
+
+       if (role == P2P_GO_IN_A_GROUP) {
+               p2p_dbg(p2p, "Peer OOB GO operating channel: %u MHz", freq);
+               params->go_freq = freq;
+       } else if (role == P2P_CLIENT_IN_A_GROUP) {
+               p2p_dbg(p2p, "Peer (client) OOB GO operating channel: %u MHz",
+                       freq);
+               params->go_freq = freq;
+       } else
+               p2p_dbg(p2p, "Peer OOB GO Neg channel: %u MHz", freq);
+       dev->oob_go_neg_freq = freq;
+
+       if (!params->sel && role != P2P_GO_IN_A_GROUP) {
+               freq = p2p_channel_to_freq(p2p->cfg->reg_class,
+                                          p2p->cfg->channel);
+               if (freq < 0) {
+                       p2p_dbg(p2p, "Own listen channel not known");
+                       return -1;
+               }
+               p2p_dbg(p2p, "Use own Listen channel as OOB GO Neg channel: %u MHz", freq);
+               dev->oob_go_neg_freq = freq;
+       }
+
+       if (msg.group_id) {
+               os_memcpy(params->go_dev_addr, msg.group_id, ETH_ALEN);
+               params->go_ssid_len = msg.group_id_len - ETH_ALEN;
+               os_memcpy(params->go_ssid, msg.group_id + ETH_ALEN,
+                         params->go_ssid_len);
+       }
+
+       if (dev->flags & P2P_DEV_USER_REJECTED) {
+               p2p_dbg(p2p, "Do not report rejected device");
+               p2p_parse_free(&msg);
+               return 0;
+       }
+
+       if (!(dev->flags & P2P_DEV_REPORTED)) {
+               p2p->cfg->dev_found(p2p->cfg->cb_ctx, p2p_dev_addr, &dev->info,
+                                   !(dev->flags & P2P_DEV_REPORTED_ONCE));
+               dev->flags |= P2P_DEV_REPORTED | P2P_DEV_REPORTED_ONCE;
+       }
+       p2p_parse_free(&msg);
+
+       if (role == P2P_GO_IN_A_GROUP && p2p->num_groups > 0)
+               params->next_step = BOTH_GO;
+       else if (role == P2P_GO_IN_A_GROUP)
+               params->next_step = JOIN_GROUP;
+       else if (role == P2P_CLIENT_IN_A_GROUP) {
+               dev->flags |= P2P_DEV_GROUP_CLIENT_ONLY;
+               params->next_step = PEER_CLIENT;
+       } else if (p2p->num_groups > 0)
+               params->next_step = AUTH_JOIN;
+       else if (params->sel)
+               params->next_step = INIT_GO_NEG;
+       else
+               params->next_step = RESP_GO_NEG;
+
+       return 0;
+}
+
+
+void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id,
+                                     int go_intent,
+                                     const u8 *own_interface_addr)
+{
+
+       p2p->authorized_oob_dev_pw_id = dev_pw_id;
+       if (dev_pw_id == 0) {
+               p2p_dbg(p2p, "NFC OOB Password unauthorized for static handover");
+               return;
+       }
+
+       p2p_dbg(p2p, "NFC OOB Password (id=%u) authorized for static handover",
+               dev_pw_id);
+
+       p2p->go_intent = go_intent;
+       os_memcpy(p2p->intended_addr, own_interface_addr, ETH_ALEN);
+}
+
+#endif /* CONFIG_WPS_NFC */
+
+
+int p2p_set_passphrase_len(struct p2p_data *p2p, unsigned int len)
+{
+       if (len < 8 || len > 63)
+               return -1;
+       p2p->cfg->passphrase_len = len;
+       return 0;
+}
+
+
+void p2p_set_vendor_elems(struct p2p_data *p2p, struct wpabuf **vendor_elem)
+{
+       p2p->vendor_elem = vendor_elem;
+}
+
+
+void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct p2p_data *p2p = eloop_ctx;
+
+       p2p_dbg(p2p,
+               "Timeout on waiting peer to become ready for GO Negotiation");
+       p2p_go_neg_failed(p2p, -1);
+}
old mode 100644 (file)
new mode 100755 (executable)
index 2be73ae..33d8959
@@ -9,6 +9,18 @@
 #ifndef P2P_H
 #define P2P_H
 
+#include "wps/wps_defs.h"
+
+/* P2P ASP Setup Capability */
+#define P2PS_SETUP_NONE 0
+#define P2PS_SETUP_NEW BIT(0)
+#define P2PS_SETUP_CLIENT BIT(1)
+#define P2PS_SETUP_GROUP_OWNER BIT(2)
+
+#define P2PS_WILD_HASH_STR "org.wi-fi.wfds"
+#define P2PS_HASH_LEN 6
+#define P2P_MAX_QUERY_HASH 6
+
 /**
  * P2P_MAX_REG_CLASSES - Maximum number of regulatory classes
  */
@@ -50,7 +62,8 @@ struct p2p_channels {
 };
 
 enum p2p_wps_method {
-       WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC
+       WPS_NOT_READY, WPS_PIN_DISPLAY, WPS_PIN_KEYPAD, WPS_PBC, WPS_NFC,
+       WPS_P2PS
 };
 
 /**
@@ -77,6 +90,8 @@ struct p2p_go_neg_results {
 
        int ht40;
 
+       int vht;
+
        /**
         * ssid - SSID of the group
         */
@@ -138,11 +153,99 @@ struct p2p_go_neg_results {
        unsigned int peer_config_timeout;
 };
 
+struct p2ps_provision {
+       /**
+        * status - Remote returned provisioning status code
+        */
+       int status;
+
+       /**
+        * adv_id - P2PS Advertisement ID
+        */
+       u32 adv_id;
+
+       /**
+        * session_id - P2PS Session ID
+        */
+       u32 session_id;
+
+       /**
+        * method - WPS Method (to be) used to establish session
+        */
+       u16 method;
+
+       /**
+        * conncap - Connection Capabilities negotiated between P2P peers
+        */
+       u8 conncap;
+
+       /**
+        * role - Info about the roles to be used for this connection
+        */
+       u8 role;
+
+       /**
+        * session_mac - MAC address of the peer that started the session
+        */
+       u8 session_mac[ETH_ALEN];
+
+       /**
+        * adv_mac - MAC address of the peer advertised the service
+        */
+       u8 adv_mac[ETH_ALEN];
+
+       /**
+        * info - Vendor defined extra Provisioning information
+        */
+       char info[0];
+};
+
+struct p2ps_advertisement {
+       struct p2ps_advertisement *next;
+
+       /**
+        * svc_info - Pointer to (internal) Service defined information
+        */
+       char *svc_info;
+
+       /**
+        * id - P2PS Advertisement ID
+        */
+       u32 id;
+
+       /**
+        * config_methods - WPS Methods which are allowed for this service
+        */
+       u16 config_methods;
+
+       /**
+        * state - Current state of the service: 0 - Out Of Service, 1-255 Vendor defined
+        */
+       u8 state;
+
+       /**
+        * auto_accept - Automatically Accept provisioning request if possible.
+        */
+       u8 auto_accept;
+
+       /**
+        * hash - 6 octet Service Name has to match against incoming Probe Requests
+        */
+       u8 hash[P2PS_HASH_LEN];
+
+       /**
+        * svc_name - NULL Terminated UTF-8 Service Name, and svc_info storage
+        */
+       char svc_name[0];
+};
+
+
 struct p2p_data;
 
 enum p2p_scan_type {
        P2P_SCAN_SOCIAL,
        P2P_SCAN_FULL,
+       P2P_SCAN_SPECIFIC,
        P2P_SCAN_SOCIAL_PLUS_ONE
 };
 
@@ -226,6 +329,19 @@ struct p2p_peer_info {
         * wfd_subelems - Wi-Fi Display subelements from WFD IE(s)
         */
        struct wpabuf *wfd_subelems;
+
+       /**
+        * vendor_elems - Unrecognized vendor elements
+        *
+        * This buffer includes any other vendor element than P2P, WPS, and WFD
+        * IE(s) from the frame that was used to discover the peer.
+        */
+       struct wpabuf *vendor_elems;
+
+       /**
+        * p2ps_instance - P2PS Application Service Info
+        */
+       struct wpabuf *p2ps_instance;
 };
 
 enum p2p_prov_disc_status {
@@ -233,6 +349,7 @@ enum p2p_prov_disc_status {
        P2P_PROV_DISC_TIMEOUT,
        P2P_PROV_DISC_REJECTED,
        P2P_PROV_DISC_TIMEOUT_JOIN,
+       P2P_PROV_DISC_INFO_UNAVAILABLE,
 };
 
 struct p2p_channel {
@@ -263,6 +380,12 @@ struct p2p_config {
        u8 channel;
 
        /**
+        * channel_forced - the listen channel was forced by configuration
+        *                  or by control interface and cannot be overridden
+        */
+       u8 channel_forced;
+
+       /**
         * Regulatory class for own operational channel
         */
        u8 op_reg_class;
@@ -287,6 +410,20 @@ struct p2p_config {
        struct p2p_channels channels;
 
        /**
+        * cli_channels - Additional client channels
+        *
+        * This list of channels (if any) will be used when advertising local
+        * channels during GO Negotiation or Invitation for the cases where the
+        * local end may become the client. This may allow the peer to become a
+        * GO on additional channels if it supports these options. The main use
+        * case for this is to include passive-scan channels on devices that may
+        * not know their current location and have configured most channels to
+        * not allow initiation of radition (i.e., another device needs to take
+        * master responsibilities).
+        */
+       struct p2p_channels cli_channels;
+
+       /**
         * num_pref_chan - Number of pref_chan entries
         */
        unsigned int num_pref_chan;
@@ -316,7 +453,7 @@ struct p2p_config {
         */
        size_t num_sec_dev_types;
 
-#ifdef TIZEN_EXT
+#ifdef BCM_DRIVER_V115
        /**
         * own_addr - Device Own MAC Address
         */
@@ -378,6 +515,14 @@ struct p2p_config {
        unsigned int max_listen;
 
        /**
+        * passphrase_len - Passphrase length (8..63)
+        *
+        * This parameter controls the length of the random passphrase that is
+        * generated at the GO.
+        */
+       unsigned int passphrase_len;
+
+       /**
         * cb_ctx - Context to use with callback functions
         */
        void *cb_ctx;
@@ -408,7 +553,8 @@ struct p2p_config {
         * operation to be completed. Type type argument specifies which type
         * of scan is to be done. @P2P_SCAN_SOCIAL indicates that only the
         * social channels (1, 6, 11) should be scanned. @P2P_SCAN_FULL
-        * indicates that all channels are to be scanned.
+        * indicates that all channels are to be scanned. @P2P_SCAN_SPECIFIC
+        * request a scan of a single channel specified by freq.
         * @P2P_SCAN_SOCIAL_PLUS_ONE request scan of all the social channels
         * plus one extra channel specified by freq.
         *
@@ -672,6 +818,9 @@ struct p2p_config {
         * @ctx: Callback context from cb_ctx
         * @peer: Source address of the response
         * @status: Cause of failure, will not be %P2P_PROV_DISC_SUCCESS
+        * @adv_id: If non-zero, then the adv_id of the PD Request
+        * @adv_mac: P2P Device Address of the advertizer
+        * @deferred_session_resp: Deferred session response sent by advertizer
         *
         * This callback is used to indicate either a failure or no response
         * to an earlier provision discovery request.
@@ -680,7 +829,9 @@ struct p2p_config {
         * is not used or failures do not need to be indicated.
         */
        void (*prov_disc_fail)(void *ctx, const u8 *peer,
-                              enum p2p_prov_disc_status status);
+                              enum p2p_prov_disc_status status,
+                              u32 adv_id, const u8 *adv_mac,
+                              const char *deferred_session_resp);
 
        /**
         * invitation_process - Optional callback for processing Invitations
@@ -697,6 +848,8 @@ struct p2p_config {
         *      persistent group (instead of invitation to join an active
         *      group)
         * @channels: Available operating channels for the group
+        * @dev_pw_id: Device Password ID for NFC static handover or -1 if not
+        *      used
         * Returns: Status code (P2P_SC_*)
         *
         * This optional callback can be used to implement persistent reconnect
@@ -718,7 +871,8 @@ struct p2p_config {
                                 const u8 *go_dev_addr, const u8 *ssid,
                                 size_t ssid_len, int *go, u8 *group_bssid,
                                 int *force_freq, int persistent_group,
-                                const struct p2p_channels *channels);
+                                const struct p2p_channels *channels,
+                                int dev_pw_id);
 
        /**
         * invitation_received - Callback on Invitation Request RX
@@ -749,6 +903,9 @@ struct p2p_config {
         * @bssid: P2P Group BSSID or %NULL if not received
         * @channels: Available operating channels for the group
         * @addr: Peer address
+        * @freq: Frequency (in MHz) indicated during invitation or 0
+        * @peer_oper_freq: Operating frequency (in MHz) advertized by the peer
+        * during invitation or 0
         *
         * This callback is used to indicate result of an Invitation procedure
         * started with a call to p2p_invite(). The indicated status code is
@@ -758,7 +915,7 @@ struct p2p_config {
         */
        void (*invitation_result)(void *ctx, int status, const u8 *bssid,
                                  const struct p2p_channels *channels,
-                                 const u8 *addr);
+                                 const u8 *addr, int freq, int peer_oper_freq);
 
        /**
         * go_connected - Check whether we are connected to a GO
@@ -768,6 +925,111 @@ struct p2p_config {
         * or 0 if not.
         */
        int (*go_connected)(void *ctx, const u8 *dev_addr);
+
+       /**
+        * presence_resp - Callback on Presence Response
+        * @ctx: Callback context from cb_ctx
+        * @src: Source address (GO's P2P Interface Address)
+        * @status: Result of the request (P2P_SC_*)
+        * @noa: Returned NoA value
+        * @noa_len: Length of the NoA buffer in octets
+        */
+       void (*presence_resp)(void *ctx, const u8 *src, u8 status,
+                             const u8 *noa, size_t noa_len);
+
+       /**
+        * is_concurrent_session_active - Check whether concurrent session is
+        * active on other virtual interfaces
+        * @ctx: Callback context from cb_ctx
+        * Returns: 1 if concurrent session is active on other virtual interface
+        * or 0 if not.
+        */
+       int (*is_concurrent_session_active)(void *ctx);
+
+       /**
+        * is_p2p_in_progress - Check whether P2P operation is in progress
+        * @ctx: Callback context from cb_ctx
+        * Returns: 1 if P2P operation (e.g., group formation) is in progress
+        * or 0 if not.
+        */
+       int (*is_p2p_in_progress)(void *ctx);
+
+       /**
+        * Determine if we have a persistent group we share with remote peer
+        * @ctx: Callback context from cb_ctx
+        * @addr: Peer device address to search for
+        * @ssid: Persistent group SSID or %NULL if any
+        * @ssid_len: Length of @ssid
+        * @go_dev_addr: Buffer for returning intended GO P2P Device Address
+        * @ret_ssid: Buffer for returning group SSID
+        * @ret_ssid_len: Buffer for returning length of @ssid
+        * Returns: 1 if a matching persistent group was found, 0 otherwise
+        */
+       int (*get_persistent_group)(void *ctx, const u8 *addr, const u8 *ssid,
+                                   size_t ssid_len, u8 *go_dev_addr,
+                                   u8 *ret_ssid, size_t *ret_ssid_len);
+
+       /**
+        * Get information about a possible local GO role
+        * @ctx: Callback context from cb_ctx
+        * @intended_addr: Buffer for returning intended GO interface address
+        * @ssid: Buffer for returning group SSID
+        * @ssid_len: Buffer for returning length of @ssid
+        * @group_iface: Buffer for returning whether a separate group interface
+        *      would be used
+        * Returns: 1 if GO info found, 0 otherwise
+        *
+        * This is used to compose New Group settings (SSID, and intended
+        * address) during P2PS provisioning if results of provisioning *might*
+        * result in our being an autonomous GO.
+        */
+       int (*get_go_info)(void *ctx, u8 *intended_addr,
+                          u8 *ssid, size_t *ssid_len, int *group_iface);
+
+       /**
+        * remove_stale_groups - Remove stale P2PS groups
+        *
+        * Because P2PS stages *potential* GOs, and remote devices can remove
+        * credentials unilaterally, we need to make sure we don't let stale
+        * unusable groups build up.
+        */
+       int (*remove_stale_groups)(void *ctx, const u8 *peer, const u8 *go,
+                                  const u8 *ssid, size_t ssid_len);
+
+       /**
+        * p2ps_prov_complete - P2PS provisioning complete
+        *
+        * When P2PS provisioning completes (successfully or not) we must
+        * transmit all of the results to the upper layers.
+        */
+       void (*p2ps_prov_complete)(void *ctx, u8 status, const u8 *dev,
+                                  const u8 *adv_mac, const u8 *ses_mac,
+                                  const u8 *grp_mac, u32 adv_id, u32 ses_id,
+                                  u8 conncap, int passwd_id,
+                                  const u8 *persist_ssid,
+                                  size_t persist_ssid_size, int response_done,
+                                  int prov_start, const char *session_info);
+
+       /**
+        * prov_disc_resp_cb - Callback for indicating completion of PD Response
+        * @ctx: Callback context from cb_ctx
+        * Returns: 1 if operation was started, 0 otherwise
+        *
+        * This callback can be used to perform any pending actions after
+        * provisioning. It is mainly used for P2PS pending group creation.
+        */
+       int (*prov_disc_resp_cb)(void *ctx);
+
+       /**
+        * p2ps_group_capability - Determine group capability
+        *
+        * This function can be used to determine group capability based on
+        * information from P2PS PD exchange and the current state of ongoing
+        * groups and driver capabilities.
+        *
+        * P2PS_SETUP_* bitmap is used as the parameters and return value.
+        */
+       u8 (*p2ps_group_capability)(void *ctx, u8 incoming, u8 role);
 };
 
 
@@ -874,12 +1136,26 @@ enum p2p_discovery_type {
  *     requested device types.
  * @dev_id: Device ID to search for or %NULL to find all devices
  * @search_delay: Extra delay in milliseconds between search iterations
+ * @seek_count: Number of ASP Service Strings in the seek_string array
+ * @seek_string: ASP Service Strings to query for in Probe Requests
+ * @freq: Requested first scan frequency (in MHz) to modify type ==
+ *     P2P_FIND_START_WITH_FULL behavior. 0 = Use normal full scan.
+ *     If p2p_find is already in progress, this parameter is ignored and full
+ *     scan will be executed.
  * Returns: 0 on success, -1 on failure
  */
 int p2p_find(struct p2p_data *p2p, unsigned int timeout,
             enum p2p_discovery_type type,
             unsigned int num_req_dev_types, const u8 *req_dev_types,
-            const u8 *dev_id, unsigned int search_delay);
+            const u8 *dev_id, unsigned int search_delay,
+            u8 seek_count, const char **seek_string, int freq);
+
+/**
+ * p2p_notify_scan_trigger_status - Indicate scan trigger status
+ * @p2p: P2P module context from p2p_init()
+ * @status: 0 on success, -1 on failure
+ */
+void p2p_notify_scan_trigger_status(struct p2p_data *p2p, int status);
 
 /**
  * p2p_stop_find - Stop P2P Find (Device Discovery)
@@ -911,6 +1187,12 @@ void p2p_stop_find_for_freq(struct p2p_data *p2p, int freq);
 int p2p_listen(struct p2p_data *p2p, unsigned int timeout);
 
 /**
+ * p2p_stop_listen - Stop P2P Listen
+ * @p2p: P2P module context from p2p_init()
+ */
+void p2p_stop_listen(struct p2p_data *p2p);
+
+/**
  * p2p_connect - Start P2P group formation (GO negotiation)
  * @p2p: P2P module context from p2p_init()
  * @peer_addr: MAC address of the peer P2P client
@@ -936,7 +1218,7 @@ int p2p_connect(struct p2p_data *p2p, const u8 *peer_addr,
                int go_intent, const u8 *own_interface_addr,
                unsigned int force_freq, int persistent_group,
                const u8 *force_ssid, size_t force_ssid_len,
-               int pd_before_go_neg, unsigned int pref_freq);
+               int pd_before_go_neg, unsigned int pref_freq, u16 oob_pw_id);
 
 /**
  * p2p_authorize - Authorize P2P group formation (GO negotiation)
@@ -964,7 +1246,7 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
                  int go_intent, const u8 *own_interface_addr,
                  unsigned int force_freq, int persistent_group,
                  const u8 *force_ssid, size_t force_ssid_len,
-                 unsigned int pref_freq);
+                 unsigned int pref_freq, u16 oob_pw_id);
 
 /**
  * p2p_reject - Reject peer device (explicitly block connection attempts)
@@ -974,14 +1256,11 @@ int p2p_authorize(struct p2p_data *p2p, const u8 *peer_addr,
  */
 int p2p_reject(struct p2p_data *p2p, const u8 *peer_addr);
 
-#ifdef TIZEN_EXT_P2P
-int p2p_reject_connection(struct p2p_data *p2p, const u8 *peer_addr);
-#endif
-
 /**
  * p2p_prov_disc_req - Send Provision Discovery Request
  * @p2p: P2P module context from p2p_init()
  * @peer_addr: MAC address of the peer P2P client
+ * @p2ps_prov: Provisioning info for P2PS
  * @config_methods: WPS Config Methods value (only one bit set)
  * @join: Whether this is used by a client joining an active group
  * @force_freq: Forced TX frequency for the frame (mainly for the join case)
@@ -997,7 +1276,8 @@ int p2p_reject_connection(struct p2p_data *p2p, const u8 *peer_addr);
  * indicated with the p2p_config::prov_disc_resp() callback.
  */
 int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
-                     u16 config_methods, int join, int force_freq,
+                     struct p2ps_provision *p2ps_prov, u16 config_methods,
+                     int join, int force_freq,
                      int user_initiated_pd);
 
 /**
@@ -1070,12 +1350,14 @@ enum p2p_invite_role {
  * @persistent_group: Whether this is to reinvoke a persistent group
  * @pref_freq: Preferred operating frequency in MHz or 0 (this is only used if
  *     force_freq == 0)
+ * @dev_pw_id: Device Password ID from OOB Device Password (NFC) static handover
+ *     case or -1 if not used
  * Returns: 0 on success, -1 on failure
  */
 int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
               const u8 *bssid, const u8 *ssid, size_t ssid_len,
               unsigned int force_freq, const u8 *go_dev_addr,
-              int persistent_group, unsigned int pref_freq);
+              int persistent_group, unsigned int pref_freq, int dev_pw_id);
 
 /**
  * p2p_presence_req - Request GO presence
@@ -1233,7 +1515,7 @@ void p2p_rx_action(struct p2p_data *p2p, const u8 *da, const u8 *sa,
  * start of a pending operation, e.g., to start a pending GO negotiation.
  */
 int p2p_scan_res_handler(struct p2p_data *p2p, const u8 *bssid, int freq,
-                        struct os_time *rx_time, int level, const u8 *ies,
+                        struct os_reltime *rx_time, int level, const u8 *ies,
                         size_t ies_len);
 
 /**
@@ -1340,6 +1622,11 @@ struct p2p_group_config {
        size_t ssid_len;
 
        /**
+        * freq - Operating channel of the group
+        */
+       int freq;
+
+       /**
         * cb_ctx - Context to use with callback functions
         */
        void *cb_ctx;
@@ -1615,10 +1902,30 @@ void p2p_set_client_discoverability(struct p2p_data *p2p, int enabled);
  */
 void p2p_set_managed_oper(struct p2p_data *p2p, int enabled);
 
-int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel);
+/**
+ * p2p_config_get_random_social - Return a random social channel
+ * @p2p: P2P config
+ * @op_class: Selected operating class
+ * @op_channel: Selected social channel
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used before p2p_init is called. A random social channel
+ * from supports bands 2.4 GHz (channels 1,6,11) and 60 GHz (channel 2) is
+ * returned on success.
+ */
+int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class,
+                                u8 *op_channel);
+
+int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel,
+                          u8 forced);
+
+u8 p2p_get_listen_channel(struct p2p_data *p2p);
 
 int p2p_set_ssid_postfix(struct p2p_data *p2p, const u8 *postfix, size_t len);
 
+int p2p_get_member_in_go_dev(struct p2p_data *p2p, const u8 *dev_addr,
+                          u8 *member_in_go_dev);
+
 int p2p_get_interface_addr(struct p2p_data *p2p, const u8 *dev_addr,
                           u8 *iface_addr);
 int p2p_get_dev_addr(struct p2p_data *p2p, const u8 *iface_addr,
@@ -1645,6 +1952,9 @@ void p2p_set_intra_bss_dist(struct p2p_data *p2p, int enabled);
 int p2p_channels_includes_freq(const struct p2p_channels *channels,
                               unsigned int freq);
 
+int p2p_channels_to_freqs(const struct p2p_channels *channels,
+                         int *freq_list, unsigned int max_len);
+
 /**
  * p2p_supported_freq - Check whether channel is supported for P2P
  * @p2p: P2P module context from p2p_init()
@@ -1654,6 +1964,22 @@ int p2p_channels_includes_freq(const struct p2p_channels *channels,
 int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq);
 
 /**
+ * p2p_supported_freq_go - Check whether channel is supported for P2P GO operation
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Channel frequency in MHz
+ * Returns: 0 if channel not usable for P2P, 1 if usable for P2P
+ */
+int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq);
+
+/**
+ * p2p_supported_freq_cli - Check whether channel is supported for P2P client operation
+ * @p2p: P2P module context from p2p_init()
+ * @freq: Channel frequency in MHz
+ * Returns: 0 if channel not usable for P2P, 1 if usable for P2P
+ */
+int p2p_supported_freq_cli(struct p2p_data *p2p, unsigned int freq);
+
+/**
  * p2p_get_pref_freq - Get channel from preferred channel list
  * @p2p: P2P module context from p2p_init()
  * @channels: List of channels
@@ -1662,7 +1988,9 @@ int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq);
 unsigned int p2p_get_pref_freq(struct p2p_data *p2p,
                               const struct p2p_channels *channels);
 
-void p2p_update_channel_list(struct p2p_data *p2p, struct p2p_channels *chan);
+void p2p_update_channel_list(struct p2p_data *p2p,
+                            const struct p2p_channels *chan,
+                            const struct p2p_channels *cli_chan);
 
 /**
  * p2p_set_best_channels - Update best channel information
@@ -1695,11 +2023,18 @@ const u8 * p2p_get_go_neg_peer(struct p2p_data *p2p);
 unsigned int p2p_get_group_num_members(struct p2p_group *group);
 
 /**
+ * p2p_client_limit_reached - Check if client limit is reached
+ * @group: P2P group context from p2p_group_init()
+ * Returns: 1 if no of clients limit reached
+ */
+int p2p_client_limit_reached(struct p2p_group *group);
+
+/**
  * p2p_iterate_group_members - Iterate group members
  * @group: P2P group context from p2p_group_init()
  * @next: iteration pointer, must be a pointer to a void * that is set to %NULL
  *     on the first call and not modified later
- * Returns: A P2P Interface Address for each call and %NULL for no more members
+ * Returns: A P2P Device Address for each call and %NULL for no more members
  */
 const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next);
 
@@ -1721,6 +2056,26 @@ const u8 * p2p_group_get_dev_addr(struct p2p_group *group, const u8 *addr);
 int p2p_group_is_client_connected(struct p2p_group *group, const u8 *dev_addr);
 
 /**
+ * p2p_group_get_config - Get the group configuration
+ * @group: P2P group context from p2p_group_init()
+ * Returns: The group configuration pointer
+ */
+const struct p2p_group_config * p2p_group_get_config(struct p2p_group *group);
+
+/**
+ * p2p_loop_on_all_groups - Run the given callback on all groups
+ * @p2p: P2P module context from p2p_init()
+ * @group_callback: The callback function pointer
+ * @user_data: Some user data pointer which can be %NULL
+ *
+ * The group_callback function can stop the iteration by returning 0.
+ */
+void p2p_loop_on_all_groups(struct p2p_data *p2p,
+                           int (*group_callback)(struct p2p_group *group,
+                                                 void *user_data),
+                           void *user_data);
+
+/**
  * p2p_get_peer_found - Get P2P peer info structure of a found peer
  * @p2p: P2P module context from p2p_init()
  * @addr: P2P Device Address of the peer or %NULL to indicate the first peer
@@ -1770,18 +2125,21 @@ int p2p_set_pref_chan(struct p2p_data *p2p, unsigned int num_pref_chan,
                      const struct p2p_channel *pref_chan);
 
 /**
- * p2p_in_progress - Check whether a P2P operation is progress
+ * p2p_set_no_go_freq - Set no GO channel ranges
  * @p2p: P2P module context from p2p_init()
- * Returns: 0 if P2P module is idle or 1 if an operation is in progress
+ * @list: Channel ranges or %NULL to remove restriction
+ * Returns: 0 on success, -1 on failure
  */
-int p2p_in_progress(struct p2p_data *p2p);
+int p2p_set_no_go_freq(struct p2p_data *p2p,
+                      const struct wpa_freq_range_list *list);
 
 /**
- * p2p_other_scan_completed - Notify completion of non-P2P scan
+ * p2p_in_progress - Check whether a P2P operation is progress
  * @p2p: P2P module context from p2p_init()
- * Returns: 0 if P2P module is idle or 1 if an operation was started
+ * Returns: 0 if P2P module is idle, 1 if an operation is in progress but not
+ * in search state, or 2 if search state operation is in progress
  */
-int p2p_other_scan_completed(struct p2p_data *p2p);
+int p2p_in_progress(struct p2p_data *p2p);
 
 const char * p2p_wps_method_text(enum p2p_wps_method method);
 
@@ -1794,8 +2152,6 @@ const char * p2p_wps_method_text(enum p2p_wps_method method);
 void p2p_set_config_timeout(struct p2p_data *p2p, u8 go_timeout,
                            u8 client_timeout);
 
-void p2p_increase_search_delay(struct p2p_data *p2p, unsigned int delay);
-
 int p2p_set_wfd_ie_beacon(struct p2p_data *p2p, struct wpabuf *ie);
 int p2p_set_wfd_ie_probe_req(struct p2p_data *p2p, struct wpabuf *ie);
 int p2p_set_wfd_ie_probe_resp(struct p2p_data *p2p, struct wpabuf *ie);
@@ -1831,4 +2187,71 @@ struct wpabuf * wifi_display_encaps(struct wpabuf *subelems);
 int p2p_set_disc_int(struct p2p_data *p2p, int min_disc_int, int max_disc_int,
                     int max_disc_tu);
 
+/**
+ * p2p_get_state_txt - Get current P2P state for debug purposes
+ * @p2p: P2P module context from p2p_init()
+ * Returns: Name of the current P2P module state
+ *
+ * It should be noted that the P2P module state names are internal information
+ * and subject to change at any point, i.e., this information should be used
+ * mainly for debugging purposes.
+ */
+const char * p2p_get_state_txt(struct p2p_data *p2p);
+
+struct wpabuf * p2p_build_nfc_handover_req(struct p2p_data *p2p,
+                                          int client_freq,
+                                          const u8 *go_dev_addr,
+                                          const u8 *ssid, size_t ssid_len);
+struct wpabuf * p2p_build_nfc_handover_sel(struct p2p_data *p2p,
+                                          int client_freq,
+                                          const u8 *go_dev_addr,
+                                          const u8 *ssid, size_t ssid_len);
+
+struct p2p_nfc_params {
+       int sel;
+       const u8 *wsc_attr;
+       size_t wsc_len;
+       const u8 *p2p_attr;
+       size_t p2p_len;
+
+       enum {
+               NO_ACTION, JOIN_GROUP, AUTH_JOIN, INIT_GO_NEG, RESP_GO_NEG,
+               BOTH_GO, PEER_CLIENT
+       } next_step;
+       struct p2p_peer_info *peer;
+       u8 oob_dev_pw[WPS_OOB_PUBKEY_HASH_LEN + 2 +
+                     WPS_OOB_DEVICE_PASSWORD_LEN];
+       size_t oob_dev_pw_len;
+       int go_freq;
+       u8 go_dev_addr[ETH_ALEN];
+       u8 go_ssid[32];
+       size_t go_ssid_len;
+};
+
+int p2p_process_nfc_connection_handover(struct p2p_data *p2p,
+                                       struct p2p_nfc_params *params);
+
+void p2p_set_authorized_oob_dev_pw_id(struct p2p_data *p2p, u16 dev_pw_id,
+                                     int go_intent,
+                                     const u8 *own_interface_addr);
+
+int p2p_set_passphrase_len(struct p2p_data *p2p, unsigned int len);
+
+void p2p_loop_on_known_peers(struct p2p_data *p2p,
+                            void (*peer_callback)(struct p2p_peer_info *peer,
+                                                  void *user_data),
+                            void *user_data);
+
+void p2p_set_vendor_elems(struct p2p_data *p2p, struct wpabuf **vendor_elem);
+
+void p2p_set_intended_addr(struct p2p_data *p2p, const u8 *intended_addr);
+
+struct p2ps_advertisement *
+p2p_service_p2ps_id(struct p2p_data *p2p, u32 adv_id);
+int p2p_service_add_asp(struct p2p_data *p2p, int auto_accept, u32 adv_id,
+                       const char *adv_str, u8 svc_state,
+                       u16 config_methods, const char *svc_info);
+int p2p_service_del_asp(struct p2p_data *p2p, u32 adv_id);
+struct p2ps_advertisement * p2p_get_p2ps_adv_list(struct p2p_data *p2p);
+
 #endif /* P2P_H */
old mode 100644 (file)
new mode 100755 (executable)
index 5838d35..92c9206
@@ -17,8 +17,7 @@
 void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token)
 {
        wpabuf_put_u8(buf, WLAN_ACTION_VENDOR_SPECIFIC);
-       wpabuf_put_be24(buf, OUI_WFA);
-       wpabuf_put_u8(buf, P2P_OUI_TYPE);
+       wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
 
        wpabuf_put_u8(buf, subtype); /* OUI Subtype */
        wpabuf_put_u8(buf, dialog_token);
@@ -31,8 +30,7 @@ void p2p_buf_add_public_action_hdr(struct wpabuf *buf, u8 subtype,
 {
        wpabuf_put_u8(buf, WLAN_ACTION_PUBLIC);
        wpabuf_put_u8(buf, WLAN_PA_VENDOR_SPECIFIC);
-       wpabuf_put_be24(buf, OUI_WFA);
-       wpabuf_put_u8(buf, P2P_OUI_TYPE);
+       wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
 
        wpabuf_put_u8(buf, subtype); /* OUI Subtype */
        wpabuf_put_u8(buf, dialog_token);
@@ -47,8 +45,7 @@ u8 * p2p_buf_add_ie_hdr(struct wpabuf *buf)
        /* P2P IE header */
        wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
        len = wpabuf_put(buf, 1); /* IE length to be filled */
-       wpabuf_put_be24(buf, OUI_WFA);
-       wpabuf_put_u8(buf, P2P_OUI_TYPE);
+       wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
        wpa_printf(MSG_DEBUG, "P2P: * P2P IE header");
        return len;
 }
@@ -167,15 +164,18 @@ void p2p_buf_add_device_info(struct wpabuf *buf, struct p2p_data *p2p,
                if (peer->wps_method == WPS_PBC)
                        methods |= WPS_CONFIG_PUSHBUTTON;
                else if (peer->wps_method == WPS_PIN_DISPLAY ||
-                        peer->wps_method == WPS_PIN_KEYPAD)
+                        peer->wps_method == WPS_PIN_KEYPAD) {
                        methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+                       methods |= WPS_CONFIG_P2PS;
+               }
        } else if (p2p->cfg->config_methods) {
                methods |= p2p->cfg->config_methods &
                        (WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_DISPLAY |
-                        WPS_CONFIG_KEYPAD);
+                        WPS_CONFIG_KEYPAD | WPS_CONFIG_P2PS);
        } else {
                methods |= WPS_CONFIG_PUSHBUTTON;
                methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
+               methods |= WPS_CONFIG_P2PS;
        }
        wpabuf_put_be16(buf, methods);
 
@@ -258,6 +258,7 @@ void p2p_buf_add_group_id(struct wpabuf *buf, const u8 *dev_addr,
        wpabuf_put_data(buf, ssid, ssid_len);
        wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR,
                   MAC2STR(dev_addr));
+       wpa_hexdump_ascii(MSG_DEBUG, "P2P: P2P Group ID SSID", ssid, ssid_len);
 }
 
 
@@ -327,13 +328,282 @@ void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p)
 }
 
 
-static void p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
-                              const char *val)
+void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country,
+                                   u8 oper_class, u8 channel,
+                                   enum p2p_role_indication role)
+{
+       /* OOB Group Owner Negotiation Channel */
+       wpabuf_put_u8(buf, P2P_ATTR_OOB_GO_NEG_CHANNEL);
+       wpabuf_put_le16(buf, 6);
+       wpabuf_put_data(buf, country, 3);
+       wpabuf_put_u8(buf, oper_class); /* Operating Class */
+       wpabuf_put_u8(buf, channel); /* Channel Number */
+       wpabuf_put_u8(buf, (u8) role); /* Role indication */
+       wpa_printf(MSG_DEBUG, "P2P: * OOB GO Negotiation Channel: Operating "
+                  "Class %u Channel %u Role %d",
+                  oper_class, channel, role);
+}
+
+
+void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p)
+{
+       if (!p2p)
+               return;
+
+       /* Service Hash */
+       wpabuf_put_u8(buf, P2P_ATTR_SERVICE_HASH);
+       wpabuf_put_le16(buf, p2p->p2ps_seek_count * P2PS_HASH_LEN);
+       wpabuf_put_data(buf, p2p->query_hash,
+                       p2p->p2ps_seek_count * P2PS_HASH_LEN);
+       wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash",
+                   p2p->query_hash, p2p->p2ps_seek_count * P2PS_HASH_LEN);
+}
+
+
+void p2p_buf_add_session_info(struct wpabuf *buf, const char *info)
+{
+       size_t info_len = 0;
+
+       if (info && info[0])
+               info_len = os_strlen(info);
+
+       /* Session Information Data Info */
+       wpabuf_put_u8(buf, P2P_ATTR_SESSION_INFORMATION_DATA);
+       wpabuf_put_le16(buf, (u16) info_len);
+
+       if (info) {
+               wpabuf_put_data(buf, info, info_len);
+               wpa_printf(MSG_DEBUG, "P2P: * Session Info Data (%s)", info);
+       }
+}
+
+
+void p2p_buf_add_connection_capability(struct wpabuf *buf, u8 connection_cap)
+{
+       /* Connection Capability Info */
+       wpabuf_put_u8(buf, P2P_ATTR_CONNECTION_CAPABILITY);
+       wpabuf_put_le16(buf, 1);
+       wpabuf_put_u8(buf, connection_cap);
+       wpa_printf(MSG_DEBUG, "P2P: * Connection Capability: 0x%x",
+                  connection_cap);
+}
+
+
+void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac)
+{
+       if (!buf || !mac)
+               return;
+
+       /* Advertisement ID Info */
+       wpabuf_put_u8(buf, P2P_ATTR_ADVERTISEMENT_ID);
+       wpabuf_put_le16(buf, (u16) (sizeof(u32) + ETH_ALEN));
+       wpabuf_put_le32(buf, id);
+       wpabuf_put_data(buf, mac, ETH_ALEN);
+       wpa_printf(MSG_DEBUG, "P2P: * Advertisement ID (%x) " MACSTR,
+                  id, MAC2STR(mac));
+}
+
+
+void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p,
+                                 u8 hash_count, const u8 *hash,
+                                 struct p2ps_advertisement *adv_list)
+{
+       struct p2ps_advertisement *adv;
+       struct wpabuf *tmp_buf;
+       u8 *tag_len = NULL, *ie_len = NULL;
+       size_t svc_len = 0, remaining = 0, total_len = 0;
+
+       if (!adv_list || !hash)
+               return;
+
+       /* Allocate temp buffer, allowing for overflow of 1 instance */
+       tmp_buf = wpabuf_alloc(MAX_SVC_ADV_IE_LEN + 256 + P2PS_HASH_LEN);
+       if (!tmp_buf)
+               return;
+
+       for (adv = adv_list; adv && total_len <= MAX_SVC_ADV_LEN;
+            adv = adv->next) {
+               u8 count = hash_count;
+               const u8 *test = hash;
+
+               while (count--) {
+                       /* Check for wildcard */
+                       if (os_memcmp(test, p2p->wild_card_hash,
+                                     P2PS_HASH_LEN) == 0) {
+                               total_len = MAX_SVC_ADV_LEN + 1;
+                               goto wild_hash;
+                       }
+
+                       if (os_memcmp(test, adv->hash, P2PS_HASH_LEN) == 0)
+                               goto hash_match;
+
+                       test += P2PS_HASH_LEN;
+               }
+
+               /* No matches found - Skip this Adv Instance */
+               continue;
+
+hash_match:
+               if (!tag_len) {
+                       tag_len = p2p_buf_add_ie_hdr(tmp_buf);
+                       remaining = 255 - 4;
+                       if (!ie_len) {
+                               wpabuf_put_u8(tmp_buf,
+                                             P2P_ATTR_ADVERTISED_SERVICE);
+                               ie_len = wpabuf_put(tmp_buf, sizeof(u16));
+                               remaining -= (sizeof(u8) + sizeof(u16));
+                       }
+               }
+
+               svc_len = os_strlen(adv->svc_name);
+
+               if (7 + svc_len + total_len > MAX_SVC_ADV_LEN) {
+                       /* Can't fit... return wildcard */
+                       total_len = MAX_SVC_ADV_LEN + 1;
+                       break;
+               }
+
+               if (remaining <= (sizeof(adv->id) +
+                                 sizeof(adv->config_methods))) {
+                       size_t front = remaining;
+                       size_t back = (sizeof(adv->id) +
+                                      sizeof(adv->config_methods)) - front;
+                       u8 holder[sizeof(adv->id) +
+                                 sizeof(adv->config_methods)];
+
+                       /* This works even if front or back == 0 */
+                       WPA_PUT_LE32(holder, adv->id);
+                       WPA_PUT_BE16(&holder[sizeof(adv->id)],
+                                    adv->config_methods);
+                       wpabuf_put_data(tmp_buf, holder, front);
+                       p2p_buf_update_ie_hdr(tmp_buf, tag_len);
+                       tag_len = p2p_buf_add_ie_hdr(tmp_buf);
+                       wpabuf_put_data(tmp_buf, &holder[front], back);
+                       remaining = 255 - (sizeof(adv->id) +
+                                          sizeof(adv->config_methods)) - back;
+               } else {
+                       wpabuf_put_le32(tmp_buf, adv->id);
+                       wpabuf_put_be16(tmp_buf, adv->config_methods);
+                       remaining -= (sizeof(adv->id) +
+                                     sizeof(adv->config_methods));
+               }
+
+               /* We are guaranteed at least one byte for svc_len */
+               wpabuf_put_u8(tmp_buf, svc_len);
+               remaining -= sizeof(u8);
+
+               if (remaining < svc_len) {
+                       size_t front = remaining;
+                       size_t back = svc_len - front;
+
+                       wpabuf_put_data(tmp_buf, adv->svc_name, front);
+                       p2p_buf_update_ie_hdr(tmp_buf, tag_len);
+                       tag_len = p2p_buf_add_ie_hdr(tmp_buf);
+
+                       /* In rare cases, we must split across 3 attributes */
+                       if (back > 255 - 4) {
+                               wpabuf_put_data(tmp_buf,
+                                               &adv->svc_name[front], 255 - 4);
+                               back -= 255 - 4;
+                               front += 255 - 4;
+                               p2p_buf_update_ie_hdr(tmp_buf, tag_len);
+                               tag_len = p2p_buf_add_ie_hdr(tmp_buf);
+                       }
+
+                       wpabuf_put_data(tmp_buf, &adv->svc_name[front], back);
+                       remaining = 255 - 4 - back;
+               } else {
+                       wpabuf_put_data(tmp_buf, adv->svc_name, svc_len);
+                       remaining -= svc_len;
+               }
+
+               /*           adv_id      config_methods     svc_string */
+               total_len += sizeof(u32) + sizeof(u16) + sizeof(u8) + svc_len;
+       }
+
+       if (tag_len)
+               p2p_buf_update_ie_hdr(tmp_buf, tag_len);
+
+       if (ie_len)
+               WPA_PUT_LE16(ie_len, (u16) total_len);
+
+wild_hash:
+       /* If all fit, return matching instances, otherwise the wildcard */
+       if (total_len <= MAX_SVC_ADV_LEN) {
+               wpabuf_put_buf(buf, tmp_buf);
+       } else {
+               char *wild_card = P2PS_WILD_HASH_STR;
+               u8 wild_len;
+
+               /* Insert wildcard instance */
+               tag_len = p2p_buf_add_ie_hdr(buf);
+               wpabuf_put_u8(buf, P2P_ATTR_ADVERTISED_SERVICE);
+               ie_len = wpabuf_put(buf, sizeof(u16));
+
+               wild_len = (u8) os_strlen(wild_card);
+               wpabuf_put_le32(buf, 0);
+               wpabuf_put_be16(buf, 0);
+               wpabuf_put_u8(buf, wild_len);
+               wpabuf_put_data(buf, wild_card, wild_len);
+
+               WPA_PUT_LE16(ie_len, 4 + 2 + 1 + wild_len);
+               p2p_buf_update_ie_hdr(buf, tag_len);
+       }
+
+       wpabuf_free(tmp_buf);
+}
+
+
+void p2p_buf_add_session_id(struct wpabuf *buf, u32 id, const u8 *mac)
+{
+       if (!buf || !mac)
+               return;
+
+       /* Session ID Info */
+       wpabuf_put_u8(buf, P2P_ATTR_SESSION_ID);
+       wpabuf_put_le16(buf, (u16) (sizeof(u32) + ETH_ALEN));
+       wpabuf_put_le32(buf, id);
+       wpabuf_put_data(buf, mac, ETH_ALEN);
+       wpa_printf(MSG_DEBUG, "P2P: * Session ID Info (%x) " MACSTR,
+                  id, MAC2STR(mac));
+}
+
+
+void p2p_buf_add_feature_capability(struct wpabuf *buf, u16 len, const u8 *mask)
+{
+       if (!buf || !len || !mask)
+               return;
+
+       /* Feature Capability */
+       wpabuf_put_u8(buf, P2P_ATTR_FEATURE_CAPABILITY);
+       wpabuf_put_le16(buf, len);
+       wpabuf_put_data(buf, mask, len);
+       wpa_printf(MSG_DEBUG, "P2P: * Feature Capability (%d)", len);
+}
+
+
+void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr,
+                                      const u8 *ssid, size_t ssid_len)
+{
+       /* P2P Group ID */
+       wpabuf_put_u8(buf, P2P_ATTR_PERSISTENT_GROUP);
+       wpabuf_put_le16(buf, ETH_ALEN + ssid_len);
+       wpabuf_put_data(buf, dev_addr, ETH_ALEN);
+       wpabuf_put_data(buf, ssid, ssid_len);
+       wpa_printf(MSG_DEBUG, "P2P: * P2P Group ID " MACSTR,
+                  MAC2STR(dev_addr));
+}
+
+
+static int p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
+                             const char *val)
 {
        size_t len;
 
-       wpabuf_put_be16(buf, attr);
        len = val ? os_strlen(val) : 0;
+       if (wpabuf_tailroom(buf) < 4 + len)
+               return -1;
+       wpabuf_put_be16(buf, attr);
 #ifndef CONFIG_WPS_STRICT
        if (len == 0) {
                /*
@@ -341,36 +611,46 @@ static void p2p_add_wps_string(struct wpabuf *buf, enum wps_attribute attr,
                 * attributes. As a workaround, send a space character if the
                 * device attribute string is empty.
                 */
+               if (wpabuf_tailroom(buf) < 3)
+                       return -1;
                wpabuf_put_be16(buf, 1);
                wpabuf_put_u8(buf, ' ');
-               return;
+               return 0;
        }
 #endif /* CONFIG_WPS_STRICT */
        wpabuf_put_be16(buf, len);
        if (val)
                wpabuf_put_data(buf, val, len);
+       return 0;
 }
 
 
-void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
-                     int all_attr)
+int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
+                    int all_attr)
 {
        u8 *len;
        int i;
 
+       if (wpabuf_tailroom(buf) < 6)
+               return -1;
        wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
        len = wpabuf_put(buf, 1);
        wpabuf_put_be32(buf, WPS_DEV_OUI_WFA);
 
-       wps_build_version(buf);
+       if (wps_build_version(buf) < 0)
+               return -1;
 
        if (all_attr) {
+               if (wpabuf_tailroom(buf) < 5)
+                       return -1;
                wpabuf_put_be16(buf, ATTR_WPS_STATE);
                wpabuf_put_be16(buf, 1);
                wpabuf_put_u8(buf, WPS_STATE_NOT_CONFIGURED);
        }
 
        if (pw_id >= 0) {
+               if (wpabuf_tailroom(buf) < 6)
+                       return -1;
                /* Device Password ID */
                wpabuf_put_be16(buf, ATTR_DEV_PASSWORD_ID);
                wpabuf_put_be16(buf, 2);
@@ -380,33 +660,47 @@ void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
        }
 
        if (all_attr) {
+               if (wpabuf_tailroom(buf) < 5)
+                       return -1;
                wpabuf_put_be16(buf, ATTR_RESPONSE_TYPE);
                wpabuf_put_be16(buf, 1);
                wpabuf_put_u8(buf, WPS_RESP_ENROLLEE_INFO);
 
-               wps_build_uuid_e(buf, p2p->cfg->uuid);
-               p2p_add_wps_string(buf, ATTR_MANUFACTURER,
-                                  p2p->cfg->manufacturer);
-               p2p_add_wps_string(buf, ATTR_MODEL_NAME, p2p->cfg->model_name);
-               p2p_add_wps_string(buf, ATTR_MODEL_NUMBER,
-                                  p2p->cfg->model_number);
-               p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER,
-                                  p2p->cfg->serial_number);
-
+               if (wps_build_uuid_e(buf, p2p->cfg->uuid) < 0 ||
+                   p2p_add_wps_string(buf, ATTR_MANUFACTURER,
+                                      p2p->cfg->manufacturer) < 0 ||
+                   p2p_add_wps_string(buf, ATTR_MODEL_NAME,
+                                      p2p->cfg->model_name) < 0 ||
+                   p2p_add_wps_string(buf, ATTR_MODEL_NUMBER,
+                                      p2p->cfg->model_number) < 0 ||
+                   p2p_add_wps_string(buf, ATTR_SERIAL_NUMBER,
+                                      p2p->cfg->serial_number) < 0)
+                       return -1;
+
+               if (wpabuf_tailroom(buf) < 4 + WPS_DEV_TYPE_LEN)
+                       return -1;
                wpabuf_put_be16(buf, ATTR_PRIMARY_DEV_TYPE);
                wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN);
                wpabuf_put_data(buf, p2p->cfg->pri_dev_type, WPS_DEV_TYPE_LEN);
 
-               p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name);
+               if (p2p_add_wps_string(buf, ATTR_DEV_NAME, p2p->cfg->dev_name)
+                   < 0)
+                       return -1;
 
+               if (wpabuf_tailroom(buf) < 6)
+                       return -1;
                wpabuf_put_be16(buf, ATTR_CONFIG_METHODS);
                wpabuf_put_be16(buf, 2);
                wpabuf_put_be16(buf, p2p->cfg->config_methods);
        }
 
-       wps_build_wfa_ext(buf, 0, NULL, 0);
+       if (wps_build_wfa_ext(buf, 0, NULL, 0) < 0)
+               return -1;
 
        if (all_attr && p2p->cfg->num_sec_dev_types) {
+               if (wpabuf_tailroom(buf) <
+                   4 + WPS_DEV_TYPE_LEN * p2p->cfg->num_sec_dev_types)
+                       return -1;
                wpabuf_put_be16(buf, ATTR_SECONDARY_DEV_TYPE_LIST);
                wpabuf_put_be16(buf, WPS_DEV_TYPE_LEN *
                                p2p->cfg->num_sec_dev_types);
@@ -428,4 +722,6 @@ void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
        }
 
        p2p_buf_update_ie_hdr(buf, len);
+
+       return 0;
 }
old mode 100644 (file)
new mode 100755 (executable)
index 76d01cf..86bae1a
@@ -68,6 +68,7 @@ int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev)
 {
        struct p2p_device *go;
        struct wpabuf *req;
+       unsigned int wait_time;
 
        go = p2p_get_device(p2p, dev->member_in_go_dev);
        if (go == NULL || dev->oper_freq <= 0) {
@@ -88,9 +89,12 @@ int p2p_send_dev_disc_req(struct p2p_data *p2p, struct p2p_device *dev)
        os_memcpy(p2p->pending_client_disc_addr, dev->info.p2p_device_addr,
                  ETH_ALEN);
        p2p->pending_action_state = P2P_PENDING_DEV_DISC_REQUEST;
+       wait_time = 1000;
+       if (p2p->cfg->max_listen && wait_time > p2p->cfg->max_listen)
+               wait_time = p2p->cfg->max_listen;
        if (p2p_send_action(p2p, dev->oper_freq, go->info.p2p_device_addr,
                            p2p->cfg->dev_addr, go->info.p2p_device_addr,
-                           wpabuf_head(req), wpabuf_len(req), 1000) < 0) {
+                           wpabuf_head(req), wpabuf_len(req), wait_time) < 0) {
                p2p_dbg(p2p, "Failed to send Action frame");
                wpabuf_free(req);
                /* TODO: how to recover from failure? */
old mode 100644 (file)
new mode 100755 (executable)
index 53914b1..fd7bcae
@@ -9,7 +9,9 @@
 #include "includes.h"
 
 #include "common.h"
+#include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
 #include "wps/wps_defs.h"
 #include "p2p_i.h"
 #include "p2p.h"
@@ -103,6 +105,10 @@ u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method)
                return DEV_PW_USER_SPECIFIED;
        case WPS_PBC:
                return DEV_PW_PUSHBUTTON;
+       case WPS_NFC:
+               return DEV_PW_NFC_CONNECTION_HANDOVER;
+       case WPS_P2PS:
+               return DEV_PW_P2PS_DEFAULT;
        default:
                return DEV_PW_DEFAULT;
        }
@@ -118,6 +124,10 @@ static const char * p2p_wps_method_str(enum p2p_wps_method wps_method)
                return "Keypad";
        case WPS_PBC:
                return "PBC";
+       case WPS_NFC:
+               return "NFC";
+       case WPS_P2PS:
+               return "P2PS";
        default:
                return "??";
        }
@@ -131,12 +141,16 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
        u8 *len;
        u8 group_capab;
        size_t extra = 0;
+       u16 pw_id;
 
 #ifdef CONFIG_WIFI_DISPLAY
        if (p2p->wfd_ie_go_neg)
                extra = wpabuf_len(p2p->wfd_ie_go_neg);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+       if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ])
+               extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]);
+
        buf = wpabuf_alloc(1000 + extra);
        if (buf == NULL)
                return NULL;
@@ -172,13 +186,23 @@ static struct wpabuf * p2p_build_go_neg_req(struct p2p_data *p2p,
        p2p_buf_update_ie_hdr(buf, len);
 
        /* WPS IE with Device Password ID attribute */
-       p2p_build_wps_ie(p2p, buf, p2p_wps_method_pw_id(peer->wps_method), 0);
+       pw_id = p2p_wps_method_pw_id(peer->wps_method);
+       if (peer->oob_pw_id)
+               pw_id = peer->oob_pw_id;
+       if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) {
+               p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Request");
+               wpabuf_free(buf);
+               return NULL;
+       }
 
 #ifdef CONFIG_WIFI_DISPLAY
        if (p2p->wfd_ie_go_neg)
                wpabuf_put_buf(buf, p2p->wfd_ie_go_neg);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+       if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ])
+               wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_REQ]);
+
        return buf;
 }
 
@@ -198,13 +222,17 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev)
                        config_method = WPS_CONFIG_DISPLAY;
                else if (dev->wps_method == WPS_PBC)
                        config_method = WPS_CONFIG_PUSHBUTTON;
+               else if (dev->wps_method == WPS_P2PS)
+                       config_method = WPS_CONFIG_P2PS;
                else
                        return -1;
                return p2p_prov_disc_req(p2p, dev->info.p2p_device_addr,
-                                        config_method, 0, 0, 1);
+                                        NULL, config_method, 0, 0, 1);
        }
 
        freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+       if (dev->oob_go_neg_freq > 0)
+               freq = dev->oob_go_neg_freq;
        if (freq <= 0) {
                p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
                        MACSTR " to send GO Negotiation Request",
@@ -219,10 +247,11 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev)
        p2p_set_state(p2p, P2P_CONNECT);
        p2p->pending_action_state = P2P_PENDING_GO_NEG_REQUEST;
        p2p->go_neg_peer = dev;
+       eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
        dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE;
        dev->connect_reqs++;
        if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
-#ifdef TIZEN_EXT
+#ifdef BCM_DRIVER_V115
                            p2p->cfg->own_addr, dev->info.p2p_device_addr,
 #else
                            p2p->cfg->dev_addr, dev->info.p2p_device_addr,
@@ -238,47 +267,6 @@ int p2p_connect_send(struct p2p_data *p2p, struct p2p_device *dev)
 
        return 0;
 }
-#ifdef TIZEN_EXT_P2P
-int p2p_reject_send(struct p2p_data *p2p, struct p2p_device *dev)
-{
-       struct wpabuf *req;
-       int freq;
-
-       freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
-       if (freq <= 0) {
-               p2p_dbg(p2p, "P2P: No Listen/Operating frequency known for the "
-                       "peer " MACSTR " to send GO Negotiation Request",
-                       MAC2STR(dev->info.p2p_device_addr));
-               return -1;
-       }
-
-       dev->status = P2P_SC_FAIL_REJECTED_BY_USER;
-       dev->flags |= P2P_DEV_USER_REJECTED;
-
-       req = p2p_build_go_neg_req(p2p, dev);
-       if (req == NULL)
-               return -1;
-       p2p_dbg(p2p, "P2P: Sending GO Negotiation Request(reject)");
-       p2p_set_state(p2p, P2P_CONNECT);
-       p2p->pending_action_state = P2P_PENDING_GO_NEG_REQUEST;
-       p2p->go_neg_peer = dev;
-       dev->flags |= P2P_DEV_WAIT_GO_NEG_RESPONSE;
-       dev->connect_reqs++;
-       wpa_printf(MSG_DEBUG, "[KGB_DEBUG] %s(): Sending GO Neg request. Dst [" MACSTR "], Src [" MACSTR "], BSSID [" MACSTR "]",
-                               __func__, MAC2STR(dev->info.p2p_device_addr), MAC2STR(p2p->cfg->own_addr), MAC2STR(dev->info.p2p_device_addr));
-       if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
-                           p2p->cfg->own_addr, dev->info.p2p_device_addr,
-                           wpabuf_head(req), wpabuf_len(req), 200) < 0) {
-               p2p_dbg(p2p, "P2P: Failed to send Action frame");
-               /* Use P2P find to recover and retry */
-               p2p_set_timeout(p2p, 0, 0);
-       }
-
-       wpabuf_free(req);
-
-       return 0;
-}
-#endif
 
 
 static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
@@ -290,6 +278,7 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
        u8 *len;
        u8 group_capab;
        size_t extra = 0;
+       u16 pw_id;
 
        p2p_dbg(p2p, "Building GO Negotiation Response");
 
@@ -298,6 +287,9 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
                extra = wpabuf_len(p2p->wfd_ie_go_neg);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+       if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP])
+               extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]);
+
        buf = wpabuf_alloc(1000 + extra);
        if (buf == NULL)
                return NULL;
@@ -346,7 +338,7 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
        }
        p2p_buf_add_device_info(buf, p2p, peer);
        if (peer && peer->go_state == LOCAL_GO) {
-#ifdef TIZEN_EXT_P2P
+#ifdef BCM_DRIVER_V115
                p2p_buf_add_group_id(buf, p2p->cfg->own_addr, p2p->ssid,
                                     p2p->ssid_len);
 #else
@@ -357,15 +349,22 @@ static struct wpabuf * p2p_build_go_neg_resp(struct p2p_data *p2p,
        p2p_buf_update_ie_hdr(buf, len);
 
        /* WPS IE with Device Password ID attribute */
-       p2p_build_wps_ie(p2p, buf,
-                        p2p_wps_method_pw_id(peer ? peer->wps_method :
-                                             WPS_NOT_READY), 0);
+       pw_id = p2p_wps_method_pw_id(peer ? peer->wps_method : WPS_NOT_READY);
+       if (peer && peer->oob_pw_id)
+               pw_id = peer->oob_pw_id;
+       if (p2p_build_wps_ie(p2p, buf, pw_id, 0) < 0) {
+               p2p_dbg(p2p, "Failed to build WPS IE for GO Negotiation Response");
+               wpabuf_free(buf);
+               return NULL;
+       }
 
 #ifdef CONFIG_WIFI_DISPLAY
        if (p2p->wfd_ie_go_neg)
                wpabuf_put_buf(buf, p2p->wfd_ie_go_neg);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+       if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP])
+               wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_RESP]);
 
        return buf;
 }
@@ -389,6 +388,9 @@ void p2p_reselect_channel(struct p2p_data *p2p,
        int freq;
        u8 op_reg_class, op_channel;
        unsigned int i;
+       const int op_classes_5ghz[] = { 124, 115, 0 };
+       const int op_classes_ht40[] = { 126, 127, 116, 117, 0 };
+       const int op_classes_vht[] = { 128, 0 };
 
        if (p2p->own_freq_preference > 0 &&
            p2p_freq_to_channel(p2p->own_freq_preference,
@@ -453,17 +455,28 @@ void p2p_reselect_channel(struct p2p_data *p2p,
                }
        }
 
+       /* Try a channel where we might be able to use VHT */
+       if (p2p_channel_select(intersection, op_classes_vht,
+                              &p2p->op_reg_class, &p2p->op_channel) == 0) {
+               p2p_dbg(p2p, "Pick possible VHT channel (op_class %u channel %u) from intersection",
+                       p2p->op_reg_class, p2p->op_channel);
+               return;
+       }
+
        /* Try a channel where we might be able to use HT40 */
-       for (i = 0; i < intersection->reg_classes; i++) {
-               struct p2p_reg_class *c = &intersection->reg_class[i];
-               if (c->reg_class == 116 || c->reg_class == 117 ||
-                   c->reg_class == 126 || c->reg_class == 127) {
-                       p2p_dbg(p2p, "Pick possible HT40 channel (reg_class %u channel %u) from intersection",
-                               c->reg_class, c->channel[0]);
-                       p2p->op_reg_class = c->reg_class;
-                       p2p->op_channel = c->channel[0];
-                       return;
-               }
+       if (p2p_channel_select(intersection, op_classes_ht40,
+                              &p2p->op_reg_class, &p2p->op_channel) == 0) {
+               p2p_dbg(p2p, "Pick possible HT40 channel (op_class %u channel %u) from intersection",
+                       p2p->op_reg_class, p2p->op_channel);
+               return;
+       }
+
+       /* Prefer a 5 GHz channel */
+       if (p2p_channel_select(intersection, op_classes_5ghz,
+                              &p2p->op_reg_class, &p2p->op_channel) == 0) {
+               p2p_dbg(p2p, "Pick possible 5 GHz channel (op_class %u channel %u) from intersection",
+                       p2p->op_reg_class, p2p->op_channel);
+               return;
        }
 
        /*
@@ -490,13 +503,20 @@ void p2p_reselect_channel(struct p2p_data *p2p,
 }
 
 
-static int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
-                                u8 *status)
+int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
+                         u8 *status)
 {
-       struct p2p_channels intersection;
-       size_t i;
-
-       p2p_channels_intersect(&p2p->channels, &dev->channels, &intersection);
+       struct p2p_channels tmp, intersection;
+
+       p2p_channels_dump(p2p, "own channels", &p2p->channels);
+       p2p_channels_dump(p2p, "peer channels", &dev->channels);
+       p2p_channels_intersect(&p2p->channels, &dev->channels, &tmp);
+       p2p_channels_dump(p2p, "intersection", &tmp);
+       p2p_channels_remove_freqs(&tmp, &p2p->no_go_freq);
+       p2p_channels_dump(p2p, "intersection after no-GO removal", &tmp);
+       p2p_channels_intersect(&tmp, &p2p->cfg->channels, &intersection);
+       p2p_channels_dump(p2p, "intersection with local channel list",
+                         &intersection);
        if (intersection.reg_classes == 0 ||
            intersection.reg_class[0].channels == 0) {
                *status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
@@ -504,14 +524,6 @@ static int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
                return -1;
        }
 
-       for (i = 0; i < intersection.reg_classes; i++) {
-               struct p2p_reg_class *c;
-               c = &intersection.reg_class[i];
-               p2p_dbg(p2p, "reg_class %u", c->reg_class);
-               wpa_hexdump(MSG_DEBUG, "P2P: channels",
-                           c->channel, c->channels);
-       }
-
        if (!p2p_channels_includes(&intersection, p2p->op_reg_class,
                                   p2p->op_channel)) {
                if (dev->flags & P2P_DEV_FORCE_FREQ) {
@@ -611,17 +623,56 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
        if (msg.status && *msg.status) {
                p2p_dbg(p2p, "Unexpected Status attribute (%d) in GO Negotiation Request",
                        *msg.status);
+               if (dev && p2p->go_neg_peer == dev &&
+                   *msg.status == P2P_SC_FAIL_REJECTED_BY_USER) {
+                       /*
+                        * This mechanism for using Status attribute in GO
+                        * Negotiation Request is not compliant with the P2P
+                        * specification, but some deployed devices use it to
+                        * indicate rejection of GO Negotiation in a case where
+                        * they have sent out GO Negotiation Response with
+                        * status 1. The P2P specification explicitly disallows
+                        * this. To avoid unnecessary interoperability issues
+                        * and extra frames, mark the pending negotiation as
+                        * failed and do not reply to this GO Negotiation
+                        * Request frame.
+                        */
+                       p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+                       p2p_go_neg_failed(p2p, *msg.status);
+                       p2p_parse_free(&msg);
+                       return;
+               }
                goto fail;
        }
 
        if (dev == NULL)
                dev = p2p_add_dev_from_go_neg_req(p2p, sa, &msg);
-       else if (dev->flags & P2P_DEV_PROBE_REQ_ONLY)
+       else if ((dev->flags & P2P_DEV_PROBE_REQ_ONLY) ||
+                 !(dev->flags & P2P_DEV_REPORTED))
+               p2p_add_dev_info(p2p, sa, dev, &msg);
+       else if (!dev->listen_freq && !dev->oper_freq) {
+               /*
+                * This may happen if the peer entry was added based on PD
+                * Request and no Probe Request/Response frame has been received
+                * from this peer (or that information has timed out).
+                */
+               p2p_dbg(p2p, "Update peer " MACSTR
+                       " based on GO Neg Req since listen/oper freq not known",
+                       MAC2STR(dev->info.p2p_device_addr));
                p2p_add_dev_info(p2p, sa, dev, &msg);
+       }
+
+       if (p2p->go_neg_peer && p2p->go_neg_peer == dev)
+               eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
+
        if (dev && dev->flags & P2P_DEV_USER_REJECTED) {
                p2p_dbg(p2p, "User has rejected this peer");
                status = P2P_SC_FAIL_REJECTED_BY_USER;
-       } else if (dev == NULL || dev->wps_method == WPS_NOT_READY) {
+       } else if (dev == NULL ||
+                  (dev->wps_method == WPS_NOT_READY &&
+                   (p2p->authorized_oob_dev_pw_id == 0 ||
+                    p2p->authorized_oob_dev_pw_id !=
+                    msg.dev_password_id))) {
                p2p_dbg(p2p, "Not ready for GO negotiation with " MACSTR,
                        MAC2STR(sa));
                status = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
@@ -707,7 +758,39 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
                                goto fail;
                        }
                        break;
+               case DEV_PW_P2PS_DEFAULT:
+                       p2p_dbg(p2p, "Peer using P2PS pin");
+                       if (dev->wps_method != WPS_P2PS) {
+                               p2p_dbg(p2p,
+                                       "We have wps_method=%s -> incompatible",
+                                       p2p_wps_method_str(dev->wps_method));
+                               status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+                               goto fail;
+                       }
+                       break;
                default:
+                       if (msg.dev_password_id &&
+                           msg.dev_password_id == dev->oob_pw_id) {
+                               p2p_dbg(p2p, "Peer using NFC");
+                               if (dev->wps_method != WPS_NFC) {
+                                       p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
+                                               p2p_wps_method_str(
+                                                       dev->wps_method));
+                                       status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+                                       goto fail;
+                               }
+                               break;
+                       }
+#ifdef CONFIG_WPS_NFC
+                       if (p2p->authorized_oob_dev_pw_id &&
+                           msg.dev_password_id ==
+                           p2p->authorized_oob_dev_pw_id) {
+                               p2p_dbg(p2p, "Using static handover with our device password from NFC Tag");
+                               dev->wps_method = WPS_NFC;
+                               dev->oob_pw_id = p2p->authorized_oob_dev_pw_id;
+                               break;
+                       }
+#endif /* CONFIG_WPS_NFC */
                        p2p_dbg(p2p, "Unsupported Device Password ID %d",
                                msg.dev_password_id);
                        status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
@@ -736,6 +819,7 @@ void p2p_process_go_neg_req(struct p2p_data *p2p, const u8 *sa,
                dev->dialog_token = msg.dialog_token;
                os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
                p2p->go_neg_peer = dev;
+               eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p, NULL);
                status = P2P_SC_SUCCESS;
        }
 
@@ -776,7 +860,7 @@ fail:
                p2p->pending_action_state =
                        P2P_PENDING_GO_NEG_RESPONSE_FAILURE;
        if (p2p_send_action(p2p, freq, sa,
-#ifdef TIZEN_EXT_P2P
+#ifdef BCM_DRIVER_V115
                            p2p->cfg->own_addr, p2p->cfg->own_addr,
 #else
                            p2p->cfg->dev_addr, p2p->cfg->dev_addr,
@@ -807,6 +891,9 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p,
                extra = wpabuf_len(p2p->wfd_ie_go_neg);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+       if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF])
+               extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]);
+
        buf = wpabuf_alloc(1000 + extra);
        if (buf == NULL)
                return NULL;
@@ -841,7 +928,7 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p,
        p2p_channels_intersect(&p2p->channels, &peer->channels, &res);
        p2p_buf_add_channel_list(buf, p2p->cfg->country, &res);
        if (go) {
-#ifdef TIZEN_EXT_P2P
+#ifdef BCM_DRIVER_V115
                p2p_buf_add_group_id(buf, p2p->cfg->own_addr, p2p->ssid,
                                     p2p->ssid_len);
 #else
@@ -856,6 +943,9 @@ static struct wpabuf * p2p_build_go_neg_conf(struct p2p_data *p2p,
                wpabuf_put_buf(buf, p2p->wfd_ie_go_neg);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+       if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF])
+               wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_GO_NEG_CONF]);
+
        return buf;
 }
 
@@ -864,7 +954,6 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
                             const u8 *data, size_t len, int rx_freq)
 {
        struct p2p_device *dev;
-       struct wpabuf *conf;
        int go = -1;
        struct p2p_message msg;
        u8 status = P2P_SC_SUCCESS;
@@ -908,12 +997,18 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
                if (*msg.status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
                        p2p_dbg(p2p, "Wait for the peer to become ready for GO Negotiation");
                        dev->flags |= P2P_DEV_NOT_YET_READY;
-                       dev->wait_count = 0;
-                       p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
+                       eloop_cancel_timeout(p2p_go_neg_wait_timeout, p2p,
+                                            NULL);
+                       eloop_register_timeout(120, 0, p2p_go_neg_wait_timeout,
+                                              p2p, NULL);
+                       if (p2p->state == P2P_CONNECT_LISTEN)
+                               p2p_set_state(p2p, P2P_WAIT_PEER_CONNECT);
+                       else
+                               p2p_set_state(p2p, P2P_WAIT_PEER_IDLE);
                        p2p_set_timeout(p2p, 0, 0);
                } else {
                        p2p_dbg(p2p, "Stop GO Negotiation attempt");
-                       p2p_go_neg_failed(p2p, dev, *msg.status);
+                       p2p_go_neg_failed(p2p, *msg.status);
                }
                p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
                p2p_parse_free(&msg);
@@ -1041,7 +1136,27 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
                        goto fail;
                }
                break;
+       case DEV_PW_P2PS_DEFAULT:
+               p2p_dbg(p2p, "P2P: Peer using P2PS default pin");
+               if (dev->wps_method != WPS_P2PS) {
+                       p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
+                               p2p_wps_method_str(dev->wps_method));
+                       status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+                       goto fail;
+               }
+               break;
        default:
+               if (msg.dev_password_id &&
+                   msg.dev_password_id == dev->oob_pw_id) {
+                       p2p_dbg(p2p, "Peer using NFC");
+                       if (dev->wps_method != WPS_NFC) {
+                               p2p_dbg(p2p, "We have wps_method=%s -> incompatible",
+                                       p2p_wps_method_str(dev->wps_method));
+                               status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
+                               goto fail;
+                       }
+                       break;
+               }
                p2p_dbg(p2p, "Unsupported Device Password ID %d",
                        msg.dev_password_id);
                status = P2P_SC_FAIL_INCOMPATIBLE_PROV_METHOD;
@@ -1058,10 +1173,13 @@ void p2p_process_go_neg_resp(struct p2p_data *p2p, const u8 *sa,
        os_memcpy(dev->intended_addr, msg.intended_addr, ETH_ALEN);
 
 fail:
-       conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token, status,
-                                    msg.operating_channel, go);
+       /* Store GO Negotiation Confirmation to allow retransmission */
+       wpabuf_free(dev->go_neg_conf);
+       dev->go_neg_conf = p2p_build_go_neg_conf(p2p, dev, msg.dialog_token,
+                                                status, msg.operating_channel,
+                                                go);
        p2p_parse_free(&msg);
-       if (conf == NULL)
+       if (dev->go_neg_conf == NULL)
                return;
        p2p_dbg(p2p, "Sending GO Negotiation Confirm");
        if (status == P2P_SC_SUCCESS) {
@@ -1073,20 +1191,26 @@ fail:
                freq = rx_freq;
        else
                freq = dev->listen_freq;
-       if (p2p_send_action(p2p, freq, sa,
-#ifdef TIZEN_EXT_P2P
+
+       dev->go_neg_conf_freq = freq;
+       dev->go_neg_conf_sent = 0;
+
+       if (p2p_send_action(p2p, freq, sa, 
+#ifdef BCM_DRIVER_V115
                            p2p->cfg->own_addr, sa,
 #else
                            p2p->cfg->dev_addr, sa,
 #endif
-                           wpabuf_head(conf), wpabuf_len(conf), 0) < 0) {
+                           wpabuf_head(dev->go_neg_conf),
+                           wpabuf_len(dev->go_neg_conf), 200) < 0) {
                p2p_dbg(p2p, "Failed to send Action frame");
-               p2p_go_neg_failed(p2p, dev, -1);
-       }
-       wpabuf_free(conf);
+               p2p_go_neg_failed(p2p, -1);
+               p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+       } else
+               dev->go_neg_conf_sent++;
        if (status != P2P_SC_SUCCESS) {
                p2p_dbg(p2p, "GO Negotiation failed");
-               p2p_go_neg_failed(p2p, dev, status);
+               p2p_go_neg_failed(p2p, status);
        }
 }
 
@@ -1117,9 +1241,11 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
 
        if (!(dev->flags & P2P_DEV_WAIT_GO_NEG_CONFIRM)) {
                p2p_dbg(p2p, "Was not expecting GO Negotiation Confirm - ignore");
+               p2p_parse_free(&msg);
                return;
        }
        dev->flags &= ~P2P_DEV_WAIT_GO_NEG_CONFIRM;
+       p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
 
        if (msg.dialog_token != dev->dialog_token) {
                p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)",
@@ -1135,7 +1261,7 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
        }
        if (*msg.status) {
                p2p_dbg(p2p, "GO Negotiation rejected: status %d", *msg.status);
-               p2p_go_neg_failed(p2p, dev, *msg.status);
+               p2p_go_neg_failed(p2p, *msg.status);
                p2p_parse_free(&msg);
                return;
        }
@@ -1147,7 +1273,7 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
        } else if (dev->go_state == REMOTE_GO) {
                p2p_dbg(p2p, "Mandatory P2P Group ID attribute missing from GO Negotiation Confirmation");
                p2p->ssid_len = 0;
-               p2p_go_neg_failed(p2p, dev, P2P_SC_FAIL_INVALID_PARAMS);
+               p2p_go_neg_failed(p2p, P2P_SC_FAIL_INVALID_PARAMS);
                p2p_parse_free(&msg);
                return;
        }
@@ -1158,6 +1284,14 @@ void p2p_process_go_neg_conf(struct p2p_data *p2p, const u8 *sa,
                p2p_parse_free(&msg);
                return;
 #endif /* CONFIG_P2P_STRICT */
+       } else if (dev->go_state == REMOTE_GO) {
+               int oper_freq = p2p_channel_to_freq(msg.operating_channel[3],
+                                                   msg.operating_channel[4]);
+               if (oper_freq != dev->oper_freq) {
+                       p2p_dbg(p2p, "Updated peer (GO) operating channel preference from %d MHz to %d MHz",
+                               dev->oper_freq, oper_freq);
+                       dev->oper_freq = oper_freq;
+               }
        }
 
        if (!msg.channel_list) {
old mode 100644 (file)
new mode 100755 (executable)
index 15e7622..41ca99f
@@ -11,6 +11,7 @@
 #include "common.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
+#include "common/wpa_ctrl.h"
 #include "wps/wps_defs.h"
 #include "wps/wps_i.h"
 #include "p2p_i.h"
@@ -154,6 +155,7 @@ static void p2p_group_add_common_ies(struct p2p_group *group,
                group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
        if (group->num_members >= group->cfg->max_clients)
                group_capab |= P2P_GROUP_CAPAB_GROUP_LIMIT;
+       group_capab |= P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION;
        p2p_buf_add_capability(ie, dev_capab, group_capab);
 }
 
@@ -213,6 +215,10 @@ static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group)
                extra = wpabuf_len(group->p2p->wfd_ie_beacon);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+       if (group->p2p->vendor_elem &&
+           group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO])
+               extra += wpabuf_len(group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]);
+
        ie = wpabuf_alloc(257 + extra);
        if (ie == NULL)
                return NULL;
@@ -222,6 +228,11 @@ static struct wpabuf * p2p_group_build_beacon_ie(struct p2p_group *group)
                wpabuf_put_buf(ie, group->p2p->wfd_ie_beacon);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+       if (group->p2p->vendor_elem &&
+           group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO])
+               wpabuf_put_buf(ie,
+                              group->p2p->vendor_elem[VENDOR_ELEM_BEACON_P2P_GO]);
+
        len = p2p_buf_add_ie_hdr(ie);
        p2p_group_add_common_ies(group, ie);
        p2p_buf_add_device_id(ie, group->p2p->cfg->dev_addr);
@@ -397,10 +408,38 @@ static void wifi_display_group_update(struct p2p_group *group)
 #endif /* CONFIG_WIFI_DISPLAY */
 
 
+void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf,
+                           int max_clients)
+{
+       u8 *group_info;
+       int count = 0;
+       struct p2p_group_member *m;
+
+       p2p_dbg(group->p2p, "* P2P Group Info");
+       group_info = wpabuf_put(buf, 0);
+       wpabuf_put_u8(buf, P2P_ATTR_GROUP_INFO);
+       wpabuf_put_le16(buf, 0); /* Length to be filled */
+       for (m = group->members; m; m = m->next) {
+               p2p_client_info(buf, m);
+               count++;
+               if (max_clients >= 0 && count >= max_clients)
+                       break;
+       }
+       WPA_PUT_LE16(group_info + 1,
+                    (u8 *) wpabuf_put(buf, 0) - group_info - 3);
+}
+
+
+void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf)
+{
+       p2p_buf_add_group_id(buf, group->p2p->cfg->dev_addr, group->cfg->ssid,
+                            group->cfg->ssid_len);
+}
+
+
 static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
 {
        struct wpabuf *p2p_subelems, *ie;
-       struct p2p_group_member *m;
 
        p2p_subelems = wpabuf_alloc(500);
        if (p2p_subelems == NULL)
@@ -413,21 +452,19 @@ static struct wpabuf * p2p_group_build_probe_resp_ie(struct p2p_group *group)
        p2p_buf_add_device_info(p2p_subelems, group->p2p, NULL);
 
        /* P2P Group Info: Only when at least one P2P Client is connected */
-       if (group->members) {
-               u8 *group_info;
-               group_info = wpabuf_put(p2p_subelems, 0);
-               wpabuf_put_u8(p2p_subelems, P2P_ATTR_GROUP_INFO);
-               wpabuf_put_le16(p2p_subelems, 0); /* Length to be filled */
-               for (m = group->members; m; m = m->next)
-                       p2p_client_info(p2p_subelems, m);
-               WPA_PUT_LE16(group_info + 1,
-                            (u8 *) wpabuf_put(p2p_subelems, 0) - group_info -
-                            3);
-       }
+       if (group->members)
+               p2p_buf_add_group_info(group, p2p_subelems, -1);
 
        ie = p2p_group_encaps_probe_resp(p2p_subelems);
        wpabuf_free(p2p_subelems);
 
+       if (group->p2p->vendor_elem &&
+           group->p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P_GO]) {
+               struct wpabuf *extra;
+               extra = wpabuf_dup(group->p2p->vendor_elem[VENDOR_ELEM_PROBE_RESP_P2P_GO]);
+               ie = wpabuf_concat(extra, ie);
+       }
+
 #ifdef CONFIG_WIFI_DISPLAY
        if (group->wfd_ie) {
                struct wpabuf *wfd = wpabuf_dup(group->wfd_ie);
@@ -614,6 +651,10 @@ struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status)
                extra = wpabuf_len(group->wfd_ie);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+       if (group->p2p->vendor_elem &&
+           group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP])
+               extra += wpabuf_len(group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP]);
+
        /*
         * (Re)Association Response - P2P IE
         * Status attribute (shall be present when association request is
@@ -629,6 +670,11 @@ struct wpabuf * p2p_group_assoc_resp_ie(struct p2p_group *group, u8 status)
                wpabuf_put_buf(resp, group->wfd_ie);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+       if (group->p2p->vendor_elem &&
+           group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP])
+               wpabuf_put_buf(resp,
+                              group->p2p->vendor_elem[VENDOR_ELEM_P2P_ASSOC_RESP]);
+
        rlen = p2p_buf_add_ie_hdr(resp);
        if (status != P2P_SC_SUCCESS)
                p2p_buf_add_status(resp, status);
@@ -935,10 +981,22 @@ u8 p2p_group_presence_req(struct p2p_group *group,
 
 unsigned int p2p_get_group_num_members(struct p2p_group *group)
 {
+       if (!group)
+               return 0;
+
        return group->num_members;
 }
 
 
+int p2p_client_limit_reached(struct p2p_group *group)
+{
+       if (!group || !group->cfg)
+               return 1;
+
+       return group->num_members >= group->cfg->max_clients;
+}
+
+
 const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next)
 {
        struct p2p_group_member *iter = *next;
@@ -953,7 +1011,7 @@ const u8 * p2p_iterate_group_members(struct p2p_group *group, void **next)
        if (!iter)
                return NULL;
 
-       return iter->addr;
+       return iter->dev_addr;
 }
 
 
@@ -980,3 +1038,36 @@ int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id,
        return os_memcmp(group_id + ETH_ALEN, group->cfg->ssid,
                         group->cfg->ssid_len) == 0;
 }
+
+
+void p2p_group_force_beacon_update_ies(struct p2p_group *group)
+{
+       group->beacon_update = 1;
+       p2p_group_update_ies(group);
+}
+
+
+int p2p_group_get_freq(struct p2p_group *group)
+{
+       return group->cfg->freq;
+}
+
+
+const struct p2p_group_config * p2p_group_get_config(struct p2p_group *group)
+{
+       return group->cfg;
+}
+
+
+void p2p_loop_on_all_groups(struct p2p_data *p2p,
+                           int (*group_callback)(struct p2p_group *group,
+                                                 void *user_data),
+                           void *user_data)
+{
+       unsigned int i;
+
+       for (i = 0; i < p2p->num_groups; i++) {
+               if (!group_callback(p2p->groups[i], user_data))
+                       break;
+       }
+}
old mode 100644 (file)
new mode 100755 (executable)
index 75c1511..6af19ce
 #include "utils/list.h"
 #include "p2p.h"
 
+#define P2P_GO_NEG_CNF_MAX_RETRY_COUNT 1
+
+enum p2p_role_indication;
+
+/*
+ * To force Service Instances to fit within a single P2P Tag, MAX_SVC_ADV_LEN
+ * must equal 248 or less. Must have a minimum size of 19.
+ */
+#define MAX_SVC_ADV_LEN        600
+#define MAX_SVC_ADV_IE_LEN (9 + MAX_SVC_ADV_LEN + (5 * (MAX_SVC_ADV_LEN / 240)))
+
 enum p2p_go_state {
        UNKNOWN_GO,
        LOCAL_GO,
@@ -23,9 +34,11 @@ enum p2p_go_state {
  */
 struct p2p_device {
        struct dl_list list;
-       struct os_time last_seen;
+       struct os_reltime last_seen;
        int listen_freq;
+       int oob_go_neg_freq;
        enum p2p_wps_method wps_method;
+       u16 oob_pw_id;
 
        struct p2p_peer_info info;
 
@@ -77,8 +90,6 @@ struct p2p_device {
 #define P2P_DEV_PROBE_REQ_ONLY BIT(0)
 #define P2P_DEV_REPORTED BIT(1)
 #define P2P_DEV_NOT_YET_READY BIT(2)
-#define P2P_DEV_SD_INFO BIT(3)
-#define P2P_DEV_SD_SCHEDULE BIT(4)
 #define P2P_DEV_PD_PEER_DISPLAY BIT(5)
 #define P2P_DEV_PD_PEER_KEYPAD BIT(6)
 #define P2P_DEV_USER_REJECTED BIT(7)
@@ -93,18 +104,39 @@ struct p2p_device {
 #define P2P_DEV_PREFER_PERSISTENT_RECONN BIT(16)
 #define P2P_DEV_PD_BEFORE_GO_NEG BIT(17)
 #define P2P_DEV_NO_PREF_CHAN BIT(18)
+#define P2P_DEV_WAIT_INV_REQ_ACK BIT(19)
+#define P2P_DEV_P2PS_REPORTED BIT(20)
+#define P2P_DEV_PD_PEER_P2PS BIT(21)
        unsigned int flags;
 
        int status; /* enum p2p_status_code */
        unsigned int wait_count;
        unsigned int connect_reqs;
        unsigned int invitation_reqs;
+       unsigned int sd_reqs;
 
        u16 ext_listen_period;
        u16 ext_listen_interval;
 
        u8 go_timeout;
        u8 client_timeout;
+
+       /**
+        * go_neg_conf_sent - Number of GO Negotiation Confirmation retries
+        */
+       u8 go_neg_conf_sent;
+
+       /**
+        * freq - Frquency on which the GO Negotiation Confirmation is sent
+        */
+       int go_neg_conf_freq;
+
+       /**
+        * go_neg_conf - GO Negotiation Confirmation frame
+        */
+       struct wpabuf *go_neg_conf;
+
+       int sd_pending_bcast_queries;
 };
 
 struct p2p_sd_query {
@@ -205,16 +237,6 @@ struct p2p_data {
                 * P2P_INVITE_LISTEN - Listen during Invite
                 */
                P2P_INVITE_LISTEN,
-
-               /**
-                * P2P_SEARCH_WHEN_READY - Waiting to start Search
-                */
-               P2P_SEARCH_WHEN_READY,
-
-               /**
-                * P2P_CONTINUE_SEARCH_WHEN_READY - Waiting to continue Search
-                */
-               P2P_CONTINUE_SEARCH_WHEN_READY,
        } state;
 
        /**
@@ -247,8 +269,17 @@ struct p2p_data {
         */
        struct p2p_device *invite_peer;
 
+       /**
+        * last_p2p_find_oper - Pointer to last pre-find operation peer
+        */
+       struct p2p_device *last_p2p_find_oper;
+
        const u8 *invite_go_dev_addr;
        u8 invite_go_dev_addr_buf[ETH_ALEN];
+       int invite_dev_pw_id;
+
+       unsigned int retry_invite_req:1;
+       unsigned int retry_invite_req_sent:1;
 
        /**
         * sd_peer - Pointer to Service Discovery peer
@@ -260,6 +291,12 @@ struct p2p_data {
         */
        struct p2p_sd_query *sd_query;
 
+       /**
+        * num_p2p_sd_queries - Total number of broadcast SD queries present in
+        * the list
+        */
+       int num_p2p_sd_queries;
+
        /* GO Negotiation data */
 
        /**
@@ -316,6 +353,8 @@ struct p2p_data {
         */
        struct p2p_channels channels;
 
+       struct wpa_freq_range_list no_go_freq;
+
        enum p2p_pending_action_state {
                P2P_NO_PENDING_ACTION,
                P2P_PENDING_GO_NEG_REQUEST,
@@ -324,6 +363,7 @@ struct p2p_data {
                P2P_PENDING_GO_NEG_CONFIRM,
                P2P_PENDING_SD,
                P2P_PENDING_PD,
+               P2P_PENDING_PD_RESPONSE,
                P2P_PENDING_INVITATION_REQUEST,
                P2P_PENDING_INVITATION_RESPONSE,
                P2P_PENDING_DEV_DISC_REQUEST,
@@ -386,6 +426,7 @@ struct p2p_data {
        u8 after_scan_peer[ETH_ALEN];
        struct p2p_pending_action_tx *after_scan_tx;
        unsigned int after_scan_tx_in_progress:1;
+       unsigned int send_action_in_progress:1;
 
        /* Requested device types for find/search */
        unsigned int num_req_dev_types;
@@ -393,7 +434,7 @@ struct p2p_data {
        u8 *find_dev_id;
        u8 find_dev_id_buf[ETH_ALEN];
 
-       struct os_time find_start; /* time of last p2p_find start */
+       struct os_reltime find_start; /* time of last p2p_find start */
 
        struct p2p_group **groups;
        size_t num_groups;
@@ -442,6 +483,14 @@ struct p2p_data {
         */
        int pd_retries;
 
+       /**
+        * pd_force_freq - Forced frequency for PD retries or 0 to auto-select
+        *
+        * This is is used during PD retries for join-a-group case to use the
+        * correct operating frequency determined from a BSS entry for the GO.
+        */
+       int pd_force_freq;
+
        u8 go_timeout;
        u8 client_timeout;
 
@@ -449,6 +498,20 @@ struct p2p_data {
        unsigned int search_delay;
        int in_search_delay;
 
+       u8 pending_reg_class;
+       u8 pending_channel;
+       u8 pending_channel_forced;
+
+       /* ASP Support */
+       struct p2ps_advertisement *p2ps_adv_list;
+       struct p2ps_provision *p2ps_prov;
+       u8 wild_card_hash[P2PS_HASH_LEN];
+       u8 query_hash[P2P_MAX_QUERY_HASH * P2PS_HASH_LEN];
+       u8 query_count;
+       u8 p2ps_seek;
+       u8 p2ps_seek_count;
+       u8 p2ps_svc_found;
+
 #ifdef CONFIG_WIFI_DISPLAY
        struct wpabuf *wfd_ie_beacon;
        struct wpabuf *wfd_ie_probe_req;
@@ -462,6 +525,10 @@ struct p2p_data {
        struct wpabuf *wfd_assoc_bssid;
        struct wpabuf *wfd_coupled_sink_info;
 #endif /* CONFIG_WIFI_DISPLAY */
+
+       u16 authorized_oob_dev_pw_id;
+
+       struct wpabuf **vendor_elem;
 };
 
 /**
@@ -503,6 +570,8 @@ struct p2p_message {
 
        const u8 *minor_reason_code;
 
+       const u8 *oob_go_neg_channel;
+
        /* P2P Device Info */
        const u8 *p2p_device_info;
        size_t p2p_device_info_len;
@@ -514,6 +583,7 @@ struct p2p_message {
 
        /* WPS IE */
        u16 dev_password_id;
+       int dev_password_id_present;
        u16 wps_config_methods;
        const u8 *wps_pri_dev_type;
        const u8 *wps_sec_dev_type_list;
@@ -528,12 +598,39 @@ struct p2p_message {
        size_t model_number_len;
        const u8 *serial_number;
        size_t serial_number_len;
+       const u8 *oob_dev_password;
+       size_t oob_dev_password_len;
 
        /* DS Parameter Set IE */
        const u8 *ds_params;
 
        /* SSID IE */
        const u8 *ssid;
+
+       /* P2PS */
+       u8 service_hash_count;
+       const u8 *service_hash;
+
+       const u8 *session_info;
+       size_t session_info_len;
+
+       const u8 *conn_cap;
+
+       const u8 *adv_id;
+       const u8 *adv_mac;
+
+       const u8 *adv_service_instance;
+       size_t adv_service_instance_len;
+
+       const u8 *session_id;
+       const u8 *session_mac;
+
+       const u8 *feature_cap;
+       size_t feature_cap_len;
+
+       const u8 *persistent_dev;
+       const u8 *persistent_ssid;
+       size_t persistent_ssid_len;
 };
 
 
@@ -562,13 +659,28 @@ int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel);
 void p2p_channels_intersect(const struct p2p_channels *a,
                            const struct p2p_channels *b,
                            struct p2p_channels *res);
+void p2p_channels_union_inplace(struct p2p_channels *res,
+                               const struct p2p_channels *b);
+void p2p_channels_union(const struct p2p_channels *a,
+                       const struct p2p_channels *b,
+                       struct p2p_channels *res);
+void p2p_channels_remove_freqs(struct p2p_channels *chan,
+                              const struct wpa_freq_range_list *list);
 int p2p_channels_includes(const struct p2p_channels *channels, u8 reg_class,
                          u8 channel);
+void p2p_channels_dump(struct p2p_data *p2p, const char *title,
+                      const struct p2p_channels *chan);
+int p2p_channel_select(struct p2p_channels *chans, const int *classes,
+                      u8 *op_class, u8 *op_channel);
+int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class,
+                             u8 *op_channel);
 
 /* p2p_parse.c */
 int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg);
 int p2p_parse_ies(const u8 *data, size_t len, struct p2p_message *msg);
 int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg);
+int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p,
+                          size_t p2p_len, struct p2p_message *msg);
 void p2p_parse_free(struct p2p_message *msg);
 int p2p_attr_text(struct wpabuf *data, char *buf, char *end);
 int p2p_group_info_parse(const u8 *gi, size_t gi_len,
@@ -591,7 +703,12 @@ u8 p2p_group_presence_req(struct p2p_group *group,
 int p2p_group_is_group_id_match(struct p2p_group *group, const u8 *group_id,
                                size_t group_id_len);
 void p2p_group_update_ies(struct p2p_group *group);
+void p2p_group_force_beacon_update_ies(struct p2p_group *group);
 struct wpabuf * p2p_group_get_wfd_ie(struct p2p_group *g);
+void p2p_buf_add_group_info(struct p2p_group *group, struct wpabuf *buf,
+                           int max_clients);
+void p2p_group_buf_add_id(struct p2p_group *group, struct wpabuf *buf);
+int p2p_group_get_freq(struct p2p_group *group);
 
 
 void p2p_buf_add_action_hdr(struct wpabuf *buf, u8 subtype, u8 dialog_token);
@@ -623,8 +740,23 @@ void p2p_buf_add_noa(struct wpabuf *buf, u8 noa_index, u8 opp_ps, u8 ctwindow,
 void p2p_buf_add_ext_listen_timing(struct wpabuf *buf, u16 period,
                                   u16 interval);
 void p2p_buf_add_p2p_interface(struct wpabuf *buf, struct p2p_data *p2p);
-void p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
-                     int all_attr);
+void p2p_buf_add_oob_go_neg_channel(struct wpabuf *buf, const char *country,
+                                   u8 oper_class, u8 channel,
+                                   enum p2p_role_indication role);
+void p2p_buf_add_service_hash(struct wpabuf *buf, struct p2p_data *p2p);
+void p2p_buf_add_session_info(struct wpabuf *buf, const char *info);
+void p2p_buf_add_connection_capability(struct wpabuf *buf, u8 connection_cap);
+void p2p_buf_add_advertisement_id(struct wpabuf *buf, u32 id, const u8 *mac);
+void p2p_buf_add_service_instance(struct wpabuf *buf, struct p2p_data *p2p,
+                                 u8 count, const u8 *hash,
+                                 struct p2ps_advertisement *adv_list);
+void p2p_buf_add_session_id(struct wpabuf *buf, u32 id, const u8 *mac);
+void p2p_buf_add_feature_capability(struct wpabuf *buf, u16 len,
+                                   const u8 *mask);
+void p2p_buf_add_persistent_group_info(struct wpabuf *buf, const u8 *dev_addr,
+                                      const u8 *ssid, size_t ssid_len);
+int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
+                    int all_attr);
 
 /* p2p_sd.c */
 struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
@@ -655,9 +787,6 @@ u16 p2p_wps_method_pw_id(enum p2p_wps_method wps_method);
 void p2p_reselect_channel(struct p2p_data *p2p,
                          struct p2p_channels *intersection);
 
-#ifdef TIZEN_EXT_P2P
-int p2p_reject_send(struct p2p_data *p2p, struct p2p_device *dev);
-#endif
 /* p2p_pd.c */
 void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
                               const u8 *data, size_t len, int rx_freq);
@@ -673,7 +802,7 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
 void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
                                 const u8 *data, size_t len);
 int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
-                   const u8 *go_dev_addr);
+                   const u8 *go_dev_addr, int dev_pw_id);
 void p2p_invitation_req_cb(struct p2p_data *p2p, int success);
 void p2p_invitation_resp_cb(struct p2p_data *p2p, int success);
 
@@ -701,13 +830,12 @@ struct p2p_device * p2p_add_dev_from_go_neg_req(struct p2p_data *p2p,
 void p2p_add_dev_info(struct p2p_data *p2p, const u8 *addr,
                      struct p2p_device *dev, struct p2p_message *msg);
 int p2p_add_device(struct p2p_data *p2p, const u8 *addr, int freq,
-                  struct os_time *rx_time, int level, const u8 *ies,
+                  struct os_reltime *rx_time, int level, const u8 *ies,
                   size_t ies_len, int scan_res);
 struct p2p_device * p2p_get_device(struct p2p_data *p2p, const u8 *addr);
 struct p2p_device * p2p_get_device_interface(struct p2p_data *p2p,
                                             const u8 *addr);
-void p2p_go_neg_failed(struct p2p_data *p2p, struct p2p_device *peer,
-                      int status);
+void p2p_go_neg_failed(struct p2p_data *p2p, int status);
 void p2p_go_complete(struct p2p_data *p2p, struct p2p_device *peer);
 int p2p_match_dev_type(struct p2p_data *p2p, struct wpabuf *wps);
 int dev_type_list_match(const u8 *dev_type, const u8 *req_dev_type[],
@@ -719,7 +847,11 @@ int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
                    size_t len, unsigned int wait_time);
 void p2p_stop_listen_for_freq(struct p2p_data *p2p, int freq);
 int p2p_prepare_channel(struct p2p_data *p2p, struct p2p_device *dev,
-                       unsigned int force_freq, unsigned int pref_freq);
+                       unsigned int force_freq, unsigned int pref_freq,
+                       int go);
+void p2p_go_neg_wait_timeout(void *eloop_ctx, void *timeout_ctx);
+int p2p_go_select_channel(struct p2p_data *p2p, struct p2p_device *dev,
+                         u8 *status);
 void p2p_dbg(struct p2p_data *p2p, const char *fmt, ...)
 PRINTF_FORMAT(2, 3);
 void p2p_info(struct p2p_data *p2p, const char *fmt, ...)
old mode 100644 (file)
new mode 100755 (executable)
index 10ab430..1ea33ae
 
 #include "common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
 #include "p2p_i.h"
 #include "p2p.h"
 
 
 static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
                                                struct p2p_device *peer,
-                                               const u8 *go_dev_addr)
+                                               const u8 *go_dev_addr,
+                                               int dev_pw_id)
 {
        struct wpabuf *buf;
        u8 *len;
@@ -44,6 +46,9 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
                extra = wpabuf_len(wfd_ie);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+       if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ])
+               extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]);
+
        buf = wpabuf_alloc(1000 + extra);
        if (buf == NULL)
                return NULL;
@@ -85,6 +90,14 @@ static struct wpabuf * p2p_build_invitation_req(struct p2p_data *p2p,
                wpabuf_put_buf(buf, wfd_ie);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+       if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ])
+               wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_INV_REQ]);
+
+       if (dev_pw_id >= 0) {
+               /* WSC IE in Invitation Request for NFC static handover */
+               p2p_build_wps_ie(p2p, buf, dev_pw_id, 0);
+       }
+
        return buf;
 }
 
@@ -161,7 +174,7 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
        u8 group_bssid[ETH_ALEN], *bssid;
        int op_freq = 0;
        u8 reg_class = 0, channel = 0;
-       struct p2p_channels intersection, *channels = NULL;
+       struct p2p_channels all_channels, intersection, *channels = NULL;
        int persistent;
 
        os_memset(group_bssid, 0, sizeof(group_bssid));
@@ -213,7 +226,10 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
                persistent = 1;
        }
 
-       if (p2p_peer_channels_check(p2p, &p2p->cfg->channels, dev,
+       p2p_channels_union(&p2p->cfg->channels, &p2p->cfg->cli_channels,
+                          &all_channels);
+
+       if (p2p_peer_channels_check(p2p, &all_channels, dev,
                                    msg.channel_list, msg.channel_list_len) <
            0) {
                p2p_dbg(p2p, "No common channels found");
@@ -221,14 +237,30 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
                goto fail;
        }
 
-       p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
+       p2p_channels_dump(p2p, "own channels", &p2p->cfg->channels);
+       p2p_channels_dump(p2p, "own client channels", &all_channels);
+       p2p_channels_dump(p2p, "peer channels", &dev->channels);
+       p2p_channels_intersect(&all_channels, &dev->channels,
                               &intersection);
+       p2p_channels_dump(p2p, "intersection", &intersection);
 
        if (p2p->cfg->invitation_process) {
                status = p2p->cfg->invitation_process(
                        p2p->cfg->cb_ctx, sa, msg.group_bssid, msg.group_id,
                        msg.group_id + ETH_ALEN, msg.group_id_len - ETH_ALEN,
-                       &go, group_bssid, &op_freq, persistent, &intersection);
+                       &go, group_bssid, &op_freq, persistent, &intersection,
+                       msg.dev_password_id_present ? msg.dev_password_id : -1);
+       }
+
+       if (go) {
+               p2p_channels_intersect(&p2p->cfg->channels, &dev->channels,
+                                      &intersection);
+               p2p_channels_dump(p2p, "intersection(GO)", &intersection);
+               if (intersection.reg_classes == 0) {
+                       p2p_dbg(p2p, "No common channels found (GO)");
+                       status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
+                       goto fail;
+               }
        }
 
        if (op_freq) {
@@ -281,7 +313,9 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
                        }
                }
 
-               if (!p2p_channels_includes(&intersection, p2p->op_reg_class,
+               /* Reselect the channel only for the case of the GO */
+               if (go &&
+                   !p2p_channels_includes(&intersection, p2p->op_reg_class,
                                           p2p->op_channel)) {
                        p2p_dbg(p2p, "Initially selected channel (op_class %d channel %d) not in channel intersection - try to reselect",
                                p2p->op_reg_class, p2p->op_channel);
@@ -296,7 +330,7 @@ void p2p_process_invitation_req(struct p2p_data *p2p, const u8 *sa,
                                status = P2P_SC_FAIL_NO_COMMON_CHANNELS;
                                goto fail;
                        }
-               } else if (!(dev->flags & P2P_DEV_FORCE_FREQ) &&
+               } else if (go && !(dev->flags & P2P_DEV_FORCE_FREQ) &&
                           !p2p->cfg->cfg_op_channel) {
                        p2p_dbg(p2p, "Try to reselect channel selection with peer information received; previously selected op_class %u channel %u",
                                p2p->op_reg_class, p2p->op_channel);
@@ -352,18 +386,23 @@ fail:
                p2p->inv_group_bssid_ptr = p2p->inv_group_bssid;
        } else
                p2p->inv_group_bssid_ptr = NULL;
-       if (msg.group_id_len - ETH_ALEN <= 32) {
-               os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN,
-                         msg.group_id_len - ETH_ALEN);
-               p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN;
+       if (msg.group_id) {
+               if (msg.group_id_len - ETH_ALEN <= 32) {
+                       os_memcpy(p2p->inv_ssid, msg.group_id + ETH_ALEN,
+                                 msg.group_id_len - ETH_ALEN);
+                       p2p->inv_ssid_len = msg.group_id_len - ETH_ALEN;
+               }
+               os_memcpy(p2p->inv_go_dev_addr, msg.group_id, ETH_ALEN);
+       } else {
+               p2p->inv_ssid_len = 0;
+               os_memset(p2p->inv_go_dev_addr, 0, ETH_ALEN);
        }
-       os_memcpy(p2p->inv_go_dev_addr, msg.group_id, ETH_ALEN);
        p2p->inv_status = status;
        p2p->inv_op_freq = op_freq;
 
        p2p->pending_action_state = P2P_PENDING_INVITATION_RESPONSE;
        if (p2p_send_action(p2p, freq, sa,
-#ifdef TIZEN_EXT
+#ifdef BCM_DRIVER_V115
                            p2p->cfg->own_addr, p2p->cfg->own_addr,
 #else
                            p2p->cfg->dev_addr, p2p->cfg->dev_addr,
@@ -392,26 +431,69 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
        if (dev == NULL) {
                p2p_dbg(p2p, "Ignore Invitation Response from unknown peer "
                        MACSTR, MAC2STR(sa));
+               p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
                return;
        }
 
        if (dev != p2p->invite_peer) {
                p2p_dbg(p2p, "Ignore unexpected Invitation Response from peer "
                        MACSTR, MAC2STR(sa));
+               p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
                return;
        }
 
-       if (p2p_parse(data, len, &msg))
+       if (p2p_parse(data, len, &msg)) {
+               p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
                return;
+       }
 
        if (!msg.status) {
                p2p_dbg(p2p, "Mandatory Status attribute missing in Invitation Response from "
                        MACSTR, MAC2STR(sa));
                p2p_parse_free(&msg);
+               p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
                return;
        }
 
-       if (!msg.channel_list) {
+       /*
+        * We should not really receive a replayed response twice since
+        * duplicate frames are supposed to be dropped. However, not all drivers
+        * do that for pre-association frames. We did not use to verify dialog
+        * token matches for invitation response frames, but that check can be
+        * safely used to drop a replayed response to the previous Invitation
+        * Request in case the suggested operating channel was changed. This
+        * allows a duplicated reject frame to be dropped with the assumption
+        * that the real response follows after it.
+        */
+       if (*msg.status == P2P_SC_FAIL_NO_COMMON_CHANNELS &&
+           p2p->retry_invite_req_sent &&
+           msg.dialog_token != dev->dialog_token) {
+               p2p_dbg(p2p, "Unexpected Dialog Token %u (expected %u)",
+                       msg.dialog_token, dev->dialog_token);
+               p2p_parse_free(&msg);
+               return;
+       }
+
+       if (*msg.status == P2P_SC_FAIL_NO_COMMON_CHANNELS &&
+           p2p->retry_invite_req &&
+           p2p_channel_random_social(&p2p->cfg->channels, &p2p->op_reg_class,
+                                     &p2p->op_channel) == 0) {
+               p2p->retry_invite_req = 0;
+               p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+               p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
+               p2p_set_state(p2p, P2P_INVITE);
+               p2p_dbg(p2p, "Resend Invitation Request setting op_class %u channel %u as operating channel",
+                       p2p->op_reg_class, p2p->op_channel);
+               p2p->retry_invite_req_sent = 1;
+               p2p_invite_send(p2p, p2p->invite_peer, p2p->invite_go_dev_addr,
+                               p2p->invite_dev_pw_id);
+               p2p_parse_free(&msg);
+               return;
+       }
+       p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
+       p2p->retry_invite_req = 0;
+
+       if (!msg.channel_list && *msg.status == P2P_SC_SUCCESS) {
                p2p_dbg(p2p, "Mandatory Channel List attribute missing in Invitation Response from "
                        MACSTR, MAC2STR(sa));
 #ifdef CONFIG_P2P_STRICT
@@ -420,6 +502,9 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
 #endif /* CONFIG_P2P_STRICT */
                /* Try to survive without peer channel list */
                channels = &p2p->channels;
+       } else if (!msg.channel_list) {
+               /* Non-success cases are not required to include Channel List */
+               channels = &p2p->channels;
        } else if (p2p_peer_channels_check(p2p, &p2p->channels, dev,
                                           msg.channel_list,
                                           msg.channel_list_len) < 0) {
@@ -432,9 +517,25 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
                channels = &intersection;
        }
 
-       if (p2p->cfg->invitation_result)
+       if (p2p->cfg->invitation_result) {
+               int peer_oper_freq = 0;
+               int freq = p2p_channel_to_freq(p2p->op_reg_class,
+                                              p2p->op_channel);
+               if (freq < 0)
+                       freq = 0;
+
+               if (msg.operating_channel) {
+                       peer_oper_freq = p2p_channel_to_freq(
+                               msg.operating_channel[3],
+                               msg.operating_channel[4]);
+                       if (peer_oper_freq < 0)
+                               peer_oper_freq = 0;
+               }
+
                p2p->cfg->invitation_result(p2p->cfg->cb_ctx, *msg.status,
-                                           msg.group_bssid, channels, sa);
+                                           msg.group_bssid, channels, sa,
+                                           freq, peer_oper_freq);
+       }
 
        p2p_parse_free(&msg);
 
@@ -445,12 +546,14 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
 
 
 int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
-                   const u8 *go_dev_addr)
+                   const u8 *go_dev_addr, int dev_pw_id)
 {
        struct wpabuf *req;
        int freq;
 
        freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
+       if (freq <= 0)
+               freq = dev->oob_go_neg_freq;
        if (freq <= 0) {
                p2p_dbg(p2p, "No Listen/Operating frequency known for the peer "
                        MACSTR " to send Invitation Request",
@@ -458,7 +561,7 @@ int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
                return -1;
        }
 
-       req = p2p_build_invitation_req(p2p, dev, go_dev_addr);
+       req = p2p_build_invitation_req(p2p, dev, go_dev_addr, dev_pw_id);
        if (req == NULL)
                return -1;
        if (p2p->state != P2P_IDLE)
@@ -469,15 +572,17 @@ int p2p_invite_send(struct p2p_data *p2p, struct p2p_device *dev,
        p2p->invite_peer = dev;
        dev->invitation_reqs++;
        if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
-#ifdef TIZEN_EXT_P2P
+#ifdef BCM_DRIVER_V115
                            p2p->cfg->own_addr, dev->info.p2p_device_addr,
 #else
                            p2p->cfg->dev_addr, dev->info.p2p_device_addr,
 #endif
-                           wpabuf_head(req), wpabuf_len(req), 200) < 0) {
+                           wpabuf_head(req), wpabuf_len(req), 500) < 0) {
                p2p_dbg(p2p, "Failed to send Action frame");
                /* Use P2P find to recover and retry */
                p2p_set_timeout(p2p, 0, 0);
+       } else {
+               dev->flags |= P2P_DEV_WAIT_INV_REQ_ACK;
        }
 
        wpabuf_free(req);
@@ -495,12 +600,15 @@ void p2p_invitation_req_cb(struct p2p_data *p2p, int success)
                return;
        }
 
+       if (success)
+               p2p->invite_peer->flags &= ~P2P_DEV_WAIT_INV_REQ_ACK;
+
        /*
         * Use P2P find, if needed, to find the other device from its listen
         * channel.
         */
        p2p_set_state(p2p, P2P_INVITE);
-       p2p_set_timeout(p2p, 0, success ? 350000 : 100000);
+       p2p_set_timeout(p2p, 0, success ? 500000 : 100000);
 }
 
 
@@ -527,7 +635,7 @@ void p2p_invitation_resp_cb(struct p2p_data *p2p, int success)
 int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
               const u8 *bssid, const u8 *ssid, size_t ssid_len,
               unsigned int force_freq, const u8 *go_dev_addr,
-              int persistent_group, unsigned int pref_freq)
+              int persistent_group, unsigned int pref_freq, int dev_pw_id)
 {
        struct p2p_device *dev;
 
@@ -545,15 +653,25 @@ int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
                p2p->invite_go_dev_addr = NULL;
        wpa_hexdump_ascii(MSG_DEBUG, "Invitation for SSID",
                          ssid, ssid_len);
+       if (dev_pw_id >= 0) {
+               p2p_dbg(p2p, "Invitation to use Device Password ID %d",
+                       dev_pw_id);
+       }
+       p2p->invite_dev_pw_id = dev_pw_id;
+       p2p->retry_invite_req = role == P2P_INVITE_ROLE_GO &&
+               persistent_group && !force_freq;
+       p2p->retry_invite_req_sent = 0;
 
        dev = p2p_get_device(p2p, peer);
-       if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0)) {
+       if (dev == NULL || (dev->listen_freq <= 0 && dev->oper_freq <= 0 &&
+                           dev->oob_go_neg_freq <= 0)) {
                p2p_dbg(p2p, "Cannot invite unknown P2P Device " MACSTR,
                        MAC2STR(peer));
                return -1;
        }
 
-       if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq) < 0)
+       if (p2p_prepare_channel(p2p, dev, force_freq, pref_freq,
+                               role != P2P_INVITE_ROLE_CLIENT) < 0)
                return -1;
 
        if (persistent_group && role == P2P_INVITE_ROLE_CLIENT && !force_freq &&
@@ -584,5 +702,5 @@ int p2p_invite(struct p2p_data *p2p, const u8 *peer, enum p2p_invite_role role,
        os_memcpy(p2p->inv_ssid, ssid, ssid_len);
        p2p->inv_ssid_len = ssid_len;
        p2p->inv_persistent = persistent_group;
-       return p2p_invite_send(p2p, dev, go_dev_addr);
+       return p2p_invite_send(p2p, dev, go_dev_addr, dev_pw_id);
 }
old mode 100644 (file)
new mode 100755 (executable)
index 097a31d..fd6a461
@@ -268,6 +268,125 @@ static int p2p_parse_attribute(u8 id, const u8 *data, u16 len,
                wpa_printf(MSG_DEBUG, "P2P: * Minor Reason Code: %u",
                           *msg->minor_reason_code);
                break;
+       case P2P_ATTR_OOB_GO_NEG_CHANNEL:
+               if (len < 6) {
+                       wpa_printf(MSG_DEBUG, "P2P: Too short OOB GO Neg "
+                                  "Channel attribute (length %d)", len);
+                       return -1;
+               }
+               msg->oob_go_neg_channel = data;
+               wpa_printf(MSG_DEBUG, "P2P: * OOB GO Neg Channel: "
+                          "Country %c%c(0x%02x) Operating Class %d "
+                          "Channel Number %d Role %d",
+                          data[0], data[1], data[2], data[3], data[4],
+                          data[5]);
+               break;
+       case P2P_ATTR_SERVICE_HASH:
+               if (len < P2PS_HASH_LEN) {
+                       wpa_printf(MSG_DEBUG,
+                                  "P2P: Too short Service Hash (length %u)",
+                                  len);
+                       return -1;
+               }
+               msg->service_hash_count = len / P2PS_HASH_LEN;
+               msg->service_hash = data;
+               wpa_hexdump(MSG_DEBUG, "P2P: * Service Hash(s)", data, len);
+               break;
+       case P2P_ATTR_SESSION_INFORMATION_DATA:
+               msg->session_info = data;
+               msg->session_info_len = len;
+               wpa_printf(MSG_DEBUG, "P2P: * Service Instance: %u bytes - %p",
+                          len, data);
+               break;
+       case P2P_ATTR_CONNECTION_CAPABILITY:
+               if (len < 1) {
+                       wpa_printf(MSG_DEBUG,
+                                  "P2P: Too short Connection Capability (length %u)",
+                                  len);
+                       return -1;
+               }
+               msg->conn_cap = data;
+               wpa_printf(MSG_DEBUG, "P2P: * Connection Capability: 0x%x",
+                          *msg->conn_cap);
+               break;
+       case P2P_ATTR_ADVERTISEMENT_ID:
+               if (len < 10) {
+                       wpa_printf(MSG_DEBUG,
+                                  "P2P: Too short Advertisement ID (length %u)",
+                                  len);
+                       return -1;
+               }
+               msg->adv_id = data;
+               msg->adv_mac = &data[sizeof(u32)];
+               wpa_printf(MSG_DEBUG, "P2P: * Advertisement ID %x",
+                          WPA_GET_LE32(data));
+               break;
+       case P2P_ATTR_ADVERTISED_SERVICE:
+               if (len < 8) {
+                       wpa_printf(MSG_DEBUG,
+                                  "P2P: Too short Service Instance (length %u)",
+                                  len);
+                       return -1;
+               }
+               msg->adv_service_instance = data;
+               msg->adv_service_instance_len = len;
+               if (len <= 255 + 8) {
+                       char str[256];
+                       u8 namelen;
+
+                       namelen = data[6];
+                       if (namelen > len - 7)
+                               break;
+                       os_memcpy(str, &data[7], namelen);
+                       str[namelen] = '\0';
+                       wpa_printf(MSG_DEBUG, "P2P: * Service Instance: %x-%s",
+                                  WPA_GET_LE32(data), str);
+               } else {
+                       wpa_printf(MSG_DEBUG, "P2P: * Service Instance: %p",
+                                  data);
+               }
+               break;
+       case P2P_ATTR_SESSION_ID:
+               if (len < sizeof(u32) + ETH_ALEN) {
+                       wpa_printf(MSG_DEBUG,
+                                  "P2P: Too short Session ID Info (length %u)",
+                                  len);
+                       return -1;
+               }
+               msg->session_id = data;
+               msg->session_mac = &data[sizeof(u32)];
+               wpa_printf(MSG_DEBUG, "P2P: * Session ID: %x " MACSTR,
+                          WPA_GET_LE32(data), MAC2STR(msg->session_mac));
+               break;
+       case P2P_ATTR_FEATURE_CAPABILITY:
+               if (!len) {
+                       wpa_printf(MSG_DEBUG,
+                                  "P2P: Too short Feature Capability (length %u)",
+                                  len);
+                       return -1;
+               }
+               msg->feature_cap = data;
+               msg->feature_cap_len = len;
+               wpa_printf(MSG_DEBUG, "P2P: * Feature Cap (length=%u)", len);
+               break;
+       case P2P_ATTR_PERSISTENT_GROUP:
+       {
+               if (len < ETH_ALEN) {
+                       wpa_printf(MSG_DEBUG,
+                                  "P2P: Too short Persistent Group Info (length %u)",
+                                  len);
+                       return -1;
+               }
+
+               msg->persistent_dev = data;
+               msg->persistent_ssid_len = len - ETH_ALEN;
+               msg->persistent_ssid = &data[ETH_ALEN];
+               wpa_printf(MSG_DEBUG, "P2P: * Persistent Group: " MACSTR " %s",
+                          MAC2STR(msg->persistent_dev),
+                          wpa_ssid_txt(msg->persistent_ssid,
+                                       msg->persistent_ssid_len));
+               break;
+       }
        default:
                wpa_printf(MSG_DEBUG, "P2P: Skipped unknown attribute %d "
                           "(length %d)", id, len);
@@ -296,23 +415,27 @@ int p2p_parse_p2p_ie(const struct wpabuf *buf, struct p2p_message *msg)
 
        while (pos < end) {
                u16 attr_len;
-               if (pos + 2 >= end) {
+               u8 id;
+
+               if (end - pos < 3) {
                        wpa_printf(MSG_DEBUG, "P2P: Invalid P2P attribute");
                        return -1;
                }
-               attr_len = WPA_GET_LE16(pos + 1);
+               id = *pos++;
+               attr_len = WPA_GET_LE16(pos);
+               pos += 2;
                wpa_printf(MSG_DEBUG, "P2P: Attribute %d length %u",
-                          pos[0], attr_len);
-               if (pos + 3 + attr_len > end) {
+                          id, attr_len);
+               if (attr_len > end - pos) {
                        wpa_printf(MSG_DEBUG, "P2P: Attribute underflow "
                                   "(len=%u left=%d)",
-                                  attr_len, (int) (end - pos - 3));
+                                  attr_len, (int) (end - pos));
                        wpa_hexdump(MSG_MSGDUMP, "P2P: Data", pos, end - pos);
                        return -1;
                }
-               if (p2p_parse_attribute(pos[0], pos + 3, attr_len, msg))
+               if (p2p_parse_attribute(id, pos, attr_len, msg))
                        return -1;
-               pos += 3 + attr_len;
+               pos += attr_len;
        }
 
        return 0;
@@ -340,6 +463,7 @@ static int p2p_parse_wps_ie(const struct wpabuf *buf, struct p2p_message *msg)
                msg->dev_password_id = WPA_GET_BE16(attr.dev_password_id);
                wpa_printf(MSG_DEBUG, "P2P: Device Password ID: %d",
                           msg->dev_password_id);
+               msg->dev_password_id_present = 1;
        }
        if (attr.primary_dev_type) {
                char devtype[WPS_DEV_TYPE_BUFSIZE];
@@ -367,6 +491,9 @@ static int p2p_parse_wps_ie(const struct wpabuf *buf, struct p2p_message *msg)
        msg->serial_number = attr.serial_number;
        msg->serial_number_len = attr.serial_number_len;
 
+       msg->oob_dev_password = attr.oob_dev_password;
+       msg->oob_dev_password_len = attr.oob_dev_password_len;
+
        return 0;
 }
 
@@ -450,6 +577,33 @@ int p2p_parse(const u8 *data, size_t len, struct p2p_message *msg)
 }
 
 
+int p2p_parse_ies_separate(const u8 *wsc, size_t wsc_len, const u8 *p2p,
+                          size_t p2p_len, struct p2p_message *msg)
+{
+       os_memset(msg, 0, sizeof(*msg));
+
+       msg->wps_attributes = wpabuf_alloc_copy(wsc, wsc_len);
+       if (msg->wps_attributes &&
+           p2p_parse_wps_ie(msg->wps_attributes, msg)) {
+               p2p_parse_free(msg);
+               return -1;
+       }
+
+       msg->p2p_attributes = wpabuf_alloc_copy(p2p, p2p_len);
+       if (msg->p2p_attributes &&
+           p2p_parse_p2p_ie(msg->p2p_attributes, msg)) {
+               wpa_printf(MSG_DEBUG, "P2P: Failed to parse P2P IE data");
+               if (msg->p2p_attributes)
+                       wpa_hexdump_buf(MSG_MSGDUMP, "P2P: P2P IE data",
+                                       msg->p2p_attributes);
+               p2p_parse_free(msg);
+               return -1;
+       }
+
+       return 0;
+}
+
+
 /**
  * p2p_parse_free - Free temporary data from P2P parsing
  * @msg: Parsed attributes
@@ -559,7 +713,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf,
                                  "dev=" MACSTR " iface=" MACSTR,
                                  MAC2STR(cli->p2p_device_addr),
                                  MAC2STR(cli->p2p_interface_addr));
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
 
@@ -570,7 +724,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf,
                                  wps_dev_type_bin2str(cli->pri_dev_type,
                                                       devtype,
                                                       sizeof(devtype)));
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
 
@@ -579,7 +733,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf,
                                          wps_dev_type_bin2str(
                                                  &cli->sec_dev_types[s * 8],
                                                  devtype, sizeof(devtype)));
-                       if (ret < 0 || ret >= end - pos)
+                       if (os_snprintf_error(end - pos, ret))
                                return pos - buf;
                        pos += ret;
                }
@@ -594,7 +748,7 @@ static int p2p_group_info_text(const u8 *gi, size_t gi_len, char *buf,
                }
 
                ret = os_snprintf(pos, end - pos, " dev_name='%s'\n", name);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
@@ -628,7 +782,7 @@ int p2p_attr_text(struct wpabuf *data, char *buf, char *end)
                                  "p2p_dev_capab=0x%x\n"
                                  "p2p_group_capab=0x%x\n",
                                  msg.capability[0], msg.capability[1]);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
@@ -640,14 +794,14 @@ int p2p_attr_text(struct wpabuf *data, char *buf, char *end)
                                  wps_dev_type_bin2str(msg.pri_dev_type,
                                                       devtype,
                                                       sizeof(devtype)));
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
 
        ret = os_snprintf(pos, end - pos, "p2p_device_name=%s\n",
                          msg.device_name);
-       if (ret < 0 || ret >= end - pos)
+       if (os_snprintf_error(end - pos, ret))
                return pos - buf;
        pos += ret;
 
@@ -655,14 +809,14 @@ int p2p_attr_text(struct wpabuf *data, char *buf, char *end)
                ret = os_snprintf(pos, end - pos, "p2p_device_addr=" MACSTR
                                  "\n",
                                  MAC2STR(msg.p2p_device_addr));
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
 
        ret = os_snprintf(pos, end - pos, "p2p_config_methods=0x%x\n",
                          msg.config_methods);
-       if (ret < 0 || ret >= end - pos)
+       if (os_snprintf_error(end - pos, ret))
                return pos - buf;
        pos += ret;
 
old mode 100644 (file)
new mode 100755 (executable)
index c455d01..bb55aa8
@@ -10,6 +10,7 @@
 
 #include "common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
 #include "wps/wps_defs.h"
 #include "p2p_i.h"
 #include "p2p.h"
@@ -39,20 +40,145 @@ static void p2p_build_wps_ie_config_methods(struct wpabuf *buf,
 }
 
 
+static void p2ps_add_new_group_info(struct p2p_data *p2p, struct wpabuf *buf)
+{
+       int found;
+       u8 intended_addr[ETH_ALEN];
+       u8 ssid[32];
+       size_t ssid_len;
+       int group_iface;
+
+       if (!p2p->cfg->get_go_info)
+               return;
+
+       found = p2p->cfg->get_go_info(
+               p2p->cfg->cb_ctx, intended_addr, ssid,
+               &ssid_len, &group_iface);
+       if (found) {
+               p2p_buf_add_group_id(buf, p2p->cfg->dev_addr,
+                                    ssid, ssid_len);
+               p2p_buf_add_intended_addr(buf, intended_addr);
+       } else {
+               if (!p2p->ssid_set) {
+                       p2p_build_ssid(p2p, p2p->ssid, &p2p->ssid_len);
+                       p2p->ssid_set = 1;
+               }
+
+               /* Add pre-composed P2P Group ID */
+               p2p_buf_add_group_id(buf, p2p->cfg->dev_addr,
+                                    p2p->ssid, p2p->ssid_len);
+
+               if (group_iface)
+                       p2p_buf_add_intended_addr(
+                               buf, p2p->intended_addr);
+               else
+                       p2p_buf_add_intended_addr(
+                               buf, p2p->cfg->dev_addr);
+       }
+}
+
+
+static void p2ps_add_pd_req_attrs(struct p2p_data *p2p, struct p2p_device *dev,
+                                 struct wpabuf *buf, u16 config_methods)
+{
+       struct p2ps_provision *prov = p2p->p2ps_prov;
+       u8 feat_cap_mask[] = { 1, 0 };
+       int shared_group = 0;
+       u8 ssid[32];
+       size_t ssid_len;
+       u8 go_dev_addr[ETH_ALEN];
+
+       /* If we might be explicite group owner, add GO details */
+       if (prov->conncap & (P2PS_SETUP_GROUP_OWNER |
+                            P2PS_SETUP_NEW))
+               p2ps_add_new_group_info(p2p, buf);
+
+       if (prov->status >= 0)
+               p2p_buf_add_status(buf, (u8) prov->status);
+       else
+               prov->method = config_methods;
+
+       if (p2p->cfg->get_persistent_group) {
+               shared_group = p2p->cfg->get_persistent_group(
+                       p2p->cfg->cb_ctx, dev->info.p2p_device_addr, NULL, 0,
+                       go_dev_addr, ssid, &ssid_len);
+       }
+
+       /* Add Operating Channel if conncap includes GO */
+       if (shared_group ||
+           (prov->conncap & (P2PS_SETUP_GROUP_OWNER |
+                             P2PS_SETUP_NEW))) {
+               u8 tmp;
+
+               p2p_go_select_channel(p2p, dev, &tmp);
+
+               if (p2p->op_reg_class && p2p->op_channel)
+                       p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+                                                     p2p->op_reg_class,
+                                                     p2p->op_channel);
+               else
+                       p2p_buf_add_operating_channel(buf, p2p->cfg->country,
+                                                     p2p->cfg->op_reg_class,
+                                                     p2p->cfg->op_channel);
+       }
+
+       p2p_buf_add_channel_list(buf, p2p->cfg->country, &p2p->cfg->channels);
+
+       if (prov->info[0])
+               p2p_buf_add_session_info(buf, prov->info);
+
+       p2p_buf_add_connection_capability(buf, prov->conncap);
+
+       p2p_buf_add_advertisement_id(buf, prov->adv_id, prov->adv_mac);
+
+       if (shared_group || prov->conncap == P2PS_SETUP_NEW ||
+           prov->conncap ==
+           (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW) ||
+           prov->conncap ==
+           (P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT)) {
+               /* Add Config Timeout */
+               p2p_buf_add_config_timeout(buf, p2p->go_timeout,
+                                          p2p->client_timeout);
+       }
+
+       p2p_buf_add_listen_channel(buf, p2p->cfg->country, p2p->cfg->reg_class,
+                                  p2p->cfg->channel);
+
+       p2p_buf_add_session_id(buf, prov->session_id, prov->session_mac);
+
+       p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask),
+                                      feat_cap_mask);
+
+       if (shared_group)
+               p2p_buf_add_persistent_group_info(buf, go_dev_addr,
+                                                 ssid, ssid_len);
+}
+
+
 static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
-                                              u8 dialog_token,
-                                              u16 config_methods,
-                                              struct p2p_device *go)
+                                              struct p2p_device *dev,
+                                              int join)
 {
        struct wpabuf *buf;
        u8 *len;
        size_t extra = 0;
+       u8 dialog_token = dev->dialog_token;
+       u16 config_methods = dev->req_config_methods;
+       struct p2p_device *go = join ? dev : NULL;
+       u8 group_capab;
 
 #ifdef CONFIG_WIFI_DISPLAY
        if (p2p->wfd_ie_prov_disc_req)
                extra = wpabuf_len(p2p->wfd_ie_prov_disc_req);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+       if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ])
+               extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]);
+
+       if (p2p->p2ps_prov)
+               extra += os_strlen(p2p->p2ps_prov->info) + 1 +
+                       sizeof(struct p2ps_provision);
+
        buf = wpabuf_alloc(1000 + extra);
        if (buf == NULL)
                return NULL;
@@ -60,10 +186,23 @@ static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
        p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_REQ, dialog_token);
 
        len = p2p_buf_add_ie_hdr(buf);
+
+       group_capab = 0;
+       if (p2p->p2ps_prov) {
+               group_capab |= P2P_GROUP_CAPAB_PERSISTENT_GROUP;
+               group_capab |= P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+               if (p2p->cross_connect)
+                       group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+               if (p2p->cfg->p2p_intra_bss)
+                       group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+       }
        p2p_buf_add_capability(buf, p2p->dev_capab &
-                              ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY, 0);
+                              ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
+                              group_capab);
        p2p_buf_add_device_info(buf, p2p, NULL);
-       if (go) {
+       if (p2p->p2ps_prov) {
+               p2ps_add_pd_req_attrs(p2p, dev, buf, config_methods);
+       } else if (go) {
                p2p_buf_add_group_id(buf, go->info.p2p_device_addr,
                                     go->oper_ssid, go->oper_ssid_len);
        }
@@ -77,18 +216,27 @@ static struct wpabuf * p2p_build_prov_disc_req(struct p2p_data *p2p,
                wpabuf_put_buf(buf, p2p->wfd_ie_prov_disc_req);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+       if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ])
+               wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_REQ]);
+
        return buf;
 }
 
 
 static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
+                                               struct p2p_device *dev,
                                                u8 dialog_token,
+                                               enum p2p_status_code status,
                                                u16 config_methods,
+                                               u32 adv_id,
                                                const u8 *group_id,
-                                               size_t group_id_len)
+                                               size_t group_id_len,
+                                               const u8 *persist_ssid,
+                                               size_t persist_ssid_len)
 {
        struct wpabuf *buf;
        size_t extra = 0;
+       int persist = 0;
 
 #ifdef CONFIG_WIFI_DISPLAY
        struct wpabuf *wfd_ie = p2p->wfd_ie_prov_disc_resp;
@@ -111,12 +259,106 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
                extra = wpabuf_len(wfd_ie);
 #endif /* CONFIG_WIFI_DISPLAY */
 
-       buf = wpabuf_alloc(100 + extra);
+       if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP])
+               extra += wpabuf_len(p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]);
+
+       buf = wpabuf_alloc(1000 + extra);
        if (buf == NULL)
                return NULL;
 
        p2p_buf_add_public_action_hdr(buf, P2P_PROV_DISC_RESP, dialog_token);
 
+       /* Add P2P IE for P2PS */
+       if (p2p->p2ps_prov && p2p->p2ps_prov->adv_id == adv_id) {
+               u8 feat_cap_mask[] = { 1, 0 };
+               u8 *len = p2p_buf_add_ie_hdr(buf);
+               struct p2ps_provision *prov = p2p->p2ps_prov;
+               u8 group_capab;
+
+               if (!status && prov->status != -1)
+                       status = prov->status;
+
+               p2p_buf_add_status(buf, status);
+               group_capab = P2P_GROUP_CAPAB_PERSISTENT_GROUP |
+                       P2P_GROUP_CAPAB_PERSISTENT_RECONN;
+               if (p2p->cross_connect)
+                       group_capab |= P2P_GROUP_CAPAB_CROSS_CONN;
+               if (p2p->cfg->p2p_intra_bss)
+                       group_capab |= P2P_GROUP_CAPAB_INTRA_BSS_DIST;
+               p2p_buf_add_capability(buf, p2p->dev_capab &
+                                      ~P2P_DEV_CAPAB_CLIENT_DISCOVERABILITY,
+                                      group_capab);
+               p2p_buf_add_device_info(buf, p2p, NULL);
+
+               if (persist_ssid && p2p->cfg->get_persistent_group &&
+                   (status == P2P_SC_SUCCESS ||
+                    status == P2P_SC_SUCCESS_DEFERRED)) {
+                       u8 ssid[32];
+                       size_t ssid_len;
+                       u8 go_dev_addr[ETH_ALEN];
+
+                       persist = p2p->cfg->get_persistent_group(
+                               p2p->cfg->cb_ctx,
+                               dev->info.p2p_device_addr,
+                               persist_ssid, persist_ssid_len, go_dev_addr,
+                               ssid, &ssid_len);
+                       if (persist)
+                               p2p_buf_add_persistent_group_info(
+                                       buf, go_dev_addr, ssid, ssid_len);
+               }
+
+               if (!persist && (prov->conncap & P2PS_SETUP_GROUP_OWNER))
+                       p2ps_add_new_group_info(p2p, buf);
+
+               /* Add Operating Channel if conncap indicates GO */
+               if (persist || (prov->conncap & P2PS_SETUP_GROUP_OWNER)) {
+                       u8 tmp;
+
+                       if (dev)
+                               p2p_go_select_channel(p2p, dev, &tmp);
+
+                       if (p2p->op_reg_class && p2p->op_channel)
+                               p2p_buf_add_operating_channel(
+                                       buf, p2p->cfg->country,
+                                       p2p->op_reg_class,
+                                       p2p->op_channel);
+                       else
+                               p2p_buf_add_operating_channel(
+                                       buf, p2p->cfg->country,
+                                       p2p->cfg->op_reg_class,
+                                       p2p->cfg->op_channel);
+               }
+
+               p2p_buf_add_channel_list(buf, p2p->cfg->country,
+                                        &p2p->cfg->channels);
+
+               if (!persist && (status == P2P_SC_SUCCESS ||
+                                status == P2P_SC_SUCCESS_DEFERRED))
+                       p2p_buf_add_connection_capability(buf, prov->conncap);
+
+               p2p_buf_add_advertisement_id(buf, adv_id, prov->adv_mac);
+
+               p2p_buf_add_config_timeout(buf, p2p->go_timeout,
+                                          p2p->client_timeout);
+
+               p2p_buf_add_session_id(buf, prov->session_id,
+                                      prov->session_mac);
+
+               p2p_buf_add_feature_capability(buf, sizeof(feat_cap_mask),
+                                              feat_cap_mask);
+               p2p_buf_update_ie_hdr(buf, len);
+       } else if (status != P2P_SC_SUCCESS || adv_id) {
+               u8 *len = p2p_buf_add_ie_hdr(buf);
+
+               p2p_buf_add_status(buf, status);
+
+               if (p2p->p2ps_prov)
+                       p2p_buf_add_advertisement_id(buf, adv_id,
+                                                    p2p->p2ps_prov->adv_mac);
+
+               p2p_buf_update_ie_hdr(buf, len);
+       }
+
        /* WPS IE with Config Methods attribute */
        p2p_build_wps_ie_config_methods(buf, config_methods);
 
@@ -125,18 +367,57 @@ static struct wpabuf * p2p_build_prov_disc_resp(struct p2p_data *p2p,
                wpabuf_put_buf(buf, wfd_ie);
 #endif /* CONFIG_WIFI_DISPLAY */
 
+       if (p2p->vendor_elem && p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP])
+               wpabuf_put_buf(buf, p2p->vendor_elem[VENDOR_ELEM_P2P_PD_RESP]);
+
        return buf;
 }
 
 
+static int p2ps_setup_p2ps_prov(struct p2p_data *p2p, u32 adv_id,
+                               u32 session_id, u16 method,
+                               const u8 *session_mac, const u8 *adv_mac)
+{
+       struct p2ps_provision *tmp;
+
+       if (!p2p->p2ps_prov) {
+               p2p->p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + 1);
+               if (!p2p->p2ps_prov)
+                       return -1;
+       } else {
+               os_memset(p2p->p2ps_prov, 0, sizeof(struct p2ps_provision) + 1);
+       }
+
+       tmp = p2p->p2ps_prov;
+       tmp->adv_id = adv_id;
+       tmp->session_id = session_id;
+       tmp->method = method;
+       os_memcpy(tmp->session_mac, session_mac, ETH_ALEN);
+       os_memcpy(tmp->adv_mac, adv_mac, ETH_ALEN);
+       tmp->info[0] = '\0';
+
+       return 0;
+}
+
+
 void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
                               const u8 *data, size_t len, int rx_freq)
 {
        struct p2p_message msg;
        struct p2p_device *dev;
        int freq;
-       int reject = 1;
+       enum p2p_status_code reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
        struct wpabuf *resp;
+       u32 adv_id = 0;
+       struct p2ps_advertisement *p2ps_adv = NULL;
+       u8 conncap = P2PS_SETUP_NEW;
+       u8 auto_accept = 0;
+       u32 session_id = 0;
+       u8 session_mac[ETH_ALEN];
+       u8 adv_mac[ETH_ALEN];
+       u8 group_mac[ETH_ALEN];
+       int passwd_id = DEV_PW_DEFAULT;
+       u16 config_methods;
 
        if (p2p_parse(data, len, &msg))
                return;
@@ -162,12 +443,13 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
 
        if (!(msg.wps_config_methods &
              (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD |
-              WPS_CONFIG_PUSHBUTTON))) {
+              WPS_CONFIG_PUSHBUTTON | WPS_CONFIG_P2PS))) {
                p2p_dbg(p2p, "Unsupported Config Methods in Provision Discovery Request");
                goto out;
        }
 
-       if (msg.group_id) {
+       /* Legacy (non-P2PS) - Unknown groups allowed for P2PS */
+       if (!msg.adv_id && msg.group_id) {
                size_t i;
                for (i = 0; i < p2p->num_groups; i++) {
                        if (p2p_group_is_group_id_match(p2p->groups[i],
@@ -181,28 +463,203 @@ void p2p_process_prov_disc_req(struct p2p_data *p2p, const u8 *sa,
                }
        }
 
-       if (dev)
+       if (dev) {
                dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
-                               P2P_DEV_PD_PEER_KEYPAD);
+                               P2P_DEV_PD_PEER_KEYPAD |
+                               P2P_DEV_PD_PEER_P2PS);
+
+               /* Remove stale persistent groups */
+               if (p2p->cfg->remove_stale_groups) {
+                       p2p->cfg->remove_stale_groups(
+                               p2p->cfg->cb_ctx, dev->info.p2p_device_addr,
+                               msg.persistent_dev,
+                               msg.persistent_ssid, msg.persistent_ssid_len);
+               }
+       }
        if (msg.wps_config_methods & WPS_CONFIG_DISPLAY) {
                p2p_dbg(p2p, "Peer " MACSTR
                        " requested us to show a PIN on display", MAC2STR(sa));
                if (dev)
                        dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
+               passwd_id = DEV_PW_USER_SPECIFIED;
        } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
                p2p_dbg(p2p, "Peer " MACSTR
                        " requested us to write its PIN using keypad",
                        MAC2STR(sa));
                if (dev)
                        dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
+               passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
+       } else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
+               p2p_dbg(p2p, "Peer " MACSTR " requesting P2PS PIN",
+                       MAC2STR(sa));
+               if (dev)
+                       dev->flags |= P2P_DEV_PD_PEER_P2PS;
+               passwd_id = DEV_PW_P2PS_DEFAULT;
        }
 
-       reject = 0;
+       reject = P2P_SC_SUCCESS;
+
+       os_memset(session_mac, 0, ETH_ALEN);
+       os_memset(adv_mac, 0, ETH_ALEN);
+       os_memset(group_mac, 0, ETH_ALEN);
+
+       if (msg.adv_id && msg.session_id && msg.session_mac && msg.adv_mac &&
+           (msg.status || msg.conn_cap)) {
+               u8 remote_conncap;
+
+               if (msg.intended_addr)
+                       os_memcpy(group_mac, msg.intended_addr, ETH_ALEN);
+
+               os_memcpy(session_mac, msg.session_mac, ETH_ALEN);
+               os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
+
+               session_id = WPA_GET_LE32(msg.session_id);
+               adv_id = WPA_GET_LE32(msg.adv_id);
+
+               if (!msg.status)
+                       p2ps_adv = p2p_service_p2ps_id(p2p, adv_id);
+
+               p2p_dbg(p2p, "adv_id: %x - p2ps_adv - %p", adv_id, p2ps_adv);
+
+               if (msg.conn_cap)
+                       conncap = *msg.conn_cap;
+               remote_conncap = conncap;
+
+               if (p2ps_adv) {
+                       auto_accept = p2ps_adv->auto_accept;
+                       conncap = p2p->cfg->p2ps_group_capability(
+                               p2p->cfg->cb_ctx, conncap, auto_accept);
+
+                       p2p_dbg(p2p, "Conncap: local:%d remote:%d result:%d",
+                               auto_accept, remote_conncap, conncap);
+
+                       if (p2ps_adv->config_methods &&
+                           !(msg.wps_config_methods &
+                             p2ps_adv->config_methods)) {
+                               p2p_dbg(p2p,
+                                       "Unsupported config methods in Provision Discovery Request (own=0x%x peer=0x%x)",
+                                       p2ps_adv->config_methods,
+                                       msg.wps_config_methods);
+                               reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+                       } else if (!p2ps_adv->state) {
+                               p2p_dbg(p2p, "P2PS state unavailable");
+                               reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+                       } else if (!conncap) {
+                               p2p_dbg(p2p, "Conncap resolution failed");
+                               reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+                       }
+
+                       if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
+                               p2p_dbg(p2p, "Keypad - always defer");
+                               auto_accept = 0;
+                       }
+
+                       if (auto_accept || reject != P2P_SC_SUCCESS) {
+                               struct p2ps_provision *tmp;
+
+                               if (reject == P2P_SC_SUCCESS && !conncap) {
+                                       reject =
+                                               P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+                               }
+
+                               if (p2ps_setup_p2ps_prov(
+                                           p2p, adv_id, session_id,
+                                           msg.wps_config_methods,
+                                           session_mac, adv_mac) < 0) {
+                                       reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+                                       goto out;
+                               }
+
+                               tmp = p2p->p2ps_prov;
+                               if (conncap) {
+                                       tmp->conncap = conncap;
+                                       tmp->status = P2P_SC_SUCCESS;
+                               } else {
+                                       tmp->conncap = auto_accept;
+                                       tmp->status = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+                               }
+
+                               if (reject != P2P_SC_SUCCESS)
+                                       goto out;
+                       }
+               } else if (!msg.status) {
+                       reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+                       goto out;
+               }
+
+               if (!msg.status && !auto_accept &&
+                   (!p2p->p2ps_prov || p2p->p2ps_prov->adv_id != adv_id)) {
+                       struct p2ps_provision *tmp;
+
+                       if (!conncap) {
+                               reject = P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+                               goto out;
+                       }
+
+                       if (p2ps_setup_p2ps_prov(p2p, adv_id, session_id,
+                                                msg.wps_config_methods,
+                                                session_mac, adv_mac) < 0) {
+                               reject = P2P_SC_FAIL_UNABLE_TO_ACCOMMODATE;
+                               goto out;
+                       }
+                       tmp = p2p->p2ps_prov;
+                       reject = P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
+                       tmp->status = reject;
+               }
+
+               if (msg.status) {
+                       if (*msg.status &&
+                           *msg.status != P2P_SC_SUCCESS_DEFERRED) {
+                               reject = *msg.status;
+                       } else if (*msg.status == P2P_SC_SUCCESS_DEFERRED &&
+                                  p2p->p2ps_prov) {
+                               u16 method = p2p->p2ps_prov->method;
+
+                               conncap = p2p->cfg->p2ps_group_capability(
+                                       p2p->cfg->cb_ctx, remote_conncap,
+                                       p2p->p2ps_prov->conncap);
+
+                               p2p_dbg(p2p,
+                                       "Conncap: local:%d remote:%d result:%d",
+                                       p2p->p2ps_prov->conncap,
+                                       remote_conncap, conncap);
+
+                               /*
+                                * Ensure that if we asked for PIN originally,
+                                * our method is consistent with original
+                                * request.
+                                */
+                               if (method & WPS_CONFIG_DISPLAY)
+                                       method = WPS_CONFIG_KEYPAD;
+                               else if (method & WPS_CONFIG_KEYPAD)
+                                       method = WPS_CONFIG_DISPLAY;
+
+                               /* Reject this "Deferred Accept* if incompatible
+                                * conncap or method */
+                               if (!conncap ||
+                                   !(msg.wps_config_methods & method))
+                                       reject =
+                                               P2P_SC_FAIL_INCOMPATIBLE_PARAMS;
+                               else
+                                       reject = P2P_SC_SUCCESS;
+
+                               p2p->p2ps_prov->status = reject;
+                               p2p->p2ps_prov->conncap = conncap;
+                       }
+               }
+       }
 
 out:
-       resp = p2p_build_prov_disc_resp(p2p, msg.dialog_token,
-                                       reject ? 0 : msg.wps_config_methods,
-                                       msg.group_id, msg.group_id_len);
+       if (reject == P2P_SC_SUCCESS ||
+           reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE)
+               config_methods = msg.wps_config_methods;
+       else
+               config_methods = 0;
+       resp = p2p_build_prov_disc_resp(p2p, dev, msg.dialog_token, reject,
+                                       config_methods, adv_id,
+                                       msg.group_id, msg.group_id_len,
+                                       msg.persistent_ssid,
+                                       msg.persistent_ssid_len);
        if (resp == NULL) {
                p2p_parse_free(&msg);
                return;
@@ -219,23 +676,108 @@ out:
                p2p_parse_free(&msg);
                return;
        }
-#ifdef TIZEN_EXT
+#ifdef BCM_DRIVER_V115
        u8 null_mac[ETH_ALEN] = {0, };
 #endif
-       p2p->pending_action_state = P2P_NO_PENDING_ACTION;
+       p2p->pending_action_state = P2P_PENDING_PD_RESPONSE;
        if (p2p_send_action(p2p, freq, sa,
-#ifdef TIZEN_EXT_P2P
+#ifdef BCM_DRIVER_V115
                            p2p->cfg->own_addr, null_mac,
 #else
                            p2p->cfg->dev_addr, p2p->cfg->dev_addr,
 #endif
                            wpabuf_head(resp), wpabuf_len(resp), 200) < 0) {
                p2p_dbg(p2p, "Failed to send Action frame");
-       }
+       } else
+               p2p->send_action_in_progress = 1;
 
        wpabuf_free(resp);
 
-       if (!reject && p2p->cfg->prov_disc_req) {
+       if (!p2p->cfg->p2ps_prov_complete) {
+               /* Don't emit anything */
+       } else if (msg.status && *msg.status != P2P_SC_SUCCESS &&
+                  *msg.status != P2P_SC_SUCCESS_DEFERRED) {
+               reject = *msg.status;
+               p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject,
+                                            sa, adv_mac, session_mac,
+                                            NULL, adv_id, session_id,
+                                            0, 0, msg.persistent_ssid,
+                                            msg.persistent_ssid_len,
+                                            0, 0, NULL);
+       } else if (msg.status && *msg.status == P2P_SC_SUCCESS_DEFERRED &&
+                  p2p->p2ps_prov) {
+               p2p->p2ps_prov->status = reject;
+               p2p->p2ps_prov->conncap = conncap;
+
+               if (reject != P2P_SC_SUCCESS)
+                       p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, reject,
+                                                    sa, adv_mac, session_mac,
+                                                    NULL, adv_id,
+                                                    session_id, conncap, 0,
+                                                    msg.persistent_ssid,
+                                                    msg.persistent_ssid_len, 0,
+                                                    0, NULL);
+               else
+                       p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx,
+                                                    *msg.status,
+                                                    sa, adv_mac, session_mac,
+                                                    group_mac, adv_id,
+                                                    session_id, conncap,
+                                                    passwd_id,
+                                                    msg.persistent_ssid,
+                                                    msg.persistent_ssid_len, 0,
+                                                    0, NULL);
+       } else if (msg.status && p2p->p2ps_prov) {
+               p2p->p2ps_prov->status = P2P_SC_SUCCESS;
+               p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, *msg.status, sa,
+                                            adv_mac, session_mac, group_mac,
+                                            adv_id, session_id, conncap,
+                                            passwd_id,
+                                            msg.persistent_ssid,
+                                            msg.persistent_ssid_len,
+                                            0, 0, NULL);
+       } else if (msg.status) {
+       } else if (auto_accept && reject == P2P_SC_SUCCESS) {
+               p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
+                                            sa, adv_mac, session_mac,
+                                            group_mac, adv_id, session_id,
+                                            conncap, passwd_id,
+                                            msg.persistent_ssid,
+                                            msg.persistent_ssid_len,
+                                            0, 0, NULL);
+       } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
+                  (!msg.session_info || !msg.session_info_len)) {
+               p2p->p2ps_prov->method = msg.wps_config_methods;
+
+               p2p->cfg->p2ps_prov_complete(p2p->cfg->cb_ctx, P2P_SC_SUCCESS,
+                                            sa, adv_mac, session_mac,
+                                            group_mac, adv_id, session_id,
+                                            conncap, passwd_id,
+                                            msg.persistent_ssid,
+                                            msg.persistent_ssid_len,
+                                            0, 1, NULL);
+       } else if (reject == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+               size_t buf_len = msg.session_info_len;
+               char *buf = os_malloc(2 * buf_len + 1);
+
+               if (buf) {
+                       p2p->p2ps_prov->method = msg.wps_config_methods;
+
+                       utf8_escape((char *) msg.session_info, buf_len,
+                                   buf, 2 * buf_len + 1);
+
+                       p2p->cfg->p2ps_prov_complete(
+                               p2p->cfg->cb_ctx, P2P_SC_SUCCESS, sa,
+                               adv_mac, session_mac, group_mac, adv_id,
+                               session_id, conncap, passwd_id,
+                               msg.persistent_ssid, msg.persistent_ssid_len,
+                               0, 1, buf);
+
+                       os_free(buf);
+               }
+       }
+
+       if (reject == P2P_SC_SUCCESS && p2p->cfg->prov_disc_req) {
                const u8 *dev_addr = sa;
                if (msg.p2p_device_addr)
                        dev_addr = msg.p2p_device_addr;
@@ -258,11 +800,48 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
        struct p2p_message msg;
        struct p2p_device *dev;
        u16 report_config_methods = 0, req_config_methods;
+       u8 status = P2P_SC_SUCCESS;
        int success = 0;
+       u32 adv_id = 0;
+       u8 conncap = P2PS_SETUP_NEW;
+       u8 adv_mac[ETH_ALEN];
+       u8 group_mac[ETH_ALEN];
+       int passwd_id = DEV_PW_DEFAULT;
 
        if (p2p_parse(data, len, &msg))
                return;
 
+       /* Parse the P2PS members present */
+       if (msg.status)
+               status = *msg.status;
+
+       if (msg.intended_addr)
+               os_memcpy(group_mac, msg.intended_addr, ETH_ALEN);
+       else
+               os_memset(group_mac, 0, ETH_ALEN);
+
+       if (msg.adv_mac)
+               os_memcpy(adv_mac, msg.adv_mac, ETH_ALEN);
+       else
+               os_memset(adv_mac, 0, ETH_ALEN);
+
+       if (msg.adv_id)
+               adv_id = WPA_GET_LE32(msg.adv_id);
+
+       if (msg.conn_cap) {
+               conncap = *msg.conn_cap;
+
+               /* Switch bits to local relative */
+               switch (conncap) {
+               case P2PS_SETUP_GROUP_OWNER:
+                       conncap = P2PS_SETUP_CLIENT;
+                       break;
+               case P2PS_SETUP_CLIENT:
+                       conncap = P2PS_SETUP_GROUP_OWNER;
+                       break;
+               }
+       }
+
        p2p_dbg(p2p, "Received Provision Discovery Response from " MACSTR
                " with config methods 0x%x",
                MAC2STR(sa), msg.wps_config_methods);
@@ -306,23 +885,109 @@ void p2p_process_prov_disc_resp(struct p2p_data *p2p, const u8 *sa,
                        msg.wps_config_methods, req_config_methods);
                if (p2p->cfg->prov_disc_fail)
                        p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
-                                                P2P_PROV_DISC_REJECTED);
+                                                P2P_PROV_DISC_REJECTED,
+                                                adv_id, adv_mac, NULL);
                p2p_parse_free(&msg);
+               os_free(p2p->p2ps_prov);
+               p2p->p2ps_prov = NULL;
                goto out;
        }
 
        report_config_methods = req_config_methods;
        dev->flags &= ~(P2P_DEV_PD_PEER_DISPLAY |
-                       P2P_DEV_PD_PEER_KEYPAD);
+                       P2P_DEV_PD_PEER_KEYPAD |
+                       P2P_DEV_PD_PEER_P2PS);
        if (req_config_methods & WPS_CONFIG_DISPLAY) {
                p2p_dbg(p2p, "Peer " MACSTR
                        " accepted to show a PIN on display", MAC2STR(sa));
                dev->flags |= P2P_DEV_PD_PEER_DISPLAY;
+               passwd_id = DEV_PW_REGISTRAR_SPECIFIED;
        } else if (msg.wps_config_methods & WPS_CONFIG_KEYPAD) {
                p2p_dbg(p2p, "Peer " MACSTR
                        " accepted to write our PIN using keypad",
                        MAC2STR(sa));
                dev->flags |= P2P_DEV_PD_PEER_KEYPAD;
+               passwd_id = DEV_PW_USER_SPECIFIED;
+       } else if (msg.wps_config_methods & WPS_CONFIG_P2PS) {
+               p2p_dbg(p2p, "Peer " MACSTR " accepted P2PS PIN",
+                       MAC2STR(sa));
+               dev->flags |= P2P_DEV_PD_PEER_P2PS;
+               passwd_id = DEV_PW_P2PS_DEFAULT;
+       }
+
+       if ((msg.conn_cap || msg.persistent_dev) &&
+           msg.adv_id &&
+           (status == P2P_SC_SUCCESS || status == P2P_SC_SUCCESS_DEFERRED) &&
+           p2p->p2ps_prov) {
+               if (p2p->cfg->p2ps_prov_complete) {
+                       p2p->cfg->p2ps_prov_complete(
+                               p2p->cfg->cb_ctx, status, sa, adv_mac,
+                               p2p->p2ps_prov->session_mac,
+                               group_mac, adv_id, p2p->p2ps_prov->session_id,
+                               conncap, passwd_id, msg.persistent_ssid,
+                               msg.persistent_ssid_len, 1, 0, NULL);
+               }
+               os_free(p2p->p2ps_prov);
+               p2p->p2ps_prov = NULL;
+       }
+
+       if (status != P2P_SC_SUCCESS &&
+           status != P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE &&
+           status != P2P_SC_SUCCESS_DEFERRED && p2p->p2ps_prov) {
+               if (p2p->cfg->p2ps_prov_complete)
+                       p2p->cfg->p2ps_prov_complete(
+                               p2p->cfg->cb_ctx, status, sa, adv_mac,
+                               p2p->p2ps_prov->session_mac,
+                               group_mac, adv_id, p2p->p2ps_prov->session_id,
+                               0, 0, NULL, 0, 1, 0, NULL);
+               os_free(p2p->p2ps_prov);
+               p2p->p2ps_prov = NULL;
+       }
+
+       if (status == P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE) {
+               if (p2p->cfg->remove_stale_groups) {
+                       p2p->cfg->remove_stale_groups(p2p->cfg->cb_ctx,
+                                                     dev->info.p2p_device_addr,
+                                                     NULL, NULL, 0);
+               }
+
+               if (msg.session_info && msg.session_info_len) {
+                       size_t info_len = msg.session_info_len;
+                       char *deferred_sess_resp = os_malloc(2 * info_len + 1);
+
+                       if (!deferred_sess_resp) {
+                               p2p_parse_free(&msg);
+                               os_free(p2p->p2ps_prov);
+                               p2p->p2ps_prov = NULL;
+                               goto out;
+                       }
+                       utf8_escape((char *) msg.session_info, info_len,
+                                   deferred_sess_resp, 2 * info_len + 1);
+
+                       if (p2p->cfg->prov_disc_fail)
+                               p2p->cfg->prov_disc_fail(
+                                       p2p->cfg->cb_ctx, sa,
+                                       P2P_PROV_DISC_INFO_UNAVAILABLE,
+                                       adv_id, adv_mac,
+                                       deferred_sess_resp);
+                       os_free(deferred_sess_resp);
+               } else
+                       if (p2p->cfg->prov_disc_fail)
+                               p2p->cfg->prov_disc_fail(
+                                       p2p->cfg->cb_ctx, sa,
+                                       P2P_PROV_DISC_INFO_UNAVAILABLE,
+                                       adv_id, adv_mac, NULL);
+       } else if (msg.wps_config_methods != dev->req_config_methods ||
+                  status != P2P_SC_SUCCESS) {
+               p2p_dbg(p2p, "Peer rejected our Provision Discovery Request");
+               if (p2p->cfg->prov_disc_fail)
+                       p2p->cfg->prov_disc_fail(p2p->cfg->cb_ctx, sa,
+                                                P2P_PROV_DISC_REJECTED, 0,
+                                                NULL, NULL);
+               p2p_parse_free(&msg);
+               os_free(p2p->p2ps_prov);
+               p2p->p2ps_prov = NULL;
+               goto out;
        }
 
        /* Store the provisioning info */
@@ -381,9 +1046,33 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
                /* TODO: use device discoverability request through GO */
        }
 
-       req = p2p_build_prov_disc_req(p2p, dev->dialog_token,
-                                     dev->req_config_methods,
-                                     join ? dev : NULL);
+       if (p2p->p2ps_prov) {
+               if (p2p->p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED) {
+                       if (p2p->p2ps_prov->method == WPS_CONFIG_DISPLAY)
+                               dev->req_config_methods = WPS_CONFIG_KEYPAD;
+                       else if (p2p->p2ps_prov->method == WPS_CONFIG_KEYPAD)
+                               dev->req_config_methods = WPS_CONFIG_DISPLAY;
+                       else
+                               dev->req_config_methods = WPS_CONFIG_P2PS;
+               } else {
+                       /* Order of preference, based on peer's capabilities */
+                       if (p2p->p2ps_prov->method)
+                               dev->req_config_methods =
+                                       p2p->p2ps_prov->method;
+                       else if (dev->info.config_methods & WPS_CONFIG_P2PS)
+                               dev->req_config_methods = WPS_CONFIG_P2PS;
+                       else if (dev->info.config_methods & WPS_CONFIG_DISPLAY)
+                               dev->req_config_methods = WPS_CONFIG_DISPLAY;
+                       else
+                               dev->req_config_methods = WPS_CONFIG_KEYPAD;
+               }
+               p2p_dbg(p2p,
+                       "Building PD Request based on P2PS config method 0x%x status %d --> req_config_methods 0x%x",
+                       p2p->p2ps_prov->method, p2p->p2ps_prov->status,
+                       dev->req_config_methods);
+       }
+
+       req = p2p_build_prov_disc_req(p2p, dev, join);
        if (req == NULL)
                return -1;
 
@@ -391,7 +1080,7 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
                p2p_stop_listen_for_freq(p2p, freq);
        p2p->pending_action_state = P2P_PENDING_PD;
        if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
-#ifdef TIZEN_EXT_P2P
+#ifdef BCM_DRIVER_V115
                            p2p->cfg->own_addr, dev->info.p2p_device_addr,
 #else
                            p2p->cfg->dev_addr, dev->info.p2p_device_addr,
@@ -410,6 +1099,7 @@ int p2p_send_prov_disc_req(struct p2p_data *p2p, struct p2p_device *dev,
 
 
 int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
+                     struct p2ps_provision *p2ps_prov,
                      u16 config_methods, int join, int force_freq,
                      int user_initiated_pd)
 {
@@ -421,17 +1111,28 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
        if (dev == NULL || (dev->flags & P2P_DEV_PROBE_REQ_ONLY)) {
                p2p_dbg(p2p, "Provision Discovery Request destination " MACSTR
                        " not yet known", MAC2STR(peer_addr));
+               os_free(p2ps_prov);
                return -1;
        }
 
        p2p_dbg(p2p, "Provision Discovery Request with " MACSTR
                " (config methods 0x%x)",
                MAC2STR(peer_addr), config_methods);
-       if (config_methods == 0)
+       if (config_methods == 0 && !p2ps_prov) {
+               os_free(p2ps_prov);
                return -1;
+       }
+
+       if (p2ps_prov && p2ps_prov->status == P2P_SC_SUCCESS_DEFERRED &&
+           p2p->p2ps_prov) {
+               /* Use cached method from deferred provisioning */
+               p2ps_prov->method = p2p->p2ps_prov->method;
+       }
 
        /* Reset provisioning info */
        dev->wps_prov_info = 0;
+       os_free(p2p->p2ps_prov);
+       p2p->p2ps_prov = p2ps_prov;
 
        dev->req_config_methods = config_methods;
        if (join)
@@ -448,6 +1149,7 @@ int p2p_prov_disc_req(struct p2p_data *p2p, const u8 *peer_addr,
        }
 
        p2p->user_initiated_pd = user_initiated_pd;
+       p2p->pd_force_freq = force_freq;
 
        if (p2p->user_initiated_pd)
                p2p->pd_retries = MAX_PROV_DISC_REQ_RETRIES;
@@ -483,4 +1185,5 @@ void p2p_reset_pending_pd(struct p2p_data *p2p)
        p2p->user_initiated_pd = 0;
        os_memset(p2p->pending_pd_devaddr, 0, ETH_ALEN);
        p2p->pd_retries = 0;
+       p2p->pd_force_freq = 0;
 }
old mode 100644 (file)
new mode 100755 (executable)
index baa8bea..29118ef
@@ -52,6 +52,7 @@ struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
 {
        struct p2p_sd_query *q;
        int wsd = 0;
+       int count = 0;
 
        if (!(dev->info.dev_capab & P2P_DEV_CAPAB_SERVICE_DISCOVERY))
                return NULL; /* peer does not support SD */
@@ -64,15 +65,52 @@ struct p2p_sd_query * p2p_pending_sd_req(struct p2p_data *p2p,
                /* Use WSD only if the peer indicates support or it */
                if (q->wsd && !wsd)
                        continue;
-               if (q->for_all_peers && !(dev->flags & P2P_DEV_SD_INFO))
-                       return q;
+               /* if the query is a broadcast query */
+               if (q->for_all_peers) {
+                       /*
+                        * check if there are any broadcast queries pending for
+                        * this device
+                        */
+                       if (dev->sd_pending_bcast_queries <= 0)
+                               return NULL;
+                       /* query number that needs to be send to the device */
+                       if (count == dev->sd_pending_bcast_queries - 1)
+                               goto found;
+                       count++;
+               }
                if (!q->for_all_peers &&
                    os_memcmp(q->peer, dev->info.p2p_device_addr, ETH_ALEN) ==
                    0)
-                       return q;
+                       goto found;
        }
 
        return NULL;
+
+found:
+       if (dev->sd_reqs > 100) {
+               p2p_dbg(p2p, "Too many SD request attempts to " MACSTR
+                       " - skip remaining queries",
+                       MAC2STR(dev->info.p2p_device_addr));
+               return NULL;
+       }
+       return q;
+}
+
+
+static void p2p_decrease_sd_bc_queries(struct p2p_data *p2p, int query_number)
+{
+       struct p2p_device *dev;
+
+       p2p->num_p2p_sd_queries--;
+       dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+               if (query_number <= dev->sd_pending_bcast_queries - 1) {
+                       /*
+                        * Query not yet sent to the device and it is to be
+                        * removed, so update the pending count.
+                       */
+                       dev->sd_pending_bcast_queries--;
+               }
+       }
 }
 
 
@@ -80,10 +118,16 @@ static int p2p_unlink_sd_query(struct p2p_data *p2p,
                               struct p2p_sd_query *query)
 {
        struct p2p_sd_query *q, *prev;
+       int query_number = 0;
+
        q = p2p->sd_queries;
        prev = NULL;
        while (q) {
                if (q == query) {
+                       /* If the query is a broadcast query, decrease one from
+                        * all the devices */
+                       if (query->for_all_peers)
+                               p2p_decrease_sd_bc_queries(p2p, query_number);
                        if (prev)
                                prev->next = q->next;
                        else
@@ -92,6 +136,8 @@ static int p2p_unlink_sd_query(struct p2p_data *p2p,
                                p2p->sd_query = NULL;
                        return 1;
                }
+               if (q->for_all_peers)
+                       query_number++;
                prev = q;
                q = q->next;
        }
@@ -118,6 +164,7 @@ void p2p_free_sd_queries(struct p2p_data *p2p)
                q = q->next;
                p2p_free_sd_query(prev);
        }
+       p2p->num_p2p_sd_queries = 0;
 }
 
 
@@ -133,8 +180,7 @@ static struct wpabuf * p2p_build_sd_query(u16 update_indic,
 
        /* ANQP Query Request Frame */
        len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
-       wpabuf_put_be24(buf, OUI_WFA);
-       wpabuf_put_u8(buf, P2P_OUI_TYPE);
+       wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
        wpabuf_put_le16(buf, update_indic); /* Service Update Indicator */
        wpabuf_put_buf(buf, tlvs);
        gas_anqp_set_element_len(buf, len_pos);
@@ -156,7 +202,7 @@ static void p2p_send_gas_comeback_req(struct p2p_data *p2p, const u8 *dst,
 
        p2p->pending_action_state = P2P_NO_PENDING_ACTION;
        if (p2p_send_action(p2p, freq, dst,
-#ifdef TIZEN_EXT
+#ifdef BCM_DRIVER_V115
                            p2p->cfg->own_addr, dst,
 #else
                            p2p->cfg->dev_addr, dst,
@@ -185,8 +231,7 @@ static struct wpabuf * p2p_build_sd_response(u8 dialog_token, u16 status_code,
        if (tlvs) {
                /* ANQP Query Response Frame */
                len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
-               wpabuf_put_be24(buf, OUI_WFA);
-               wpabuf_put_u8(buf, P2P_OUI_TYPE);
+               wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
                 /* Service Update Indicator */
                wpabuf_put_le16(buf, update_indic);
                wpabuf_put_buf(buf, tlvs);
@@ -217,8 +262,7 @@ static struct wpabuf * p2p_build_gas_comeback_resp(u8 dialog_token,
                /* ANQP Query Response Frame */
                wpabuf_put_le16(buf, ANQP_VENDOR_SPECIFIC); /* Info ID */
                wpabuf_put_le16(buf, 3 + 1 + 2 + total_len);
-               wpabuf_put_be24(buf, OUI_WFA);
-               wpabuf_put_u8(buf, P2P_OUI_TYPE);
+               wpabuf_put_be32(buf, P2P_IE_VENDOR_TYPE);
                /* Service Update Indicator */
                wpabuf_put_le16(buf, update_indic);
        }
@@ -236,6 +280,7 @@ int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev)
        int ret = 0;
        struct p2p_sd_query *query;
        int freq;
+       unsigned int wait_time;
 
        freq = dev->listen_freq > 0 ? dev->listen_freq : dev->oper_freq;
        if (freq <= 0) {
@@ -256,17 +301,21 @@ int p2p_start_sd(struct p2p_data *p2p, struct p2p_device *dev)
        if (req == NULL)
                return -1;
 
+       dev->sd_reqs++;
        p2p->sd_peer = dev;
        p2p->sd_query = query;
        p2p->pending_action_state = P2P_PENDING_SD;
 
+       wait_time = 5000;
+       if (p2p->cfg->max_listen && wait_time > p2p->cfg->max_listen)
+               wait_time = p2p->cfg->max_listen;
        if (p2p_send_action(p2p, freq, dev->info.p2p_device_addr,
-#ifdef TIZEN_EXT
+#ifdef BCM_DRIVER_V115
                            p2p->cfg->own_addr, dev->info.p2p_device_addr,
 #else
                            p2p->cfg->dev_addr, dev->info.p2p_device_addr,
 #endif
-                           wpabuf_head(req), wpabuf_len(req), 5000) < 0) {
+                           wpabuf_head(req), wpabuf_len(req), wait_time) < 0) {
                p2p_dbg(p2p, "Failed to send Action frame");
                ret = -1;
        }
@@ -354,17 +403,12 @@ void p2p_rx_gas_initial_req(struct p2p_data *p2p, const u8 *sa,
                return;
        }
 
-       if (WPA_GET_BE24(pos) != OUI_WFA) {
-               p2p_dbg(p2p, "Unsupported ANQP OUI %06x", WPA_GET_BE24(pos));
+       if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) {
+               p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x",
+                       WPA_GET_BE32(pos));
                return;
        }
-       pos += 3;
-
-       if (*pos != P2P_OUI_TYPE) {
-               p2p_dbg(p2p, "Unsupported ANQP vendor type %u", *pos);
-               return;
-       }
-       pos++;
+       pos += 4;
 
        if (pos + 2 > end)
                return;
@@ -419,7 +463,7 @@ void p2p_sd_response(struct p2p_data *p2p, int freq, const u8 *dst,
 
        p2p->pending_action_state = P2P_NO_PENDING_ACTION;
        if (p2p_send_action(p2p, freq, dst,
-#ifdef TIZEN_EXT
+#ifdef BCM_DRIVER_V115
                            p2p->cfg->own_addr, p2p->cfg->own_addr,
 #else
                            p2p->cfg->dev_addr, p2p->cfg->dev_addr,
@@ -536,17 +580,12 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa,
                return;
        }
 
-       if (WPA_GET_BE24(pos) != OUI_WFA) {
-               p2p_dbg(p2p, "Unsupported ANQP OUI %06x", WPA_GET_BE24(pos));
+       if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) {
+               p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x",
+                       WPA_GET_BE32(pos));
                return;
        }
-       pos += 3;
-
-       if (*pos != P2P_OUI_TYPE) {
-               p2p_dbg(p2p, "Unsupported ANQP vendor type %u", *pos);
-               return;
-       }
-       pos++;
+       pos += 4;
 
        if (pos + 2 > end)
                return;
@@ -554,8 +593,6 @@ void p2p_rx_gas_initial_resp(struct p2p_data *p2p, const u8 *sa,
        p2p_dbg(p2p, "Service Update Indicator: %u", update_indic);
        pos += 2;
 
-       p2p->sd_peer->flags |= P2P_DEV_SD_INFO;
-       p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
        p2p->sd_peer = NULL;
 
        if (p2p->sd_query) {
@@ -635,7 +672,7 @@ void p2p_rx_gas_comeback_req(struct p2p_data *p2p, const u8 *sa,
 
        p2p->pending_action_state = P2P_NO_PENDING_ACTION;
        if (p2p_send_action(p2p, rx_freq, sa,
-#ifdef TIZEN_EXT
+#ifdef BCM_DRIVER_V115
                            p2p->cfg->own_addr, p2p->cfg->own_addr,
 #else
                            p2p->cfg->dev_addr, p2p->cfg->dev_addr,
@@ -766,17 +803,12 @@ void p2p_rx_gas_comeback_resp(struct p2p_data *p2p, const u8 *sa,
        if (pos + 4 > end)
                return;
 
-       if (WPA_GET_BE24(pos) != OUI_WFA) {
-               p2p_dbg(p2p, "Unsupported ANQP OUI %06x", WPA_GET_BE24(pos));
+       if (WPA_GET_BE32(pos) != P2P_IE_VENDOR_TYPE) {
+               p2p_dbg(p2p, "Unsupported ANQP vendor OUI-type %08x",
+                       WPA_GET_BE32(pos));
                return;
        }
-       pos += 3;
-
-       if (*pos != P2P_OUI_TYPE) {
-               p2p_dbg(p2p, "Unsupported ANQP vendor type %u", *pos);
-               return;
-       }
-       pos++;
+       pos += 4;
 
        if (pos + 2 > end)
                return;
@@ -804,8 +836,6 @@ skip_nqp_header:
                return;
        }
 
-       p2p->sd_peer->flags |= P2P_DEV_SD_INFO;
-       p2p->sd_peer->flags &= ~P2P_DEV_SD_SCHEDULE;
        p2p->sd_peer = NULL;
 
        if (p2p->sd_query) {
@@ -858,8 +888,16 @@ void * p2p_sd_request(struct p2p_data *p2p, const u8 *dst,
 
        if (dst == NULL) {
                struct p2p_device *dev;
-               dl_list_for_each(dev, &p2p->devices, struct p2p_device, list)
-                       dev->flags &= ~P2P_DEV_SD_INFO;
+
+               p2p->num_p2p_sd_queries++;
+
+               /* Update all the devices for the newly added broadcast query */
+               dl_list_for_each(dev, &p2p->devices, struct p2p_device, list) {
+                       if (dev->sd_pending_bcast_queries <= 0)
+                               dev->sd_pending_bcast_queries = 1;
+                       else
+                               dev->sd_pending_bcast_queries++;
+               }
        }
 
        return q;
index 0da2682..f32751d 100644 (file)
@@ -9,6 +9,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "common/ieee802_11_common.h"
 #include "p2p_i.h"
 
 
@@ -54,48 +55,7 @@ int p2p_random(char *buf, size_t len)
  */
 int p2p_channel_to_freq(int op_class, int channel)
 {
-       /* Table E-4 in IEEE Std 802.11-2012 - Global operating classes */
-       /* TODO: more operating classes */
-       switch (op_class) {
-       case 81:
-               /* channels 1..13 */
-               if (channel < 1 || channel > 13)
-                       return -1;
-               return 2407 + 5 * channel;
-       case 82:
-               /* channel 14 */
-               if (channel != 14)
-                       return -1;
-               return 2414 + 5 * channel;
-       case 83: /* channels 1..9; 40 MHz */
-       case 84: /* channels 5..13; 40 MHz */
-               if (channel < 1 || channel > 13)
-                       return -1;
-               return 2407 + 5 * channel;
-       case 115: /* channels 36,40,44,48; indoor only */
-       case 118: /* channels 52,56,60,64; dfs */
-               if (channel < 36 || channel > 64)
-                       return -1;
-               return 5000 + 5 * channel;
-       case 124: /* channels 149,153,157,161 */
-       case 125: /* channels 149,153,157,161,165,169 */
-               if (channel < 149 || channel > 161)
-                       return -1;
-               return 5000 + 5 * channel;
-       case 116: /* channels 36,44; 40 MHz; indoor only */
-       case 117: /* channels 40,48; 40 MHz; indoor only */
-       case 119: /* channels 52,60; 40 MHz; dfs */
-       case 120: /* channels 56,64; 40 MHz; dfs */
-               if (channel < 36 || channel > 64)
-                       return -1;
-               return 5000 + 5 * channel;
-       case 126: /* channels 149,157; 40 MHz */
-       case 127: /* channels 153,161; 40 MHz */
-               if (channel < 149 || channel > 161)
-                       return -1;
-               return 5000 + 5 * channel;
-       }
-       return -1;
+       return ieee80211_chan_to_freq(NULL, op_class, channel);
 }
 
 
@@ -109,6 +69,9 @@ int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel)
 {
        /* TODO: more operating classes */
        if (freq >= 2412 && freq <= 2472) {
+               if ((freq - 2407) % 5)
+                       return -1;
+
                *op_class = 81; /* 2.407 GHz, channels 1..13 */
                *channel = (freq - 2407) / 5;
                return 0;
@@ -121,17 +84,32 @@ int p2p_freq_to_channel(unsigned int freq, u8 *op_class, u8 *channel)
        }
 
        if (freq >= 5180 && freq <= 5240) {
+               if ((freq - 5000) % 5)
+                       return -1;
+
                *op_class = 115; /* 5 GHz, channels 36..48 */
                *channel = (freq - 5000) / 5;
                return 0;
        }
 
        if (freq >= 5745 && freq <= 5805) {
+               if ((freq - 5000) % 5)
+                       return -1;
+
                *op_class = 124; /* 5 GHz, channels 149..161 */
                *channel = (freq - 5000) / 5;
                return 0;
        }
 
+       if (freq >= 58320 && freq <= 64800) {
+               if ((freq - 58320) % 2160)
+                       return -1;
+
+               *op_class = 180; /* 60 GHz, channels 1..4 */
+               *channel = (freq - 56160) / 2160;
+               return 0;
+       }
+
        return -1;
 }
 
@@ -195,6 +173,115 @@ void p2p_channels_intersect(const struct p2p_channels *a,
 }
 
 
+static void p2p_op_class_union(struct p2p_reg_class *cl,
+                              const struct p2p_reg_class *b_cl)
+{
+       size_t i, j;
+
+       for (i = 0; i < b_cl->channels; i++) {
+               for (j = 0; j < cl->channels; j++) {
+                       if (b_cl->channel[i] == cl->channel[j])
+                               break;
+               }
+               if (j == cl->channels) {
+                       if (cl->channels == P2P_MAX_REG_CLASS_CHANNELS)
+                               return;
+                       cl->channel[cl->channels++] = b_cl->channel[i];
+               }
+       }
+}
+
+
+/**
+ * p2p_channels_union_inplace - Inplace union of channel lists
+ * @res: Input data and place for returning union of the channel sets
+ * @b: Second set of channels
+ */
+void p2p_channels_union_inplace(struct p2p_channels *res,
+                               const struct p2p_channels *b)
+{
+       size_t i, j;
+
+       for (i = 0; i < res->reg_classes; i++) {
+               struct p2p_reg_class *cl = &res->reg_class[i];
+               for (j = 0; j < b->reg_classes; j++) {
+                       const struct p2p_reg_class *b_cl = &b->reg_class[j];
+                       if (cl->reg_class != b_cl->reg_class)
+                               continue;
+                       p2p_op_class_union(cl, b_cl);
+               }
+       }
+
+       for (j = 0; j < b->reg_classes; j++) {
+               const struct p2p_reg_class *b_cl = &b->reg_class[j];
+
+               for (i = 0; i < res->reg_classes; i++) {
+                       struct p2p_reg_class *cl = &res->reg_class[i];
+                       if (cl->reg_class == b_cl->reg_class)
+                               break;
+               }
+
+               if (i == res->reg_classes) {
+                       if (res->reg_classes == P2P_MAX_REG_CLASSES)
+                               return;
+                       os_memcpy(&res->reg_class[res->reg_classes++],
+                                 b_cl, sizeof(struct p2p_reg_class));
+               }
+       }
+}
+
+
+/**
+ * p2p_channels_union - Union of channel lists
+ * @a: First set of channels
+ * @b: Second set of channels
+ * @res: Data structure for returning the union of channels
+ */
+void p2p_channels_union(const struct p2p_channels *a,
+                       const struct p2p_channels *b,
+                       struct p2p_channels *res)
+{
+       os_memcpy(res, a, sizeof(*res));
+       p2p_channels_union_inplace(res, b);
+}
+
+
+void p2p_channels_remove_freqs(struct p2p_channels *chan,
+                              const struct wpa_freq_range_list *list)
+{
+       size_t o, c;
+
+       if (list == NULL)
+               return;
+
+       o = 0;
+       while (o < chan->reg_classes) {
+               struct p2p_reg_class *op = &chan->reg_class[o];
+
+               c = 0;
+               while (c < op->channels) {
+                       int freq = p2p_channel_to_freq(op->reg_class,
+                                                      op->channel[c]);
+                       if (freq > 0 && freq_range_list_includes(list, freq)) {
+                               op->channels--;
+                               os_memmove(&op->channel[c],
+                                          &op->channel[c + 1],
+                                          op->channels - c);
+                       } else
+                               c++;
+               }
+
+               if (op->channels == 0) {
+                       chan->reg_classes--;
+                       os_memmove(&chan->reg_class[o], &chan->reg_class[o + 1],
+                                  (chan->reg_classes - o) *
+                                  sizeof(struct p2p_reg_class));
+               } else
+                       o++;
+       }
+}
+
+
 /**
  * p2p_channels_includes - Check whether a channel is included in the list
  * @channels: List of supported channels
@@ -245,29 +332,182 @@ int p2p_supported_freq(struct p2p_data *p2p, unsigned int freq)
 }
 
 
+int p2p_supported_freq_go(struct p2p_data *p2p, unsigned int freq)
+{
+       u8 op_reg_class, op_channel;
+       if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0)
+               return 0;
+       return p2p_channels_includes(&p2p->cfg->channels, op_reg_class,
+                                    op_channel) &&
+               !freq_range_list_includes(&p2p->no_go_freq, freq);
+}
+
+
+int p2p_supported_freq_cli(struct p2p_data *p2p, unsigned int freq)
+{
+       u8 op_reg_class, op_channel;
+       if (p2p_freq_to_channel(freq, &op_reg_class, &op_channel) < 0)
+               return 0;
+       return p2p_channels_includes(&p2p->cfg->channels, op_reg_class,
+                                    op_channel) ||
+               p2p_channels_includes(&p2p->cfg->cli_channels, op_reg_class,
+                                     op_channel);
+}
+
+
 unsigned int p2p_get_pref_freq(struct p2p_data *p2p,
                               const struct p2p_channels *channels)
 {
        unsigned int i;
        int freq = 0;
+       const struct p2p_channels *tmpc = channels ?
+               channels : &p2p->cfg->channels;
 
-       if (channels == NULL) {
-               if (p2p->cfg->num_pref_chan) {
-                       freq = p2p_channel_to_freq(
-                               p2p->cfg->pref_chan[0].op_class,
-                               p2p->cfg->pref_chan[0].chan);
-                       if (freq < 0)
-                               freq = 0;
-               }
-               return freq;
-       }
+       if (tmpc == NULL)
+               return 0;
 
        for (i = 0; p2p->cfg->pref_chan && i < p2p->cfg->num_pref_chan; i++) {
                freq = p2p_channel_to_freq(p2p->cfg->pref_chan[i].op_class,
                                           p2p->cfg->pref_chan[i].chan);
-               if (p2p_channels_includes_freq(channels, freq))
+               if (p2p_channels_includes_freq(tmpc, freq))
                        return freq;
        }
+       return 0;
+}
+
+
+void p2p_channels_dump(struct p2p_data *p2p, const char *title,
+                      const struct p2p_channels *chan)
+{
+       char buf[500], *pos, *end;
+       size_t i, j;
+       int ret;
+
+       pos = buf;
+       end = pos + sizeof(buf);
+
+       for (i = 0; i < chan->reg_classes; i++) {
+               const struct p2p_reg_class *c;
+               c = &chan->reg_class[i];
+               ret = os_snprintf(pos, end - pos, " %u:", c->reg_class);
+               if (os_snprintf_error(end - pos, ret))
+                       break;
+               pos += ret;
+
+               for (j = 0; j < c->channels; j++) {
+                       ret = os_snprintf(pos, end - pos, "%s%u",
+                                         j == 0 ? "" : ",",
+                                         c->channel[j]);
+                       if (os_snprintf_error(end - pos, ret))
+                               break;
+                       pos += ret;
+               }
+       }
+       *pos = '\0';
+
+       p2p_dbg(p2p, "%s:%s", title, buf);
+}
+
+
+static u8 p2p_channel_pick_random(const u8 *channels, unsigned int num_channels)
+{
+       unsigned int r;
+       if (os_get_random((u8 *) &r, sizeof(r)) < 0)
+               r = 0;
+       r %= num_channels;
+       return channels[r];
+}
+
+
+int p2p_channel_select(struct p2p_channels *chans, const int *classes,
+                      u8 *op_class, u8 *op_channel)
+{
+       unsigned int i, j;
+
+       for (j = 0; classes == NULL || classes[j]; j++) {
+               for (i = 0; i < chans->reg_classes; i++) {
+                       struct p2p_reg_class *c = &chans->reg_class[i];
+
+                       if (c->channels == 0)
+                               continue;
+
+                       if (classes == NULL || c->reg_class == classes[j]) {
+                               /*
+                                * Pick one of the available channels in the
+                                * operating class at random.
+                                */
+                               *op_class = c->reg_class;
+                               *op_channel = p2p_channel_pick_random(
+                                       c->channel, c->channels);
+                               return 0;
+                       }
+               }
+               if (classes == NULL)
+                       break;
+       }
+
+       return -1;
+}
+
+
+int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class,
+                             u8 *op_channel)
+{
+       u8 chan[4];
+       unsigned int num_channels = 0;
+
+       /* Try to find available social channels from 2.4 GHz */
+       if (p2p_channels_includes(chans, 81, 1))
+               chan[num_channels++] = 1;
+       if (p2p_channels_includes(chans, 81, 6))
+               chan[num_channels++] = 6;
+       if (p2p_channels_includes(chans, 81, 11))
+               chan[num_channels++] = 11;
+
+       /* Try to find available social channels from 60 GHz */
+       if (p2p_channels_includes(chans, 180, 2))
+               chan[num_channels++] = 2;
+
+       if (num_channels == 0)
+               return -1;
+
+       *op_channel = p2p_channel_pick_random(chan, num_channels);
+       if (*op_channel == 2)
+               *op_class = 180;
+       else
+               *op_class = 81;
 
        return 0;
 }
+
+
+int p2p_channels_to_freqs(const struct p2p_channels *channels, int *freq_list,
+                         unsigned int max_len)
+{
+       unsigned int i, idx;
+
+       if (!channels || max_len == 0)
+               return 0;
+
+       for (i = 0, idx = 0; i < channels->reg_classes; i++) {
+               const struct p2p_reg_class *c = &channels->reg_class[i];
+               unsigned int j;
+
+               if (idx + 1 == max_len)
+                       break;
+               for (j = 0; j < c->channels; j++) {
+                       int freq;
+                       if (idx + 1 == max_len)
+                               break;
+                       freq = p2p_channel_to_freq(c->reg_class,
+                                                  c->channel[j]);
+                       if (freq < 0)
+                               continue;
+                       freq_list[idx++] = freq;
+               }
+       }
+
+       freq_list[idx] = 0;
+
+       return idx;
+}
diff --git a/src/pae/Makefile b/src/pae/Makefile
new file mode 100644 (file)
index 0000000..9c41962
--- /dev/null
@@ -0,0 +1,8 @@
+all:
+       @echo Nothing to be made.
+
+clean:
+       rm -f *~ *.o *.d
+
+install:
+       @echo Nothing to be made.
diff --git a/src/pae/ieee802_1x_cp.c b/src/pae/ieee802_1x_cp.c
new file mode 100644 (file)
index 0000000..cf43c59
--- /dev/null
@@ -0,0 +1,744 @@
+/*
+ * IEEE 802.1X-2010 Controlled Port of PAE state machine - CP state machine
+ * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/defs.h"
+#include "common/ieee802_1x_defs.h"
+#include "utils/state_machine.h"
+#include "ieee802_1x_kay.h"
+#include "ieee802_1x_secy_ops.h"
+#include "pae/ieee802_1x_cp.h"
+
+#define STATE_MACHINE_DATA struct ieee802_1x_cp_sm
+#define STATE_MACHINE_DEBUG_PREFIX "CP"
+
+static u8 default_cs_id[] = CS_ID_GCM_AES_128;
+
+/* The variable defined in clause 12 in IEEE Std 802.1X-2010 */
+enum connect_type { PENDING, UNAUTHENTICATED, AUTHENTICATED, SECURE };
+
+struct ieee802_1x_cp_sm {
+       enum cp_states {
+               CP_BEGIN, CP_INIT, CP_CHANGE, CP_ALLOWED, CP_AUTHENTICATED,
+               CP_SECURED, CP_RECEIVE, CP_RECEIVING, CP_READY, CP_TRANSMIT,
+               CP_TRANSMITTING, CP_ABANDON, CP_RETIRE
+       } CP_state;
+       Boolean changed;
+
+       /* CP -> Client */
+       Boolean port_valid;
+
+       /* Logon -> CP */
+       enum connect_type connect;
+       u8 *authorization_data;
+
+       /* KaY -> CP */
+       Boolean chgd_server; /* clear by CP */
+       Boolean elected_self;
+       u8 *authorization_data1;
+       enum confidentiality_offset cipher_offset;
+       u8 *cipher_suite;
+       Boolean new_sak; /* clear by CP */
+       struct ieee802_1x_mka_ki distributed_ki;
+       u8 distributed_an;
+       Boolean using_receive_sas;
+       Boolean all_receiving;
+       Boolean server_transmitting;
+       Boolean using_transmit_sa;
+
+       /* CP -> KaY */
+       struct ieee802_1x_mka_ki *lki;
+       u8 lan;
+       Boolean ltx;
+       Boolean lrx;
+       struct ieee802_1x_mka_ki *oki;
+       u8 oan;
+       Boolean otx;
+       Boolean orx;
+
+       /* CP -> SecY */
+       Boolean protect_frames;
+       enum validate_frames validate_frames;
+
+       Boolean replay_protect;
+       u32 replay_window;
+
+       u8 *current_cipher_suite;
+       enum confidentiality_offset confidentiality_offset;
+       Boolean controlled_port_enabled;
+
+       /* SecY -> CP */
+       Boolean port_enabled; /* SecY->CP */
+
+       /* private */
+       u32 transmit_when;
+       u32 transmit_delay;
+       u32 retire_when;
+       u32 retire_delay;
+
+       /* not defined IEEE Std 802.1X-2010 */
+       struct ieee802_1x_kay *kay;
+};
+
+static void ieee802_1x_cp_retire_when_timeout(void *eloop_ctx,
+                                             void *timeout_ctx);
+static void ieee802_1x_cp_transmit_when_timeout(void *eloop_ctx,
+                                               void *timeout_ctx);
+
+
+static int changed_cipher(struct ieee802_1x_cp_sm *sm)
+{
+       return sm->confidentiality_offset != sm->cipher_offset ||
+               os_memcmp(sm->current_cipher_suite, sm->cipher_suite,
+                         CS_ID_LEN) != 0;
+}
+
+
+static int changed_connect(struct ieee802_1x_cp_sm *sm)
+{
+       return sm->connect != SECURE || sm->chgd_server || changed_cipher(sm);
+}
+
+
+SM_STATE(CP, INIT)
+{
+       SM_ENTRY(CP, INIT);
+
+       sm->controlled_port_enabled = FALSE;
+       secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
+
+       sm->port_valid = FALSE;
+
+       os_free(sm->lki);
+       sm->lki = NULL;
+       sm->ltx = FALSE;
+       sm->lrx = FALSE;
+
+       os_free(sm->oki);
+       sm->oki = NULL;
+       sm->otx = FALSE;
+       sm->orx = FALSE;
+
+       sm->port_enabled = TRUE;
+       sm->chgd_server = FALSE;
+}
+
+
+SM_STATE(CP, CHANGE)
+{
+       SM_ENTRY(CP, CHANGE);
+
+       sm->port_valid = FALSE;
+       sm->controlled_port_enabled = FALSE;
+       secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
+
+       if (sm->lki)
+               ieee802_1x_kay_delete_sas(sm->kay, sm->lki);
+       if (sm->oki)
+               ieee802_1x_kay_delete_sas(sm->kay, sm->oki);
+}
+
+
+SM_STATE(CP, ALLOWED)
+{
+       SM_ENTRY(CP, ALLOWED);
+
+       sm->protect_frames = FALSE;
+       sm->replay_protect = FALSE;
+       sm->validate_frames = Checked;
+
+       sm->port_valid = FALSE;
+       sm->controlled_port_enabled = TRUE;
+
+       secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
+       secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
+       secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
+       secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
+}
+
+
+SM_STATE(CP, AUTHENTICATED)
+{
+       SM_ENTRY(CP, AUTHENTICATED);
+
+       sm->protect_frames = FALSE;
+       sm->replay_protect = FALSE;
+       sm->validate_frames = Checked;
+
+       sm->port_valid = FALSE;
+       sm->controlled_port_enabled = TRUE;
+
+       secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
+       secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
+       secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
+       secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
+}
+
+
+SM_STATE(CP, SECURED)
+{
+       struct ieee802_1x_cp_conf conf;
+
+       SM_ENTRY(CP, SECURED);
+
+       sm->chgd_server = FALSE;
+
+       ieee802_1x_kay_cp_conf(sm->kay, &conf);
+       sm->protect_frames = conf.protect;
+       sm->replay_protect = conf.replay_protect;
+       sm->validate_frames = conf.validate;
+
+       /* NOTE: now no other than default cipher suiter(AES-GCM-128) */
+       os_memcpy(sm->current_cipher_suite, sm->cipher_suite, CS_ID_LEN);
+       secy_cp_control_current_cipher_suite(sm->kay, sm->current_cipher_suite,
+                                            CS_ID_LEN);
+
+       sm->confidentiality_offset = sm->cipher_offset;
+
+       sm->port_valid = TRUE;
+
+       secy_cp_control_confidentiality_offset(sm->kay,
+                                              sm->confidentiality_offset);
+       secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
+       secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
+       secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
+}
+
+
+SM_STATE(CP, RECEIVE)
+{
+       SM_ENTRY(CP, RECEIVE);
+       /* RECEIVE state machine not keep with Figure 12-2 in
+        * IEEE Std 802.1X-2010 */
+       sm->oki = sm->lki;
+       sm->oan = sm->lan;
+       sm->otx = sm->ltx;
+       sm->orx = sm->lrx;
+       ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
+                                      sm->otx, sm->orx);
+
+       sm->lki = os_malloc(sizeof(*sm->lki));
+       if (!sm->lki) {
+               wpa_printf(MSG_ERROR, "CP-%s: Out of memory", __func__);
+               return;
+       }
+       os_memcpy(sm->lki, &sm->distributed_ki, sizeof(*sm->lki));
+       sm->lan = sm->distributed_an;
+       sm->ltx = FALSE;
+       sm->lrx = FALSE;
+       ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
+                                         sm->ltx, sm->lrx);
+       ieee802_1x_kay_create_sas(sm->kay, sm->lki);
+       ieee802_1x_kay_enable_rx_sas(sm->kay, sm->lki);
+       sm->new_sak = FALSE;
+       sm->all_receiving = FALSE;
+}
+
+
+SM_STATE(CP, RECEIVING)
+{
+       SM_ENTRY(CP, RECEIVING);
+
+       sm->lrx = TRUE;
+       ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
+                                         sm->ltx, sm->lrx);
+       sm->transmit_when = sm->transmit_delay;
+       eloop_cancel_timeout(ieee802_1x_cp_transmit_when_timeout, sm, NULL);
+       eloop_register_timeout(sm->transmit_when / 1000, 0,
+                              ieee802_1x_cp_transmit_when_timeout, sm, NULL);
+       /* the electedSelf have been set before CP entering to RECEIVING
+        * but the CP will transmit from RECEIVING to READY under
+        * the !electedSelf when KaY is not key server */
+       ieee802_1x_cp_sm_step(sm);
+       sm->using_receive_sas = FALSE;
+       sm->server_transmitting = FALSE;
+}
+
+
+SM_STATE(CP, READY)
+{
+       SM_ENTRY(CP, READY);
+
+       ieee802_1x_kay_enable_new_info(sm->kay);
+}
+
+
+SM_STATE(CP, TRANSMIT)
+{
+       SM_ENTRY(CP, TRANSMIT);
+
+       sm->controlled_port_enabled = TRUE;
+       secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
+       sm->ltx = TRUE;
+       ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
+                                         sm->ltx, sm->lrx);
+       ieee802_1x_kay_enable_tx_sas(sm->kay,  sm->lki);
+       sm->all_receiving = FALSE;
+       sm->server_transmitting = FALSE;
+}
+
+
+SM_STATE(CP, TRANSMITTING)
+{
+       SM_ENTRY(CP, TRANSMITTING);
+       sm->retire_when = sm->orx ? sm->retire_delay : 0;
+       sm->otx = FALSE;
+       ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
+                                      sm->otx, sm->orx);
+       ieee802_1x_kay_enable_new_info(sm->kay);
+       eloop_cancel_timeout(ieee802_1x_cp_retire_when_timeout, sm, NULL);
+       eloop_register_timeout(sm->retire_when / 1000, 0,
+                              ieee802_1x_cp_retire_when_timeout, sm, NULL);
+       sm->using_transmit_sa = FALSE;
+}
+
+
+SM_STATE(CP, ABANDON)
+{
+       SM_ENTRY(CP, ABANDON);
+       sm->lrx = FALSE;
+       ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
+                                         sm->ltx, sm->lrx);
+       ieee802_1x_kay_delete_sas(sm->kay, sm->lki);
+
+       os_free(sm->lki);
+       sm->lki = NULL;
+       ieee802_1x_kay_set_latest_sa_attr(sm->kay, sm->lki, sm->lan,
+                                         sm->ltx, sm->lrx);
+       sm->new_sak = FALSE;
+}
+
+
+SM_STATE(CP, RETIRE)
+{
+       SM_ENTRY(CP, RETIRE);
+       /* RETIRE state machine not keep with Figure 12-2 in
+        * IEEE Std 802.1X-2010 */
+       os_free(sm->oki);
+       sm->oki = NULL;
+       sm->orx = FALSE;
+       sm->otx = FALSE;
+       ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
+                                      sm->otx, sm->orx);
+}
+
+
+/**
+ * CP state machine handler entry
+ */
+SM_STEP(CP)
+{
+       if (!sm->port_enabled)
+               SM_ENTER(CP, INIT);
+
+       switch (sm->CP_state) {
+       case CP_BEGIN:
+               SM_ENTER(CP, INIT);
+               break;
+
+       case CP_INIT:
+               SM_ENTER(CP, CHANGE);
+               break;
+
+       case CP_CHANGE:
+               if (sm->connect == UNAUTHENTICATED)
+                       SM_ENTER(CP, ALLOWED);
+               else if (sm->connect == AUTHENTICATED)
+                       SM_ENTER(CP, AUTHENTICATED);
+               else if (sm->connect == SECURE)
+                       SM_ENTER(CP, SECURED);
+               break;
+
+       case CP_ALLOWED:
+               if (sm->connect != UNAUTHENTICATED)
+                       SM_ENTER(CP, CHANGE);
+               break;
+
+       case CP_AUTHENTICATED:
+               if (sm->connect != AUTHENTICATED)
+                       SM_ENTER(CP, CHANGE);
+               break;
+
+       case CP_SECURED:
+               if (changed_connect(sm))
+                       SM_ENTER(CP, CHANGE);
+               else if (sm->new_sak)
+                       SM_ENTER(CP, RECEIVE);
+               break;
+
+       case CP_RECEIVE:
+               if (sm->using_receive_sas)
+                       SM_ENTER(CP, RECEIVING);
+               break;
+
+       case CP_RECEIVING:
+               if (sm->new_sak || changed_connect(sm))
+                       SM_ENTER(CP, ABANDON);
+               if (!sm->elected_self)
+                       SM_ENTER(CP, READY);
+               if (sm->elected_self &&
+                   (sm->all_receiving || !sm->transmit_when))
+                       SM_ENTER(CP, TRANSMIT);
+               break;
+
+       case CP_TRANSMIT:
+               if (sm->using_transmit_sa)
+                       SM_ENTER(CP, TRANSMITTING);
+               break;
+
+       case CP_TRANSMITTING:
+               if (!sm->retire_when || changed_connect(sm))
+                       SM_ENTER(CP, RETIRE);
+               break;
+
+       case CP_RETIRE:
+               if (changed_connect(sm))
+                       SM_ENTER(CP, CHANGE);
+               else if (sm->new_sak)
+                       SM_ENTER(CP, RECEIVE);
+               break;
+
+       case CP_READY:
+               if (sm->new_sak || changed_connect(sm))
+                       SM_ENTER(CP, RECEIVE);
+               if (sm->server_transmitting)
+                       SM_ENTER(CP, TRANSMIT);
+               break;
+       case CP_ABANDON:
+               if (changed_connect(sm))
+                       SM_ENTER(CP, RETIRE);
+               else if (sm->new_sak)
+                       SM_ENTER(CP, RECEIVE);
+               break;
+       default:
+               wpa_printf(MSG_ERROR, "CP: the state machine is not defined");
+               break;
+       }
+}
+
+
+/**
+ * ieee802_1x_cp_sm_init -
+ */
+struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(
+       struct ieee802_1x_kay *kay,
+       struct ieee802_1x_cp_conf *pcp_conf)
+{
+       struct ieee802_1x_cp_sm *sm;
+
+       sm = os_zalloc(sizeof(*sm));
+       if (sm == NULL) {
+               wpa_printf(MSG_ERROR, "CP-%s: out of memory", __func__);
+               return NULL;
+       }
+
+       sm->kay = kay;
+
+       sm->port_valid = FALSE;
+
+       sm->chgd_server = FALSE;
+
+       sm->protect_frames = pcp_conf->protect;
+       sm->validate_frames = pcp_conf->validate;
+       sm->replay_protect = pcp_conf->replay_protect;
+       sm->replay_window = pcp_conf->replay_window;
+
+       sm->controlled_port_enabled = FALSE;
+
+       sm->lki = NULL;
+       sm->lrx = FALSE;
+       sm->ltx = FALSE;
+       sm->oki = NULL;
+       sm->orx = FALSE;
+       sm->otx = FALSE;
+
+       sm->cipher_suite = os_zalloc(CS_ID_LEN);
+       sm->current_cipher_suite = os_zalloc(CS_ID_LEN);
+       if (!sm->cipher_suite || !sm->current_cipher_suite) {
+               wpa_printf(MSG_ERROR, "CP-%s: out of memory", __func__);
+               os_free(sm->cipher_suite);
+               os_free(sm->current_cipher_suite);
+               os_free(sm);
+               return NULL;
+       }
+       os_memcpy(sm->current_cipher_suite, default_cs_id, CS_ID_LEN);
+       os_memcpy(sm->cipher_suite, default_cs_id, CS_ID_LEN);
+       sm->cipher_offset = CONFIDENTIALITY_OFFSET_0;
+       sm->confidentiality_offset = sm->cipher_offset;
+       sm->transmit_delay = MKA_LIFE_TIME;
+       sm->retire_delay = MKA_SAK_RETIRE_TIME;
+       sm->CP_state = CP_BEGIN;
+       sm->changed = FALSE;
+       sm->authorization_data = NULL;
+
+       wpa_printf(MSG_DEBUG, "CP: state machine created");
+
+       secy_cp_control_protect_frames(sm->kay, sm->protect_frames);
+       secy_cp_control_validate_frames(sm->kay, sm->validate_frames);
+       secy_cp_control_replay(sm->kay, sm->replay_protect, sm->replay_window);
+       secy_cp_control_enable_port(sm->kay, sm->controlled_port_enabled);
+       secy_cp_control_confidentiality_offset(sm->kay,
+                                              sm->confidentiality_offset);
+
+       SM_ENTER(CP, INIT);
+       SM_STEP_RUN(CP);
+
+       return sm;
+}
+
+
+static void ieee802_1x_cp_step_run(struct ieee802_1x_cp_sm *sm)
+{
+       enum cp_states prev_state;
+       int i;
+
+       for (i = 0; i < 100; i++) {
+               prev_state = sm->CP_state;
+               SM_STEP_RUN(CP);
+               if (prev_state == sm->CP_state)
+                       break;
+       }
+}
+
+
+static void ieee802_1x_cp_step_cb(void *eloop_ctx, void *timeout_ctx)
+{
+       struct ieee802_1x_cp_sm *sm = eloop_ctx;
+       ieee802_1x_cp_step_run(sm);
+}
+
+
+/**
+ * ieee802_1x_cp_sm_deinit -
+ */
+void ieee802_1x_cp_sm_deinit(struct ieee802_1x_cp_sm *sm)
+{
+       wpa_printf(MSG_DEBUG, "CP: state machine removed");
+       if (!sm)
+               return;
+
+       eloop_cancel_timeout(ieee802_1x_cp_retire_when_timeout, sm, NULL);
+       eloop_cancel_timeout(ieee802_1x_cp_transmit_when_timeout, sm, NULL);
+       eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL);
+       os_free(sm->lki);
+       os_free(sm->oki);
+       os_free(sm->cipher_suite);
+       os_free(sm->current_cipher_suite);
+       os_free(sm->authorization_data);
+       os_free(sm);
+}
+
+
+/**
+ * ieee802_1x_cp_connect_pending
+ */
+void ieee802_1x_cp_connect_pending(void *cp_ctx)
+{
+       struct ieee802_1x_cp_sm *sm = cp_ctx;
+
+       sm->connect = PENDING;
+}
+
+
+/**
+ * ieee802_1x_cp_connect_unauthenticated
+ */
+void ieee802_1x_cp_connect_unauthenticated(void *cp_ctx)
+{
+       struct ieee802_1x_cp_sm *sm = (struct ieee802_1x_cp_sm *)cp_ctx;
+
+       sm->connect = UNAUTHENTICATED;
+}
+
+
+/**
+ * ieee802_1x_cp_connect_authenticated
+ */
+void ieee802_1x_cp_connect_authenticated(void *cp_ctx)
+{
+       struct ieee802_1x_cp_sm *sm = cp_ctx;
+
+       sm->connect = AUTHENTICATED;
+}
+
+
+/**
+ * ieee802_1x_cp_connect_secure
+ */
+void ieee802_1x_cp_connect_secure(void *cp_ctx)
+{
+       struct ieee802_1x_cp_sm *sm = cp_ctx;
+
+       sm->connect = SECURE;
+}
+
+
+/**
+ * ieee802_1x_cp_set_chgdserver -
+ */
+void ieee802_1x_cp_signal_chgdserver(void *cp_ctx)
+{
+       struct ieee802_1x_cp_sm *sm = cp_ctx;
+
+       sm->chgd_server = TRUE;
+}
+
+
+/**
+ * ieee802_1x_cp_set_electedself -
+ */
+void ieee802_1x_cp_set_electedself(void *cp_ctx, Boolean status)
+{
+       struct ieee802_1x_cp_sm *sm = cp_ctx;
+       sm->elected_self = status;
+}
+
+
+/**
+ * ieee802_1x_cp_set_authorizationdata -
+ */
+void ieee802_1x_cp_set_authorizationdata(void *cp_ctx, u8 *pdata, int len)
+{
+       struct ieee802_1x_cp_sm *sm = cp_ctx;
+       os_free(sm->authorization_data);
+       sm->authorization_data = os_zalloc(len);
+       if (sm->authorization_data)
+               os_memcpy(sm->authorization_data, pdata, len);
+}
+
+
+/**
+ * ieee802_1x_cp_set_ciphersuite -
+ */
+void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, void *pid)
+{
+       struct ieee802_1x_cp_sm *sm = cp_ctx;
+       os_memcpy(sm->cipher_suite, pid, CS_ID_LEN);
+}
+
+
+/**
+ * ieee802_1x_cp_set_offset -
+ */
+void ieee802_1x_cp_set_offset(void *cp_ctx, enum confidentiality_offset offset)
+{
+       struct ieee802_1x_cp_sm *sm = cp_ctx;
+       sm->cipher_offset = offset;
+}
+
+
+/**
+ * ieee802_1x_cp_signal_newsak -
+ */
+void ieee802_1x_cp_signal_newsak(void *cp_ctx)
+{
+       struct ieee802_1x_cp_sm *sm = cp_ctx;
+       sm->new_sak = TRUE;
+}
+
+
+/**
+ * ieee802_1x_cp_set_distributedki -
+ */
+void ieee802_1x_cp_set_distributedki(void *cp_ctx,
+                                    const struct ieee802_1x_mka_ki *dki)
+{
+       struct ieee802_1x_cp_sm *sm = cp_ctx;
+       os_memcpy(&sm->distributed_ki, dki, sizeof(struct ieee802_1x_mka_ki));
+}
+
+
+/**
+ * ieee802_1x_cp_set_distributedan -
+ */
+void ieee802_1x_cp_set_distributedan(void *cp_ctx, u8 an)
+{
+       struct ieee802_1x_cp_sm *sm = cp_ctx;
+       sm->distributed_an = an;
+}
+
+
+/**
+ * ieee802_1x_cp_set_usingreceivesas -
+ */
+void ieee802_1x_cp_set_usingreceivesas(void *cp_ctx, Boolean status)
+{
+       struct ieee802_1x_cp_sm *sm = cp_ctx;
+       sm->using_receive_sas = status;
+}
+
+
+/**
+ * ieee802_1x_cp_set_allreceiving -
+ */
+void ieee802_1x_cp_set_allreceiving(void *cp_ctx, Boolean status)
+{
+       struct ieee802_1x_cp_sm *sm = cp_ctx;
+       sm->all_receiving = status;
+}
+
+
+/**
+ * ieee802_1x_cp_set_servertransmitting -
+ */
+void ieee802_1x_cp_set_servertransmitting(void *cp_ctx, Boolean status)
+{
+       struct ieee802_1x_cp_sm *sm = cp_ctx;
+       sm->server_transmitting = status;
+}
+
+
+/**
+ * ieee802_1x_cp_set_usingtransmitsas -
+ */
+void ieee802_1x_cp_set_usingtransmitas(void *cp_ctx, Boolean status)
+{
+       struct ieee802_1x_cp_sm *sm = cp_ctx;
+       sm->using_transmit_sa = status;
+}
+
+
+/**
+ * ieee802_1x_cp_sm_step - Advance EAPOL state machines
+ * @sm: EAPOL state machine
+ *
+ * This function is called to advance CP state machines after any change
+ * that could affect their state.
+ */
+void ieee802_1x_cp_sm_step(void *cp_ctx)
+{
+       /*
+        * Run ieee802_1x_cp_step_run from a registered timeout
+        * to make sure that other possible timeouts/events are processed
+        * and to avoid long function call chains.
+        */
+       struct ieee802_1x_cp_sm *sm = cp_ctx;
+       eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL);
+       eloop_register_timeout(0, 0, ieee802_1x_cp_step_cb, sm, NULL);
+}
+
+
+static void ieee802_1x_cp_retire_when_timeout(void *eloop_ctx,
+                                             void *timeout_ctx)
+{
+       struct ieee802_1x_cp_sm *sm = eloop_ctx;
+       sm->retire_when = 0;
+       ieee802_1x_cp_step_run(sm);
+}
+
+
+static void
+ieee802_1x_cp_transmit_when_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct ieee802_1x_cp_sm *sm = eloop_ctx;
+       sm->transmit_when = 0;
+       ieee802_1x_cp_step_run(sm);
+}
diff --git a/src/pae/ieee802_1x_cp.h b/src/pae/ieee802_1x_cp.h
new file mode 100644 (file)
index 0000000..773c930
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * IEEE Std 802.1X-2010 Controlled Port of PAE state machine - CP state machine
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_1X_CP_H
+#define IEEE802_1X_CP_H
+
+#include "common/defs.h"
+#include "common/ieee802_1x_defs.h"
+
+struct ieee802_1x_cp_sm;
+struct ieee802_1x_kay;
+struct ieee802_1x_mka_ki;
+
+struct ieee802_1x_cp_conf {
+       Boolean protect;
+       Boolean replay_protect;
+       enum validate_frames validate;
+       u32 replay_window;
+};
+
+
+struct ieee802_1x_cp_sm *
+ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay,
+                     struct ieee802_1x_cp_conf *pcp_conf);
+void ieee802_1x_cp_sm_deinit(struct ieee802_1x_cp_sm *sm);
+void ieee802_1x_cp_sm_step(void *cp_ctx);
+void ieee802_1x_cp_connect_pending(void *cp_ctx);
+void ieee802_1x_cp_connect_unauthenticated(void *cp_ctx);
+void ieee802_1x_cp_connect_authenticated(void *cp_ctx);
+void ieee802_1x_cp_connect_secure(void *cp_ctx);
+void ieee802_1x_cp_signal_chgdserver(void *cp_ctx);
+void ieee802_1x_cp_set_electedself(void *cp_ctx, Boolean status);
+void ieee802_1x_cp_set_authorizationdata(void *cp_ctx, u8 *pdata, int len);
+void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, void *pid);
+void ieee802_1x_cp_set_offset(void *cp_ctx, enum confidentiality_offset offset);
+void ieee802_1x_cp_signal_newsak(void *cp_ctx);
+void ieee802_1x_cp_set_distributedki(void *cp_ctx,
+                                    const struct ieee802_1x_mka_ki *dki);
+void ieee802_1x_cp_set_distributedan(void *cp_ctx, u8 an);
+void ieee802_1x_cp_set_usingreceivesas(void *cp_ctx, Boolean status);
+void ieee802_1x_cp_set_allreceiving(void *cp_ctx, Boolean status);
+void ieee802_1x_cp_set_servertransmitting(void *cp_ctx, Boolean status);
+void ieee802_1x_cp_set_usingtransmitas(void *cp_ctx, Boolean status);
+
+#endif /* IEEE802_1X_CP_H */
diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c
new file mode 100644 (file)
index 0000000..ef74430
--- /dev/null
@@ -0,0 +1,3541 @@
+/*
+ * IEEE 802.1X-2010 Key Agree Protocol of PAE state machine
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include <time.h>
+#include "includes.h"
+#include "common.h"
+#include "list.h"
+#include "eloop.h"
+#include "wpabuf.h"
+#include "state_machine.h"
+#include "l2_packet/l2_packet.h"
+#include "common/eapol_common.h"
+#include "crypto/aes_wrap.h"
+#include "ieee802_1x_cp.h"
+#include "ieee802_1x_key.h"
+#include "ieee802_1x_kay.h"
+#include "ieee802_1x_kay_i.h"
+#include "ieee802_1x_secy_ops.h"
+
+
+#define DEFAULT_SA_KEY_LEN     16
+#define DEFAULT_ICV_LEN                16
+#define MAX_ICV_LEN            32  /* 32 bytes, 256 bits */
+
+#define PENDING_PN_EXHAUSTION 0xC0000000
+
+/* IEEE Std 802.1X-2010, Table 9-1 - MKA Algorithm Agility */
+#define MKA_ALGO_AGILITY_2009 { 0x00, 0x80, 0xC2, 0x01 }
+static u8 mka_algo_agility[4] = MKA_ALGO_AGILITY_2009;
+
+/* IEEE802.1AE-2006 Table 14-1 MACsec Cipher Suites */
+static struct macsec_ciphersuite cipher_suite_tbl[] = {
+       /* GCM-AES-128 */
+       {
+               CS_ID_GCM_AES_128,
+               CS_NAME_GCM_AES_128,
+               MACSEC_CAP_INTEG_AND_CONF_0_30_50,
+               16,
+
+               0 /* index */
+       },
+};
+#define CS_TABLE_SIZE (ARRAY_SIZE(cipher_suite_tbl))
+#define DEFAULT_CS_INDEX  0
+
+static struct mka_alg mka_alg_tbl[] = {
+       {
+               MKA_ALGO_AGILITY_2009,
+               /* 128-bit CAK, KEK, ICK, ICV */
+               16, 16, 16, 16,
+               ieee802_1x_cak_128bits_aes_cmac,
+               ieee802_1x_ckn_128bits_aes_cmac,
+               ieee802_1x_kek_128bits_aes_cmac,
+               ieee802_1x_ick_128bits_aes_cmac,
+               ieee802_1x_icv_128bits_aes_cmac,
+
+               1, /* index */
+       },
+};
+#define MKA_ALG_TABLE_SIZE (ARRAY_SIZE(mka_alg_tbl))
+
+
+static int is_ki_equal(struct ieee802_1x_mka_ki *ki1,
+                      struct ieee802_1x_mka_ki *ki2)
+{
+       return os_memcmp(ki1->mi, ki2->mi, MI_LEN) == 0 &&
+               ki1->kn == ki2->kn;
+}
+
+
+struct mka_param_body_handler {
+       int (*body_tx)(struct ieee802_1x_mka_participant *participant,
+                      struct wpabuf *buf);
+       int (*body_rx)(struct ieee802_1x_mka_participant *participant,
+                      const u8 *mka_msg, size_t msg_len);
+       int (*body_length)(struct ieee802_1x_mka_participant *participant);
+       Boolean (*body_present)(struct ieee802_1x_mka_participant *participant);
+};
+
+
+static void set_mka_param_body_len(void *body, unsigned int len)
+{
+       struct ieee802_1x_mka_hdr *hdr = body;
+       hdr->length = (len >> 8) & 0x0f;
+       hdr->length1 = len & 0xff;
+}
+
+
+static unsigned int get_mka_param_body_len(const void *body)
+{
+       const struct ieee802_1x_mka_hdr *hdr = body;
+       return (hdr->length << 8) | hdr->length1;
+}
+
+
+static int get_mka_param_body_type(const void *body)
+{
+       const struct ieee802_1x_mka_hdr *hdr = body;
+       return hdr->type;
+}
+
+
+/**
+ * ieee802_1x_mka_dump_basic_body -
+ */
+static void
+ieee802_1x_mka_dump_basic_body(struct ieee802_1x_mka_basic_body *body)
+{
+       size_t body_len;
+
+       if (!body)
+               return;
+
+       body_len = get_mka_param_body_len(body);
+       wpa_printf(MSG_DEBUG, "*** MKA Basic Parameter set ***");
+       wpa_printf(MSG_DEBUG, "\tVersion.......: %d", body->version);
+       wpa_printf(MSG_DEBUG, "\tPriority......: %d", body->priority);
+       wpa_printf(MSG_DEBUG, "\tKeySvr........: %d", body->key_server);
+       wpa_printf(MSG_DEBUG, "\tMACSecDesired.: %d", body->macsec_desired);
+       wpa_printf(MSG_DEBUG, "\tMACSecCapable.: %d", body->macsec_capbility);
+       wpa_printf(MSG_DEBUG, "\tBody Length...: %d", (int) body_len);
+       wpa_printf(MSG_DEBUG, "\tSCI MAC.......: " MACSTR,
+                  MAC2STR(body->actor_sci.addr));
+       wpa_printf(MSG_DEBUG, "\tSCI Port .....: %d",
+                  be_to_host16(body->actor_sci.port));
+       wpa_hexdump(MSG_DEBUG, "\tMember Id.....:",
+                   body->actor_mi, sizeof(body->actor_mi));
+       wpa_printf(MSG_DEBUG, "\tMessage Number: %d",
+                  be_to_host32(body->actor_mn));
+       wpa_hexdump(MSG_DEBUG, "\tAlgo Agility..:",
+                   body->algo_agility, sizeof(body->algo_agility));
+       wpa_hexdump_ascii(MSG_DEBUG, "\tCAK Name......:", body->ckn,
+                         body_len + MKA_HDR_LEN - sizeof(*body));
+}
+
+
+/**
+ * ieee802_1x_mka_dump_peer_body -
+ */
+static void
+ieee802_1x_mka_dump_peer_body(struct ieee802_1x_mka_peer_body *body)
+{
+       size_t body_len;
+       size_t i;
+       u8 *mi;
+       u32 mn;
+
+       if (body == NULL)
+               return;
+
+       body_len = get_mka_param_body_len(body);
+       if (body->type == MKA_LIVE_PEER_LIST) {
+               wpa_printf(MSG_DEBUG, "*** Live Peer List ***");
+               wpa_printf(MSG_DEBUG, "\tBody Length...: %d", (int) body_len);
+       } else if (body->type == MKA_POTENTIAL_PEER_LIST) {
+               wpa_printf(MSG_DEBUG, "*** Potential Live Peer List ***");
+               wpa_printf(MSG_DEBUG, "\tBody Length...: %d", (int) body_len);
+       }
+
+       for (i = 0; i < body_len; i += MI_LEN + sizeof(mn)) {
+               mi = body->peer + i;
+               os_memcpy(&mn, mi + MI_LEN, sizeof(mn));
+               wpa_hexdump_ascii(MSG_DEBUG, "\tMember Id.....:", mi, MI_LEN);
+               wpa_printf(MSG_DEBUG, "\tMessage Number: %d", be_to_host32(mn));
+       }
+}
+
+
+/**
+ * ieee802_1x_mka_dump_dist_sak_body -
+ */
+static void
+ieee802_1x_mka_dump_dist_sak_body(struct ieee802_1x_mka_dist_sak_body *body)
+{
+       size_t body_len;
+
+       if (body == NULL)
+               return;
+
+       body_len = get_mka_param_body_len(body);
+       wpa_printf(MSG_INFO, "*** Distributed SAK ***");
+       wpa_printf(MSG_INFO, "\tDistributed AN........: %d", body->dan);
+       wpa_printf(MSG_INFO, "\tConfidentiality Offset: %d",
+                  body->confid_offset);
+       wpa_printf(MSG_INFO, "\tBody Length...........: %d", (int) body_len);
+       if (!body_len)
+               return;
+
+       wpa_printf(MSG_INFO, "\tKey Number............: %d",
+                  be_to_host32(body->kn));
+       wpa_hexdump(MSG_INFO, "\tAES Key Wrap of SAK...:", body->sak, 24);
+}
+
+
+static const char * yes_no(int val)
+{
+       return val ? "Yes" : "No";
+}
+
+
+/**
+ * ieee802_1x_mka_dump_sak_use_body -
+ */
+static void
+ieee802_1x_mka_dump_sak_use_body(struct ieee802_1x_mka_sak_use_body *body)
+{
+       int body_len;
+
+       if (body == NULL)
+               return;
+
+       body_len = get_mka_param_body_len(body);
+       wpa_printf(MSG_DEBUG, "*** MACsec SAK Use ***");
+       wpa_printf(MSG_DEBUG, "\tLatest Key AN....: %d", body->lan);
+       wpa_printf(MSG_DEBUG, "\tLatest Key Tx....: %s", yes_no(body->ltx));
+       wpa_printf(MSG_DEBUG, "\tLatest Key Rx....: %s", yes_no(body->lrx));
+       wpa_printf(MSG_DEBUG, "\tOld Key AN....: %d", body->oan);
+       wpa_printf(MSG_DEBUG, "\tOld Key Tx....: %s", yes_no(body->otx));
+       wpa_printf(MSG_DEBUG, "\tOld Key Rx....: %s", yes_no(body->orx));
+       wpa_printf(MSG_DEBUG, "\tPlain Key Tx....: %s", yes_no(body->ptx));
+       wpa_printf(MSG_DEBUG, "\tPlain Key Rx....: %s", yes_no(body->prx));
+       wpa_printf(MSG_DEBUG, "\tDelay Protect....: %s",
+                  yes_no(body->delay_protect));
+       wpa_printf(MSG_DEBUG, "\tBody Length......: %d", body_len);
+       if (!body_len)
+               return;
+
+       wpa_hexdump(MSG_DEBUG, "\tKey Server MI....:",
+                   body->lsrv_mi, sizeof(body->lsrv_mi));
+       wpa_printf(MSG_DEBUG, "\tKey Number.......: %u",
+                  be_to_host32(body->lkn));
+       wpa_printf(MSG_DEBUG, "\tLowest PN........: %u",
+                  be_to_host32(body->llpn));
+       wpa_hexdump_ascii(MSG_DEBUG, "\tOld Key Server MI....:",
+                         body->osrv_mi, sizeof(body->osrv_mi));
+       wpa_printf(MSG_DEBUG, "\tOld Key Number.......: %u",
+                  be_to_host32(body->okn));
+       wpa_printf(MSG_DEBUG, "\tOld Lowest PN........: %u",
+                  be_to_host32(body->olpn));
+}
+
+
+/**
+ * ieee802_1x_kay_get_participant -
+ */
+static struct ieee802_1x_mka_participant *
+ieee802_1x_kay_get_participant(struct ieee802_1x_kay *kay, const u8 *ckn)
+{
+       struct ieee802_1x_mka_participant *participant;
+
+       dl_list_for_each(participant, &kay->participant_list,
+                        struct ieee802_1x_mka_participant, list) {
+               if (os_memcmp(participant->ckn.name, ckn,
+                             participant->ckn.len) == 0)
+                       return participant;
+       }
+
+       wpa_printf(MSG_DEBUG, "KaY: participant is not found");
+
+       return NULL;
+}
+
+
+/**
+ * ieee802_1x_kay_get_principal_participant -
+ */
+static struct ieee802_1x_mka_participant *
+ieee802_1x_kay_get_principal_participant(struct ieee802_1x_kay *kay)
+{
+       struct ieee802_1x_mka_participant *participant;
+
+       dl_list_for_each(participant, &kay->participant_list,
+                        struct ieee802_1x_mka_participant, list) {
+               if (participant->principal)
+                       return participant;
+       }
+
+       wpa_printf(MSG_DEBUG, "KaY: principal participant is not founded");
+       return NULL;
+}
+
+
+static struct ieee802_1x_kay_peer * get_peer_mi(struct dl_list *peers,
+                                               const u8 *mi)
+{
+       struct ieee802_1x_kay_peer *peer;
+
+       dl_list_for_each(peer, peers, struct ieee802_1x_kay_peer, list) {
+               if (os_memcmp(peer->mi, mi, MI_LEN) == 0)
+                       return peer;
+       }
+
+       return NULL;
+}
+
+
+/**
+ * ieee802_1x_kay_is_in_potential_peer
+ */
+static Boolean
+ieee802_1x_kay_is_in_potential_peer(
+       struct ieee802_1x_mka_participant *participant, const u8 *mi)
+{
+       return get_peer_mi(&participant->potential_peers, mi) != NULL;
+}
+
+
+/**
+ * ieee802_1x_kay_is_in_live_peer
+ */
+static Boolean
+ieee802_1x_kay_is_in_live_peer(
+       struct ieee802_1x_mka_participant *participant, const u8 *mi)
+{
+       return get_peer_mi(&participant->live_peers, mi) != NULL;
+}
+
+
+/**
+ * ieee802_1x_kay_is_in_peer
+ */
+static Boolean
+ieee802_1x_kay_is_in_peer(struct ieee802_1x_mka_participant *participant,
+                         const u8 *mi)
+{
+       return ieee802_1x_kay_is_in_live_peer(participant, mi) ||
+               ieee802_1x_kay_is_in_potential_peer(participant, mi);
+}
+
+
+/**
+ * ieee802_1x_kay_get_peer
+ */
+static struct ieee802_1x_kay_peer *
+ieee802_1x_kay_get_peer(struct ieee802_1x_mka_participant *participant,
+                       const u8 *mi)
+{
+       struct ieee802_1x_kay_peer *peer;
+
+       peer = get_peer_mi(&participant->live_peers, mi);
+       if (peer)
+               return peer;
+
+       return get_peer_mi(&participant->potential_peers, mi);
+}
+
+
+/**
+ * ieee802_1x_kay_get_live_peer
+ */
+static struct ieee802_1x_kay_peer *
+ieee802_1x_kay_get_live_peer(struct ieee802_1x_mka_participant *participant,
+                            const u8 *mi)
+{
+       return get_peer_mi(&participant->live_peers, mi);
+}
+
+
+/**
+ * ieee802_1x_kay_get_cipher_suite
+ */
+static struct macsec_ciphersuite *
+ieee802_1x_kay_get_cipher_suite(struct ieee802_1x_mka_participant *participant,
+                               u8 *cs_id)
+{
+       unsigned int i;
+
+       for (i = 0; i < CS_TABLE_SIZE; i++) {
+               if (os_memcmp(cipher_suite_tbl[i].id, cs_id, CS_ID_LEN) == 0)
+                       break;
+       }
+       if (i >= CS_TABLE_SIZE)
+               return NULL;
+
+       return &cipher_suite_tbl[i];
+}
+
+
+/**
+ * ieee802_1x_kay_get_peer_sci
+ */
+static struct ieee802_1x_kay_peer *
+ieee802_1x_kay_get_peer_sci(struct ieee802_1x_mka_participant *participant,
+                           const struct ieee802_1x_mka_sci *sci)
+{
+       struct ieee802_1x_kay_peer *peer;
+
+       dl_list_for_each(peer, &participant->live_peers,
+                        struct ieee802_1x_kay_peer, list) {
+               if (os_memcmp(&peer->sci, sci, sizeof(peer->sci)) == 0)
+                       return peer;
+       }
+
+       dl_list_for_each(peer, &participant->potential_peers,
+                        struct ieee802_1x_kay_peer, list) {
+               if (os_memcmp(&peer->sci, sci, sizeof(peer->sci)) == 0)
+                       return peer;
+       }
+
+       return NULL;
+}
+
+
+/**
+ * ieee802_1x_kay_init_receive_sa -
+ */
+static struct receive_sa *
+ieee802_1x_kay_init_receive_sa(struct receive_sc *psc, u8 an, u32 lowest_pn,
+                              struct data_key *key)
+{
+       struct receive_sa *psa;
+
+       if (!psc || !key)
+               return NULL;
+
+       psa = os_zalloc(sizeof(*psa));
+       if (!psa) {
+               wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
+               return NULL;
+       }
+
+       psa->pkey = key;
+       psa->lowest_pn = lowest_pn;
+       psa->next_pn = lowest_pn;
+       psa->an = an;
+       psa->sc = psc;
+
+       os_get_time(&psa->created_time);
+       psa->in_use = FALSE;
+
+       dl_list_add(&psc->sa_list, &psa->list);
+       wpa_printf(MSG_DEBUG,
+                  "KaY: Create receive SA(AN: %d lowest_pn: %u of SC(channel: %d)",
+                  (int) an, lowest_pn, psc->channel);
+
+       return psa;
+}
+
+
+/**
+ * ieee802_1x_kay_deinit_receive_sa -
+ */
+static void ieee802_1x_kay_deinit_receive_sa(struct receive_sa *psa)
+{
+       psa->pkey = NULL;
+       wpa_printf(MSG_DEBUG,
+                  "KaY: Delete receive SA(an: %d) of SC(channel: %d)",
+                  psa->an, psa->sc->channel);
+       dl_list_del(&psa->list);
+       os_free(psa);
+}
+
+
+/**
+ * ieee802_1x_kay_init_receive_sc -
+ */
+static struct receive_sc *
+ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci,
+                              int channel)
+{
+       struct receive_sc *psc;
+
+       if (!psci)
+               return NULL;
+
+       psc = os_zalloc(sizeof(*psc));
+       if (!psc) {
+               wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
+               return NULL;
+       }
+
+       os_memcpy(&psc->sci, psci, sizeof(psc->sci));
+       psc->channel = channel;
+
+       os_get_time(&psc->created_time);
+       psc->receiving = FALSE;
+
+       dl_list_init(&psc->sa_list);
+       wpa_printf(MSG_DEBUG, "KaY: Create receive SC(channel: %d)", channel);
+       wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)psci, sizeof(*psci));
+
+       return psc;
+}
+
+
+/**
+ * ieee802_1x_kay_deinit_receive_sc -
+ **/
+static void
+ieee802_1x_kay_deinit_receive_sc(
+       struct ieee802_1x_mka_participant *participant, struct receive_sc *psc)
+{
+       struct receive_sa *psa, *pre_sa;
+
+       wpa_printf(MSG_DEBUG, "KaY: Delete receive SC(channel: %d)",
+                  psc->channel);
+       dl_list_for_each_safe(psa, pre_sa, &psc->sa_list, struct receive_sa,
+                             list)  {
+               secy_disable_receive_sa(participant->kay, psa);
+               ieee802_1x_kay_deinit_receive_sa(psa);
+       }
+       dl_list_del(&psc->list);
+       os_free(psc);
+}
+
+
+/**
+ * ieee802_1x_kay_create_live_peer
+ */
+static struct ieee802_1x_kay_peer *
+ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant,
+                               u8 *mi, u32 mn)
+{
+       struct ieee802_1x_kay_peer *peer;
+       struct receive_sc *rxsc;
+       u32 sc_ch = 0;
+
+       peer = os_zalloc(sizeof(*peer));
+       if (peer == NULL) {
+               wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
+               return NULL;
+       }
+
+       os_memcpy(peer->mi, mi, MI_LEN);
+       peer->mn = mn;
+       peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
+       peer->sak_used = FALSE;
+       os_memcpy(&peer->sci, &participant->current_peer_sci,
+                 sizeof(peer->sci));
+       dl_list_add(&participant->live_peers, &peer->list);
+
+       secy_get_available_receive_sc(participant->kay, &sc_ch);
+
+       rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci, sc_ch);
+       if (!rxsc)
+               return NULL;
+
+       dl_list_add(&participant->rxsc_list, &rxsc->list);
+       secy_create_receive_sc(participant->kay, rxsc);
+
+       wpa_printf(MSG_DEBUG, "KaY: Live peer created");
+       wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, sizeof(peer->mi));
+       wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
+       wpa_hexdump(MSG_DEBUG, "\tSCI Addr: ", peer->sci.addr, ETH_ALEN);
+       wpa_printf(MSG_DEBUG, "\tPort: %d", peer->sci.port);
+
+       return peer;
+}
+
+
+/**
+ * ieee802_1x_kay_create_potential_peer
+ */
+static struct ieee802_1x_kay_peer *
+ieee802_1x_kay_create_potential_peer(
+       struct ieee802_1x_mka_participant *participant, const u8 *mi, u32 mn)
+{
+       struct ieee802_1x_kay_peer *peer;
+
+       peer = os_zalloc(sizeof(*peer));
+       if (peer == NULL) {
+               wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
+               return NULL;
+       }
+
+       os_memcpy(peer->mi, mi, MI_LEN);
+       peer->mn = mn;
+       peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
+       peer->sak_used = FALSE;
+
+       dl_list_add(&participant->potential_peers, &peer->list);
+
+       wpa_printf(MSG_DEBUG, "KaY: potential peer created");
+       wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, sizeof(peer->mi));
+       wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
+       wpa_hexdump(MSG_DEBUG, "\tSCI Addr: ", peer->sci.addr, ETH_ALEN);
+       wpa_printf(MSG_DEBUG, "\tPort: %d", peer->sci.port);
+
+       return peer;
+}
+
+
+/**
+ * ieee802_1x_kay_move_live_peer
+ */
+static struct ieee802_1x_kay_peer *
+ieee802_1x_kay_move_live_peer(struct ieee802_1x_mka_participant *participant,
+                             u8 *mi, u32 mn)
+{
+       struct ieee802_1x_kay_peer *peer;
+       struct receive_sc *rxsc;
+       u32 sc_ch = 0;
+
+       dl_list_for_each(peer, &participant->potential_peers,
+                        struct ieee802_1x_kay_peer, list) {
+               if (os_memcmp(peer->mi, mi, MI_LEN) == 0)
+                       break;
+       }
+
+       os_memcpy(&peer->sci, &participant->current_peer_sci,
+                 sizeof(peer->sci));
+       peer->mn = mn;
+       peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
+
+       wpa_printf(MSG_DEBUG, "KaY: move potential peer to live peer");
+       wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, sizeof(peer->mi));
+       wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
+       wpa_hexdump(MSG_DEBUG, "\tSCI Addr: ", peer->sci.addr, ETH_ALEN);
+       wpa_printf(MSG_DEBUG, "\tPort: %d", peer->sci.port);
+
+       dl_list_del(&peer->list);
+       dl_list_add_tail(&participant->live_peers, &peer->list);
+
+       secy_get_available_receive_sc(participant->kay, &sc_ch);
+
+       rxsc = ieee802_1x_kay_init_receive_sc(&peer->sci, sc_ch);
+       if (!rxsc)
+               return NULL;
+
+       dl_list_add(&participant->rxsc_list, &rxsc->list);
+       secy_create_receive_sc(participant->kay, rxsc);
+
+       return peer;
+}
+
+
+
+/**
+ *  ieee802_1x_mka_basic_body_present -
+ */
+static Boolean
+ieee802_1x_mka_basic_body_present(
+       struct ieee802_1x_mka_participant *participant)
+{
+       return TRUE;
+}
+
+
+/**
+ * ieee802_1x_mka_basic_body_length -
+ */
+static int
+ieee802_1x_mka_basic_body_length(struct ieee802_1x_mka_participant *participant)
+{
+       int length;
+
+       length = sizeof(struct ieee802_1x_mka_basic_body);
+       length += participant->ckn.len;
+       return (length + 0x3) & ~0x3;
+}
+
+
+/**
+ * ieee802_1x_mka_encode_basic_body
+ */
+static int
+ieee802_1x_mka_encode_basic_body(
+       struct ieee802_1x_mka_participant *participant,
+       struct wpabuf *buf)
+{
+       struct ieee802_1x_mka_basic_body *body;
+       struct ieee802_1x_kay *kay = participant->kay;
+       unsigned int length = ieee802_1x_mka_basic_body_length(participant);
+
+       body = wpabuf_put(buf, length);
+
+       body->version = kay->mka_version;
+       body->priority = kay->actor_priority;
+       if (participant->is_elected)
+               body->key_server = participant->is_key_server;
+       else
+               body->key_server = participant->can_be_key_server;
+
+       body->macsec_desired = kay->macsec_desired;
+       body->macsec_capbility = kay->macsec_capable;
+       set_mka_param_body_len(body, length - MKA_HDR_LEN);
+
+       os_memcpy(body->actor_sci.addr, kay->actor_sci.addr,
+                 sizeof(kay->actor_sci.addr));
+       body->actor_sci.port = host_to_be16(kay->actor_sci.port);
+
+       os_memcpy(body->actor_mi, participant->mi, sizeof(body->actor_mi));
+       participant->mn = participant->mn + 1;
+       body->actor_mn = host_to_be32(participant->mn);
+       os_memcpy(body->algo_agility, participant->kay->algo_agility,
+                 sizeof(body->algo_agility));
+
+       os_memcpy(body->ckn, participant->ckn.name, participant->ckn.len);
+
+       ieee802_1x_mka_dump_basic_body(body);
+
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_decode_basic_body -
+ */
+static struct ieee802_1x_mka_participant *
+ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg,
+                                size_t msg_len)
+{
+       struct ieee802_1x_mka_participant *participant;
+       const struct ieee802_1x_mka_basic_body *body;
+       struct ieee802_1x_kay_peer *peer;
+
+       body = (const struct ieee802_1x_mka_basic_body *) mka_msg;
+
+       if (body->version > MKA_VERSION_ID) {
+               wpa_printf(MSG_DEBUG,
+                          "KaY: peer's version(%d) greater than mka current version(%d)",
+                          body->version, MKA_VERSION_ID);
+       }
+       if (kay->is_obliged_key_server && body->key_server) {
+               wpa_printf(MSG_DEBUG, "I must be as key server");
+               return NULL;
+       }
+
+       participant = ieee802_1x_kay_get_participant(kay, body->ckn);
+       if (!participant) {
+               wpa_printf(MSG_DEBUG, "Peer is not included in my CA");
+               return NULL;
+       }
+
+       /* If the peer's MI is my MI, I will choose new MI */
+       if (os_memcmp(body->actor_mi, participant->mi, MI_LEN) == 0) {
+               if (os_get_random(participant->mi, sizeof(participant->mi)) < 0)
+                       return NULL;
+               participant->mn = 0;
+       }
+
+       os_memcpy(participant->current_peer_id.mi, body->actor_mi, MI_LEN);
+       participant->current_peer_id.mn =  be_to_host32(body->actor_mn);
+       os_memcpy(participant->current_peer_sci.addr, body->actor_sci.addr,
+                 sizeof(participant->current_peer_sci.addr));
+       participant->current_peer_sci.port = be_to_host16(body->actor_sci.port);
+
+       /* handler peer */
+       peer = ieee802_1x_kay_get_peer(participant, body->actor_mi);
+       if (!peer) {
+               /* Check duplicated SCI */
+               /* TODO: What policy should be applied to detect duplicated SCI
+                * is active attacker or a valid peer whose MI is be changed?
+                */
+               peer = ieee802_1x_kay_get_peer_sci(participant,
+                                                  &body->actor_sci);
+               if (peer) {
+                       wpa_printf(MSG_WARNING,
+                                  "KaY: duplicated SCI detected, Maybe active attacker");
+                       dl_list_del(&peer->list);
+                       os_free(peer);
+               }
+
+               peer = ieee802_1x_kay_create_potential_peer(
+                       participant, body->actor_mi,
+                       be_to_host32(body->actor_mn));
+               if (!peer)
+                       return NULL;
+
+               peer->macsec_desired = body->macsec_desired;
+               peer->macsec_capbility = body->macsec_capbility;
+               peer->is_key_server = (Boolean) body->key_server;
+               peer->key_server_priority = body->priority;
+       } else if (peer->mn < be_to_host32(body->actor_mn)) {
+               peer->mn = be_to_host32(body->actor_mn);
+               peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
+               peer->macsec_desired = body->macsec_desired;
+               peer->macsec_capbility = body->macsec_capbility;
+               peer->is_key_server = (Boolean) body->key_server;
+               peer->key_server_priority = body->priority;
+       } else {
+               wpa_printf(MSG_WARNING, "KaY: The peer MN have received");
+               return NULL;
+       }
+
+       return participant;
+}
+
+
+/**
+ * ieee802_1x_mka_live_peer_body_present
+ */
+static Boolean
+ieee802_1x_mka_live_peer_body_present(
+       struct ieee802_1x_mka_participant *participant)
+{
+       return !dl_list_empty(&participant->live_peers);
+}
+
+
+/**
+ * ieee802_1x_kay_get_live_peer_length
+ */
+static int
+ieee802_1x_mka_get_live_peer_length(
+       struct ieee802_1x_mka_participant *participant)
+{
+       int len = MKA_HDR_LEN;
+       struct ieee802_1x_kay_peer *peer;
+
+       dl_list_for_each(peer, &participant->live_peers,
+                        struct ieee802_1x_kay_peer, list)
+               len += sizeof(struct ieee802_1x_mka_peer_id);
+
+       return (len + 0x3) & ~0x3;
+}
+
+
+/**
+ * ieee802_1x_mka_encode_live_peer_body -
+ */
+static int
+ieee802_1x_mka_encode_live_peer_body(
+       struct ieee802_1x_mka_participant *participant,
+       struct wpabuf *buf)
+{
+       struct ieee802_1x_mka_peer_body *body;
+       struct ieee802_1x_kay_peer *peer;
+       unsigned int length;
+       struct ieee802_1x_mka_peer_id *body_peer;
+
+       length = ieee802_1x_mka_get_live_peer_length(participant);
+       body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_peer_body));
+
+       body->type = MKA_LIVE_PEER_LIST;
+       set_mka_param_body_len(body, length - MKA_HDR_LEN);
+
+       dl_list_for_each(peer, &participant->live_peers,
+                        struct ieee802_1x_kay_peer, list) {
+               body_peer = wpabuf_put(buf,
+                                      sizeof(struct ieee802_1x_mka_peer_id));
+               os_memcpy(body_peer->mi, peer->mi, MI_LEN);
+               body_peer->mn = host_to_be32(peer->mn);
+               body_peer++;
+       }
+
+       ieee802_1x_mka_dump_peer_body(body);
+       return 0;
+}
+
+/**
+ * ieee802_1x_mka_potential_peer_body_present
+ */
+static Boolean
+ieee802_1x_mka_potential_peer_body_present(
+       struct ieee802_1x_mka_participant *participant)
+{
+       return !dl_list_empty(&participant->potential_peers);
+}
+
+
+/**
+ * ieee802_1x_kay_get_potential_peer_length
+ */
+static int
+ieee802_1x_mka_get_potential_peer_length(
+       struct ieee802_1x_mka_participant *participant)
+{
+       int len = MKA_HDR_LEN;
+       struct ieee802_1x_kay_peer *peer;
+
+       dl_list_for_each(peer, &participant->potential_peers,
+                        struct ieee802_1x_kay_peer, list)
+               len += sizeof(struct ieee802_1x_mka_peer_id);
+
+       return (len + 0x3) & ~0x3;
+}
+
+
+/**
+ * ieee802_1x_mka_encode_potential_peer_body -
+ */
+static int
+ieee802_1x_mka_encode_potential_peer_body(
+       struct ieee802_1x_mka_participant *participant,
+       struct wpabuf *buf)
+{
+       struct ieee802_1x_mka_peer_body *body;
+       struct ieee802_1x_kay_peer *peer;
+       unsigned int length;
+       struct ieee802_1x_mka_peer_id *body_peer;
+
+       length = ieee802_1x_mka_get_potential_peer_length(participant);
+       body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_peer_body));
+
+       body->type = MKA_POTENTIAL_PEER_LIST;
+       set_mka_param_body_len(body, length - MKA_HDR_LEN);
+
+       dl_list_for_each(peer, &participant->potential_peers,
+                        struct ieee802_1x_kay_peer, list) {
+               body_peer = wpabuf_put(buf,
+                                      sizeof(struct ieee802_1x_mka_peer_id));
+               os_memcpy(body_peer->mi, peer->mi, MI_LEN);
+               body_peer->mn = host_to_be32(peer->mn);
+               body_peer++;
+       }
+
+       ieee802_1x_mka_dump_peer_body(body);
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_i_in_peerlist -
+ */
+static Boolean
+ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant,
+                            const u8 *mka_msg, size_t msg_len)
+{
+       Boolean included = FALSE;
+       struct ieee802_1x_mka_hdr *hdr;
+       size_t body_len;
+       size_t left_len;
+       int body_type;
+       u32 peer_mn;
+       const u8 *peer_mi;
+       const u8 *pos;
+       size_t i;
+
+       pos = mka_msg;
+       left_len = msg_len;
+       while (left_len > (MKA_HDR_LEN + DEFAULT_ICV_LEN)) {
+               hdr = (struct ieee802_1x_mka_hdr *) pos;
+               body_len = get_mka_param_body_len(hdr);
+               body_type = get_mka_param_body_type(hdr);
+
+               if (body_type != MKA_LIVE_PEER_LIST &&
+                   body_type != MKA_POTENTIAL_PEER_LIST)
+                       goto SKIP_PEER;
+
+               ieee802_1x_mka_dump_peer_body(
+                       (struct ieee802_1x_mka_peer_body *)pos);
+
+               if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) {
+                       wpa_printf(MSG_ERROR,
+                                  "KaY: MKA Peer Packet Body Length (%d bytes) is less than the Parameter Set Header Length (%d bytes) + the Parameter Set Body Length (%d bytes) + %d bytes of ICV",
+                                  (int) left_len, (int) MKA_HDR_LEN,
+                                  (int) body_len, DEFAULT_ICV_LEN);
+                       goto SKIP_PEER;
+               }
+
+               if ((body_len % 16) != 0) {
+                       wpa_printf(MSG_ERROR,
+                                  "KaY: MKA Peer Packet Body Length (%d bytes) should multiple of 16 octets",
+                                  (int) body_len);
+                       goto SKIP_PEER;
+               }
+
+               for (i = 0; i < body_len; i += MI_LEN + sizeof(peer_mn)) {
+                       peer_mi = MKA_HDR_LEN + pos + i;
+                       os_memcpy(&peer_mn, peer_mi + MI_LEN, sizeof(peer_mn));
+                       peer_mn = be_to_host32(peer_mn);
+                       if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0 &&
+                           peer_mn == participant->mn) {
+                               included = TRUE;
+                               break;
+                       }
+               }
+
+               if (included)
+                       return TRUE;
+
+SKIP_PEER:
+               left_len -= body_len + MKA_HDR_LEN;
+               pos += body_len + MKA_HDR_LEN;
+       }
+
+       return FALSE;
+}
+
+
+/**
+ * ieee802_1x_mka_decode_live_peer_body -
+ */
+static int ieee802_1x_mka_decode_live_peer_body(
+       struct ieee802_1x_mka_participant *participant,
+       const u8 *peer_msg, size_t msg_len)
+{
+       const struct ieee802_1x_mka_hdr *hdr;
+       struct ieee802_1x_kay_peer *peer;
+       size_t body_len;
+       u32 peer_mn;
+       const u8 *peer_mi;
+       size_t i;
+       Boolean is_included;
+
+       is_included = ieee802_1x_kay_is_in_live_peer(
+               participant, participant->current_peer_id.mi);
+
+       hdr = (const struct ieee802_1x_mka_hdr *) peer_msg;
+       body_len = get_mka_param_body_len(hdr);
+
+       for (i = 0; i < body_len; i += MI_LEN + sizeof(peer_mn)) {
+               peer_mi = MKA_HDR_LEN + peer_msg + i;
+               os_memcpy(&peer_mn, peer_mi + MI_LEN, sizeof(peer_mn));
+               peer_mn = be_to_host32(peer_mn);
+
+               /* it is myself */
+               if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) {
+                       /* My message id is used by other participant */
+                       if (peer_mn > participant->mn) {
+                               if (os_get_random(participant->mi,
+                                                 sizeof(participant->mi)) < 0)
+                                       wpa_printf(MSG_DEBUG,
+                                                  "KaY: Could not update mi");
+                               participant->mn = 0;
+                       }
+                       continue;
+               }
+               if (!is_included)
+                       continue;
+
+               peer = ieee802_1x_kay_get_peer(participant, peer_mi);
+               if (NULL != peer) {
+                       peer->mn = peer_mn;
+                       peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
+               } else {
+                       if (!ieee802_1x_kay_create_potential_peer(
+                               participant, peer_mi, peer_mn)) {
+                               return -1;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_decode_potential_peer_body -
+ */
+static int
+ieee802_1x_mka_decode_potential_peer_body(
+       struct ieee802_1x_mka_participant *participant,
+       const u8 *peer_msg, size_t msg_len)
+{
+       struct ieee802_1x_mka_hdr *hdr;
+       size_t body_len;
+       u32 peer_mn;
+       const u8 *peer_mi;
+       size_t i;
+
+       hdr = (struct ieee802_1x_mka_hdr *) peer_msg;
+       body_len = get_mka_param_body_len(hdr);
+
+       for (i = 0; i < body_len; i += MI_LEN + sizeof(peer_mn)) {
+               peer_mi = MKA_HDR_LEN + peer_msg + i;
+               os_memcpy(&peer_mn, peer_mi + MI_LEN, sizeof(peer_mn));
+               peer_mn = be_to_host32(peer_mn);
+
+               /* it is myself */
+               if (os_memcmp(peer_mi, participant->mi, MI_LEN) == 0) {
+                       /* My message id is used by other participant */
+                       if (peer_mn > participant->mn) {
+                               if (os_get_random(participant->mi,
+                                                 sizeof(participant->mi)) < 0)
+                                       wpa_printf(MSG_DEBUG,
+                                                  "KaY: Could not update mi");
+                               participant->mn = 0;
+                       }
+                       continue;
+               }
+       }
+
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_sak_use_body_present
+ */
+static Boolean
+ieee802_1x_mka_sak_use_body_present(
+       struct ieee802_1x_mka_participant *participant)
+{
+       if (participant->to_use_sak)
+               return TRUE;
+       else
+               return FALSE;
+}
+
+
+/**
+ * ieee802_1x_mka_get_sak_use_length
+ */
+static int
+ieee802_1x_mka_get_sak_use_length(
+       struct ieee802_1x_mka_participant *participant)
+{
+       int length = MKA_HDR_LEN;
+
+       if (participant->kay->macsec_desired && participant->advised_desired)
+               length = sizeof(struct ieee802_1x_mka_sak_use_body);
+       else
+               length = MKA_HDR_LEN;
+
+       length = (length + 0x3) & ~0x3;
+
+       return length;
+}
+
+
+/**
+ *
+ */
+static u32
+ieee802_1x_mka_get_lpn(struct ieee802_1x_mka_participant *principal,
+                      struct ieee802_1x_mka_ki *ki)
+{
+       struct receive_sa *rxsa;
+       struct receive_sc *rxsc;
+       u32 lpn = 0;
+
+       dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
+               dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list)
+               {
+                       if (is_ki_equal(&rxsa->pkey->key_identifier, ki)) {
+                               secy_get_receive_lowest_pn(principal->kay,
+                                                          rxsa);
+
+                               lpn = lpn > rxsa->lowest_pn ?
+                                       lpn : rxsa->lowest_pn;
+                               break;
+                       }
+               }
+       }
+
+       if (lpn == 0)
+               lpn = 1;
+
+       return lpn;
+}
+
+
+/**
+ * ieee802_1x_mka_encode_sak_use_body -
+ */
+static int
+ieee802_1x_mka_encode_sak_use_body(
+       struct ieee802_1x_mka_participant *participant,
+       struct wpabuf *buf)
+{
+       struct ieee802_1x_mka_sak_use_body *body;
+       unsigned int length;
+       u32 pn = 1;
+
+       length = ieee802_1x_mka_get_sak_use_length(participant);
+       body = wpabuf_put(buf, sizeof(struct ieee802_1x_mka_sak_use_body));
+
+       body->type = MKA_SAK_USE;
+       set_mka_param_body_len(body, length - MKA_HDR_LEN);
+
+       if (length == MKA_HDR_LEN) {
+               body->ptx = TRUE;
+               body->prx = TRUE;
+               body->lan = 0;
+               body->lrx = FALSE;
+               body->ltx = FALSE;
+               body->delay_protect = FALSE;
+               return 0;
+       }
+
+       /* data protect, lowest accept packet number */
+       body->delay_protect = participant->kay->macsec_replay_protect;
+       pn = ieee802_1x_mka_get_lpn(participant, &participant->lki);
+       if (pn > participant->kay->pn_exhaustion) {
+               wpa_printf(MSG_WARNING, "KaY: My LPN exhaustion");
+               if (participant->is_key_server)
+                       participant->new_sak = TRUE;
+       }
+
+       body->llpn = host_to_be32(pn);
+       pn = ieee802_1x_mka_get_lpn(participant, &participant->oki);
+       body->olpn = host_to_be32(pn);
+
+       /* plain tx, plain rx */
+       if (participant->kay->macsec_protect)
+               body->ptx = FALSE;
+       else
+               body->ptx = TRUE;
+
+       if (participant->kay->macsec_validate == Strict)
+               body->prx = FALSE;
+       else
+               body->prx = TRUE;
+
+       /* latest key: rx, tx, key server member identifier key number */
+       body->lan = participant->lan;
+       os_memcpy(body->lsrv_mi, participant->lki.mi,
+                 sizeof(body->lsrv_mi));
+       body->lkn = host_to_be32(participant->lki.kn);
+       body->lrx = participant->lrx;
+       body->ltx = participant->ltx;
+
+       /* old key: rx, tx, key server member identifier key number */
+       body->oan = participant->oan;
+       if (participant->oki.kn != participant->lki.kn &&
+           participant->oki.kn != 0) {
+               body->otx = TRUE;
+               body->orx = TRUE;
+               os_memcpy(body->osrv_mi, participant->oki.mi,
+                         sizeof(body->osrv_mi));
+               body->okn = host_to_be32(participant->oki.kn);
+       } else {
+               body->otx = FALSE;
+               body->orx = FALSE;
+       }
+
+       /* set CP's variable */
+       if (body->ltx) {
+               if (!participant->kay->tx_enable)
+                       participant->kay->tx_enable = TRUE;
+
+               if (!participant->kay->port_enable)
+                       participant->kay->port_enable = TRUE;
+       }
+       if (body->lrx) {
+               if (!participant->kay->rx_enable)
+                       participant->kay->rx_enable = TRUE;
+       }
+
+       ieee802_1x_mka_dump_sak_use_body(body);
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_decode_sak_use_body -
+ */
+static int
+ieee802_1x_mka_decode_sak_use_body(
+       struct ieee802_1x_mka_participant *participant,
+       const u8 *mka_msg, size_t msg_len)
+{
+       struct ieee802_1x_mka_hdr *hdr;
+       struct ieee802_1x_mka_sak_use_body *body;
+       struct ieee802_1x_kay_peer *peer;
+       struct transmit_sa *txsa;
+       struct data_key *sa_key = NULL;
+       size_t body_len;
+       struct ieee802_1x_mka_ki ki;
+       u32 lpn;
+       Boolean all_receiving;
+       Boolean founded;
+
+       if (!participant->principal) {
+               wpa_printf(MSG_WARNING, "KaY: Participant is not principal");
+               return -1;
+       }
+       peer = ieee802_1x_kay_get_live_peer(participant,
+                                           participant->current_peer_id.mi);
+       if (!peer) {
+               wpa_printf(MSG_WARNING, "KaY: the peer is not my live peer");
+               return -1;
+       }
+
+       hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
+       body_len = get_mka_param_body_len(hdr);
+       body = (struct ieee802_1x_mka_sak_use_body *) mka_msg;
+       ieee802_1x_mka_dump_sak_use_body(body);
+
+       if ((body_len != 0) && (body_len < 40)) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 0, 40, or more octets",
+                          (int) body_len);
+               return -1;
+       }
+
+       /* TODO: what action should I take when peer does not support MACsec */
+       if (body_len == 0) {
+               wpa_printf(MSG_WARNING, "KaY: Peer does not support MACsec");
+               return 0;
+       }
+
+       /* TODO: when the plain tx or rx of peer is true, should I change
+        * the attribute of controlled port
+        */
+       if (body->prx)
+               wpa_printf(MSG_WARNING, "KaY: peer's plain rx are TRUE");
+
+       if (body->ptx)
+               wpa_printf(MSG_WARNING, "KaY: peer's plain tx are TRUE");
+
+       /* check latest key is valid */
+       if (body->ltx || body->lrx) {
+               founded = FALSE;
+               os_memcpy(ki.mi, body->lsrv_mi, sizeof(ki.mi));
+               ki.kn = ntohl(body->lkn);
+               dl_list_for_each(sa_key, &participant->sak_list,
+                                struct data_key, list) {
+                       if (is_ki_equal(&sa_key->key_identifier, &ki)) {
+                               founded = TRUE;
+                               break;
+                       }
+               }
+               if (!founded) {
+                       wpa_printf(MSG_WARNING, "KaY: Latest key is invalid");
+                       return -1;
+               }
+               if (os_memcmp(participant->lki.mi, body->lsrv_mi,
+                             sizeof(participant->lki.mi)) == 0 &&
+                   ntohl(body->lkn) == participant->lki.kn &&
+                   body->lan == participant->lan) {
+                       peer->sak_used = TRUE;
+               }
+               if (body->ltx && peer->is_key_server) {
+                       ieee802_1x_cp_set_servertransmitting(
+                               participant->kay->cp, TRUE);
+                       ieee802_1x_cp_sm_step(participant->kay->cp);
+               }
+       }
+
+       /* check old key is valid */
+       if (body->otx || body->orx) {
+               if (os_memcmp(participant->oki.mi, body->osrv_mi,
+                             sizeof(participant->oki.mi)) != 0 ||
+                   ntohl(body->okn) != participant->oki.kn ||
+                   body->oan != participant->oan) {
+                       wpa_printf(MSG_WARNING, "KaY: Old key is invalid");
+                       return -1;
+               }
+       }
+
+       /* TODO: how to set the MACsec hardware when delay_protect is true */
+       if (body->delay_protect && (!ntohl(body->llpn) || !ntohl(body->olpn))) {
+               wpa_printf(MSG_WARNING,
+                          "KaY: Lowest packet number should greater than 0 when delay_protect is TRUE");
+               return -1;
+       }
+
+       /* check all live peer have used the sak for receiving sa */
+       all_receiving = TRUE;
+       dl_list_for_each(peer, &participant->live_peers,
+                        struct ieee802_1x_kay_peer, list) {
+               if (!peer->sak_used) {
+                       all_receiving = FALSE;
+                       break;
+               }
+       }
+       if (all_receiving) {
+               participant->to_dist_sak = FALSE;
+               ieee802_1x_cp_set_allreceiving(participant->kay->cp, TRUE);
+               ieee802_1x_cp_sm_step(participant->kay->cp);
+       }
+
+       /* if i'm key server, and detects peer member pn exhaustion, rekey.*/
+       lpn = ntohl(body->llpn);
+       if (lpn > participant->kay->pn_exhaustion) {
+               if (participant->is_key_server) {
+                       participant->new_sak = TRUE;
+                       wpa_printf(MSG_WARNING, "KaY: Peer LPN exhaustion");
+               }
+       }
+
+       founded = FALSE;
+       dl_list_for_each(txsa, &participant->txsc->sa_list,
+                        struct transmit_sa, list) {
+               if (sa_key != NULL && txsa->pkey == sa_key) {
+                       founded = TRUE;
+                       break;
+               }
+       }
+       if (!founded) {
+               wpa_printf(MSG_WARNING, "KaY: Can't find txsa");
+               return -1;
+       }
+
+       /* FIXME: Secy creates txsa with default npn. If MKA detected Latest Key
+        * npn is larger than txsa's npn, set it to txsa.
+        */
+       secy_get_transmit_next_pn(participant->kay, txsa);
+       if (lpn > txsa->next_pn) {
+               secy_set_transmit_next_pn(participant->kay, txsa);
+               wpa_printf(MSG_INFO, "KaY: update lpn =0x%x", lpn);
+       }
+
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_dist_sak_body_present
+ */
+static Boolean
+ieee802_1x_mka_dist_sak_body_present(
+       struct ieee802_1x_mka_participant *participant)
+{
+       if (!participant->to_dist_sak || !participant->new_key)
+               return FALSE;
+
+       return TRUE;
+}
+
+
+/**
+ * ieee802_1x_kay_get_dist_sak_length
+ */
+static int
+ieee802_1x_mka_get_dist_sak_length(
+       struct ieee802_1x_mka_participant *participant)
+{
+       int length;
+       int cs_index = participant->kay->macsec_csindex;
+
+       if (participant->advised_desired) {
+               length = sizeof(struct ieee802_1x_mka_dist_sak_body);
+               if (cs_index != DEFAULT_CS_INDEX)
+                       length += CS_ID_LEN;
+
+               length += cipher_suite_tbl[cs_index].sak_len + 8;
+       } else {
+               length = MKA_HDR_LEN;
+       }
+       length = (length + 0x3) & ~0x3;
+
+       return length;
+}
+
+
+/**
+ * ieee802_1x_mka_encode_dist_sak_body -
+ */
+static int
+ieee802_1x_mka_encode_dist_sak_body(
+       struct ieee802_1x_mka_participant *participant,
+       struct wpabuf *buf)
+{
+       struct ieee802_1x_mka_dist_sak_body *body;
+       struct data_key *sak;
+       unsigned int length;
+       int cs_index;
+       int sak_pos;
+
+       length = ieee802_1x_mka_get_dist_sak_length(participant);
+       body = wpabuf_put(buf, length);
+       body->type = MKA_DISTRIBUTED_SAK;
+       set_mka_param_body_len(body, length - MKA_HDR_LEN);
+       if (length == MKA_HDR_LEN) {
+               body->confid_offset = 0;
+               body->dan = 0;
+               return 0;
+       }
+
+       sak = participant->new_key;
+       body->confid_offset = sak->confidentiality_offset;
+       body->dan = sak->an;
+       body->kn = host_to_be32(sak->key_identifier.kn);
+       cs_index = participant->kay->macsec_csindex;
+       sak_pos = 0;
+       if (cs_index != DEFAULT_CS_INDEX) {
+               os_memcpy(body->sak, cipher_suite_tbl[cs_index].id, CS_ID_LEN);
+               sak_pos = CS_ID_LEN;
+       }
+       if (aes_wrap(participant->kek.key, 16,
+                    cipher_suite_tbl[cs_index].sak_len / 8,
+                    sak->key, body->sak + sak_pos)) {
+               wpa_printf(MSG_ERROR, "KaY: AES wrap failed");
+               return -1;
+       }
+
+       ieee802_1x_mka_dump_dist_sak_body(body);
+
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_init_data_key -
+ */
+static struct data_key *
+ieee802_1x_kay_init_data_key(const struct key_conf *conf)
+{
+       struct data_key *pkey;
+
+       if (!conf)
+               return NULL;
+
+       pkey = os_zalloc(sizeof(*pkey));
+       if (pkey == NULL) {
+               wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
+               return NULL;
+       }
+
+       pkey->key = os_zalloc(conf->key_len);
+       if (pkey->key == NULL) {
+               wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
+               os_free(pkey);
+               return NULL;
+       }
+
+       os_memcpy(pkey->key, conf->key, conf->key_len);
+       os_memcpy(&pkey->key_identifier, &conf->ki,
+                 sizeof(pkey->key_identifier));
+       pkey->confidentiality_offset = conf->offset;
+       pkey->an = conf->an;
+       pkey->transmits = conf->tx;
+       pkey->receives = conf->rx;
+       os_get_time(&pkey->created_time);
+
+       pkey->user = 1;
+
+       return pkey;
+}
+
+
+/**
+ * ieee802_1x_kay_decode_dist_sak_body -
+ */
+static int
+ieee802_1x_mka_decode_dist_sak_body(
+       struct ieee802_1x_mka_participant *participant,
+       const u8 *mka_msg, size_t msg_len)
+{
+       struct ieee802_1x_mka_hdr *hdr;
+       struct ieee802_1x_mka_dist_sak_body *body;
+       struct ieee802_1x_kay_peer *peer;
+       struct macsec_ciphersuite *cs;
+       size_t body_len;
+       struct key_conf *conf;
+       struct data_key *sa_key = NULL;
+       struct ieee802_1x_mka_ki sak_ki;
+       int sak_len;
+       u8 *wrap_sak;
+       u8 *unwrap_sak;
+
+       hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
+       body_len = get_mka_param_body_len(hdr);
+       if ((body_len != 0) && (body_len != 28) && (body_len < 36)) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 0, 28, 36, or more octets",
+                          (int) body_len);
+               return -1;
+       }
+
+       if (!participant->principal) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: I can't accept the distributed SAK as I am not principal");
+               return -1;
+       }
+       if (participant->is_key_server) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: I can't accept the distributed SAK as myself is key server ");
+               return -1;
+       }
+       if (!participant->kay->macsec_desired ||
+           participant->kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: I am not MACsec-desired or without MACsec capable");
+               return -1;
+       }
+
+       peer = ieee802_1x_kay_get_live_peer(participant,
+                                           participant->current_peer_id.mi);
+       if (!peer) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: The key server is not in my live peers list");
+               return -1;
+       }
+       if (os_memcmp(&participant->kay->key_server_sci,
+                     &peer->sci, sizeof(struct ieee802_1x_mka_sci)) != 0) {
+               wpa_printf(MSG_ERROR, "KaY: The key server is not elected");
+               return -1;
+       }
+       if (body_len == 0) {
+               participant->kay->authenticated = TRUE;
+               participant->kay->secured = FALSE;
+               participant->kay->failed = FALSE;
+               participant->advised_desired = FALSE;
+               ieee802_1x_cp_connect_authenticated(participant->kay->cp);
+               ieee802_1x_cp_sm_step(participant->kay->cp);
+               wpa_printf(MSG_WARNING, "KaY:The Key server advise no MACsec");
+               participant->to_use_sak = TRUE;
+               return 0;
+       }
+       participant->advised_desired = TRUE;
+       participant->kay->authenticated = FALSE;
+       participant->kay->secured = TRUE;
+       participant->kay->failed = FALSE;
+       ieee802_1x_cp_connect_secure(participant->kay->cp);
+       ieee802_1x_cp_sm_step(participant->kay->cp);
+
+       body = (struct ieee802_1x_mka_dist_sak_body *)mka_msg;
+       ieee802_1x_mka_dump_dist_sak_body(body);
+       dl_list_for_each(sa_key, &participant->sak_list, struct data_key, list)
+       {
+               if (os_memcmp(sa_key->key_identifier.mi,
+                             participant->current_peer_id.mi, MI_LEN) == 0 &&
+                   sa_key->key_identifier.kn == be_to_host32(body->kn)) {
+                       wpa_printf(MSG_WARNING, "KaY:The Key has installed");
+                       return 0;
+               }
+       }
+       if (body_len == 28) {
+               sak_len = DEFAULT_SA_KEY_LEN;
+               wrap_sak =  body->sak;
+               participant->kay->macsec_csindex = DEFAULT_CS_INDEX;
+       } else {
+               cs = ieee802_1x_kay_get_cipher_suite(participant, body->sak);
+               if (!cs) {
+                       wpa_printf(MSG_ERROR,
+                                  "KaY: I can't support the Cipher Suite advised by key server");
+                       return -1;
+               }
+               sak_len = cs->sak_len;
+               wrap_sak = body->sak + CS_ID_LEN;
+               participant->kay->macsec_csindex = cs->index;
+       }
+
+       unwrap_sak = os_zalloc(sak_len);
+       if (!unwrap_sak) {
+               wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
+               return -1;
+       }
+       if (aes_unwrap(participant->kek.key, 16, sak_len >> 3, wrap_sak,
+                      unwrap_sak)) {
+               wpa_printf(MSG_ERROR, "KaY: AES unwrap failed");
+               os_free(unwrap_sak);
+               return -1;
+       }
+       wpa_hexdump(MSG_DEBUG, "\tAES Key Unwrap of SAK:", unwrap_sak, sak_len);
+
+       conf = os_zalloc(sizeof(*conf));
+       if (!conf) {
+               wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
+               os_free(unwrap_sak);
+               return -1;
+       }
+       conf->key_len = sak_len;
+
+       conf->key = os_zalloc(conf->key_len);
+       if (!conf->key) {
+               wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
+               os_free(unwrap_sak);
+               os_free(conf);
+               return -1;
+       }
+
+       os_memcpy(conf->key, unwrap_sak, conf->key_len);
+
+       os_memcpy(&sak_ki.mi, &participant->current_peer_id.mi,
+                 sizeof(sak_ki.mi));
+       sak_ki.kn = be_to_host32(body->kn);
+
+       os_memcpy(conf->ki.mi, sak_ki.mi, MI_LEN);
+       conf->ki.kn = sak_ki.kn;
+       conf->an = body->dan;
+       conf->offset = body->confid_offset;
+       conf->rx = TRUE;
+       conf->tx = TRUE;
+
+       sa_key = ieee802_1x_kay_init_data_key(conf);
+       if (!sa_key) {
+               os_free(unwrap_sak);
+               os_free(conf->key);
+               os_free(conf);
+               return -1;
+       }
+
+       dl_list_add(&participant->sak_list, &sa_key->list);
+
+       ieee802_1x_cp_set_ciphersuite(
+               participant->kay->cp,
+               cipher_suite_tbl[participant->kay->macsec_csindex].id);
+       ieee802_1x_cp_sm_step(participant->kay->cp);
+       ieee802_1x_cp_set_offset(participant->kay->cp, body->confid_offset);
+       ieee802_1x_cp_sm_step(participant->kay->cp);
+       ieee802_1x_cp_set_distributedki(participant->kay->cp, &sak_ki);
+       ieee802_1x_cp_set_distributedan(participant->kay->cp, body->dan);
+       ieee802_1x_cp_signal_newsak(participant->kay->cp);
+       ieee802_1x_cp_sm_step(participant->kay->cp);
+
+       participant->to_use_sak = TRUE;
+
+       os_free(unwrap_sak);
+       os_free(conf->key);
+       os_free(conf);
+
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_icv_body_present
+ */
+static Boolean
+ieee802_1x_mka_icv_body_present(struct ieee802_1x_mka_participant *participant)
+{
+       return TRUE;
+}
+
+
+/**
+ * ieee802_1x_kay_get_icv_length
+ */
+static int
+ieee802_1x_mka_get_icv_length(struct ieee802_1x_mka_participant *participant)
+{
+       int length;
+
+       length = sizeof(struct ieee802_1x_mka_icv_body);
+       length += mka_alg_tbl[participant->kay->mka_algindex].icv_len;
+
+       return (length + 0x3) & ~0x3;
+}
+
+
+/**
+ * ieee802_1x_mka_encode_icv_body -
+ */
+static int
+ieee802_1x_mka_encode_icv_body(struct ieee802_1x_mka_participant *participant,
+                              struct wpabuf *buf)
+{
+       struct ieee802_1x_mka_icv_body *body;
+       unsigned int length;
+       u8 cmac[MAX_ICV_LEN];
+
+       length = ieee802_1x_mka_get_icv_length(participant);
+       if (length != DEFAULT_ICV_LEN)  {
+               body = wpabuf_put(buf, MKA_HDR_LEN);
+               body->type = MKA_ICV_INDICATOR;
+               set_mka_param_body_len(body, length - MKA_HDR_LEN);
+       }
+
+       if (mka_alg_tbl[participant->kay->mka_algindex].icv_hash(
+                   participant->ick.key, wpabuf_head(buf), buf->used, cmac)) {
+               wpa_printf(MSG_ERROR, "KaY, omac1_aes_128 failed");
+               return -1;
+       }
+
+       if (length != DEFAULT_ICV_LEN)  {
+               os_memcpy(wpabuf_put(buf, length - MKA_HDR_LEN), cmac,
+                         length - MKA_HDR_LEN);
+       } else {
+               os_memcpy(wpabuf_put(buf, length), cmac, length);
+       }
+
+       return 0;
+}
+
+/**
+ * ieee802_1x_mka_decode_icv_body -
+ */
+static u8 *
+ieee802_1x_mka_decode_icv_body(struct ieee802_1x_mka_participant *participant,
+                              const u8 *mka_msg, size_t msg_len)
+{
+       struct ieee802_1x_mka_hdr *hdr;
+       struct ieee802_1x_mka_icv_body *body;
+       size_t body_len;
+       size_t left_len;
+       int body_type;
+       const u8 *pos;
+
+       pos = mka_msg;
+       left_len = msg_len;
+       while (left_len > (MKA_HDR_LEN + DEFAULT_ICV_LEN)) {
+               hdr = (struct ieee802_1x_mka_hdr *) pos;
+               body_len = get_mka_param_body_len(hdr);
+               body_type = get_mka_param_body_type(hdr);
+
+               if (left_len < (body_len + MKA_HDR_LEN))
+                       break;
+
+               if (body_type != MKA_ICV_INDICATOR) {
+                       left_len -= MKA_HDR_LEN + body_len;
+                       pos += MKA_HDR_LEN + body_len;
+                       continue;
+               }
+
+               body = (struct ieee802_1x_mka_icv_body *)pos;
+               if (body_len
+                       < mka_alg_tbl[participant->kay->mka_algindex].icv_len) {
+                       return NULL;
+               }
+
+               return body->icv;
+       }
+
+       return (u8 *) (mka_msg + msg_len - DEFAULT_ICV_LEN);
+}
+
+
+/**
+ * ieee802_1x_mka_decode_dist_cak_body-
+ */
+static int
+ieee802_1x_mka_decode_dist_cak_body(
+       struct ieee802_1x_mka_participant *participant,
+       const u8 *mka_msg, size_t msg_len)
+{
+       struct ieee802_1x_mka_hdr *hdr;
+       size_t body_len;
+
+       hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
+       body_len = get_mka_param_body_len(hdr);
+       if (body_len < 28) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 28 or more octets",
+                          (int) body_len);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_decode_kmd_body -
+ */
+static int
+ieee802_1x_mka_decode_kmd_body(
+       struct ieee802_1x_mka_participant *participant,
+       const u8 *mka_msg, size_t msg_len)
+{
+       struct ieee802_1x_mka_hdr *hdr;
+       size_t body_len;
+
+       hdr = (struct ieee802_1x_mka_hdr *) mka_msg;
+       body_len = get_mka_param_body_len(hdr);
+       if (body_len < 5) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: MKA Use SAK Packet Body Length (%d bytes) should be 5 or more octets",
+                          (int) body_len);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_mka_decode_announce_body -
+ */
+static int ieee802_1x_mka_decode_announce_body(
+       struct ieee802_1x_mka_participant *participant,
+       const u8 *mka_msg, size_t msg_len)
+{
+       return 0;
+}
+
+
+static struct mka_param_body_handler mak_body_handler[] = {
+       /* basic parameter set */
+       {
+               ieee802_1x_mka_encode_basic_body,
+               NULL,
+               ieee802_1x_mka_basic_body_length,
+               ieee802_1x_mka_basic_body_present
+       },
+
+       /* live peer list parameter set */
+       {
+               ieee802_1x_mka_encode_live_peer_body,
+               ieee802_1x_mka_decode_live_peer_body,
+               ieee802_1x_mka_get_live_peer_length,
+               ieee802_1x_mka_live_peer_body_present
+       },
+
+       /* potential peer list parameter set */
+       {
+               ieee802_1x_mka_encode_potential_peer_body,
+               ieee802_1x_mka_decode_potential_peer_body,
+               ieee802_1x_mka_get_potential_peer_length,
+               ieee802_1x_mka_potential_peer_body_present
+       },
+
+       /* sak use parameter set */
+       {
+               ieee802_1x_mka_encode_sak_use_body,
+               ieee802_1x_mka_decode_sak_use_body,
+               ieee802_1x_mka_get_sak_use_length,
+               ieee802_1x_mka_sak_use_body_present
+       },
+
+       /* distribute sak parameter set */
+       {
+               ieee802_1x_mka_encode_dist_sak_body,
+               ieee802_1x_mka_decode_dist_sak_body,
+               ieee802_1x_mka_get_dist_sak_length,
+               ieee802_1x_mka_dist_sak_body_present
+       },
+
+       /* distribute cak parameter set */
+       {
+               NULL,
+               ieee802_1x_mka_decode_dist_cak_body,
+               NULL,
+               NULL
+       },
+
+       /* kmd parameter set */
+       {
+               NULL,
+               ieee802_1x_mka_decode_kmd_body,
+               NULL,
+               NULL
+       },
+
+       /* announce parameter set */
+       {
+               NULL,
+               ieee802_1x_mka_decode_announce_body,
+               NULL,
+               NULL
+       },
+
+       /* icv parameter set */
+       {
+               ieee802_1x_mka_encode_icv_body,
+               NULL,
+               ieee802_1x_mka_get_icv_length,
+               ieee802_1x_mka_icv_body_present
+       },
+};
+
+
+/**
+ * ieee802_1x_kay_deinit_data_key -
+ */
+void ieee802_1x_kay_deinit_data_key(struct data_key *pkey)
+{
+       if (!pkey)
+               return;
+
+       pkey->user--;
+       if (pkey->user > 1)
+               return;
+
+       dl_list_del(&pkey->list);
+       os_free(pkey->key);
+       os_free(pkey);
+}
+
+
+/**
+ * ieee802_1x_kay_generate_new_sak -
+ */
+static int
+ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant)
+{
+       struct data_key *sa_key = NULL;
+       struct key_conf *conf;
+       struct ieee802_1x_kay_peer *peer;
+       struct ieee802_1x_kay *kay = participant->kay;
+       int ctx_len, ctx_offset;
+       u8 *context;
+
+       /* check condition for generating a fresh SAK:
+        * must have one live peer
+        * and MKA life time elapse since last distribution
+        * or potential peer is empty
+        */
+       if (dl_list_empty(&participant->live_peers)) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: Live peers list must not empty when generating fresh SAK");
+               return -1;
+       }
+
+       /* FIXME: A fresh SAK not generated until
+        * the live peer list contains at least one peer and
+        * MKA life time has elapsed since the prior SAK was first distributed,
+        * or the Key server's potential peer is empty
+        * but I can't understand the second item, so
+        * here only check first item and ingore
+        *   && (!dl_list_empty(&participant->potential_peers))) {
+        */
+       if ((time(NULL) - kay->dist_time) < MKA_LIFE_TIME / 1000) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: Life time have not elapsed since prior SAK distributed");
+               return -1;
+       }
+
+       conf = os_zalloc(sizeof(*conf));
+       if (!conf) {
+               wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
+               return -1;
+       }
+       conf->key_len = cipher_suite_tbl[kay->macsec_csindex].sak_len;
+
+       conf->key = os_zalloc(conf->key_len);
+       if (!conf->key) {
+               os_free(conf);
+               wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
+               return -1;
+       }
+
+       ctx_len = conf->key_len + sizeof(kay->dist_kn);
+       dl_list_for_each(peer, &participant->live_peers,
+                        struct ieee802_1x_kay_peer, list)
+               ctx_len += sizeof(peer->mi);
+       ctx_len += sizeof(participant->mi);
+
+       context = os_zalloc(ctx_len);
+       if (!context) {
+               os_free(conf->key);
+               os_free(conf);
+               return -1;
+       }
+       ctx_offset = 0;
+       if (os_get_random(context + ctx_offset, conf->key_len) < 0) {
+               os_free(context);
+               os_free(conf->key);
+               os_free(conf);
+               return -1;
+       }
+       ctx_offset += conf->key_len;
+       dl_list_for_each(peer, &participant->live_peers,
+                        struct ieee802_1x_kay_peer, list) {
+               os_memcpy(context + ctx_offset, peer->mi, sizeof(peer->mi));
+               ctx_offset += sizeof(peer->mi);
+       }
+       os_memcpy(context + ctx_offset, participant->mi,
+                 sizeof(participant->mi));
+       ctx_offset += sizeof(participant->mi);
+       os_memcpy(context + ctx_offset, &kay->dist_kn, sizeof(kay->dist_kn));
+
+       if (conf->key_len == 16) {
+               ieee802_1x_sak_128bits_aes_cmac(participant->cak.key,
+                                               context, ctx_len, conf->key);
+       } else if (conf->key_len == 32) {
+               ieee802_1x_sak_128bits_aes_cmac(participant->cak.key,
+                                               context, ctx_len, conf->key);
+       } else {
+               wpa_printf(MSG_ERROR, "KaY: SAK Length not support");
+               os_free(conf->key);
+               os_free(conf);
+               os_free(context);
+               return -1;
+       }
+       wpa_hexdump(MSG_DEBUG, "KaY: generated new SAK",
+                   conf->key, conf->key_len);
+
+       os_memcpy(conf->ki.mi, participant->mi, MI_LEN);
+       conf->ki.kn = participant->kay->dist_kn;
+       conf->an = participant->kay->dist_an;
+       conf->offset = kay->macsec_confidentiality;
+       conf->rx = TRUE;
+       conf->tx = TRUE;
+
+       sa_key = ieee802_1x_kay_init_data_key(conf);
+       if (!sa_key) {
+               os_free(conf->key);
+               os_free(conf);
+               os_free(context);
+               return -1;
+       }
+       participant->new_key = sa_key;
+
+       dl_list_add(&participant->sak_list, &sa_key->list);
+       ieee802_1x_cp_set_ciphersuite(participant->kay->cp,
+                                     cipher_suite_tbl[kay->macsec_csindex].id);
+       ieee802_1x_cp_sm_step(kay->cp);
+       ieee802_1x_cp_set_offset(kay->cp, conf->offset);
+       ieee802_1x_cp_sm_step(kay->cp);
+       ieee802_1x_cp_set_distributedki(kay->cp, &conf->ki);
+       ieee802_1x_cp_set_distributedan(kay->cp, conf->an);
+       ieee802_1x_cp_signal_newsak(kay->cp);
+       ieee802_1x_cp_sm_step(kay->cp);
+
+       dl_list_for_each(peer, &participant->live_peers,
+                        struct ieee802_1x_kay_peer, list)
+               peer->sak_used = FALSE;
+
+       participant->kay->dist_kn++;
+       participant->kay->dist_an++;
+       if (participant->kay->dist_an > 3)
+               participant->kay->dist_an = 0;
+
+       participant->kay->dist_time = time(NULL);
+
+       os_free(conf->key);
+       os_free(conf);
+       os_free(context);
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_elect_key_server - elect the key server
+ * when to elect: whenever the live peers list changes
+ */
+static int
+ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant)
+{
+       struct ieee802_1x_kay_peer *peer;
+       struct ieee802_1x_kay_peer *key_server = NULL;
+       struct ieee802_1x_kay *kay = participant->kay;
+       Boolean i_is_key_server;
+       int i;
+
+       if (participant->is_obliged_key_server) {
+               participant->new_sak = TRUE;
+               participant->to_dist_sak = FALSE;
+               ieee802_1x_cp_set_electedself(kay->cp, TRUE);
+               return 0;
+       }
+
+       /* elect the key server among the peers */
+       dl_list_for_each(peer, &participant->live_peers,
+                        struct ieee802_1x_kay_peer, list) {
+               if (!peer->is_key_server)
+                       continue;
+
+               if (!key_server) {
+                       key_server = peer;
+                       continue;
+               }
+
+               if (peer->key_server_priority <
+                   key_server->key_server_priority) {
+                       key_server = peer;
+               } else if (peer->key_server_priority ==
+                          key_server->key_server_priority) {
+                       for (i = 0; i < 6; i++) {
+                               if (peer->sci.addr[i] <
+                                   key_server->sci.addr[i])
+                                       key_server = peer;
+                       }
+               }
+       }
+
+       /* elect the key server between me and the above elected peer */
+       i_is_key_server = FALSE;
+       if (key_server && participant->can_be_key_server) {
+               if (kay->actor_priority
+                          < key_server->key_server_priority) {
+                       i_is_key_server = TRUE;
+               } else if (kay->actor_priority
+                                       == key_server->key_server_priority) {
+                       for (i = 0; i < 6; i++) {
+                               if (kay->actor_sci.addr[i]
+                                       < key_server->sci.addr[i]) {
+                                       i_is_key_server = TRUE;
+                               }
+                       }
+               }
+       }
+
+       if (!key_server && !i_is_key_server) {
+               participant->principal = FALSE;
+               participant->is_key_server = FALSE;
+               participant->is_elected = FALSE;
+               return 0;
+       }
+
+       if (i_is_key_server) {
+               ieee802_1x_cp_set_electedself(kay->cp, TRUE);
+               if (os_memcmp(&kay->key_server_sci, &kay->actor_sci,
+                             sizeof(kay->key_server_sci))) {
+                       ieee802_1x_cp_signal_chgdserver(kay->cp);
+                       ieee802_1x_cp_sm_step(kay->cp);
+               }
+
+               participant->is_key_server = TRUE;
+               participant->principal = TRUE;
+               participant->new_sak = TRUE;
+               wpa_printf(MSG_DEBUG, "KaY: I is elected as key server");
+               participant->to_dist_sak = FALSE;
+               participant->is_elected = TRUE;
+
+               os_memcpy(&kay->key_server_sci, &kay->actor_sci,
+                         sizeof(kay->key_server_sci));
+               kay->key_server_priority = kay->actor_priority;
+       }
+
+       if (key_server) {
+               ieee802_1x_cp_set_electedself(kay->cp, FALSE);
+               if (os_memcmp(&kay->key_server_sci, &key_server->sci,
+                             sizeof(kay->key_server_sci))) {
+                       ieee802_1x_cp_signal_chgdserver(kay->cp);
+                       ieee802_1x_cp_sm_step(kay->cp);
+               }
+
+               participant->is_key_server = FALSE;
+               participant->principal = TRUE;
+               participant->is_elected = TRUE;
+
+               os_memcpy(&kay->key_server_sci, &key_server->sci,
+                         sizeof(kay->key_server_sci));
+               kay->key_server_priority = key_server->key_server_priority;
+       }
+
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_decide_macsec_use - the key server determinate
+ *              how to use MACsec: whether use MACsec and its capability
+ * protectFrames will be advised if the key server and one of its live peers are
+ * MACsec capable and one of those request MACsec protection
+ */
+static int
+ieee802_1x_kay_decide_macsec_use(
+       struct ieee802_1x_mka_participant *participant)
+{
+       struct ieee802_1x_kay *kay = participant->kay;
+       struct ieee802_1x_kay_peer *peer;
+       enum macsec_cap less_capability;
+       Boolean has_peer;
+
+       if (!participant->is_key_server)
+               return -1;
+
+       /* key server self is MACsec-desired and requesting MACsec */
+       if (!kay->macsec_desired) {
+               participant->advised_desired = FALSE;
+               return -1;
+       }
+       if (kay->macsec_capable == MACSEC_CAP_NOT_IMPLEMENTED) {
+               participant->advised_desired = FALSE;
+               return -1;
+       }
+       less_capability = kay->macsec_capable;
+
+       /* at least one of peers is MACsec-desired and requesting MACsec */
+       has_peer = FALSE;
+       dl_list_for_each(peer, &participant->live_peers,
+                        struct ieee802_1x_kay_peer, list) {
+               if (!peer->macsec_desired)
+                       continue;
+
+               if (peer->macsec_capbility == MACSEC_CAP_NOT_IMPLEMENTED)
+                       continue;
+
+               less_capability = (less_capability < peer->macsec_capbility) ?
+                       less_capability : peer->macsec_capbility;
+               has_peer = TRUE;
+       }
+
+       if (has_peer) {
+               participant->advised_desired = TRUE;
+               participant->advised_capability = less_capability;
+               kay->authenticated = FALSE;
+               kay->secured = TRUE;
+               kay->failed = FALSE;
+               ieee802_1x_cp_connect_secure(kay->cp);
+               ieee802_1x_cp_sm_step(kay->cp);
+       } else {
+               participant->advised_desired = FALSE;
+               participant->advised_capability = MACSEC_CAP_NOT_IMPLEMENTED;
+               participant->to_use_sak = FALSE;
+               kay->authenticated = TRUE;
+               kay->secured = FALSE;
+               kay->failed = FALSE;
+               kay->ltx_kn = 0;
+               kay->ltx_an = 0;
+               kay->lrx_kn = 0;
+               kay->lrx_an = 0;
+               kay->otx_kn = 0;
+               kay->otx_an = 0;
+               kay->orx_kn = 0;
+               kay->orx_an = 0;
+               ieee802_1x_cp_connect_authenticated(kay->cp);
+               ieee802_1x_cp_sm_step(kay->cp);
+       }
+
+       return 0;
+}
+
+static const u8 pae_group_addr[ETH_ALEN] = {
+       0x01, 0x80, 0xc2, 0x00, 0x00, 0x03
+};
+
+
+/**
+ * ieee802_1x_kay_encode_mkpdu -
+ */
+static int
+ieee802_1x_kay_encode_mkpdu(struct ieee802_1x_mka_participant *participant,
+                           struct wpabuf *pbuf)
+{
+       unsigned int i;
+       struct ieee8023_hdr *ether_hdr;
+       struct ieee802_1x_hdr *eapol_hdr;
+
+       ether_hdr = wpabuf_put(pbuf, sizeof(*ether_hdr));
+       os_memcpy(ether_hdr->dest, pae_group_addr, sizeof(ether_hdr->dest));
+       os_memcpy(ether_hdr->src, participant->kay->actor_sci.addr,
+                 sizeof(ether_hdr->dest));
+       ether_hdr->ethertype = host_to_be16(ETH_P_EAPOL);
+
+       eapol_hdr = wpabuf_put(pbuf, sizeof(*eapol_hdr));
+       eapol_hdr->version = EAPOL_VERSION;
+       eapol_hdr->type = IEEE802_1X_TYPE_EAPOL_MKA;
+       eapol_hdr->length = host_to_be16(pbuf->size - pbuf->used);
+
+       for (i = 0; i < ARRAY_SIZE(mak_body_handler); i++) {
+               if (mak_body_handler[i].body_present &&
+                   mak_body_handler[i].body_present(participant)) {
+                       if (mak_body_handler[i].body_tx(participant, pbuf))
+                               return -1;
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * ieee802_1x_participant_send_mkpdu -
+ */
+static int
+ieee802_1x_participant_send_mkpdu(
+       struct ieee802_1x_mka_participant *participant)
+{
+       struct wpabuf *buf;
+       struct ieee802_1x_kay *kay = participant->kay;
+       size_t length = 0;
+       unsigned int i;
+
+       wpa_printf(MSG_DEBUG, "KaY: to enpacket and send the MKPDU");
+       length += sizeof(struct ieee802_1x_hdr) + sizeof(struct ieee8023_hdr);
+       for (i = 0; i < ARRAY_SIZE(mak_body_handler); i++) {
+               if (mak_body_handler[i].body_present &&
+                   mak_body_handler[i].body_present(participant))
+                       length += mak_body_handler[i].body_length(participant);
+       }
+
+       buf = wpabuf_alloc(length);
+       if (!buf) {
+               wpa_printf(MSG_ERROR, "KaY: out of memory");
+               return -1;
+       }
+
+       if (ieee802_1x_kay_encode_mkpdu(participant, buf)) {
+               wpa_printf(MSG_ERROR, "KaY: encode mkpdu fail!");
+               return -1;
+       }
+
+       l2_packet_send(kay->l2_mka, NULL, 0, wpabuf_head(buf), wpabuf_len(buf));
+       wpabuf_free(buf);
+
+       kay->active = TRUE;
+       participant->active = TRUE;
+
+       return 0;
+}
+
+
+static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa);
+/**
+ * ieee802_1x_participant_timer -
+ */
+static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx)
+{
+       struct ieee802_1x_mka_participant *participant;
+       struct ieee802_1x_kay *kay;
+       struct ieee802_1x_kay_peer *peer, *pre_peer;
+       time_t now = time(NULL);
+       Boolean lp_changed;
+       struct receive_sc *rxsc, *pre_rxsc;
+       struct transmit_sa *txsa, *pre_txsa;
+
+       participant = (struct ieee802_1x_mka_participant *)eloop_ctx;
+       kay = participant->kay;
+       if (participant->cak_life) {
+               if (now > participant->cak_life) {
+                       kay->authenticated = FALSE;
+                       kay->secured = FALSE;
+                       kay->failed = TRUE;
+                       ieee802_1x_kay_delete_mka(kay, &participant->ckn);
+                       return;
+               }
+       }
+
+       /* should delete MKA instance if there are not live peers
+        * when the MKA life elapsed since its creating */
+       if (participant->mka_life) {
+               if (dl_list_empty(&participant->live_peers)) {
+                       if (now > participant->mka_life) {
+                               kay->authenticated = FALSE;
+                               kay->secured = FALSE;
+                               kay->failed = TRUE;
+                               ieee802_1x_kay_delete_mka(kay,
+                                                         &participant->ckn);
+                               return;
+                       }
+               } else {
+                       participant->mka_life = 0;
+               }
+       }
+
+       lp_changed = FALSE;
+       dl_list_for_each_safe(peer, pre_peer, &participant->live_peers,
+                             struct ieee802_1x_kay_peer, list) {
+               if (now > peer->expire) {
+                       wpa_printf(MSG_DEBUG, "KaY: Live peer removed");
+                       wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi,
+                                   sizeof(peer->mi));
+                       wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
+                       dl_list_for_each_safe(rxsc, pre_rxsc,
+                                             &participant->rxsc_list,
+                                             struct receive_sc, list) {
+                               if (os_memcmp(&rxsc->sci, &peer->sci,
+                                             sizeof(rxsc->sci)) == 0) {
+                                       secy_delete_receive_sc(kay, rxsc);
+                                       ieee802_1x_kay_deinit_receive_sc(
+                                               participant, rxsc);
+                               }
+                       }
+                       dl_list_del(&peer->list);
+                       os_free(peer);
+                       lp_changed = TRUE;
+               }
+       }
+
+       if (lp_changed) {
+               if (dl_list_empty(&participant->live_peers)) {
+                       participant->advised_desired = FALSE;
+                       participant->advised_capability =
+                               MACSEC_CAP_NOT_IMPLEMENTED;
+                       participant->to_use_sak = FALSE;
+                       kay->authenticated = TRUE;
+                       kay->secured = FALSE;
+                       kay->failed = FALSE;
+                       kay->ltx_kn = 0;
+                       kay->ltx_an = 0;
+                       kay->lrx_kn = 0;
+                       kay->lrx_an = 0;
+                       kay->otx_kn = 0;
+                       kay->otx_an = 0;
+                       kay->orx_kn = 0;
+                       kay->orx_an = 0;
+                       dl_list_for_each_safe(txsa, pre_txsa,
+                                             &participant->txsc->sa_list,
+                                             struct transmit_sa, list) {
+                               secy_disable_transmit_sa(kay, txsa);
+                               ieee802_1x_kay_deinit_transmit_sa(txsa);
+                       }
+
+                       ieee802_1x_cp_connect_authenticated(kay->cp);
+                       ieee802_1x_cp_sm_step(kay->cp);
+               } else {
+                       ieee802_1x_kay_elect_key_server(participant);
+                       ieee802_1x_kay_decide_macsec_use(participant);
+               }
+       }
+
+       dl_list_for_each_safe(peer, pre_peer, &participant->potential_peers,
+                             struct ieee802_1x_kay_peer, list) {
+               if (now > peer->expire) {
+                       wpa_printf(MSG_DEBUG, "KaY: Potential peer removed");
+                       wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi,
+                                   sizeof(peer->mi));
+                       wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
+                       dl_list_del(&peer->list);
+                       os_free(peer);
+               }
+       }
+
+       if (participant->new_sak) {
+               if (!ieee802_1x_kay_generate_new_sak(participant))
+                       participant->to_dist_sak = TRUE;
+
+               participant->new_sak = FALSE;
+       }
+
+       if (participant->retry_count < MAX_RETRY_CNT) {
+               ieee802_1x_participant_send_mkpdu(participant);
+               participant->retry_count++;
+       }
+
+       eloop_register_timeout(MKA_HELLO_TIME / 1000, 0,
+                              ieee802_1x_participant_timer,
+                              participant, NULL);
+}
+
+
+/**
+ * ieee802_1x_kay_init_transmit_sa -
+ */
+static struct transmit_sa *
+ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN,
+                               struct data_key *key)
+{
+       struct transmit_sa *psa;
+
+       key->tx_latest = TRUE;
+       key->rx_latest = TRUE;
+
+       psa = os_zalloc(sizeof(*psa));
+       if (!psa) {
+               wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
+               return NULL;
+       }
+
+       if (key->confidentiality_offset >= CONFIDENTIALITY_OFFSET_0 &&
+           key->confidentiality_offset <= CONFIDENTIALITY_OFFSET_50)
+               psa->confidentiality = TRUE;
+       else
+               psa->confidentiality = FALSE;
+
+       psa->an = an;
+       psa->pkey = key;
+       psa->next_pn = next_PN;
+       psa->sc = psc;
+
+       os_get_time(&psa->created_time);
+       psa->in_use = FALSE;
+
+       dl_list_add(&psc->sa_list, &psa->list);
+       wpa_printf(MSG_DEBUG,
+                  "KaY: Create transmit SA(an: %d, next_PN: %u) of SC(channel: %d)",
+                  (int) an, next_PN, psc->channel);
+
+       return psa;
+}
+
+
+/**
+ * ieee802_1x_kay_deinit_transmit_sa -
+ */
+static void ieee802_1x_kay_deinit_transmit_sa(struct transmit_sa *psa)
+{
+       psa->pkey = NULL;
+       wpa_printf(MSG_DEBUG,
+                  "KaY: Delete transmit SA(an: %d) of SC(channel: %d)",
+                  psa->an, psa->sc->channel);
+       dl_list_del(&psa->list);
+       os_free(psa);
+}
+
+
+/**
+ * init_transmit_sc -
+ */
+static struct transmit_sc *
+ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci,
+                               int channel)
+{
+       struct transmit_sc *psc;
+
+       psc = os_zalloc(sizeof(*psc));
+       if (!psc) {
+               wpa_printf(MSG_ERROR, "%s: out of memory", __func__);
+               return NULL;
+       }
+       os_memcpy(&psc->sci, sci, sizeof(psc->sci));
+       psc->channel = channel;
+
+       os_get_time(&psc->created_time);
+       psc->transmitting = FALSE;
+       psc->encoding_sa = FALSE;
+       psc->enciphering_sa = FALSE;
+
+       dl_list_init(&psc->sa_list);
+       wpa_printf(MSG_DEBUG, "KaY: Create transmit SC(channel: %d)", channel);
+       wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)sci , sizeof(*sci));
+
+       return psc;
+}
+
+
+/**
+ * ieee802_1x_kay_deinit_transmit_sc -
+ */
+static void
+ieee802_1x_kay_deinit_transmit_sc(
+       struct ieee802_1x_mka_participant *participant, struct transmit_sc *psc)
+{
+       struct transmit_sa *psa, *tmp;
+
+       wpa_printf(MSG_DEBUG, "KaY: Delete transmit SC(channel: %d)",
+                  psc->channel);
+       dl_list_for_each_safe(psa, tmp, &psc->sa_list, struct transmit_sa,
+                             list) {
+               secy_disable_transmit_sa(participant->kay, psa);
+               ieee802_1x_kay_deinit_transmit_sa(psa);
+       }
+
+       os_free(psc);
+}
+
+
+/****************** Interface between CP and KAY *********************/
+/**
+ * ieee802_1x_kay_set_latest_sa_attr -
+ */
+int ieee802_1x_kay_set_latest_sa_attr(struct ieee802_1x_kay *kay,
+                                     struct ieee802_1x_mka_ki *lki, u8 lan,
+                                     Boolean ltx, Boolean lrx)
+{
+       struct ieee802_1x_mka_participant *principal;
+
+       principal = ieee802_1x_kay_get_principal_participant(kay);
+       if (!principal)
+               return -1;
+
+       if (!lki)
+               os_memset(&principal->lki, 0, sizeof(principal->lki));
+       else
+               os_memcpy(&principal->lki, lki, sizeof(principal->lki));
+
+       principal->lan = lan;
+       principal->ltx = ltx;
+       principal->lrx = lrx;
+       if (!lki) {
+               kay->ltx_kn = 0;
+               kay->lrx_kn = 0;
+       } else {
+               kay->ltx_kn = lki->kn;
+               kay->lrx_kn = lki->kn;
+       }
+       kay->ltx_an = lan;
+       kay->lrx_an = lan;
+
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_set_old_sa_attr -
+ */
+int ieee802_1x_kay_set_old_sa_attr(struct ieee802_1x_kay *kay,
+                                  struct ieee802_1x_mka_ki *oki,
+                                  u8 oan, Boolean otx, Boolean orx)
+{
+       struct ieee802_1x_mka_participant *principal;
+
+       principal = ieee802_1x_kay_get_principal_participant(kay);
+       if (!principal)
+               return -1;
+
+       if (!oki)
+               os_memset(&principal->oki, 0, sizeof(principal->oki));
+       else
+               os_memcpy(&principal->oki, oki, sizeof(principal->oki));
+
+       principal->oan = oan;
+       principal->otx = otx;
+       principal->orx = orx;
+
+       if (!oki) {
+               kay->otx_kn = 0;
+               kay->orx_kn = 0;
+       } else {
+               kay->otx_kn = oki->kn;
+               kay->orx_kn = oki->kn;
+       }
+       kay->otx_an = oan;
+       kay->orx_an = oan;
+
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_create_sas -
+ */
+int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay,
+                             struct ieee802_1x_mka_ki *lki)
+{
+       struct data_key *sa_key, *latest_sak;
+       struct ieee802_1x_mka_participant *principal;
+       struct receive_sc *rxsc;
+       struct receive_sa *rxsa;
+       struct transmit_sa *txsa;
+
+       principal = ieee802_1x_kay_get_principal_participant(kay);
+       if (!principal)
+               return -1;
+
+       latest_sak = NULL;
+       dl_list_for_each(sa_key, &principal->sak_list, struct data_key, list) {
+               if (is_ki_equal(&sa_key->key_identifier, lki)) {
+                       sa_key->rx_latest = TRUE;
+                       sa_key->tx_latest = TRUE;
+                       latest_sak = sa_key;
+                       principal->to_use_sak = TRUE;
+               } else {
+                       sa_key->rx_latest = FALSE;
+                       sa_key->tx_latest = FALSE;
+               }
+       }
+       if (!latest_sak) {
+               wpa_printf(MSG_ERROR, "lki related sak not found");
+               return -1;
+       }
+
+       dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
+               rxsa = ieee802_1x_kay_init_receive_sa(rxsc, latest_sak->an, 1,
+                                                     latest_sak);
+               if (!rxsa)
+                       return -1;
+
+               secy_create_receive_sa(kay, rxsa);
+       }
+
+       txsa = ieee802_1x_kay_init_transmit_sa(principal->txsc, latest_sak->an,
+                                              1, latest_sak);
+       if (!txsa)
+               return -1;
+
+       secy_create_transmit_sa(kay, txsa);
+
+
+
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_delete_sas -
+ */
+int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay,
+                             struct ieee802_1x_mka_ki *ki)
+{
+       struct data_key *sa_key, *pre_key;
+       struct transmit_sa *txsa, *pre_txsa;
+       struct receive_sa *rxsa, *pre_rxsa;
+       struct receive_sc *rxsc;
+       struct ieee802_1x_mka_participant *principal;
+
+       wpa_printf(MSG_DEBUG, "KaY: Entry into %s", __func__);
+       principal = ieee802_1x_kay_get_principal_participant(kay);
+       if (!principal)
+               return -1;
+
+       /* remove the transmit sa */
+       dl_list_for_each_safe(txsa, pre_txsa, &principal->txsc->sa_list,
+                             struct transmit_sa, list) {
+               if (is_ki_equal(&txsa->pkey->key_identifier, ki)) {
+                       secy_disable_transmit_sa(kay, txsa);
+                       ieee802_1x_kay_deinit_transmit_sa(txsa);
+               }
+       }
+
+       /* remove the receive sa */
+       dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
+               dl_list_for_each_safe(rxsa, pre_rxsa, &rxsc->sa_list,
+                                     struct receive_sa, list) {
+                       if (is_ki_equal(&rxsa->pkey->key_identifier, ki)) {
+                               secy_disable_receive_sa(kay, rxsa);
+                               ieee802_1x_kay_deinit_receive_sa(rxsa);
+                       }
+               }
+       }
+
+       /* remove the sak */
+       dl_list_for_each_safe(sa_key, pre_key, &principal->sak_list,
+                             struct data_key, list) {
+               if (is_ki_equal(&sa_key->key_identifier, ki)) {
+                       ieee802_1x_kay_deinit_data_key(sa_key);
+                       break;
+               }
+               if (principal->new_key == sa_key)
+                       principal->new_key = NULL;
+       }
+
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_enable_tx_sas -
+ */
+int ieee802_1x_kay_enable_tx_sas(struct ieee802_1x_kay *kay,
+                                struct ieee802_1x_mka_ki *lki)
+{
+       struct ieee802_1x_mka_participant *principal;
+       struct transmit_sa *txsa;
+
+       principal = ieee802_1x_kay_get_principal_participant(kay);
+       if (!principal)
+               return -1;
+
+       dl_list_for_each(txsa, &principal->txsc->sa_list, struct transmit_sa,
+                        list) {
+               if (is_ki_equal(&txsa->pkey->key_identifier, lki)) {
+                       txsa->in_use = TRUE;
+                       secy_enable_transmit_sa(kay, txsa);
+                       ieee802_1x_cp_set_usingtransmitas(
+                               principal->kay->cp, TRUE);
+                       ieee802_1x_cp_sm_step(principal->kay->cp);
+               }
+       }
+
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_enable_rx_sas -
+ */
+int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay,
+                                struct ieee802_1x_mka_ki *lki)
+{
+       struct ieee802_1x_mka_participant *principal;
+       struct receive_sa *rxsa;
+       struct receive_sc *rxsc;
+
+       principal = ieee802_1x_kay_get_principal_participant(kay);
+       if (!principal)
+               return -1;
+
+       dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
+               dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list)
+               {
+                       if (is_ki_equal(&rxsa->pkey->key_identifier, lki)) {
+                               rxsa->in_use = TRUE;
+                               secy_enable_receive_sa(kay, rxsa);
+                               ieee802_1x_cp_set_usingreceivesas(
+                                       principal->kay->cp, TRUE);
+                               ieee802_1x_cp_sm_step(principal->kay->cp);
+                       }
+               }
+       }
+
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_enable_new_info -
+ */
+int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay)
+{
+       struct ieee802_1x_mka_participant *principal;
+
+       principal = ieee802_1x_kay_get_principal_participant(kay);
+       if (!principal)
+               return -1;
+
+       if (principal->retry_count < MAX_RETRY_CNT) {
+               ieee802_1x_participant_send_mkpdu(principal);
+               principal->retry_count++;
+       }
+
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_cp_conf -
+ */
+int ieee802_1x_kay_cp_conf(struct ieee802_1x_kay *kay,
+                          struct ieee802_1x_cp_conf *pconf)
+{
+       pconf->protect = kay->macsec_protect;
+       pconf->replay_protect = kay->macsec_replay_protect;
+       pconf->validate = kay->macsec_validate;
+
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_alloc_cp_sm -
+ */
+static struct ieee802_1x_cp_sm *
+ieee802_1x_kay_alloc_cp_sm(struct ieee802_1x_kay *kay)
+{
+       struct ieee802_1x_cp_conf conf;
+
+       os_memset(&conf, 0, sizeof(conf));
+       conf.protect = kay->macsec_protect;
+       conf.replay_protect = kay->macsec_replay_protect;
+       conf.validate = kay->macsec_validate;
+       conf.replay_window = kay->macsec_replay_window;
+
+       return ieee802_1x_cp_sm_init(kay, &conf);
+}
+
+
+/**
+ * ieee802_1x_kay_mkpdu_sanity_check -
+ *     sanity check specified in clause 11.11.2 of IEEE802.1X-2010
+ */
+static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay,
+                                            const u8 *buf, size_t len)
+{
+       struct ieee8023_hdr *eth_hdr;
+       struct ieee802_1x_hdr *eapol_hdr;
+       struct ieee802_1x_mka_hdr *mka_hdr;
+       struct ieee802_1x_mka_basic_body *body;
+       size_t mka_msg_len;
+       struct ieee802_1x_mka_participant *participant;
+       size_t body_len;
+       u8 icv[MAX_ICV_LEN];
+       u8 *msg_icv;
+
+       eth_hdr = (struct ieee8023_hdr *) buf;
+       eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1);
+       mka_hdr = (struct ieee802_1x_mka_hdr *) (eapol_hdr + 1);
+
+       /* destination address should be not individual address */
+       if (os_memcmp(eth_hdr->dest, pae_group_addr, ETH_ALEN) != 0) {
+               wpa_printf(MSG_MSGDUMP,
+                          "KaY: ethernet destination address is not PAE group address");
+               return -1;
+       }
+
+       /* MKPDU should not less than 32 octets */
+       mka_msg_len = be_to_host16(eapol_hdr->length);
+       if (mka_msg_len < 32) {
+               wpa_printf(MSG_MSGDUMP, "KaY: MKPDU is less than 32 octets");
+               return -1;
+       }
+       /* MKPDU should multiple 4 octets */
+       if ((mka_msg_len % 4) != 0) {
+               wpa_printf(MSG_MSGDUMP,
+                          "KaY: MKPDU is not multiple of 4 octets");
+               return -1;
+       }
+
+       body = (struct ieee802_1x_mka_basic_body *) mka_hdr;
+       ieee802_1x_mka_dump_basic_body(body);
+       body_len = get_mka_param_body_len(body);
+       /* EAPOL-MKA body should comprise basic parameter set and ICV */
+       if (mka_msg_len < MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: Received EAPOL-MKA Packet Body Length (%d bytes) is less than the Basic Parameter Set Header Length (%d bytes) + the Basic Parameter Set Body Length (%d bytes) + %d bytes of ICV",
+                          (int) mka_msg_len, (int) MKA_HDR_LEN,
+                          (int) body_len, DEFAULT_ICV_LEN);
+               return -1;
+       }
+
+       /* CKN should be owned by I */
+       participant = ieee802_1x_kay_get_participant(kay, body->ckn);
+       if (!participant) {
+               wpa_printf(MSG_DEBUG, "CKN is not included in my CA");
+               return -1;
+       }
+
+       /* algorithm agility check */
+       if (os_memcmp(body->algo_agility, mka_algo_agility,
+                     sizeof(body->algo_agility)) != 0) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: peer's algorithm agility not supported for me");
+               return -1;
+       }
+
+       /* ICV check */
+       /*
+        * The ICV will comprise the final octets of the packet body, whatever
+        * its size, not the fixed length 16 octets, indicated by the EAPOL
+        * packet body length.
+        */
+       if (mka_alg_tbl[kay->mka_algindex].icv_hash(
+                   participant->ick.key,
+                   buf, len - mka_alg_tbl[kay->mka_algindex].icv_len, icv)) {
+               wpa_printf(MSG_ERROR, "KaY: omac1_aes_128 failed");
+               return -1;
+       }
+       msg_icv = ieee802_1x_mka_decode_icv_body(participant, (u8 *) mka_hdr,
+                                                mka_msg_len);
+
+       if (msg_icv) {
+               if (os_memcmp_const(msg_icv, icv,
+                                   mka_alg_tbl[kay->mka_algindex].icv_len) !=
+                   0) {
+                       wpa_printf(MSG_ERROR,
+                                  "KaY: Computed ICV is not equal to Received ICV");
+               return -1;
+               }
+       } else {
+               wpa_printf(MSG_ERROR, "KaY: No ICV");
+               return -1;
+       }
+
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_decode_mkpdu -
+ */
+static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay,
+                                      const u8 *buf, size_t len)
+{
+       struct ieee802_1x_mka_participant *participant;
+       struct ieee802_1x_mka_hdr *hdr;
+       size_t body_len;
+       size_t left_len;
+       int body_type;
+       int i;
+       const u8 *pos;
+       Boolean my_included;
+       Boolean handled[256];
+
+       if (ieee802_1x_kay_mkpdu_sanity_check(kay, buf, len))
+               return -1;
+
+       /* handle basic parameter set */
+       pos = buf + sizeof(struct ieee8023_hdr) + sizeof(struct ieee802_1x_hdr);
+       left_len = len - sizeof(struct ieee8023_hdr) -
+               sizeof(struct ieee802_1x_hdr);
+       participant = ieee802_1x_mka_decode_basic_body(kay, pos, left_len);
+       if (!participant)
+               return -1;
+
+       /* to skip basic parameter set */
+       hdr = (struct ieee802_1x_mka_hdr *) pos;
+       body_len = get_mka_param_body_len(hdr);
+       pos += body_len + MKA_HDR_LEN;
+       left_len -= body_len + MKA_HDR_LEN;
+
+       /* check i am in the peer's peer list */
+       my_included = ieee802_1x_mka_i_in_peerlist(participant, pos, left_len);
+       if (my_included) {
+               /* accept the peer as live peer */
+               if (!ieee802_1x_kay_is_in_peer(
+                           participant,
+                           participant->current_peer_id.mi)) {
+                       if (!ieee802_1x_kay_create_live_peer(
+                                   participant,
+                                   participant->current_peer_id.mi,
+                                   participant->current_peer_id.mn))
+                               return -1;
+                       ieee802_1x_kay_elect_key_server(participant);
+                       ieee802_1x_kay_decide_macsec_use(participant);
+               }
+               if (ieee802_1x_kay_is_in_potential_peer(
+                           participant, participant->current_peer_id.mi)) {
+                       ieee802_1x_kay_move_live_peer(
+                               participant, participant->current_peer_id.mi,
+                               participant->current_peer_id.mn);
+                       ieee802_1x_kay_elect_key_server(participant);
+                       ieee802_1x_kay_decide_macsec_use(participant);
+               }
+       }
+
+       /*
+        * Handle other parameter set than basic parameter set.
+        * Each parameter set should be present only once.
+        */
+       for (i = 0; i < 256; i++)
+               handled[i] = FALSE;
+
+       handled[0] = TRUE;
+       while (left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN) {
+               hdr = (struct ieee802_1x_mka_hdr *) pos;
+               body_len = get_mka_param_body_len(hdr);
+               body_type = get_mka_param_body_type(hdr);
+
+               if (body_type == MKA_ICV_INDICATOR)
+                       return 0;
+
+               if (left_len < (MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN)) {
+                       wpa_printf(MSG_ERROR,
+                                  "KaY: MKA Peer Packet Body Length (%d bytes) is less than the Parameter Set Header Length (%d bytes) + the Parameter Set Body Length (%d bytes) + %d bytes of ICV",
+                                  (int) left_len, (int) MKA_HDR_LEN,
+                                  (int) body_len, DEFAULT_ICV_LEN);
+                       goto next_para_set;
+               }
+
+               if (handled[body_type])
+                       goto next_para_set;
+
+               handled[body_type] = TRUE;
+               if (mak_body_handler[body_type].body_rx) {
+                       mak_body_handler[body_type].body_rx
+                               (participant, pos, left_len);
+               } else {
+                       wpa_printf(MSG_ERROR,
+                                  "The type %d not supported in this MKA version %d",
+                                  body_type, MKA_VERSION_ID);
+               }
+
+next_para_set:
+               pos += body_len + MKA_HDR_LEN;
+               left_len -= body_len + MKA_HDR_LEN;
+       }
+
+       kay->active = TRUE;
+       participant->retry_count = 0;
+       participant->active = TRUE;
+
+       return 0;
+}
+
+
+
+static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf,
+                          size_t len)
+{
+       struct ieee802_1x_kay *kay = ctx;
+       struct ieee8023_hdr *eth_hdr;
+       struct ieee802_1x_hdr *eapol_hdr;
+
+       /* must contain at least ieee8023_hdr + ieee802_1x_hdr */
+       if (len < sizeof(*eth_hdr) + sizeof(*eapol_hdr)) {
+               wpa_printf(MSG_MSGDUMP, "KaY: EAPOL frame too short (%lu)",
+                          (unsigned long) len);
+               return;
+       }
+
+       eth_hdr = (struct ieee8023_hdr *) buf;
+       eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1);
+       if (len != sizeof(*eth_hdr) + sizeof(*eapol_hdr) +
+           ntohs(eapol_hdr->length)) {
+               wpa_printf(MSG_MSGDUMP, "KAY: EAPOL MPDU is invalid: (%lu-%lu)",
+                          (unsigned long) len,
+                          (unsigned long) ntohs(eapol_hdr->length));
+               return;
+       }
+
+       if (eapol_hdr->version < EAPOL_VERSION) {
+               wpa_printf(MSG_MSGDUMP, "KaY: version %d does not support MKA",
+                          eapol_hdr->version);
+               return;
+       }
+       if (ntohs(eth_hdr->ethertype) != ETH_P_PAE ||
+           eapol_hdr->type != IEEE802_1X_TYPE_EAPOL_MKA)
+               return;
+
+       wpa_hexdump(MSG_DEBUG, "RX EAPOL-MKA: ", buf, len);
+       if (dl_list_empty(&kay->participant_list)) {
+               wpa_printf(MSG_ERROR, "KaY: no MKA participant instance");
+               return;
+       }
+
+       ieee802_1x_kay_decode_mkpdu(kay, buf, len);
+}
+
+
+/**
+ * ieee802_1x_kay_init -
+ */
+struct ieee802_1x_kay *
+ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
+                   const char *ifname, const u8 *addr)
+{
+       struct ieee802_1x_kay *kay;
+
+       kay = os_zalloc(sizeof(*kay));
+       if (!kay) {
+               wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
+               return NULL;
+       }
+
+       kay->ctx = ctx;
+
+       kay->enable = TRUE;
+       kay->active = FALSE;
+
+       kay->authenticated = FALSE;
+       kay->secured = FALSE;
+       kay->failed = FALSE;
+       kay->policy = policy;
+
+       os_strlcpy(kay->if_name, ifname, IFNAMSIZ);
+       os_memcpy(kay->actor_sci.addr, addr, ETH_ALEN);
+       kay->actor_sci.port = 0x0001;
+       kay->actor_priority = DEFAULT_PRIO_NOT_KEY_SERVER;
+
+       /* While actor acts as a key server, shall distribute sakey */
+       kay->dist_kn = 1;
+       kay->dist_an = 0;
+       kay->dist_time = 0;
+
+       kay->pn_exhaustion = PENDING_PN_EXHAUSTION;
+       kay->macsec_csindex = DEFAULT_CS_INDEX;
+       kay->mka_algindex = DEFAULT_MKA_ALG_INDEX;
+       kay->mka_version = MKA_VERSION_ID;
+
+       os_memcpy(kay->algo_agility, mka_algo_agility,
+                 sizeof(kay->algo_agility));
+
+       dl_list_init(&kay->participant_list);
+
+       if (policy == DO_NOT_SECURE) {
+               kay->macsec_capable = MACSEC_CAP_NOT_IMPLEMENTED;
+               kay->macsec_desired = FALSE;
+               kay->macsec_protect = FALSE;
+               kay->macsec_validate = Disabled;
+               kay->macsec_replay_protect = FALSE;
+               kay->macsec_replay_window = 0;
+               kay->macsec_confidentiality = CONFIDENTIALITY_NONE;
+       } else {
+               kay->macsec_capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50;
+               kay->macsec_desired = TRUE;
+               kay->macsec_protect = TRUE;
+               kay->macsec_validate = Strict;
+               kay->macsec_replay_protect = FALSE;
+               kay->macsec_replay_window = 0;
+               kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0;
+       }
+
+       wpa_printf(MSG_DEBUG, "KaY: state machine created");
+
+       /* Initialize the SecY must be prio to CP, as CP will control SecY */
+       secy_init_macsec(kay);
+       secy_get_available_transmit_sc(kay, &kay->sc_ch);
+
+       wpa_printf(MSG_DEBUG, "KaY: secy init macsec done");
+
+       /* init CP */
+       kay->cp = ieee802_1x_kay_alloc_cp_sm(kay);
+       if (kay->cp == NULL) {
+               ieee802_1x_kay_deinit(kay);
+               return NULL;
+       }
+
+       if (policy == DO_NOT_SECURE) {
+               ieee802_1x_cp_connect_authenticated(kay->cp);
+               ieee802_1x_cp_sm_step(kay->cp);
+       } else {
+               kay->l2_mka = l2_packet_init(kay->if_name, NULL, ETH_P_PAE,
+                                            kay_l2_receive, kay, 1);
+               if (kay->l2_mka == NULL) {
+                       wpa_printf(MSG_WARNING,
+                                  "KaY: Failed to initialize L2 packet processing for MKA packet");
+                       ieee802_1x_kay_deinit(kay);
+                       return NULL;
+               }
+       }
+
+       return kay;
+}
+
+
+/**
+ * ieee802_1x_kay_deinit -
+ */
+void
+ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay)
+{
+       struct ieee802_1x_mka_participant *participant;
+
+       if (!kay)
+               return;
+
+       wpa_printf(MSG_DEBUG, "KaY: state machine removed");
+
+       while (!dl_list_empty(&kay->participant_list)) {
+               participant = dl_list_entry(kay->participant_list.next,
+                                           struct ieee802_1x_mka_participant,
+                                           list);
+               ieee802_1x_kay_delete_mka(kay, &participant->ckn);
+       }
+
+       ieee802_1x_cp_sm_deinit(kay->cp);
+       secy_deinit_macsec(kay);
+
+       if (kay->l2_mka) {
+               l2_packet_deinit(kay->l2_mka);
+               kay->l2_mka = NULL;
+       }
+
+       os_free(kay->ctx);
+       os_free(kay);
+}
+
+
+/**
+ * ieee802_1x_kay_create_mka -
+ */
+struct ieee802_1x_mka_participant *
+ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn,
+                         struct mka_key *cak, u32 life,
+                         enum mka_created_mode mode, Boolean is_authenticator)
+{
+       struct ieee802_1x_mka_participant *participant;
+       unsigned int usecs;
+
+       if (!kay || !ckn || !cak) {
+               wpa_printf(MSG_ERROR, "KaY: ckn or cak is null");
+               return NULL;
+       }
+
+       if (cak->len != mka_alg_tbl[kay->mka_algindex].cak_len) {
+               wpa_printf(MSG_ERROR, "KaY: CAK length not follow key schema");
+               return NULL;
+       }
+       if (ckn->len > MAX_CKN_LEN) {
+               wpa_printf(MSG_ERROR, "KaY: CKN is out of range(<=32 bytes)");
+               return NULL;
+       }
+       if (!kay->enable) {
+               wpa_printf(MSG_ERROR, "KaY: Now is at disable state");
+               return NULL;
+       }
+
+       participant = os_zalloc(sizeof(*participant));
+       if (!participant) {
+               wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
+               return NULL;
+       }
+
+       participant->ckn.len = ckn->len;
+       os_memcpy(participant->ckn.name, ckn->name, ckn->len);
+       participant->cak.len = cak->len;
+       os_memcpy(participant->cak.key, cak->key, cak->len);
+       if (life)
+               participant->cak_life = life + time(NULL);
+
+       switch (mode) {
+       case EAP_EXCHANGE:
+               if (is_authenticator) {
+                       participant->is_obliged_key_server = TRUE;
+                       participant->can_be_key_server = TRUE;
+                       participant->is_key_server = TRUE;
+                       participant->principal = TRUE;
+
+                       os_memcpy(&kay->key_server_sci, &kay->actor_sci,
+                                 sizeof(kay->key_server_sci));
+                       kay->key_server_priority = kay->actor_priority;
+                       participant->is_elected = TRUE;
+               } else {
+                       participant->is_obliged_key_server = FALSE;
+                       participant->can_be_key_server = FALSE;
+                       participant->is_key_server = FALSE;
+                       participant->is_elected = TRUE;
+               }
+               break;
+
+       default:
+               participant->is_obliged_key_server = FALSE;
+               participant->can_be_key_server = TRUE;
+               participant->is_key_server = FALSE;
+               participant->is_elected = FALSE;
+               break;
+       }
+
+       participant->cached = FALSE;
+
+       participant->active = FALSE;
+       participant->participant = FALSE;
+       participant->retain = FALSE;
+       participant->activate = DEFAULT;
+
+       if (participant->is_key_server)
+               participant->principal = TRUE;
+
+       dl_list_init(&participant->live_peers);
+       dl_list_init(&participant->potential_peers);
+
+       participant->retry_count = 0;
+       participant->kay = kay;
+
+       if (os_get_random(participant->mi, sizeof(participant->mi)) < 0)
+               goto fail;
+       participant->mn = 0;
+
+       participant->lrx = FALSE;
+       participant->ltx = FALSE;
+       participant->orx = FALSE;
+       participant->otx = FALSE;
+       participant->to_dist_sak = FALSE;
+       participant->to_use_sak = FALSE;
+       participant->new_sak = FALSE;
+       dl_list_init(&participant->sak_list);
+       participant->new_key = NULL;
+       dl_list_init(&participant->rxsc_list);
+       participant->txsc = ieee802_1x_kay_init_transmit_sc(&kay->actor_sci,
+                                                           kay->sc_ch);
+       secy_cp_control_protect_frames(kay, kay->macsec_protect);
+       secy_cp_control_replay(kay, kay->macsec_replay_protect,
+                              kay->macsec_replay_window);
+       secy_create_transmit_sc(kay, participant->txsc);
+
+       /* to derive KEK from CAK and CKN */
+       participant->kek.len = mka_alg_tbl[kay->mka_algindex].kek_len;
+       if (mka_alg_tbl[kay->mka_algindex].kek_trfm(participant->cak.key,
+                                                   participant->ckn.name,
+                                                   participant->ckn.len,
+                                                   participant->kek.key)) {
+               wpa_printf(MSG_ERROR, "KaY: Derived KEK failed");
+               goto fail;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "KaY: Derived KEK",
+                       participant->kek.key, participant->kek.len);
+
+       /* to derive ICK from CAK and CKN */
+       participant->ick.len = mka_alg_tbl[kay->mka_algindex].ick_len;
+       if (mka_alg_tbl[kay->mka_algindex].ick_trfm(participant->cak.key,
+                                                   participant->ckn.name,
+                                                   participant->ckn.len,
+                                                   participant->ick.key)) {
+               wpa_printf(MSG_ERROR, "KaY: Derived ICK failed");
+               goto fail;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "KaY: Derived ICK",
+                       participant->ick.key, participant->ick.len);
+
+       dl_list_add(&kay->participant_list, &participant->list);
+       wpa_hexdump(MSG_DEBUG, "KaY: Participant created:",
+                   ckn->name, ckn->len);
+
+       usecs = os_random() % (MKA_HELLO_TIME * 1000);
+       eloop_register_timeout(0, usecs, ieee802_1x_participant_timer,
+                              participant, NULL);
+       participant->mka_life = MKA_LIFE_TIME / 1000 + time(NULL) +
+               usecs / 1000000;
+
+       return participant;
+
+fail:
+       os_free(participant);
+       return NULL;
+}
+
+
+/**
+ * ieee802_1x_kay_delete_mka -
+ */
+void
+ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay, struct mka_key_name *ckn)
+{
+       struct ieee802_1x_mka_participant *participant;
+       struct ieee802_1x_kay_peer *peer;
+       struct data_key *sak;
+       struct receive_sc *rxsc;
+
+       if (!kay || !ckn)
+               return;
+
+       wpa_printf(MSG_DEBUG, "KaY: participant removed");
+
+       /* get the participant */
+       participant = ieee802_1x_kay_get_participant(kay, ckn->name);
+       if (!participant) {
+               wpa_hexdump(MSG_DEBUG, "KaY: participant is not found",
+                           ckn->name, ckn->len);
+               return;
+       }
+
+       dl_list_del(&participant->list);
+
+       /* remove live peer */
+       while (!dl_list_empty(&participant->live_peers)) {
+               peer = dl_list_entry(participant->live_peers.next,
+                                    struct ieee802_1x_kay_peer, list);
+               dl_list_del(&peer->list);
+               os_free(peer);
+       }
+
+       /* remove potential peer */
+       while (!dl_list_empty(&participant->potential_peers)) {
+               peer = dl_list_entry(participant->potential_peers.next,
+                                    struct ieee802_1x_kay_peer, list);
+               dl_list_del(&peer->list);
+               os_free(peer);
+       }
+
+       /* remove sak */
+       while (!dl_list_empty(&participant->sak_list)) {
+               sak = dl_list_entry(participant->sak_list.next,
+                                   struct data_key, list);
+               dl_list_del(&sak->list);
+               os_free(sak->key);
+               os_free(sak);
+       }
+       while (!dl_list_empty(&participant->rxsc_list)) {
+               rxsc = dl_list_entry(participant->rxsc_list.next,
+                                    struct receive_sc, list);
+               secy_delete_receive_sc(kay, rxsc);
+               ieee802_1x_kay_deinit_receive_sc(participant, rxsc);
+       }
+       secy_delete_transmit_sc(kay, participant->txsc);
+       ieee802_1x_kay_deinit_transmit_sc(participant, participant->txsc);
+
+       os_memset(&participant->cak, 0, sizeof(participant->cak));
+       os_memset(&participant->kek, 0, sizeof(participant->kek));
+       os_memset(&participant->ick, 0, sizeof(participant->ick));
+       os_free(participant);
+}
+
+
+/**
+ * ieee802_1x_kay_mka_participate -
+ */
+void ieee802_1x_kay_mka_participate(struct ieee802_1x_kay *kay,
+                                   struct mka_key_name *ckn,
+                                   Boolean status)
+{
+       struct ieee802_1x_mka_participant *participant;
+
+       if (!kay || !ckn)
+               return;
+
+       participant = ieee802_1x_kay_get_participant(kay, ckn->name);
+       if (!participant)
+               return;
+
+       participant->active = status;
+}
+
+
+/**
+ * ieee802_1x_kay_new_sak -
+ */
+int
+ieee802_1x_kay_new_sak(struct ieee802_1x_kay *kay)
+{
+       struct ieee802_1x_mka_participant *participant;
+
+       if (!kay)
+               return -1;
+
+       participant = ieee802_1x_kay_get_principal_participant(kay);
+       if (!participant)
+               return -1;
+
+       participant->new_sak = TRUE;
+       wpa_printf(MSG_DEBUG, "KaY: new SAK signal");
+
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_kay_change_cipher_suite -
+ */
+int
+ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay, int cs_index)
+{
+       struct ieee802_1x_mka_participant *participant;
+
+       if (!kay)
+               return -1;
+
+       if ((unsigned int) cs_index >= CS_TABLE_SIZE) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: Configured cipher suite index is out of range");
+               return -1;
+       }
+       if (kay->macsec_csindex == cs_index)
+               return -2;
+
+       if (cs_index == 0)
+               kay->macsec_desired = FALSE;
+
+       kay->macsec_csindex = cs_index;
+       kay->macsec_capable = cipher_suite_tbl[kay->macsec_csindex].capable;
+
+       participant = ieee802_1x_kay_get_principal_participant(kay);
+       if (participant) {
+               wpa_printf(MSG_INFO, "KaY: Cipher Suite changed");
+               participant->new_sak = TRUE;
+       }
+
+       return 0;
+}
diff --git a/src/pae/ieee802_1x_kay.h b/src/pae/ieee802_1x_kay.h
new file mode 100644 (file)
index 0000000..064417e
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * IEEE 802.1X-2010 Key Agree Protocol of PAE state machine
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_1X_KAY_H
+#define IEEE802_1X_KAY_H
+
+#include "utils/list.h"
+#include "common/defs.h"
+#include "common/ieee802_1x_defs.h"
+
+struct macsec_init_params;
+struct ieee802_1x_cp_conf;
+
+#define MI_LEN                 12
+#define MAX_KEY_LEN            32  /* 32 bytes, 256 bits */
+#define MAX_CKN_LEN            32  /* 32 bytes, 256 bits */
+
+/* MKA timer, unit: millisecond */
+#define MKA_HELLO_TIME         2000
+#define MKA_LIFE_TIME          6000
+#define MKA_SAK_RETIRE_TIME    3000
+
+struct ieee802_1x_mka_ki {
+       u8 mi[MI_LEN];
+       u32 kn;
+};
+
+struct ieee802_1x_mka_sci {
+       u8 addr[ETH_ALEN];
+       u16 port;
+};
+
+struct mka_key {
+       u8 key[MAX_KEY_LEN];
+       size_t len;
+};
+
+struct mka_key_name {
+       u8 name[MAX_CKN_LEN];
+       size_t len;
+};
+
+enum mka_created_mode {
+       PSK,
+       EAP_EXCHANGE,
+       DISTRIBUTED,
+       CACHED,
+};
+
+struct ieee802_1x_kay_ctx {
+       /* pointer to arbitrary upper level context */
+       void *ctx;
+
+       /* abstract wpa driver interface */
+       int (*macsec_init)(void *ctx, struct macsec_init_params *params);
+       int (*macsec_deinit)(void *ctx);
+       int (*enable_protect_frames)(void *ctx, Boolean enabled);
+       int (*set_replay_protect)(void *ctx, Boolean enabled, u32 window);
+       int (*set_current_cipher_suite)(void *ctx, const u8 *cs, size_t cs_len);
+       int (*enable_controlled_port)(void *ctx, Boolean enabled);
+       int (*get_receive_lowest_pn)(void *ctx, u32 channel, u8 an,
+                                    u32 *lowest_pn);
+       int (*get_transmit_next_pn)(void *ctx, u32 channel, u8 an,
+                                   u32 *next_pn);
+       int (*set_transmit_next_pn)(void *ctx, u32 channel, u8 an, u32 next_pn);
+       int (*get_available_receive_sc)(void *ctx, u32 *channel);
+       int (*create_receive_sc)(void *ctx, u32 channel,
+                                struct ieee802_1x_mka_sci *sci,
+                                enum validate_frames vf,
+                                enum confidentiality_offset co);
+       int (*delete_receive_sc)(void *ctx, u32 channel);
+       int (*create_receive_sa)(void *ctx, u32 channel, u8 an, u32 lowest_pn,
+                                const u8 *sak);
+       int (*enable_receive_sa)(void *ctx, u32 channel, u8 an);
+       int (*disable_receive_sa)(void *ctx, u32 channel, u8 an);
+       int (*get_available_transmit_sc)(void *ctx, u32 *channel);
+       int (*create_transmit_sc)(void *ctx, u32 channel,
+                                 const struct ieee802_1x_mka_sci *sci,
+                                 enum confidentiality_offset co);
+       int (*delete_transmit_sc)(void *ctx, u32 channel);
+       int (*create_transmit_sa)(void *ctx, u32 channel, u8 an, u32 next_pn,
+                                 Boolean confidentiality, const u8 *sak);
+       int (*enable_transmit_sa)(void *ctx, u32 channel, u8 an);
+       int (*disable_transmit_sa)(void *ctx, u32 channel, u8 an);
+};
+
+struct ieee802_1x_kay {
+       Boolean enable;
+       Boolean active;
+
+       Boolean authenticated;
+       Boolean secured;
+       Boolean failed;
+
+       struct ieee802_1x_mka_sci actor_sci;
+       u8 actor_priority;
+       struct ieee802_1x_mka_sci key_server_sci;
+       u8 key_server_priority;
+
+       enum macsec_cap macsec_capable;
+       Boolean macsec_desired;
+       Boolean macsec_protect;
+       Boolean macsec_replay_protect;
+       u32 macsec_replay_window;
+       enum validate_frames macsec_validate;
+       enum confidentiality_offset macsec_confidentiality;
+
+       u32 ltx_kn;
+       u8 ltx_an;
+       u32 lrx_kn;
+       u8 lrx_an;
+
+       u32 otx_kn;
+       u8 otx_an;
+       u32 orx_kn;
+       u8 orx_an;
+
+       /* not defined in IEEE802.1X */
+       struct ieee802_1x_kay_ctx *ctx;
+       Boolean is_key_server;
+       Boolean is_obliged_key_server;
+       char if_name[IFNAMSIZ];
+
+       int macsec_csindex;  /*  MACsec cipher suite table index */
+       int mka_algindex;  /* MKA alg table index */
+
+       u32 dist_kn;
+       u8 dist_an;
+       time_t dist_time;
+
+       u8 mka_version;
+       u8 algo_agility[4];
+       u32 sc_ch;
+
+       u32 pn_exhaustion;
+       Boolean port_enable;
+       Boolean rx_enable;
+       Boolean tx_enable;
+
+       struct dl_list participant_list;
+       enum macsec_policy policy;
+
+       struct ieee802_1x_cp_sm *cp;
+
+       struct l2_packet_data *l2_mka;
+
+       enum validate_frames vf;
+       enum confidentiality_offset co;
+};
+
+
+struct ieee802_1x_kay *
+ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
+                   const char *ifname, const u8 *addr);
+void ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay);
+
+struct ieee802_1x_mka_participant *
+ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay,
+                         struct mka_key_name *ckn, struct mka_key *cak,
+                         u32 life, enum mka_created_mode mode,
+                         Boolean is_authenticator);
+void ieee802_1x_kay_delete_mka(struct ieee802_1x_kay *kay,
+                              struct mka_key_name *ckn);
+void ieee802_1x_kay_mka_participate(struct ieee802_1x_kay *kay,
+                                   struct mka_key_name *ckn,
+                                   Boolean status);
+int ieee802_1x_kay_new_sak(struct ieee802_1x_kay *kay);
+int ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay,
+                                      int cs_index);
+
+int ieee802_1x_kay_set_latest_sa_attr(struct ieee802_1x_kay *kay,
+                                     struct ieee802_1x_mka_ki *lki, u8 lan,
+                                     Boolean ltx, Boolean lrx);
+int ieee802_1x_kay_set_old_sa_attr(struct ieee802_1x_kay *kay,
+                                  struct ieee802_1x_mka_ki *oki,
+                                  u8 oan, Boolean otx, Boolean orx);
+int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay,
+                             struct ieee802_1x_mka_ki *lki);
+int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay,
+                             struct ieee802_1x_mka_ki *ki);
+int ieee802_1x_kay_enable_tx_sas(struct ieee802_1x_kay *kay,
+                                struct ieee802_1x_mka_ki *lki);
+int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay,
+                                struct ieee802_1x_mka_ki *lki);
+int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay);
+int ieee802_1x_kay_cp_conf(struct ieee802_1x_kay *kay,
+                          struct ieee802_1x_cp_conf *pconf);
+
+#endif /* IEEE802_1X_KAY_H */
diff --git a/src/pae/ieee802_1x_kay_i.h b/src/pae/ieee802_1x_kay_i.h
new file mode 100644 (file)
index 0000000..bdad3a5
--- /dev/null
@@ -0,0 +1,419 @@
+/*
+ * IEEE 802.1X-2010 Key Agree Protocol of PAE state machine
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_1X_KAY_I_H
+#define IEEE802_1X_KAY_I_H
+
+#include "utils/list.h"
+#include "common/defs.h"
+#include "common/ieee802_1x_defs.h"
+
+#define MKA_VERSION_ID              1
+
+/* IEEE Std 802.1X-2010, 11.11.1, Table 11-7 */
+enum mka_packet_type {
+       MKA_BASIC_PARAMETER_SET = MKA_VERSION_ID,
+       MKA_LIVE_PEER_LIST = 1,
+       MKA_POTENTIAL_PEER_LIST = 2,
+       MKA_SAK_USE = 3,
+       MKA_DISTRIBUTED_SAK = 4,
+       MKA_DISTRIBUTED_CAK = 5,
+       MKA_KMD = 6,
+       MKA_ANNOUNCEMENT = 7,
+       MKA_ICV_INDICATOR = 255
+};
+
+#define ICV_LEN                         16  /* 16 bytes */
+#define SAK_WRAPPED_LEN                 24
+/* KN + Wrapper SAK */
+#define DEFAULT_DIS_SAK_BODY_LENGTH     (SAK_WRAPPED_LEN + 4)
+#define MAX_RETRY_CNT                   5
+
+struct ieee802_1x_kay;
+
+struct ieee802_1x_mka_peer_id {
+       u8 mi[MI_LEN];
+       u32 mn;
+};
+
+struct ieee802_1x_kay_peer {
+       struct ieee802_1x_mka_sci sci;
+       u8 mi[MI_LEN];
+       u32 mn;
+       time_t expire;
+       Boolean is_key_server;
+       u8 key_server_priority;
+       Boolean macsec_desired;
+       enum macsec_cap macsec_capbility;
+       Boolean sak_used;
+       struct dl_list list;
+};
+
+struct key_conf {
+       u8 *key;
+       struct ieee802_1x_mka_ki ki;
+       enum confidentiality_offset offset;
+       u8 an;
+       Boolean tx;
+       Boolean rx;
+       int key_len; /* unit: byte */
+};
+
+struct data_key {
+       u8 *key;
+       int key_len;
+       struct ieee802_1x_mka_ki key_identifier;
+       enum confidentiality_offset confidentiality_offset;
+       u8 an;
+       Boolean transmits;
+       Boolean receives;
+       struct os_time created_time;
+       u32 next_pn;
+
+       /* not defined data */
+       Boolean rx_latest;
+       Boolean tx_latest;
+
+       int user;  /* FIXME: to indicate if it can be delete safely */
+
+       struct dl_list list;
+};
+
+/* TransmitSC in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct transmit_sc {
+       struct ieee802_1x_mka_sci sci; /* const SCI sci */
+       Boolean transmitting; /* bool transmitting (read only) */
+
+       struct os_time created_time; /* Time createdTime */
+
+       u8 encoding_sa; /* AN encodingSA (read only) */
+       u8 enciphering_sa; /* AN encipheringSA (read only) */
+
+       /* not defined data */
+       unsigned int channel;
+
+       struct dl_list list;
+       struct dl_list sa_list;
+};
+
+/* TransmitSA in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct transmit_sa {
+       Boolean in_use; /* bool inUse (read only) */
+       u32 next_pn; /* PN nextPN (read only) */
+       struct os_time created_time; /* Time createdTime */
+
+       Boolean enable_transmit; /* bool EnableTransmit */
+
+       u8 an;
+       Boolean confidentiality;
+       struct data_key *pkey;
+
+       struct transmit_sc *sc;
+       struct dl_list list; /* list entry in struct transmit_sc::sa_list */
+};
+
+/* ReceiveSC in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct receive_sc {
+       struct ieee802_1x_mka_sci sci; /* const SCI sci */
+       Boolean receiving; /* bool receiving (read only) */
+
+       struct os_time created_time; /* Time createdTime */
+
+       unsigned int channel;
+
+       struct dl_list list;
+       struct dl_list sa_list;
+};
+
+/* ReceiveSA in IEEE Std 802.1AE-2006, Figure 10-6 */
+struct receive_sa {
+       Boolean enable_receive; /* bool enableReceive */
+       Boolean in_use; /* bool inUse (read only) */
+
+       u32 next_pn; /* PN nextPN (read only) */
+       u32 lowest_pn; /* PN lowestPN (read only) */
+       u8 an;
+       struct os_time created_time;
+
+       struct data_key *pkey;
+       struct receive_sc *sc; /* list entry in struct receive_sc::sa_list */
+
+       struct dl_list list;
+};
+
+struct macsec_ciphersuite {
+       u8 id[CS_ID_LEN];
+       char name[32];
+       enum macsec_cap capable;
+       int sak_len; /* unit: byte */
+
+       u32 index;
+};
+
+struct mka_alg {
+       u8 parameter[4];
+       size_t cak_len;
+       size_t kek_len;
+       size_t ick_len;
+       size_t icv_len;
+
+       int (*cak_trfm)(const u8 *msk, const u8 *mac1, const u8 *mac2, u8 *cak);
+       int (*ckn_trfm)(const u8 *msk, const u8 *mac1, const u8 *mac2,
+                       const u8 *sid, size_t sid_len, u8 *ckn);
+       int (*kek_trfm)(const u8 *cak, const u8 *ckn, size_t ckn_len, u8 *kek);
+       int (*ick_trfm)(const u8 *cak, const u8 *ckn, size_t ckn_len, u8 *ick);
+       int (*icv_hash)(const u8 *ick, const u8 *msg, size_t msg_len, u8 *icv);
+
+       int index; /* index for configuring */
+};
+
+#define DEFAULT_MKA_ALG_INDEX 0
+
+/* See IEEE Std 802.1X-2010, 9.16 MKA management */
+struct ieee802_1x_mka_participant {
+       /* used for active and potential participant */
+       struct mka_key_name ckn;
+       struct mka_key cak;
+       Boolean cached;
+
+       /* used by management to monitor and control activation */
+       Boolean active;
+       Boolean participant;
+       Boolean retain;
+
+       enum { DEFAULT, DISABLED, ON_OPER_UP, ALWAYS } activate;
+
+       /* used for active participant */
+       Boolean principal;
+       struct dl_list live_peers;
+       struct dl_list potential_peers;
+
+       /* not defined in IEEE 802.1X */
+       struct dl_list list;
+
+       struct mka_key kek;
+       struct mka_key ick;
+
+       struct ieee802_1x_mka_ki lki;
+       u8 lan;
+       Boolean ltx;
+       Boolean lrx;
+
+       struct ieee802_1x_mka_ki oki;
+       u8 oan;
+       Boolean otx;
+       Boolean orx;
+
+       Boolean is_key_server;
+       Boolean is_obliged_key_server;
+       Boolean can_be_key_server;
+       Boolean is_elected;
+
+       struct dl_list sak_list;
+       struct dl_list rxsc_list;
+
+       struct transmit_sc *txsc;
+
+       u8 mi[MI_LEN];
+       u32 mn;
+
+       struct ieee802_1x_mka_peer_id current_peer_id;
+       struct ieee802_1x_mka_sci current_peer_sci;
+       time_t cak_life;
+       time_t mka_life;
+       Boolean to_dist_sak;
+       Boolean to_use_sak;
+       Boolean new_sak;
+
+       Boolean advised_desired;
+       enum macsec_cap advised_capability;
+
+       struct data_key *new_key;
+       u32 retry_count;
+
+       struct ieee802_1x_kay *kay;
+};
+
+struct ieee802_1x_mka_hdr {
+       /* octet 1 */
+       u32 type:8;
+       /* octet 2 */
+       u32 reserve:8;
+       /* octet 3 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+       u32 length:4;
+       u32 reserve1:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+       u32 reserve1:4;
+       u32 length:4;
+#else
+#error "Please fix <bits/endian.h>"
+#endif
+       /* octet 4 */
+       u32 length1:8;
+};
+
+#define MKA_HDR_LEN sizeof(struct ieee802_1x_mka_hdr)
+
+struct ieee802_1x_mka_basic_body {
+       /* octet 1 */
+       u32 version:8;
+       /* octet 2 */
+       u32 priority:8;
+       /* octet 3 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+       u32 length:4;
+       u32 macsec_capbility:2;
+       u32 macsec_desired:1;
+       u32 key_server:1;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+       u32 key_server:1;
+       u32 macsec_desired:1;
+       u32 macsec_capbility:2;
+       u32 length:4;
+#endif
+       /* octet 4 */
+       u32 length1:8;
+
+       struct ieee802_1x_mka_sci actor_sci;
+       u8 actor_mi[MI_LEN];
+       u32 actor_mn;
+       u8 algo_agility[4];
+
+       /* followed by CAK Name*/
+       u8 ckn[0];
+};
+
+struct ieee802_1x_mka_peer_body {
+       /* octet 1 */
+       u32 type:8;
+       /* octet 2 */
+       u32 reserve:8;
+       /* octet 3 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+       u32 length:4;
+       u32 reserve1:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+       u32 reserve1:4;
+       u32 length:4;
+#endif
+       /* octet 4 */
+       u32 length1:8;
+
+       u8 peer[0];
+       /* followed by Peers */
+};
+
+struct ieee802_1x_mka_sak_use_body {
+       /* octet 1 */
+       u32 type:8;
+       /* octet 2 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+       u32 orx:1;
+       u32 otx:1;
+       u32 oan:2;
+       u32 lrx:1;
+       u32 ltx:1;
+       u32 lan:2;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+       u32 lan:2;
+       u32 ltx:1;
+       u32 lrx:1;
+       u32 oan:2;
+       u32 otx:1;
+       u32 orx:1;
+#endif
+
+       /* octet 3 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+       u32 length:4;
+       u32 delay_protect:1;
+       u32 reserve:1;
+       u32 prx:1;
+       u32 ptx:1;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+       u32 ptx:1;
+       u32 prx:1;
+       u32 reserve:1;
+       u32 delay_protect:1;
+       u32 length:4;
+#endif
+
+       /* octet 4 */
+       u32 length1:8;
+
+       /* octet 5 - 16 */
+       u8 lsrv_mi[MI_LEN];
+       /* octet 17 - 20 */
+       u32 lkn;
+       /* octet 21 - 24 */
+       u32 llpn;
+
+       /* octet 25 - 36 */
+       u8 osrv_mi[MI_LEN];
+       /* octet 37 - 40 */
+       u32 okn;
+       /* octet 41 - 44 */
+       u32 olpn;
+};
+
+
+struct ieee802_1x_mka_dist_sak_body {
+       /* octet 1 */
+       u32 type:8;
+       /* octet 2 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+       u32 reserve:4;
+       u32 confid_offset:2;
+       u32 dan:2;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+       u32 dan:2;
+       u32 confid_offset:2;
+       u32 reserve:4;
+#endif
+       /* octet 3 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+       u32 length:4;
+       u32 reserve1:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+       u32 reserve1:4;
+       u32 length:4;
+#endif
+       /* octet 4 */
+       u32 length1:8;
+       /* octet 5 - 8 */
+       u32 kn;
+
+       /* for GCM-AES-128: octet 9-32: SAK
+        * for other cipher suite: octet 9-16: cipher suite id, octet 17-: SAK
+        */
+       u8 sak[0];
+};
+
+
+struct ieee802_1x_mka_icv_body {
+       /* octet 1 */
+       u32 type:8;
+       /* octet 2 */
+       u32 reserve:8;
+       /* octet 3 */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+       u32 length:4;
+       u32 reserve1:4;
+#elif __BYTE_ORDER == __BIG_ENDIAN
+       u32 reserve1:4;
+       u32 length:4;
+#endif
+       /* octet 4 */
+       u32 length1:8;
+
+       /* octet 5 - */
+       u8 icv[0];
+};
+
+#endif /* IEEE802_1X_KAY_I_H */
diff --git a/src/pae/ieee802_1x_key.c b/src/pae/ieee802_1x_key.c
new file mode 100644 (file)
index 0000000..9a8d923
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * IEEE 802.1X-2010 Key Hierarchy
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * SAK derivation specified in IEEE Std 802.1X-2010, Clause 6.2
+*/
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "crypto/md5.h"
+#include "crypto/sha1.h"
+#include "crypto/aes_wrap.h"
+#include "crypto/crypto.h"
+#include "ieee802_1x_key.h"
+
+
+static void joint_two_mac(const u8 *mac1, const u8 *mac2, u8 *out)
+{
+       if (os_memcmp(mac1, mac2, ETH_ALEN) < 0) {
+               os_memcpy(out, mac1, ETH_ALEN);
+               os_memcpy(out + ETH_ALEN, mac2, ETH_ALEN);
+       } else {
+               os_memcpy(out, mac2, ETH_ALEN);
+               os_memcpy(out + ETH_ALEN, mac1, ETH_ALEN);
+       }
+}
+
+
+/* IEEE Std 802.1X-2010, 6.2.1 KDF */
+static int aes_kdf_128(const u8 *kdk, const char *label, const u8 *context,
+                      int ctx_bits, int ret_bits, u8 *ret)
+{
+       const int h = 128;
+       const int r = 8;
+       int i, n;
+       int lab_len, ctx_len, ret_len, buf_len;
+       u8 *buf;
+
+       lab_len = os_strlen(label);
+       ctx_len = (ctx_bits + 7) / 8;
+       ret_len = ((ret_bits & 0xffff) + 7) / 8;
+       buf_len = lab_len + ctx_len + 4;
+
+       os_memset(ret, 0, ret_len);
+
+       n = (ret_bits + h - 1) / h;
+       if (n > ((0x1 << r) - 1))
+               return -1;
+
+       buf = os_zalloc(buf_len);
+       if (buf == NULL)
+               return -1;
+
+       os_memcpy(buf + 1, label, lab_len);
+       os_memcpy(buf + lab_len + 2, context, ctx_len);
+       WPA_PUT_BE16(&buf[buf_len - 2], ret_bits);
+
+       for (i = 0; i < n; i++) {
+               buf[0] = (u8) (i + 1);
+               if (omac1_aes_128(kdk, buf, buf_len, ret)) {
+                       os_free(buf);
+                       return -1;
+               }
+               ret = ret + h / 8;
+       }
+       os_free(buf);
+       return 0;
+}
+
+
+/********** AES-CMAC-128 **********/
+/**
+ * ieee802_1x_cak_128bits_aes_cmac
+ *
+ * IEEE Std 802.1X-2010, 6.2.2
+ * CAK = KDF(Key, Label, mac1 | mac2, CAKlength)
+ */
+int ieee802_1x_cak_128bits_aes_cmac(const u8 *msk, const u8 *mac1,
+                                   const u8 *mac2, u8 *cak)
+{
+       u8 context[2 * ETH_ALEN];
+
+       joint_two_mac(mac1, mac2, context);
+       return aes_kdf_128(msk, "IEEE8021 EAP CAK",
+                          context, sizeof(context) * 8, 128, cak);
+}
+
+
+/**
+ * ieee802_1x_ckn_128bits_aes_cmac
+ *
+ * IEEE Std 802.1X-2010, 6.2.2
+ * CKN = KDF(Key, Label, ID | mac1 | mac2, CKNlength)
+ */
+int ieee802_1x_ckn_128bits_aes_cmac(const u8 *msk, const u8 *mac1,
+                                   const u8 *mac2, const u8 *sid,
+                                   size_t sid_bytes, u8 *ckn)
+{
+       int res;
+       u8 *context;
+       size_t ctx_len = sid_bytes + ETH_ALEN * 2;
+
+       context = os_zalloc(ctx_len);
+       if (!context) {
+               wpa_printf(MSG_ERROR, "MKA-%s: out of memory", __func__);
+               return -1;
+       }
+       os_memcpy(context, sid, sid_bytes);
+       joint_two_mac(mac1, mac2, context + sid_bytes);
+
+       res = aes_kdf_128(msk, "IEEE8021 EAP CKN", context, ctx_len * 8,
+                         128, ckn);
+       os_free(context);
+       return res;
+}
+
+
+/**
+ * ieee802_1x_kek_128bits_aes_cmac
+ *
+ * IEEE Std 802.1X-2010, 9.3.3
+ * KEK = KDF(Key, Label, Keyid, KEKLength)
+ */
+int ieee802_1x_kek_128bits_aes_cmac(const u8 *cak, const u8 *ckn,
+                                   size_t ckn_bytes, u8 *kek)
+{
+       u8 context[16];
+
+       /* First 16 octets of CKN, with null octets appended to pad if needed */
+       os_memset(context, 0, sizeof(context));
+       os_memcpy(context, ckn, (ckn_bytes < 16) ? ckn_bytes : 16);
+
+       return aes_kdf_128(cak, "IEEE8021 KEK", context, sizeof(context) * 8,
+                          128, kek);
+}
+
+
+/**
+ * ieee802_1x_ick_128bits_aes_cmac
+ *
+ * IEEE Std 802.1X-2010, 9.3.3
+ * ICK = KDF(Key, Label, Keyid, ICKLength)
+ */
+int ieee802_1x_ick_128bits_aes_cmac(const u8 *cak, const u8 *ckn,
+                                   size_t ckn_bytes, u8 *ick)
+{
+       u8 context[16];
+
+       /* First 16 octets of CKN, with null octets appended to pad if needed */
+       os_memset(context, 0, sizeof(context));
+       os_memcpy(context, ckn, (ckn_bytes < 16) ? ckn_bytes : 16);
+
+       return aes_kdf_128(cak, "IEEE8021 ICK", context, sizeof(context) * 8,
+                          128, ick);
+}
+
+
+/**
+ * ieee802_1x_icv_128bits_aes_cmac
+ *
+ * IEEE Std 802.1X-2010, 9.4.1
+ * ICV = AES-CMAC(ICK, M, 128)
+ */
+int ieee802_1x_icv_128bits_aes_cmac(const u8 *ick, const u8 *msg,
+                                   size_t msg_bytes, u8 *icv)
+{
+       if (omac1_aes_128(ick, msg, msg_bytes, icv)) {
+               wpa_printf(MSG_ERROR, "MKA: omac1_aes_128 failed");
+               return -1;
+       }
+       return 0;
+}
+
+
+/**
+ * ieee802_1x_sak_128bits_aes_cmac
+ *
+ * IEEE Std 802.1X-2010, 9.8.1
+ * SAK = KDF(Key, Label, KS-nonce | MI-value list | KN, SAKLength)
+ */
+int ieee802_1x_sak_128bits_aes_cmac(const u8 *cak, const u8 *ctx,
+                                   size_t ctx_bytes, u8 *sak)
+{
+       return aes_kdf_128(cak, "IEEE8021 SAK", ctx, ctx_bytes * 8, 128, sak);
+}
diff --git a/src/pae/ieee802_1x_key.h b/src/pae/ieee802_1x_key.h
new file mode 100644 (file)
index 0000000..ea318ea
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * IEEE 802.1X-2010 Key Hierarchy
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_1X_KEY_H
+#define IEEE802_1X_KEY_H
+
+int ieee802_1x_cak_128bits_aes_cmac(const u8 *msk, const u8 *mac1,
+                                   const u8 *mac2, u8 *cak);
+int ieee802_1x_ckn_128bits_aes_cmac(const u8 *msk, const u8 *mac1,
+                                   const u8 *mac2, const u8 *sid,
+                                   size_t sid_bytes, u8 *ckn);
+int ieee802_1x_kek_128bits_aes_cmac(const u8 *cak, const u8 *ckn,
+                                   size_t ckn_bytes, u8 *kek);
+int ieee802_1x_ick_128bits_aes_cmac(const u8 *cak, const u8 *ckn,
+                                   size_t ckn_bytes, u8 *ick);
+int ieee802_1x_icv_128bits_aes_cmac(const u8 *ick, const u8 *msg,
+                                   size_t msg_bytes, u8 *icv);
+int ieee802_1x_sak_128bits_aes_cmac(const u8 *cak, const u8 *ctx,
+                                   size_t ctx_bytes, u8 *sak);
+
+#endif /* IEEE802_1X_KEY_H */
diff --git a/src/pae/ieee802_1x_secy_ops.c b/src/pae/ieee802_1x_secy_ops.c
new file mode 100644 (file)
index 0000000..fbe05dc
--- /dev/null
@@ -0,0 +1,492 @@
+ /*
+ * SecY Operations
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/defs.h"
+#include "drivers/driver.h"
+#include "pae/ieee802_1x_kay.h"
+#include "pae/ieee802_1x_kay_i.h"
+#include "pae/ieee802_1x_secy_ops.h"
+
+
+int secy_cp_control_validate_frames(struct ieee802_1x_kay *kay,
+                                   enum validate_frames vf)
+{
+       kay->vf = vf;
+       return 0;
+}
+
+
+int secy_cp_control_protect_frames(struct ieee802_1x_kay *kay, Boolean enabled)
+{
+       struct ieee802_1x_kay_ctx *ops;
+
+       if (!kay) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->enable_protect_frames) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy enable_protect_frames operation not supported");
+               return -1;
+       }
+
+       return ops->enable_protect_frames(ops->ctx, enabled);
+}
+
+
+int secy_cp_control_replay(struct ieee802_1x_kay *kay, Boolean enabled, u32 win)
+{
+       struct ieee802_1x_kay_ctx *ops;
+
+       if (!kay) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->set_replay_protect) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy set_replay_protect operation not supported");
+               return -1;
+       }
+
+       return ops->set_replay_protect(ops->ctx, enabled, win);
+}
+
+
+int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay,
+                                        const u8 *cs, size_t cs_len)
+{
+       struct ieee802_1x_kay_ctx *ops;
+
+       if (!kay) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->set_current_cipher_suite) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy set_current_cipher_suite operation not supported");
+               return -1;
+       }
+
+       return ops->set_current_cipher_suite(ops->ctx, cs, cs_len);
+}
+
+
+int secy_cp_control_confidentiality_offset(struct ieee802_1x_kay *kay,
+                                          enum confidentiality_offset co)
+{
+       kay->co = co;
+       return 0;
+}
+
+
+int secy_cp_control_enable_port(struct ieee802_1x_kay *kay, Boolean enabled)
+{
+       struct ieee802_1x_kay_ctx *ops;
+
+       if (!kay) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->enable_controlled_port) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy enable_controlled_port operation not supported");
+               return -1;
+       }
+
+       return ops->enable_controlled_port(ops->ctx, enabled);
+}
+
+
+int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay,
+                              struct receive_sa *rxsa)
+{
+       struct ieee802_1x_kay_ctx *ops;
+
+       if (!kay || !rxsa) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->get_receive_lowest_pn) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy get_receive_lowest_pn operation not supported");
+               return -1;
+       }
+
+       return ops->get_receive_lowest_pn(ops->ctx,
+                                       rxsa->sc->channel,
+                                       rxsa->an,
+                                       &rxsa->lowest_pn);
+}
+
+
+int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay,
+                             struct transmit_sa *txsa)
+{
+       struct ieee802_1x_kay_ctx *ops;
+
+       if (!kay || !txsa) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->get_transmit_next_pn) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy get_receive_lowest_pn operation not supported");
+               return -1;
+       }
+
+       return ops->get_transmit_next_pn(ops->ctx,
+                                       txsa->sc->channel,
+                                       txsa->an,
+                                       &txsa->next_pn);
+}
+
+
+int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay,
+                             struct transmit_sa *txsa)
+{
+       struct ieee802_1x_kay_ctx *ops;
+
+       if (!kay || !txsa) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->set_transmit_next_pn) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy get_receive_lowest_pn operation not supported");
+               return -1;
+       }
+
+       return ops->set_transmit_next_pn(ops->ctx,
+                                       txsa->sc->channel,
+                                       txsa->an,
+                                       txsa->next_pn);
+}
+
+
+int secy_get_available_receive_sc(struct ieee802_1x_kay *kay, u32 *channel)
+{
+       struct ieee802_1x_kay_ctx *ops;
+
+       if (!kay) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->get_available_receive_sc) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy get_available_receive_sc operation not supported");
+               return -1;
+       }
+
+       return ops->get_available_receive_sc(ops->ctx, channel);
+}
+
+
+int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc)
+{
+       struct ieee802_1x_kay_ctx *ops;
+
+       if (!kay || !rxsc) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->create_receive_sc) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy create_receive_sc operation not supported");
+               return -1;
+       }
+
+       return ops->create_receive_sc(ops->ctx, rxsc->channel, &rxsc->sci,
+                                     kay->vf, kay->co);
+}
+
+
+int secy_delete_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc)
+{
+       struct ieee802_1x_kay_ctx *ops;
+
+       if (!kay || !rxsc) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->delete_receive_sc) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy delete_receive_sc operation not supported");
+               return -1;
+       }
+
+       return ops->delete_receive_sc(ops->ctx, rxsc->channel);
+}
+
+
+int secy_create_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
+{
+       struct ieee802_1x_kay_ctx *ops;
+
+       if (!kay || !rxsa) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->create_receive_sa) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy create_receive_sa operation not supported");
+               return -1;
+       }
+
+       return ops->create_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an,
+                                     rxsa->lowest_pn, rxsa->pkey->key);
+}
+
+
+int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
+{
+       struct ieee802_1x_kay_ctx *ops;
+
+       if (!kay || !rxsa) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->enable_receive_sa) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy enable_receive_sa operation not supported");
+               return -1;
+       }
+
+       rxsa->enable_receive = TRUE;
+
+       return ops->enable_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an);
+}
+
+
+int secy_disable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa)
+{
+       struct ieee802_1x_kay_ctx *ops;
+
+       if (!kay || !rxsa) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->disable_receive_sa) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy disable_receive_sa operation not supported");
+               return -1;
+       }
+
+       rxsa->enable_receive = FALSE;
+
+       return ops->disable_receive_sa(ops->ctx, rxsa->sc->channel, rxsa->an);
+}
+
+
+int secy_get_available_transmit_sc(struct ieee802_1x_kay *kay, u32 *channel)
+{
+       struct ieee802_1x_kay_ctx *ops;
+
+       if (!kay) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->get_available_transmit_sc) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy get_available_transmit_sc operation not supported");
+               return -1;
+       }
+
+       return ops->get_available_transmit_sc(ops->ctx, channel);
+}
+
+
+int secy_create_transmit_sc(struct ieee802_1x_kay *kay,
+                           struct transmit_sc *txsc)
+{
+       struct ieee802_1x_kay_ctx *ops;
+
+       if (!kay || !txsc) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->create_transmit_sc) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy create_transmit_sc operation not supported");
+               return -1;
+       }
+
+       return ops->create_transmit_sc(ops->ctx, txsc->channel, &txsc->sci,
+                                      kay->co);
+}
+
+
+int secy_delete_transmit_sc(struct ieee802_1x_kay *kay,
+                           struct transmit_sc *txsc)
+{
+       struct ieee802_1x_kay_ctx *ops;
+
+       if (!kay || !txsc) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->delete_transmit_sc) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy delete_transmit_sc operation not supported");
+               return -1;
+       }
+
+       return ops->delete_transmit_sc(ops->ctx, txsc->channel);
+}
+
+
+int secy_create_transmit_sa(struct ieee802_1x_kay *kay,
+                           struct transmit_sa *txsa)
+{
+       struct ieee802_1x_kay_ctx *ops;
+
+       if (!kay || !txsa) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->create_transmit_sa) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy create_transmit_sa operation not supported");
+               return -1;
+       }
+
+       return ops->create_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an,
+                                       txsa->next_pn, txsa->confidentiality,
+                                       txsa->pkey->key);
+}
+
+
+int secy_enable_transmit_sa(struct ieee802_1x_kay *kay,
+                           struct transmit_sa *txsa)
+{
+       struct ieee802_1x_kay_ctx *ops;
+
+       if (!kay || !txsa) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->enable_transmit_sa) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy enable_transmit_sa operation not supported");
+               return -1;
+       }
+
+       txsa->enable_transmit = TRUE;
+
+       return ops->enable_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an);
+}
+
+
+int secy_disable_transmit_sa(struct ieee802_1x_kay *kay,
+                            struct transmit_sa *txsa)
+{
+       struct ieee802_1x_kay_ctx *ops;
+
+       if (!kay || !txsa) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->disable_transmit_sa) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy disable_transmit_sa operation not supported");
+               return -1;
+       }
+
+       txsa->enable_transmit = FALSE;
+
+       return ops->disable_transmit_sa(ops->ctx, txsa->sc->channel, txsa->an);
+}
+
+
+int secy_init_macsec(struct ieee802_1x_kay *kay)
+{
+       int ret;
+       struct ieee802_1x_kay_ctx *ops;
+       struct macsec_init_params params;
+
+       if (!kay) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->macsec_init) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy macsec_init operation not supported");
+               return -1;
+       }
+
+       params.use_es = FALSE;
+       params.use_scb = FALSE;
+       params.always_include_sci = TRUE;
+
+       ret = ops->macsec_init(ops->ctx, &params);
+
+       return ret;
+}
+
+
+int secy_deinit_macsec(struct ieee802_1x_kay *kay)
+{
+       struct ieee802_1x_kay_ctx *ops;
+
+       if (!kay) {
+               wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+               return -1;
+       }
+
+       ops = kay->ctx;
+       if (!ops || !ops->macsec_deinit) {
+               wpa_printf(MSG_ERROR,
+                          "KaY: secy macsec_deinit operation not supported");
+               return -1;
+       }
+
+       return ops->macsec_deinit(ops->ctx);
+}
diff --git a/src/pae/ieee802_1x_secy_ops.h b/src/pae/ieee802_1x_secy_ops.h
new file mode 100644 (file)
index 0000000..295b823
--- /dev/null
@@ -0,0 +1,62 @@
+ /*
+ * SecY Operations
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef IEEE802_1X_SECY_OPS_H
+#define IEEE802_1X_SECY_OPS_H
+
+#include "common/defs.h"
+#include "common/ieee802_1x_defs.h"
+
+struct ieee802_1x_kay_conf;
+struct receive_sa;
+struct transmit_sa;
+struct receive_sc;
+struct transmit_sc;
+
+int secy_init_macsec(struct ieee802_1x_kay *kay);
+int secy_deinit_macsec(struct ieee802_1x_kay *kay);
+
+/****** CP -> SecY ******/
+int secy_cp_control_validate_frames(struct ieee802_1x_kay *kay,
+                                   enum validate_frames vf);
+int secy_cp_control_protect_frames(struct ieee802_1x_kay *kay, Boolean flag);
+int secy_cp_control_replay(struct ieee802_1x_kay *kay, Boolean flag, u32 win);
+int secy_cp_control_current_cipher_suite(struct ieee802_1x_kay *kay,
+                                        const u8 *cs, size_t cs_len);
+int secy_cp_control_confidentiality_offset(struct ieee802_1x_kay *kay,
+                                          enum confidentiality_offset co);
+int secy_cp_control_enable_port(struct ieee802_1x_kay *kay, Boolean flag);
+
+/****** KaY -> SecY *******/
+int secy_get_receive_lowest_pn(struct ieee802_1x_kay *kay,
+                              struct receive_sa *rxsa);
+int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay,
+                             struct transmit_sa *txsa);
+int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay,
+                             struct transmit_sa *txsa);
+int secy_get_available_receive_sc(struct ieee802_1x_kay *kay, u32 *channel);
+int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc);
+int secy_delete_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc);
+int secy_create_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa);
+int secy_enable_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa);
+int secy_disable_receive_sa(struct ieee802_1x_kay *kay,
+                           struct receive_sa *rxsa);
+
+int secy_get_available_transmit_sc(struct ieee802_1x_kay *kay, u32 *channel);
+int secy_create_transmit_sc(struct ieee802_1x_kay *kay,
+                           struct transmit_sc *txsc);
+int secy_delete_transmit_sc(struct ieee802_1x_kay *kay,
+                           struct transmit_sc *txsc);
+int secy_create_transmit_sa(struct ieee802_1x_kay *kay,
+                           struct transmit_sa *txsa);
+int secy_enable_transmit_sa(struct ieee802_1x_kay *kay,
+                           struct transmit_sa *txsa);
+int secy_disable_transmit_sa(struct ieee802_1x_kay *kay,
+                            struct transmit_sa *txsa);
+
+#endif /* IEEE802_1X_SECY_OPS_H */
index b199be8..b5d063d 100644 (file)
@@ -1,7 +1,7 @@
 all: libradius.a
 
 clean:
-       rm -f *~ *.o *.d libradius.a
+       rm -f *~ *.o *.d *.gcno *.gcda *.gcov libradius.a
 
 install:
        @echo Nothing to be made.
index d1feec9..8d878a4 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * RADIUS message processing
- * Copyright (c) 2002-2009, 2011-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2011-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -231,9 +231,32 @@ static struct radius_attr_type radius_attrs[] =
        { RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, "Chargeable-User-Identity",
          RADIUS_ATTR_TEXT },
        { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 },
-       { RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 }
+       { RADIUS_ATTR_ERROR_CAUSE, "Error-Cause", RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_EAP_KEY_NAME, "EAP-Key-Name", RADIUS_ATTR_HEXDUMP },
+       { RADIUS_ATTR_OPERATOR_NAME, "Operator-Name", RADIUS_ATTR_TEXT },
+       { RADIUS_ATTR_LOCATION_INFO, "Location-Information",
+         RADIUS_ATTR_HEXDUMP },
+       { RADIUS_ATTR_LOCATION_DATA, "Location-Data", RADIUS_ATTR_HEXDUMP },
+       { RADIUS_ATTR_BASIC_LOCATION_POLICY_RULES,
+         "Basic-Location-Policy-Rules", RADIUS_ATTR_HEXDUMP },
+       { RADIUS_ATTR_EXTENDED_LOCATION_POLICY_RULES,
+         "Extended-Location-Policy-Rules", RADIUS_ATTR_HEXDUMP },
+       { RADIUS_ATTR_LOCATION_CAPABLE, "Location-Capable", RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_REQUESTED_LOCATION_INFO, "Requested-Location-Info",
+         RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_MOBILITY_DOMAIN_ID, "Mobility-Domain-Id",
+         RADIUS_ATTR_INT32 },
+       { RADIUS_ATTR_WLAN_HESSID, "WLAN-HESSID", RADIUS_ATTR_TEXT },
+       { RADIUS_ATTR_WLAN_PAIRWISE_CIPHER, "WLAN-Pairwise-Cipher",
+         RADIUS_ATTR_HEXDUMP },
+       { RADIUS_ATTR_WLAN_GROUP_CIPHER, "WLAN-Group-Cipher",
+         RADIUS_ATTR_HEXDUMP },
+       { RADIUS_ATTR_WLAN_AKM_SUITE, "WLAN-AKM-Suite",
+         RADIUS_ATTR_HEXDUMP },
+       { RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, "WLAN-Group-Mgmt-Pairwise-Cipher",
+         RADIUS_ATTR_HEXDUMP },
 };
-#define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0]))
+#define RADIUS_ATTRS ARRAY_SIZE(radius_attrs)
 
 
 static struct radius_attr_type *radius_get_attr_type(u8 type)
@@ -249,25 +272,17 @@ static struct radius_attr_type *radius_get_attr_type(u8 type)
 }
 
 
-static void print_char(char c)
-{
-       if (c >= 32 && c < 127)
-               printf("%c", c);
-       else
-               printf("<%02x>", c);
-}
-
-
 static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
 {
        struct radius_attr_type *attr;
-       int i, len;
+       int len;
        unsigned char *pos;
+       char buf[1000];
 
        attr = radius_get_attr_type(hdr->type);
 
-       printf("   Attribute %d (%s) length=%d\n",
-              hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
+       wpa_printf(MSG_INFO, "   Attribute %d (%s) length=%d",
+                  hdr->type, attr ? attr->name : "?Unknown?", hdr->length);
 
        if (attr == NULL || hdr->length < sizeof(struct radius_attr_hdr))
                return;
@@ -277,47 +292,50 @@ static void radius_msg_dump_attr(struct radius_attr_hdr *hdr)
 
        switch (attr->data_type) {
        case RADIUS_ATTR_TEXT:
-               printf("      Value: '");
-               for (i = 0; i < len; i++)
-                       print_char(pos[i]);
-               printf("'\n");
+               printf_encode(buf, sizeof(buf), pos, len);
+               wpa_printf(MSG_INFO, "      Value: '%s'", buf);
                break;
 
        case RADIUS_ATTR_IP:
                if (len == 4) {
                        struct in_addr addr;
                        os_memcpy(&addr, pos, 4);
-                       printf("      Value: %s\n", inet_ntoa(addr));
-               } else
-                       printf("      Invalid IP address length %d\n", len);
+                       wpa_printf(MSG_INFO, "      Value: %s",
+                                  inet_ntoa(addr));
+               } else {
+                       wpa_printf(MSG_INFO, "      Invalid IP address length %d",
+                                  len);
+               }
                break;
 
 #ifdef CONFIG_IPV6
        case RADIUS_ATTR_IPV6:
                if (len == 16) {
-                       char buf[128];
                        const char *atxt;
                        struct in6_addr *addr = (struct in6_addr *) pos;
                        atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf));
-                       printf("      Value: %s\n", atxt ? atxt : "?");
-               } else
-                       printf("      Invalid IPv6 address length %d\n", len);
+                       wpa_printf(MSG_INFO, "      Value: %s",
+                                  atxt ? atxt : "?");
+               } else {
+                       wpa_printf(MSG_INFO, "      Invalid IPv6 address length %d",
+                                  len);
+               }
                break;
 #endif /* CONFIG_IPV6 */
 
        case RADIUS_ATTR_HEXDUMP:
        case RADIUS_ATTR_UNDIST:
-               printf("      Value:");
-               for (i = 0; i < len; i++)
-                       printf(" %02x", pos[i]);
-               printf("\n");
+               wpa_snprintf_hex(buf, sizeof(buf), pos, len);
+               wpa_printf(MSG_INFO, "      Value: %s", buf);
                break;
 
        case RADIUS_ATTR_INT32:
                if (len == 4)
-                       printf("      Value: %u\n", WPA_GET_BE32(pos));
+                       wpa_printf(MSG_INFO, "      Value: %u",
+                                  WPA_GET_BE32(pos));
                else
-                       printf("      Invalid INT32 length %d\n", len);
+                       wpa_printf(MSG_INFO, "      Invalid INT32 length %d",
+                                  len);
                break;
 
        default:
@@ -330,9 +348,9 @@ void radius_msg_dump(struct radius_msg *msg)
 {
        size_t i;
 
-       printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n",
-              msg->hdr->code, radius_code_string(msg->hdr->code),
-              msg->hdr->identifier, be_to_host16(msg->hdr->length));
+       wpa_printf(MSG_INFO, "RADIUS message: code=%d (%s) identifier=%d length=%d",
+                  msg->hdr->code, radius_code_string(msg->hdr->code),
+                  msg->hdr->identifier, be_to_host16(msg->hdr->length));
 
        for (i = 0; i < msg->attr_used; i++) {
                struct radius_attr_hdr *attr = radius_get_attr_hdr(msg, i);
@@ -384,7 +402,7 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret,
        attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
                                   auth, MD5_MAC_LEN);
        if (attr == NULL) {
-               printf("WARNING: Could not add Message-Authenticator\n");
+               wpa_printf(MSG_ERROR, "WARNING: Could not add Message-Authenticator");
                return -1;
        }
        msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
@@ -473,6 +491,27 @@ void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
 }
 
 
+void radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret,
+                                size_t secret_len, const u8 *req_authenticator)
+{
+       const u8 *addr[2];
+       size_t len[2];
+
+       msg->hdr->length = host_to_be16(wpabuf_len(msg->buf));
+       os_memcpy(msg->hdr->authenticator, req_authenticator, MD5_MAC_LEN);
+       addr[0] = wpabuf_head(msg->buf);
+       len[0] = wpabuf_len(msg->buf);
+       addr[1] = secret;
+       len[1] = secret_len;
+       md5_vector(2, addr, len, msg->hdr->authenticator);
+
+       if (wpabuf_len(msg->buf) > 0xffff) {
+               wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)",
+                          (unsigned long) wpabuf_len(msg->buf));
+       }
+}
+
+
 int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
                               size_t secret_len)
 {
@@ -491,7 +530,7 @@ int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
        addr[3] = secret;
        len[3] = secret_len;
        md5_vector(4, addr, len, hash);
-       return os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0;
+       return os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0;
 }
 
 
@@ -518,7 +557,7 @@ int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
        addr[3] = secret;
        len[3] = secret_len;
        md5_vector(4, addr, len, hash);
-       if (os_memcmp(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0)
+       if (os_memcmp_const(msg->hdr->authenticator, hash, MD5_MAC_LEN) != 0)
                return 1;
 
        for (i = 0; i < msg->attr_used; i++) {
@@ -551,7 +590,7 @@ int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
        os_memcpy(msg->hdr->authenticator, orig_authenticator,
                  sizeof(orig_authenticator));
 
-       return os_memcmp(orig, auth, MD5_MAC_LEN) != 0;
+       return os_memcmp_const(orig, auth, MD5_MAC_LEN) != 0;
 }
 
 
@@ -585,7 +624,7 @@ struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type,
        struct radius_attr_hdr *attr;
 
        if (data_len > RADIUS_MAX_ATTR_LEN) {
-               printf("radius_msg_add_attr: too long attribute (%lu bytes)\n",
+               wpa_printf(MSG_ERROR, "radius_msg_add_attr: too long attribute (%lu bytes)",
                       (unsigned long) data_len);
                return NULL;
        }
@@ -756,8 +795,7 @@ int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
                tmp = radius_get_attr_hdr(msg, i);
                if (tmp->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) {
                        if (attr != NULL) {
-                               printf("Multiple Message-Authenticator "
-                                      "attributes in RADIUS message\n");
+                               wpa_printf(MSG_INFO, "Multiple Message-Authenticator attributes in RADIUS message");
                                return 1;
                        }
                        attr = tmp;
@@ -765,7 +803,7 @@ int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
        }
 
        if (attr == NULL) {
-               printf("No Message-Authenticator attribute found\n");
+               wpa_printf(MSG_INFO, "No Message-Authenticator attribute found");
                return 1;
        }
 
@@ -785,8 +823,8 @@ int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret,
                          sizeof(orig_authenticator));
        }
 
-       if (os_memcmp(orig, auth, MD5_MAC_LEN) != 0) {
-               printf("Invalid Message-Authenticator!\n");
+       if (os_memcmp_const(orig, auth, MD5_MAC_LEN) != 0) {
+               wpa_printf(MSG_INFO, "Invalid Message-Authenticator!");
                return 1;
        }
 
@@ -802,7 +840,7 @@ int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
        u8 hash[MD5_MAC_LEN];
 
        if (sent_msg == NULL) {
-               printf("No matching Access-Request message found\n");
+               wpa_printf(MSG_INFO, "No matching Access-Request message found");
                return 1;
        }
 
@@ -822,8 +860,8 @@ int radius_msg_verify(struct radius_msg *msg, const u8 *secret,
        addr[3] = secret;
        len[3] = secret_len;
        md5_vector(4, addr, len, hash);
-       if (os_memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
-               printf("Response Authenticator invalid!\n");
+       if (os_memcmp_const(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) {
+               wpa_printf(MSG_INFO, "Response Authenticator invalid!");
                return 1;
        }
 
@@ -918,7 +956,6 @@ static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor,
                        vhdr = (struct radius_attr_vendor *) pos;
                        if (vhdr->vendor_length > left ||
                            vhdr->vendor_length < sizeof(*vhdr)) {
-                               left = 0;
                                break;
                        }
                        if (vhdr->vendor_type != subtype) {
@@ -956,13 +993,17 @@ static u8 * decrypt_ms_key(const u8 *key, size_t len,
 
        /* key: 16-bit salt followed by encrypted key info */
 
-       if (len < 2 + 16)
+       if (len < 2 + 16) {
+               wpa_printf(MSG_DEBUG, "RADIUS: %s: Len is too small: %d",
+                          __func__, (int) len);
                return NULL;
+       }
 
        pos = key + 2;
        left = len - 2;
        if (left % 16) {
-               printf("Invalid ms key len %lu\n", (unsigned long) left);
+               wpa_printf(MSG_INFO, "RADIUS: Invalid ms key len %lu",
+                          (unsigned long) left);
                return NULL;
        }
 
@@ -996,7 +1037,7 @@ static u8 * decrypt_ms_key(const u8 *key, size_t len,
        }
 
        if (plain[0] == 0 || plain[0] > plen - 1) {
-               printf("Failed to decrypt MPPE key\n");
+               wpa_printf(MSG_INFO, "RADIUS: Failed to decrypt MPPE key");
                os_free(plain);
                return NULL;
        }
@@ -1085,6 +1126,10 @@ radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
                                            sent_msg->hdr->authenticator,
                                            secret, secret_len,
                                            &keys->send_len);
+               if (!keys->send) {
+                       wpa_printf(MSG_DEBUG,
+                                  "RADIUS: Failed to decrypt send key");
+               }
                os_free(key);
        }
 
@@ -1096,6 +1141,10 @@ radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg,
                                            sent_msg->hdr->authenticator,
                                            secret, secret_len,
                                            &keys->recv_len);
+               if (!keys->recv) {
+                       wpa_printf(MSG_DEBUG,
+                                  "RADIUS: Failed to decrypt recv key");
+               }
                os_free(key);
        }
 
@@ -1204,30 +1253,55 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg,
 }
 
 
-/* Add User-Password attribute to a RADIUS message and encrypt it as specified
- * in RFC 2865, Chap. 5.2 */
-struct radius_attr_hdr *
-radius_msg_add_attr_user_password(struct radius_msg *msg,
-                                 const u8 *data, size_t data_len,
-                                 const u8 *secret, size_t secret_len)
+int radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data,
+                      size_t len)
 {
-       u8 buf[128];
-       size_t padlen, i, buf_len, pos;
+       struct radius_attr_hdr *attr;
+       u8 *buf, *pos;
+       size_t alen;
+
+       alen = 4 + 2 + len;
+       buf = os_malloc(alen);
+       if (buf == NULL)
+               return 0;
+       pos = buf;
+       WPA_PUT_BE32(pos, RADIUS_VENDOR_ID_WFA);
+       pos += 4;
+       *pos++ = subtype;
+       *pos++ = 2 + len;
+       os_memcpy(pos, data, len);
+       attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC,
+                                  buf, alen);
+       os_free(buf);
+       if (attr == NULL)
+               return 0;
+
+       return 1;
+}
+
+
+int radius_user_password_hide(struct radius_msg *msg,
+                             const u8 *data, size_t data_len,
+                             const u8 *secret, size_t secret_len,
+                             u8 *buf, size_t buf_len)
+{
+       size_t padlen, i, pos;
        const u8 *addr[2];
        size_t len[2];
        u8 hash[16];
 
-       if (data_len > 128)
-               return NULL;
+       if (data_len + 16 > buf_len)
+               return -1;
 
        os_memcpy(buf, data, data_len);
-       buf_len = data_len;
 
        padlen = data_len % 16;
-       if (padlen && data_len < sizeof(buf)) {
+       if (padlen && data_len < buf_len) {
                padlen = 16 - padlen;
                os_memset(buf + data_len, 0, padlen);
-               buf_len += padlen;
+               buf_len = data_len + padlen;
+       } else {
+               buf_len = data_len;
        }
 
        addr[0] = secret;
@@ -1253,8 +1327,27 @@ radius_msg_add_attr_user_password(struct radius_msg *msg,
                pos += 16;
        }
 
+       return buf_len;
+}
+
+
+/* Add User-Password attribute to a RADIUS message and encrypt it as specified
+ * in RFC 2865, Chap. 5.2 */
+struct radius_attr_hdr *
+radius_msg_add_attr_user_password(struct radius_msg *msg,
+                                 const u8 *data, size_t data_len,
+                                 const u8 *secret, size_t secret_len)
+{
+       u8 buf[128];
+       int res;
+
+       res = radius_user_password_hide(msg, data, data_len,
+                                       secret, secret_len, buf, sizeof(buf));
+       if (res < 0)
+               return NULL;
+
        return radius_msg_add_attr(msg, RADIUS_ATTR_USER_PASSWORD,
-                                  buf, buf_len);
+                                  buf, res);
 }
 
 
index 2031054..5977339 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * RADIUS message processing
- * Copyright (c) 2002-2009, 2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2009, 2012, 2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -90,7 +90,21 @@ enum { RADIUS_ATTR_USER_NAME = 1,
        RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85,
        RADIUS_ATTR_CHARGEABLE_USER_IDENTITY = 89,
        RADIUS_ATTR_NAS_IPV6_ADDRESS = 95,
-       RADIUS_ATTR_ERROR_CAUSE = 101
+       RADIUS_ATTR_ERROR_CAUSE = 101,
+       RADIUS_ATTR_EAP_KEY_NAME = 102,
+       RADIUS_ATTR_OPERATOR_NAME = 126,
+       RADIUS_ATTR_LOCATION_INFO = 127,
+       RADIUS_ATTR_LOCATION_DATA = 128,
+       RADIUS_ATTR_BASIC_LOCATION_POLICY_RULES = 129,
+       RADIUS_ATTR_EXTENDED_LOCATION_POLICY_RULES = 130,
+       RADIUS_ATTR_LOCATION_CAPABLE = 131,
+       RADIUS_ATTR_REQUESTED_LOCATION_INFO = 132,
+       RADIUS_ATTR_MOBILITY_DOMAIN_ID = 177,
+       RADIUS_ATTR_WLAN_HESSID = 181,
+       RADIUS_ATTR_WLAN_PAIRWISE_CIPHER = 186,
+       RADIUS_ATTR_WLAN_GROUP_CIPHER = 187,
+       RADIUS_ATTR_WLAN_AKM_SUITE = 188,
+       RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER = 189,
 };
 
 
@@ -163,6 +177,18 @@ enum { RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY = 16,
        RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY = 17
 };
 
+
+/* Hotspot 2.0 - WFA Vendor-specific RADIUS Attributes */
+#define RADIUS_VENDOR_ID_WFA 40808
+
+enum {
+       RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION = 1,
+       RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION = 2,
+       RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION = 3,
+       RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ = 4,
+       RADIUS_VENDOR_ATTR_WFA_HS20_SESSION_INFO_URL = 5,
+};
+
 #ifdef _MSC_VER
 #pragma pack(pop)
 #endif /* _MSC_VER */
@@ -204,6 +230,9 @@ int radius_msg_finish_das_resp(struct radius_msg *msg, const u8 *secret,
                               const struct radius_hdr *req_hdr);
 void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret,
                            size_t secret_len);
+void radius_msg_finish_acct_resp(struct radius_msg *msg, const u8 *secret,
+                                size_t secret_len,
+                                const u8 *req_authenticator);
 int radius_msg_verify_acct_req(struct radius_msg *msg, const u8 *secret,
                               size_t secret_len);
 int radius_msg_verify_das_req(struct radius_msg *msg, const u8 *secret,
@@ -234,6 +263,12 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg,
                             const u8 *secret, size_t secret_len,
                             const u8 *send_key, size_t send_key_len,
                             const u8 *recv_key, size_t recv_key_len);
+int radius_msg_add_wfa(struct radius_msg *msg, u8 subtype, const u8 *data,
+                      size_t len);
+int radius_user_password_hide(struct radius_msg *msg,
+                             const u8 *data, size_t data_len,
+                             const u8 *secret, size_t secret_len,
+                             u8 *buf, size_t buf_len);
 struct radius_attr_hdr *
 radius_msg_add_attr_user_password(struct radius_msg *msg,
                                  const u8 *data, size_t data_len,
index 425ad93..693f61e 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * RADIUS client
- * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -122,7 +122,7 @@ struct radius_msg_list {
        /**
         * last_attempt - Time of the last transmission attempt
         */
-       struct os_time last_attempt;
+       struct os_reltime last_attempt;
 
        /**
         * shared_secret - Shared secret with the target RADIUS server
@@ -236,6 +236,8 @@ radius_change_server(struct radius_client_data *radius,
                     int sock, int sock6, int auth);
 static int radius_client_init_acct(struct radius_client_data *radius);
 static int radius_client_init_auth(struct radius_client_data *radius);
+static void radius_client_auth_failover(struct radius_client_data *radius);
+static void radius_client_acct_failover(struct radius_client_data *radius);
 
 
 static void radius_client_msg_free(struct radius_msg_list *req)
@@ -295,26 +297,34 @@ int radius_client_register(struct radius_client_data *radius,
 }
 
 
-static void radius_client_handle_send_error(struct radius_client_data *radius,
-                                           int s, RadiusType msg_type)
+/*
+ * Returns >0 if message queue was flushed (i.e., the message that triggered
+ * the error is not available anymore)
+ */
+static int radius_client_handle_send_error(struct radius_client_data *radius,
+                                          int s, RadiusType msg_type)
 {
 #ifndef CONFIG_NATIVE_WINDOWS
        int _errno = errno;
-       perror("send[RADIUS]");
+       wpa_printf(MSG_INFO, "send[RADIUS,s=%d]: %s", s, strerror(errno));
        if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL ||
-           _errno == EBADF) {
+           _errno == EBADF || _errno == ENETUNREACH) {
                hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
                               HOSTAPD_LEVEL_INFO,
                               "Send failed - maybe interface status changed -"
                               " try to connect again");
-               eloop_unregister_read_sock(s);
-               close(s);
-               if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM)
+               if (msg_type == RADIUS_ACCT ||
+                   msg_type == RADIUS_ACCT_INTERIM) {
                        radius_client_init_acct(radius);
-               else
+                       return 0;
+               } else {
                        radius_client_init_auth(radius);
+                       return 1;
+               }
        }
 #endif /* CONFIG_NATIVE_WINDOWS */
+
+       return 0;
 }
 
 
@@ -325,9 +335,18 @@ static int radius_client_retransmit(struct radius_client_data *radius,
        struct hostapd_radius_servers *conf = radius->conf;
        int s;
        struct wpabuf *buf;
+       size_t prev_num_msgs;
 
        if (entry->msg_type == RADIUS_ACCT ||
            entry->msg_type == RADIUS_ACCT_INTERIM) {
+               if (radius->acct_sock < 0)
+                       radius_client_init_acct(radius);
+               if (radius->acct_sock < 0 && conf->num_acct_servers > 1) {
+                       prev_num_msgs = radius->num_msgs;
+                       radius_client_acct_failover(radius);
+                       if (prev_num_msgs != radius->num_msgs)
+                               return 0;
+               }
                s = radius->acct_sock;
                if (entry->attempts == 0)
                        conf->acct_server->requests++;
@@ -336,6 +355,14 @@ static int radius_client_retransmit(struct radius_client_data *radius,
                        conf->acct_server->retransmissions++;
                }
        } else {
+               if (radius->auth_sock < 0)
+                       radius_client_init_auth(radius);
+               if (radius->auth_sock < 0 && conf->num_auth_servers > 1) {
+                       prev_num_msgs = radius->num_msgs;
+                       radius_client_auth_failover(radius);
+                       if (prev_num_msgs != radius->num_msgs)
+                               return 0;
+               }
                s = radius->auth_sock;
                if (entry->attempts == 0)
                        conf->auth_server->requests++;
@@ -344,6 +371,11 @@ static int radius_client_retransmit(struct radius_client_data *radius,
                        conf->auth_server->retransmissions++;
                }
        }
+       if (s < 0) {
+               wpa_printf(MSG_INFO,
+                          "RADIUS: No valid socket for retransmission");
+               return 1;
+       }
 
        /* retransmit; remove entry if too many attempts */
        entry->attempts++;
@@ -351,18 +383,20 @@ static int radius_client_retransmit(struct radius_client_data *radius,
                       HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)",
                       radius_msg_get_hdr(entry->msg)->identifier);
 
-       os_get_time(&entry->last_attempt);
+       os_get_reltime(&entry->last_attempt);
        buf = radius_msg_get_buf(entry->msg);
-       if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0)
-               radius_client_handle_send_error(radius, s, entry->msg_type);
+       if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
+               if (radius_client_handle_send_error(radius, s, entry->msg_type)
+                   > 0)
+                       return 0;
+       }
 
        entry->next_try = now + entry->next_wait;
        entry->next_wait *= 2;
        if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)
                entry->next_wait = RADIUS_CLIENT_MAX_WAIT;
        if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) {
-               printf("Removing un-ACKed RADIUS message due to too many "
-                      "failed retransmit attempts\n");
+               wpa_printf(MSG_INFO, "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts");
                return 1;
        }
 
@@ -374,21 +408,23 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
 {
        struct radius_client_data *radius = eloop_ctx;
        struct hostapd_radius_servers *conf = radius->conf;
-       struct os_time now;
+       struct os_reltime now;
        os_time_t first;
        struct radius_msg_list *entry, *prev, *tmp;
        int auth_failover = 0, acct_failover = 0;
-       char abuf[50];
+       size_t prev_num_msgs;
+       int s;
 
        entry = radius->msgs;
        if (!entry)
                return;
 
-       os_get_time(&now);
+       os_get_reltime(&now);
        first = 0;
 
        prev = NULL;
        while (entry) {
+               prev_num_msgs = radius->num_msgs;
                if (now.sec >= entry->next_try &&
                    radius_client_retransmit(radius, entry, now.sec)) {
                        if (prev)
@@ -403,7 +439,18 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
                        continue;
                }
 
-               if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER) {
+               if (prev_num_msgs != radius->num_msgs) {
+                       wpa_printf(MSG_DEBUG,
+                                  "RADIUS: Message removed from queue - restart from beginning");
+                       entry = radius->msgs;
+                       prev = NULL;
+                       continue;
+               }
+
+               s = entry->msg_type == RADIUS_AUTH ? radius->auth_sock :
+                       radius->acct_sock;
+               if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER ||
+                   (s < 0 && entry->attempts > 0)) {
                        if (entry->msg_type == RADIUS_ACCT ||
                            entry->msg_type == RADIUS_ACCT_INTERIM)
                                acct_failover++;
@@ -429,60 +476,76 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
                               (long int) (first - now.sec));
        }
 
-       if (auth_failover && conf->num_auth_servers > 1) {
-               struct hostapd_radius_server *next, *old;
-               old = conf->auth_server;
-               hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
-                              HOSTAPD_LEVEL_NOTICE,
-                              "No response from Authentication server "
-                              "%s:%d - failover",
-                              hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
-                              old->port);
-
-               for (entry = radius->msgs; entry; entry = entry->next) {
-                       if (entry->msg_type == RADIUS_AUTH)
-                               old->timeouts++;
-               }
+       if (auth_failover && conf->num_auth_servers > 1)
+               radius_client_auth_failover(radius);
+
+       if (acct_failover && conf->num_acct_servers > 1)
+               radius_client_acct_failover(radius);
+}
 
-               next = old + 1;
-               if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))
-                       next = conf->auth_servers;
-               conf->auth_server = next;
-               radius_change_server(radius, next, old,
-                                    radius->auth_serv_sock,
-                                    radius->auth_serv_sock6, 1);
+
+static void radius_client_auth_failover(struct radius_client_data *radius)
+{
+       struct hostapd_radius_servers *conf = radius->conf;
+       struct hostapd_radius_server *next, *old;
+       struct radius_msg_list *entry;
+       char abuf[50];
+
+       old = conf->auth_server;
+       hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+                      HOSTAPD_LEVEL_NOTICE,
+                      "No response from Authentication server %s:%d - failover",
+                      hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
+                      old->port);
+
+       for (entry = radius->msgs; entry; entry = entry->next) {
+               if (entry->msg_type == RADIUS_AUTH)
+                       old->timeouts++;
        }
 
-       if (acct_failover && conf->num_acct_servers > 1) {
-               struct hostapd_radius_server *next, *old;
-               old = conf->acct_server;
-               hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
-                              HOSTAPD_LEVEL_NOTICE,
-                              "No response from Accounting server "
-                              "%s:%d - failover",
-                              hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
-                              old->port);
+       next = old + 1;
+       if (next > &(conf->auth_servers[conf->num_auth_servers - 1]))
+               next = conf->auth_servers;
+       conf->auth_server = next;
+       radius_change_server(radius, next, old,
+                            radius->auth_serv_sock,
+                            radius->auth_serv_sock6, 1);
+}
 
-               for (entry = radius->msgs; entry; entry = entry->next) {
-                       if (entry->msg_type == RADIUS_ACCT ||
-                           entry->msg_type == RADIUS_ACCT_INTERIM)
-                               old->timeouts++;
-               }
 
-               next = old + 1;
-               if (next > &conf->acct_servers[conf->num_acct_servers - 1])
-                       next = conf->acct_servers;
-               conf->acct_server = next;
-               radius_change_server(radius, next, old,
-                                    radius->acct_serv_sock,
-                                    radius->acct_serv_sock6, 0);
+static void radius_client_acct_failover(struct radius_client_data *radius)
+{
+       struct hostapd_radius_servers *conf = radius->conf;
+       struct hostapd_radius_server *next, *old;
+       struct radius_msg_list *entry;
+       char abuf[50];
+
+       old = conf->acct_server;
+       hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
+                      HOSTAPD_LEVEL_NOTICE,
+                      "No response from Accounting server %s:%d - failover",
+                      hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)),
+                      old->port);
+
+       for (entry = radius->msgs; entry; entry = entry->next) {
+               if (entry->msg_type == RADIUS_ACCT ||
+                   entry->msg_type == RADIUS_ACCT_INTERIM)
+                       old->timeouts++;
        }
+
+       next = old + 1;
+       if (next > &conf->acct_servers[conf->num_acct_servers - 1])
+               next = conf->acct_servers;
+       conf->acct_server = next;
+       radius_change_server(radius, next, old,
+                            radius->acct_serv_sock,
+                            radius->acct_serv_sock6, 0);
 }
 
 
 static void radius_client_update_timeout(struct radius_client_data *radius)
 {
-       struct os_time now;
+       struct os_reltime now;
        os_time_t first;
        struct radius_msg_list *entry;
 
@@ -498,7 +561,7 @@ static void radius_client_update_timeout(struct radius_client_data *radius)
                        first = entry->next_try;
        }
 
-       os_get_time(&now);
+       os_get_reltime(&now);
        if (first < now.sec)
                first = now.sec;
        eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius,
@@ -526,7 +589,7 @@ static void radius_client_list_add(struct radius_client_data *radius,
 
        entry = os_zalloc(sizeof(*entry));
        if (entry == NULL) {
-               printf("Failed to add RADIUS packet into retransmit list\n");
+               wpa_printf(MSG_INFO, "RADIUS: Failed to add packet into retransmit list");
                radius_msg_free(msg);
                return;
        }
@@ -537,7 +600,7 @@ static void radius_client_list_add(struct radius_client_data *radius,
        entry->msg_type = msg_type;
        entry->shared_secret = shared_secret;
        entry->shared_secret_len = shared_secret_len;
-       os_get_time(&entry->last_attempt);
+       os_get_reltime(&entry->last_attempt);
        entry->first_try = entry->last_attempt.sec;
        entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
        entry->attempts = 1;
@@ -547,8 +610,7 @@ static void radius_client_list_add(struct radius_client_data *radius,
        radius_client_update_timeout(radius);
 
        if (radius->num_msgs >= RADIUS_CLIENT_MAX_ENTRIES) {
-               printf("Removing the oldest un-ACKed RADIUS packet due to "
-                      "retransmit list limits.\n");
+               wpa_printf(MSG_INFO, "RADIUS: Removing the oldest un-ACKed packet due to retransmit list limits");
                prev = NULL;
                while (entry->next) {
                        prev = entry;
@@ -635,7 +697,11 @@ int radius_client_send(struct radius_client_data *radius,
        }
 
        if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) {
-               if (conf->acct_server == NULL) {
+               if (conf->acct_server && radius->acct_sock < 0)
+                       radius_client_init_acct(radius);
+
+               if (conf->acct_server == NULL || radius->acct_sock < 0 ||
+                   conf->acct_server->shared_secret == NULL) {
                        hostapd_logger(radius->ctx, NULL,
                                       HOSTAPD_MODULE_RADIUS,
                                       HOSTAPD_LEVEL_INFO,
@@ -649,7 +715,11 @@ int radius_client_send(struct radius_client_data *radius,
                s = radius->acct_sock;
                conf->acct_server->requests++;
        } else {
-               if (conf->auth_server == NULL) {
+               if (conf->auth_server && radius->auth_sock < 0)
+                       radius_client_init_auth(radius);
+
+               if (conf->auth_server == NULL || radius->auth_sock < 0 ||
+                   conf->auth_server->shared_secret == NULL) {
                        hostapd_logger(radius->ctx, NULL,
                                       HOSTAPD_MODULE_RADIUS,
                                       HOSTAPD_LEVEL_INFO,
@@ -694,7 +764,7 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
        struct radius_rx_handler *handlers;
        size_t num_handlers, i;
        struct radius_msg_list *req, *prev_req;
-       struct os_time now;
+       struct os_reltime now;
        struct hostapd_radius_server *rconf;
        int invalid_authenticator = 0;
 
@@ -710,21 +780,20 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
 
        len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT);
        if (len < 0) {
-               perror("recv[RADIUS]");
+               wpa_printf(MSG_INFO, "recv[RADIUS]: %s", strerror(errno));
                return;
        }
        hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
                       HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS "
                       "server", len);
        if (len == sizeof(buf)) {
-               printf("Possibly too long UDP frame for our buffer - "
-                      "dropping it\n");
+               wpa_printf(MSG_INFO, "RADIUS: Possibly too long UDP frame for our buffer - dropping it");
                return;
        }
 
        msg = radius_msg_parse(buf, len);
        if (msg == NULL) {
-               printf("Parsing incoming RADIUS frame failed\n");
+               wpa_printf(MSG_INFO, "RADIUS: Parsing incoming frame failed");
                rconf->malformed_responses++;
                return;
        }
@@ -775,7 +844,7 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx)
                goto fail;
        }
 
-       os_get_time(&now);
+       os_get_reltime(&now);
        roundtrip = (now.sec - req->last_attempt.sec) * 100 +
                (now.usec - req->last_attempt.usec) / 10000;
        hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS,
@@ -954,9 +1023,10 @@ radius_change_server(struct radius_client_data *radius,
                       hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)),
                       nserv->port);
 
-       if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len ||
-           os_memcmp(nserv->shared_secret, oserv->shared_secret,
-                     nserv->shared_secret_len) != 0) {
+       if (oserv && oserv != nserv &&
+           (nserv->shared_secret_len != oserv->shared_secret_len ||
+            os_memcmp(nserv->shared_secret, oserv->shared_secret,
+                      nserv->shared_secret_len) != 0)) {
                /* Pending RADIUS packets used different shared secret, so
                 * they need to be modified. Update accounting message
                 * authenticators here. Authentication messages are removed
@@ -974,7 +1044,8 @@ radius_change_server(struct radius_client_data *radius,
        }
 
        /* Reset retry counters for the new server */
-       for (entry = radius->msgs; entry; entry = entry->next) {
+       for (entry = radius->msgs; oserv && oserv != nserv && entry;
+            entry = entry->next) {
                if ((auth && entry->msg_type != RADIUS_AUTH) ||
                    (!auth && entry->msg_type != RADIUS_ACCT))
                        continue;
@@ -1015,6 +1086,13 @@ radius_change_server(struct radius_client_data *radius,
                return -1;
        }
 
+       if (sel_sock < 0) {
+               wpa_printf(MSG_INFO,
+                          "RADIUS: No server socket available (af=%d sock=%d sock6=%d auth=%d",
+                          nserv->addr.af, sock, sock6, auth);
+               return -1;
+       }
+
        if (conf->force_client_addr) {
                switch (conf->client_addr.af) {
                case AF_INET:
@@ -1041,13 +1119,14 @@ radius_change_server(struct radius_client_data *radius,
                }
 
                if (bind(sel_sock, cl_addr, claddrlen) < 0) {
-                       perror("bind[radius]");
+                       wpa_printf(MSG_INFO, "bind[radius]: %s",
+                                  strerror(errno));
                        return -1;
                }
        }
 
        if (connect(sel_sock, addr, addrlen) < 0) {
-               perror("connect[radius]");
+               wpa_printf(MSG_INFO, "connect[radius]: %s", strerror(errno));
                return -1;
        }
 
@@ -1055,19 +1134,23 @@ radius_change_server(struct radius_client_data *radius,
        switch (nserv->addr.af) {
        case AF_INET:
                claddrlen = sizeof(claddr);
-               getsockname(sel_sock, (struct sockaddr *) &claddr, &claddrlen);
-               wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
-                          inet_ntoa(claddr.sin_addr), ntohs(claddr.sin_port));
+               if (getsockname(sel_sock, (struct sockaddr *) &claddr,
+                               &claddrlen) == 0) {
+                       wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
+                                  inet_ntoa(claddr.sin_addr),
+                                  ntohs(claddr.sin_port));
+               }
                break;
 #ifdef CONFIG_IPV6
        case AF_INET6: {
                claddrlen = sizeof(claddr6);
-               getsockname(sel_sock, (struct sockaddr *) &claddr6,
-                           &claddrlen);
-               wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
-                          inet_ntop(AF_INET6, &claddr6.sin6_addr,
-                                    abuf, sizeof(abuf)),
-                          ntohs(claddr6.sin6_port));
+               if (getsockname(sel_sock, (struct sockaddr *) &claddr6,
+                               &claddrlen) == 0) {
+                       wpa_printf(MSG_DEBUG, "RADIUS local address: %s:%u",
+                                  inet_ntop(AF_INET6, &claddr6.sin6_addr,
+                                            abuf, sizeof(abuf)),
+                                  ntohs(claddr6.sin6_port));
+               }
                break;
        }
 #endif /* CONFIG_IPV6 */
@@ -1093,18 +1176,28 @@ static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx)
            conf->auth_server != conf->auth_servers) {
                oserv = conf->auth_server;
                conf->auth_server = conf->auth_servers;
-               radius_change_server(radius, conf->auth_server, oserv,
-                                    radius->auth_serv_sock,
-                                    radius->auth_serv_sock6, 1);
+               if (radius_change_server(radius, conf->auth_server, oserv,
+                                        radius->auth_serv_sock,
+                                        radius->auth_serv_sock6, 1) < 0) {
+                       conf->auth_server = oserv;
+                       radius_change_server(radius, oserv, conf->auth_server,
+                                            radius->auth_serv_sock,
+                                            radius->auth_serv_sock6, 1);
+               }
        }
 
        if (radius->acct_sock >= 0 && conf->acct_servers &&
            conf->acct_server != conf->acct_servers) {
                oserv = conf->acct_server;
                conf->acct_server = conf->acct_servers;
-               radius_change_server(radius, conf->acct_server, oserv,
-                                    radius->acct_serv_sock,
-                                    radius->acct_serv_sock6, 0);
+               if (radius_change_server(radius, conf->acct_server, oserv,
+                                        radius->acct_serv_sock,
+                                        radius->acct_serv_sock6, 0) < 0) {
+                       conf->acct_server = oserv;
+                       radius_change_server(radius, oserv, conf->acct_server,
+                                            radius->acct_serv_sock,
+                                            radius->acct_serv_sock6, 0);
+               }
        }
 
        if (conf->retry_primary_interval)
@@ -1123,21 +1216,62 @@ static int radius_client_disable_pmtu_discovery(int s)
        r = setsockopt(s, IPPROTO_IP, IP_MTU_DISCOVER, &action,
                       sizeof(action));
        if (r == -1)
-               wpa_printf(MSG_ERROR, "Failed to set IP_MTU_DISCOVER: "
-                          "%s", strerror(errno));
+               wpa_printf(MSG_ERROR, "RADIUS: Failed to set IP_MTU_DISCOVER: %s",
+                          strerror(errno));
 #endif
        return r;
 }
 
 
+static void radius_close_auth_sockets(struct radius_client_data *radius)
+{
+       radius->auth_sock = -1;
+
+       if (radius->auth_serv_sock >= 0) {
+               eloop_unregister_read_sock(radius->auth_serv_sock);
+               close(radius->auth_serv_sock);
+               radius->auth_serv_sock = -1;
+       }
+#ifdef CONFIG_IPV6
+       if (radius->auth_serv_sock6 >= 0) {
+               eloop_unregister_read_sock(radius->auth_serv_sock6);
+               close(radius->auth_serv_sock6);
+               radius->auth_serv_sock6 = -1;
+       }
+#endif /* CONFIG_IPV6 */
+}
+
+
+static void radius_close_acct_sockets(struct radius_client_data *radius)
+{
+       radius->acct_sock = -1;
+
+       if (radius->acct_serv_sock >= 0) {
+               eloop_unregister_read_sock(radius->acct_serv_sock);
+               close(radius->acct_serv_sock);
+               radius->acct_serv_sock = -1;
+       }
+#ifdef CONFIG_IPV6
+       if (radius->acct_serv_sock6 >= 0) {
+               eloop_unregister_read_sock(radius->acct_serv_sock6);
+               close(radius->acct_serv_sock6);
+               radius->acct_serv_sock6 = -1;
+       }
+#endif /* CONFIG_IPV6 */
+}
+
+
 static int radius_client_init_auth(struct radius_client_data *radius)
 {
        struct hostapd_radius_servers *conf = radius->conf;
        int ok = 0;
 
+       radius_close_auth_sockets(radius);
+
        radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
        if (radius->auth_serv_sock < 0)
-               perror("socket[PF_INET,SOCK_DGRAM]");
+               wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s",
+                          strerror(errno));
        else {
                radius_client_disable_pmtu_discovery(radius->auth_serv_sock);
                ok++;
@@ -1146,7 +1280,8 @@ static int radius_client_init_auth(struct radius_client_data *radius)
 #ifdef CONFIG_IPV6
        radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
        if (radius->auth_serv_sock6 < 0)
-               perror("socket[PF_INET6,SOCK_DGRAM]");
+               wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s",
+                          strerror(errno));
        else
                ok++;
 #endif /* CONFIG_IPV6 */
@@ -1162,8 +1297,8 @@ static int radius_client_init_auth(struct radius_client_data *radius)
            eloop_register_read_sock(radius->auth_serv_sock,
                                     radius_client_receive, radius,
                                     (void *) RADIUS_AUTH)) {
-               printf("Could not register read socket for authentication "
-                      "server\n");
+               wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server");
+               radius_close_auth_sockets(radius);
                return -1;
        }
 
@@ -1172,8 +1307,8 @@ static int radius_client_init_auth(struct radius_client_data *radius)
            eloop_register_read_sock(radius->auth_serv_sock6,
                                     radius_client_receive, radius,
                                     (void *) RADIUS_AUTH)) {
-               printf("Could not register read socket for authentication "
-                      "server\n");
+               wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for authentication server");
+               radius_close_auth_sockets(radius);
                return -1;
        }
 #endif /* CONFIG_IPV6 */
@@ -1187,9 +1322,12 @@ static int radius_client_init_acct(struct radius_client_data *radius)
        struct hostapd_radius_servers *conf = radius->conf;
        int ok = 0;
 
+       radius_close_acct_sockets(radius);
+
        radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0);
        if (radius->acct_serv_sock < 0)
-               perror("socket[PF_INET,SOCK_DGRAM]");
+               wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET,SOCK_DGRAM]: %s",
+                          strerror(errno));
        else {
                radius_client_disable_pmtu_discovery(radius->acct_serv_sock);
                ok++;
@@ -1198,7 +1336,8 @@ static int radius_client_init_acct(struct radius_client_data *radius)
 #ifdef CONFIG_IPV6
        radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0);
        if (radius->acct_serv_sock6 < 0)
-               perror("socket[PF_INET6,SOCK_DGRAM]");
+               wpa_printf(MSG_INFO, "RADIUS: socket[PF_INET6,SOCK_DGRAM]: %s",
+                          strerror(errno));
        else
                ok++;
 #endif /* CONFIG_IPV6 */
@@ -1214,8 +1353,8 @@ static int radius_client_init_acct(struct radius_client_data *radius)
            eloop_register_read_sock(radius->acct_serv_sock,
                                     radius_client_receive, radius,
                                     (void *) RADIUS_ACCT)) {
-               printf("Could not register read socket for accounting "
-                      "server\n");
+               wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server");
+               radius_close_acct_sockets(radius);
                return -1;
        }
 
@@ -1224,8 +1363,8 @@ static int radius_client_init_acct(struct radius_client_data *radius)
            eloop_register_read_sock(radius->acct_serv_sock6,
                                     radius_client_receive, radius,
                                     (void *) RADIUS_ACCT)) {
-               printf("Could not register read socket for accounting "
-                      "server\n");
+               wpa_printf(MSG_INFO, "RADIUS: Could not register read socket for accounting server");
+               radius_close_acct_sockets(radius);
                return -1;
        }
 #endif /* CONFIG_IPV6 */
@@ -1287,16 +1426,8 @@ void radius_client_deinit(struct radius_client_data *radius)
        if (!radius)
                return;
 
-       if (radius->auth_serv_sock >= 0)
-               eloop_unregister_read_sock(radius->auth_serv_sock);
-       if (radius->acct_serv_sock >= 0)
-               eloop_unregister_read_sock(radius->acct_serv_sock);
-#ifdef CONFIG_IPV6
-       if (radius->auth_serv_sock6 >= 0)
-               eloop_unregister_read_sock(radius->auth_serv_sock6);
-       if (radius->acct_serv_sock6 >= 0)
-               eloop_unregister_read_sock(radius->acct_serv_sock6);
-#endif /* CONFIG_IPV6 */
+       radius_close_auth_sockets(radius);
+       radius_close_acct_sockets(radius);
 
        eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL);
 
index bded965..39ceea8 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * RADIUS Dynamic Authorization Server (DAS) (RFC 5176)
- * Copyright (c) 2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -16,9 +16,6 @@
 #include "radius_das.h"
 
 
-extern int wpa_debug_level;
-
-
 struct radius_das_data {
        int sock;
        u8 *shared_secret;
@@ -41,11 +38,17 @@ static struct radius_msg * radius_das_disconnect(struct radius_das_data *das,
        struct radius_msg *reply;
        u8 allowed[] = {
                RADIUS_ATTR_USER_NAME,
+               RADIUS_ATTR_NAS_IP_ADDRESS,
                RADIUS_ATTR_CALLING_STATION_ID,
+               RADIUS_ATTR_NAS_IDENTIFIER,
                RADIUS_ATTR_ACCT_SESSION_ID,
+               RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
                RADIUS_ATTR_EVENT_TIMESTAMP,
                RADIUS_ATTR_MESSAGE_AUTHENTICATOR,
                RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
+#ifdef CONFIG_IPV6
+               RADIUS_ATTR_NAS_IPV6_ADDRESS,
+#endif /* CONFIG_IPV6 */
                0
        };
        int error = 405;
@@ -70,6 +73,36 @@ static struct radius_msg * radius_das_disconnect(struct radius_das_data *das,
 
        os_memset(&attrs, 0, sizeof(attrs));
 
+       if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
+                                   &buf, &len, NULL) == 0) {
+               if (len != 4) {
+                       wpa_printf(MSG_INFO, "DAS: Invalid NAS-IP-Address from %s:%d",
+                                  abuf, from_port);
+                       error = 407;
+                       goto fail;
+               }
+               attrs.nas_ip_addr = buf;
+       }
+
+#ifdef CONFIG_IPV6
+       if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS,
+                                   &buf, &len, NULL) == 0) {
+               if (len != 16) {
+                       wpa_printf(MSG_INFO, "DAS: Invalid NAS-IPv6-Address from %s:%d",
+                                  abuf, from_port);
+                       error = 407;
+                       goto fail;
+               }
+               attrs.nas_ipv6_addr = buf;
+       }
+#endif /* CONFIG_IPV6 */
+
+       if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_NAS_IDENTIFIER,
+                                   &buf, &len, NULL) == 0) {
+               attrs.nas_identifier = buf;
+               attrs.nas_identifier_len = len;
+       }
+
        if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CALLING_STATION_ID,
                                    &buf, &len, NULL) == 0) {
                if (len >= sizeof(tmp))
@@ -97,6 +130,12 @@ static struct radius_msg * radius_das_disconnect(struct radius_das_data *das,
                attrs.acct_session_id_len = len;
        }
 
+       if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_ACCT_MULTI_SESSION_ID,
+                                   &buf, &len, NULL) == 0) {
+               attrs.acct_multi_session_id = buf;
+               attrs.acct_multi_session_id_len = len;
+       }
+
        if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY,
                                    &buf, &len, NULL) == 0) {
                attrs.cui = buf;
@@ -115,6 +154,12 @@ static struct radius_msg * radius_das_disconnect(struct radius_das_data *das,
                           "%s:%d", abuf, from_port);
                error = 503;
                break;
+       case RADIUS_DAS_MULTI_SESSION_MATCH:
+               wpa_printf(MSG_INFO,
+                          "DAS: Multiple sessions match for request from %s:%d",
+                          abuf, from_port);
+               error = 508;
+               break;
        case RADIUS_DAS_SUCCESS:
                error = 0;
                break;
@@ -200,7 +245,8 @@ static void radius_das_receive(int sock, void *eloop_ctx, void *sock_ctx)
                                  (u8 *) &val, 4);
        if (res == 4) {
                u32 timestamp = ntohl(val);
-               if (abs(now.sec - timestamp) > das->time_window) {
+               if ((unsigned int) abs(now.sec - timestamp) >
+                   das->time_window) {
                        wpa_printf(MSG_DEBUG, "DAS: Unacceptable "
                                   "Event-Timestamp (%u; local time %u) in "
                                   "packet from %s:%d - drop",
@@ -284,7 +330,7 @@ static int radius_das_open_socket(int port)
 
        s = socket(PF_INET, SOCK_DGRAM, 0);
        if (s < 0) {
-               perror("socket");
+               wpa_printf(MSG_INFO, "RADIUS DAS: socket: %s", strerror(errno));
                return -1;
        }
 
@@ -292,7 +338,7 @@ static int radius_das_open_socket(int port)
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-               perror("bind");
+               wpa_printf(MSG_INFO, "RADIUS DAS: bind: %s", strerror(errno));
                close(s);
                return -1;
        }
index 738b18b..ce731d4 100644 (file)
@@ -14,15 +14,25 @@ struct radius_das_data;
 enum radius_das_res {
        RADIUS_DAS_SUCCESS,
        RADIUS_DAS_NAS_MISMATCH,
-       RADIUS_DAS_SESSION_NOT_FOUND
+       RADIUS_DAS_SESSION_NOT_FOUND,
+       RADIUS_DAS_MULTI_SESSION_MATCH,
 };
 
 struct radius_das_attrs {
+       /* NAS identification attributes */
+       const u8 *nas_ip_addr;
+       const u8 *nas_identifier;
+       size_t nas_identifier_len;
+       const u8 *nas_ipv6_addr;
+
+       /* Session identification attributes */
        const u8 *sta_addr;
        const u8 *user_name;
        size_t user_name_len;
        const u8 *acct_session_id;
        size_t acct_session_id_len;
+       const u8 *acct_multi_session_id;
+       size_t acct_multi_session_id_len;
        const u8 *cui;
        size_t cui_len;
 };
index 5b2d711..85a485e 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * RADIUS authentication server
- * Copyright (c) 2005-2009, 2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2009, 2011-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -8,11 +8,16 @@
 
 #include "includes.h"
 #include <net/if.h>
+#ifdef CONFIG_SQLITE
+#include <sqlite3.h>
+#endif /* CONFIG_SQLITE */
 
 #include "common.h"
 #include "radius.h"
 #include "eloop.h"
 #include "eap_server/eap.h"
+#include "ap/ap_config.h"
+#include "crypto/tls.h"
 #include "radius_server.h"
 
 /**
@@ -49,6 +54,13 @@ struct radius_server_counters {
        u32 bad_authenticators;
        u32 packets_dropped;
        u32 unknown_types;
+
+       u32 acct_requests;
+       u32 invalid_acct_requests;
+       u32 acct_responses;
+       u32 malformed_acct_requests;
+       u32 acct_bad_authenticators;
+       u32 unknown_acct_types;
 };
 
 /**
@@ -61,6 +73,8 @@ struct radius_session {
        unsigned int sess_id;
        struct eap_sm *eap;
        struct eap_eapol_interface *eap_if;
+       char *username; /* from User-Name attribute */
+       char *nas_ip;
 
        struct radius_msg *last_msg;
        char *last_from_addr;
@@ -70,6 +84,11 @@ struct radius_session {
        u8 last_identifier;
        struct radius_msg *last_reply;
        u8 last_authenticator[16];
+
+       unsigned int remediation:1;
+       unsigned int macacl:1;
+
+       struct hostapd_radius_attr *accept_attr;
 };
 
 /**
@@ -99,6 +118,11 @@ struct radius_server_data {
        int auth_sock;
 
        /**
+        * acct_sock - Socket for RADIUS accounting messages
+        */
+       int acct_sock;
+
+       /**
         * clients - List of authorized RADIUS clients
         */
        struct radius_client *clients;
@@ -223,6 +247,25 @@ struct radius_server_data {
        u16 pwd_group;
 
        /**
+        * server_id - Server identity
+        */
+       const char *server_id;
+
+       /**
+        * erp - Whether EAP Re-authentication Protocol (ERP) is enabled
+        *
+        * This controls whether the authentication server derives ERP key
+        * hierarchy (rRK and rIK) from full EAP authentication and allows
+        * these keys to be used to perform ERP to derive rMSK instead of full
+        * EAP authentication to derive MSK.
+        */
+       int erp;
+
+       const char *erp_domain;
+
+       struct dl_list erp_keys; /* struct eap_server_erp_key */
+
+       /**
         * wps - Wi-Fi Protected Setup context
         *
         * If WPS is used with an external RADIUS server (which is quite
@@ -239,7 +282,7 @@ struct radius_server_data {
        /**
         * start_time - Timestamp of server start
         */
-       struct os_time start_time;
+       struct os_reltime start_time;
 
        /**
         * counters - Statistics counters for server operations
@@ -290,10 +333,15 @@ struct radius_server_data {
 #ifdef CONFIG_RADIUS_TEST
        char *dump_msk_file;
 #endif /* CONFIG_RADIUS_TEST */
-};
 
+       char *subscr_remediation_url;
+       u8 subscr_remediation_method;
+
+#ifdef CONFIG_SQLITE
+       sqlite3 *db;
+#endif /* CONFIG_SQLITE */
+};
 
-extern int wpa_debug_level;
 
 #define RADIUS_DEBUG(args...) \
 wpa_printf(MSG_DEBUG, "RADIUS SRV: " args)
@@ -309,6 +357,52 @@ static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx);
 static void radius_server_session_remove_timeout(void *eloop_ctx,
                                                 void *timeout_ctx);
 
+void srv_log(struct radius_session *sess, const char *fmt, ...)
+PRINTF_FORMAT(2, 3);
+
+void srv_log(struct radius_session *sess, const char *fmt, ...)
+{
+       va_list ap;
+       char *buf;
+       int buflen;
+
+       va_start(ap, fmt);
+       buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+       va_end(ap);
+
+       buf = os_malloc(buflen);
+       if (buf == NULL)
+               return;
+       va_start(ap, fmt);
+       vsnprintf(buf, buflen, fmt, ap);
+       va_end(ap);
+
+       RADIUS_DEBUG("[0x%x %s] %s", sess->sess_id, sess->nas_ip, buf);
+
+#ifdef CONFIG_SQLITE
+       if (sess->server->db) {
+               char *sql;
+               sql = sqlite3_mprintf("INSERT INTO authlog"
+                                     "(timestamp,session,nas_ip,username,note)"
+                                     " VALUES ("
+                                     "strftime('%%Y-%%m-%%d %%H:%%M:%%f',"
+                                     "'now'),%u,%Q,%Q,%Q)",
+                                     sess->sess_id, sess->nas_ip,
+                                     sess->username, buf);
+               if (sql) {
+                       if (sqlite3_exec(sess->server->db, sql, NULL, NULL,
+                                        NULL) != SQLITE_OK) {
+                               RADIUS_ERROR("Failed to add authlog entry into sqlite database: %s",
+                                            sqlite3_errmsg(sess->server->db));
+                       }
+                       sqlite3_free(sql);
+               }
+       }
+#endif /* CONFIG_SQLITE */
+
+       os_free(buf);
+}
+
 
 static struct radius_client *
 radius_server_get_client(struct radius_server_data *data, struct in_addr *addr,
@@ -374,6 +468,8 @@ static void radius_server_session_free(struct radius_server_data *data,
        radius_msg_free(sess->last_msg);
        os_free(sess->last_from_addr);
        radius_msg_free(sess->last_reply);
+       os_free(sess->username);
+       os_free(sess->nas_ip);
        os_free(sess);
        data->num_sess--;
 }
@@ -453,47 +549,125 @@ radius_server_new_session(struct radius_server_data *data,
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
+static void radius_server_testing_options_tls(struct radius_session *sess,
+                                             const char *tls,
+                                             struct eap_config *eap_conf)
+{
+       int test = atoi(tls);
+
+       switch (test) {
+       case 1:
+               srv_log(sess, "TLS test - break VerifyData");
+               eap_conf->tls_test_flags = TLS_BREAK_VERIFY_DATA;
+               break;
+       case 2:
+               srv_log(sess, "TLS test - break ServerKeyExchange ServerParams hash");
+               eap_conf->tls_test_flags = TLS_BREAK_SRV_KEY_X_HASH;
+               break;
+       case 3:
+               srv_log(sess, "TLS test - break ServerKeyExchange ServerParams Signature");
+               eap_conf->tls_test_flags = TLS_BREAK_SRV_KEY_X_SIGNATURE;
+               break;
+       case 4:
+               srv_log(sess, "TLS test - RSA-DHE using a short 511-bit prime");
+               eap_conf->tls_test_flags = TLS_DHE_PRIME_511B;
+               break;
+       case 5:
+               srv_log(sess, "TLS test - RSA-DHE using a short 767-bit prime");
+               eap_conf->tls_test_flags = TLS_DHE_PRIME_767B;
+               break;
+       case 6:
+               srv_log(sess, "TLS test - RSA-DHE using a bogus 15 \"prime\"");
+               eap_conf->tls_test_flags = TLS_DHE_PRIME_15;
+               break;
+       case 7:
+               srv_log(sess, "TLS test - RSA-DHE using a short 58-bit prime in long container");
+               eap_conf->tls_test_flags = TLS_DHE_PRIME_58B;
+               break;
+       case 8:
+               srv_log(sess, "TLS test - RSA-DHE using a non-prime");
+               eap_conf->tls_test_flags = TLS_DHE_NON_PRIME;
+               break;
+       default:
+               srv_log(sess, "Unrecognized TLS test");
+               break;
+       }
+}
+#endif /* CONFIG_TESTING_OPTIONS */
+
+static void radius_server_testing_options(struct radius_session *sess,
+                                         struct eap_config *eap_conf)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+       const char *pos;
+
+       pos = os_strstr(sess->username, "@test-");
+       if (pos == NULL)
+               return;
+       pos += 6;
+       if (os_strncmp(pos, "tls-", 4) == 0)
+               radius_server_testing_options_tls(sess, pos + 4, eap_conf);
+       else
+               srv_log(sess, "Unrecognized test: %s", pos);
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
 static struct radius_session *
 radius_server_get_new_session(struct radius_server_data *data,
                              struct radius_client *client,
-                             struct radius_msg *msg)
+                             struct radius_msg *msg, const char *from_addr)
 {
        u8 *user;
        size_t user_len;
        int res;
        struct radius_session *sess;
        struct eap_config eap_conf;
+       struct eap_user tmp;
 
        RADIUS_DEBUG("Creating a new session");
 
-       user = os_malloc(256);
-       if (user == NULL) {
-               return NULL;
-       }
-       res = radius_msg_get_attr(msg, RADIUS_ATTR_USER_NAME, user, 256);
-       if (res < 0 || res > 256) {
+       if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &user,
+                                   &user_len, NULL) < 0) {
                RADIUS_DEBUG("Could not get User-Name");
-               os_free(user);
                return NULL;
        }
-       user_len = res;
        RADIUS_DUMP_ASCII("User-Name", user, user_len);
 
-       res = data->get_eap_user(data->conf_ctx, user, user_len, 0, NULL);
-       os_free(user);
+       os_memset(&tmp, 0, sizeof(tmp));
+       res = data->get_eap_user(data->conf_ctx, user, user_len, 0, &tmp);
+       bin_clear_free(tmp.password, tmp.password_len);
 
-       if (res == 0) {
-               RADIUS_DEBUG("Matching user entry found");
-               sess = radius_server_new_session(data, client);
-               if (sess == NULL) {
-                       RADIUS_DEBUG("Failed to create a new session");
-                       return NULL;
-               }
-       } else {
+       if (res != 0) {
                RADIUS_DEBUG("User-Name not found from user database");
                return NULL;
        }
 
+       RADIUS_DEBUG("Matching user entry found");
+       sess = radius_server_new_session(data, client);
+       if (sess == NULL) {
+               RADIUS_DEBUG("Failed to create a new session");
+               return NULL;
+       }
+       sess->accept_attr = tmp.accept_attr;
+       sess->macacl = tmp.macacl;
+
+       sess->username = os_malloc(user_len * 4 + 1);
+       if (sess->username == NULL) {
+               radius_server_session_free(data, sess);
+               return NULL;
+       }
+       printf_encode(sess->username, user_len * 4 + 1, user, user_len);
+
+       sess->nas_ip = os_strdup(from_addr);
+       if (sess->nas_ip == NULL) {
+               radius_server_session_free(data, sess);
+               return NULL;
+       }
+
+       srv_log(sess, "New session created");
+
        os_memset(&eap_conf, 0, sizeof(eap_conf));
        eap_conf.ssl_ctx = data->ssl_ctx;
        eap_conf.msg_ctx = data->msg_ctx;
@@ -511,6 +685,10 @@ radius_server_get_new_session(struct radius_server_data *data,
        eap_conf.tnc = data->tnc;
        eap_conf.wps = data->wps;
        eap_conf.pwd_group = data->pwd_group;
+       eap_conf.server_id = (const u8 *) data->server_id;
+       eap_conf.server_id_len = os_strlen(data->server_id);
+       eap_conf.erp = data->erp;
+       radius_server_testing_options(sess, &eap_conf);
        sess->eap = eap_server_sm_init(sess, &radius_server_eapol_cb,
                                       &eap_conf);
        if (sess->eap == NULL) {
@@ -605,12 +783,134 @@ radius_server_encapsulate_eap(struct radius_server_data *data,
                }
        }
 
+#ifdef CONFIG_HS20
+       if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->remediation &&
+           data->subscr_remediation_url) {
+               u8 *buf;
+               size_t url_len = os_strlen(data->subscr_remediation_url);
+               buf = os_malloc(1 + url_len);
+               if (buf == NULL) {
+                       radius_msg_free(msg);
+                       return NULL;
+               }
+               buf[0] = data->subscr_remediation_method;
+               os_memcpy(&buf[1], data->subscr_remediation_url, url_len);
+               if (!radius_msg_add_wfa(
+                           msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION,
+                           buf, 1 + url_len)) {
+                       RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
+               }
+               os_free(buf);
+       } else if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->remediation) {
+               u8 buf[1];
+               if (!radius_msg_add_wfa(
+                           msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION,
+                           buf, 0)) {
+                       RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
+               }
+       }
+#endif /* CONFIG_HS20 */
+
        if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
                RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
                radius_msg_free(msg);
                return NULL;
        }
 
+       if (code == RADIUS_CODE_ACCESS_ACCEPT) {
+               struct hostapd_radius_attr *attr;
+               for (attr = sess->accept_attr; attr; attr = attr->next) {
+                       if (!radius_msg_add_attr(msg, attr->type,
+                                                wpabuf_head(attr->val),
+                                                wpabuf_len(attr->val))) {
+                               wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
+                               radius_msg_free(msg);
+                               return NULL;
+                       }
+               }
+       }
+
+       if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
+                                 client->shared_secret_len,
+                                 hdr->authenticator) < 0) {
+               RADIUS_DEBUG("Failed to add Message-Authenticator attribute");
+       }
+
+       return msg;
+}
+
+
+static struct radius_msg *
+radius_server_macacl(struct radius_server_data *data,
+                    struct radius_client *client,
+                    struct radius_session *sess,
+                    struct radius_msg *request)
+{
+       struct radius_msg *msg;
+       int code;
+       struct radius_hdr *hdr = radius_msg_get_hdr(request);
+       u8 *pw;
+       size_t pw_len;
+
+       code = RADIUS_CODE_ACCESS_ACCEPT;
+
+       if (radius_msg_get_attr_ptr(request, RADIUS_ATTR_USER_PASSWORD, &pw,
+                                   &pw_len, NULL) < 0) {
+               RADIUS_DEBUG("Could not get User-Password");
+               code = RADIUS_CODE_ACCESS_REJECT;
+       } else {
+               int res;
+               struct eap_user tmp;
+
+               os_memset(&tmp, 0, sizeof(tmp));
+               res = data->get_eap_user(data->conf_ctx, (u8 *) sess->username,
+                                        os_strlen(sess->username), 0, &tmp);
+               if (res || !tmp.macacl || tmp.password == NULL) {
+                       RADIUS_DEBUG("No MAC ACL user entry");
+                       bin_clear_free(tmp.password, tmp.password_len);
+                       code = RADIUS_CODE_ACCESS_REJECT;
+               } else {
+                       u8 buf[128];
+                       res = radius_user_password_hide(
+                               request, tmp.password, tmp.password_len,
+                               (u8 *) client->shared_secret,
+                               client->shared_secret_len,
+                               buf, sizeof(buf));
+                       bin_clear_free(tmp.password, tmp.password_len);
+
+                       if (res < 0 || pw_len != (size_t) res ||
+                           os_memcmp_const(pw, buf, res) != 0) {
+                               RADIUS_DEBUG("Incorrect User-Password");
+                               code = RADIUS_CODE_ACCESS_REJECT;
+                       }
+               }
+       }
+
+       msg = radius_msg_new(code, hdr->identifier);
+       if (msg == NULL) {
+               RADIUS_DEBUG("Failed to allocate reply message");
+               return NULL;
+       }
+
+       if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) {
+               RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)");
+               radius_msg_free(msg);
+               return NULL;
+       }
+
+       if (code == RADIUS_CODE_ACCESS_ACCEPT) {
+               struct hostapd_radius_attr *attr;
+               for (attr = sess->accept_attr; attr; attr = attr->next) {
+                       if (!radius_msg_add_attr(msg, attr->type,
+                                                wpabuf_head(attr->val),
+                                                wpabuf_len(attr->val))) {
+                               wpa_printf(MSG_ERROR, "Could not add RADIUS attribute");
+                               radius_msg_free(msg);
+                               return NULL;
+                       }
+               }
+       }
+
        if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret,
                                  client->shared_secret_len,
                                  hdr->authenticator) < 0) {
@@ -672,7 +972,7 @@ static int radius_server_reject(struct radius_server_data *data,
        buf = radius_msg_get_buf(msg);
        if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0,
                   (struct sockaddr *) from, sizeof(*from)) < 0) {
-               perror("sendto[RADIUS SRV]");
+               wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s", strerror(errno));
                ret = -1;
        }
 
@@ -719,7 +1019,8 @@ static int radius_server_request(struct radius_server_data *data,
                                     from_addr, from_port);
                return -1;
        } else {
-               sess = radius_server_get_new_session(data, client, msg);
+               sess = radius_server_get_new_session(data, client, msg,
+                                                    from_addr);
                if (sess == NULL) {
                        RADIUS_DEBUG("Could not create a new session");
                        radius_server_reject(data, client, msg, from, fromlen,
@@ -743,7 +1044,8 @@ static int radius_server_request(struct radius_server_data *data,
                                     wpabuf_len(buf), 0,
                                     (struct sockaddr *) from, fromlen);
                        if (res < 0) {
-                               perror("sendto[RADIUS SRV]");
+                               wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
+                                          strerror(errno));
                        }
                        return 0;
                }
@@ -754,6 +1056,12 @@ static int radius_server_request(struct radius_server_data *data,
        }
                      
        eap = radius_msg_get_eap(msg);
+       if (eap == NULL && sess->macacl) {
+               reply = radius_server_macacl(data, client, sess, msg);
+               if (reply == NULL)
+                       return -1;
+               goto send_reply;
+       }
        if (eap == NULL) {
                RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s",
                             from_addr);
@@ -804,9 +1112,14 @@ static int radius_server_request(struct radius_server_data *data,
 
        if (sess->eap_if->eapSuccess || sess->eap_if->eapFail)
                is_complete = 1;
+       if (sess->eap_if->eapFail)
+               srv_log(sess, "EAP authentication failed");
+       else if (sess->eap_if->eapSuccess)
+               srv_log(sess, "EAP authentication succeeded");
 
        reply = radius_server_encapsulate_eap(data, client, sess, msg);
 
+send_reply:
        if (reply) {
                struct wpabuf *buf;
                struct radius_hdr *hdr;
@@ -818,10 +1131,12 @@ static int radius_server_request(struct radius_server_data *data,
 
                switch (radius_msg_get_hdr(reply)->code) {
                case RADIUS_CODE_ACCESS_ACCEPT:
+                       srv_log(sess, "Sending Access-Accept");
                        data->counters.access_accepts++;
                        client->counters.access_accepts++;
                        break;
                case RADIUS_CODE_ACCESS_REJECT:
+                       srv_log(sess, "Sending Access-Reject");
                        data->counters.access_rejects++;
                        client->counters.access_rejects++;
                        break;
@@ -835,7 +1150,8 @@ static int radius_server_request(struct radius_server_data *data,
                             wpabuf_len(buf), 0,
                             (struct sockaddr *) from, fromlen);
                if (res < 0) {
-                       perror("sendto[RADIUS SRV]");
+                       wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
+                                  strerror(errno));
                }
                radius_msg_free(sess->last_reply);
                sess->last_reply = reply;
@@ -890,7 +1206,8 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx,
        len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0,
                       (struct sockaddr *) &from.ss, &fromlen);
        if (len < 0) {
-               perror("recvfrom[radius_server]");
+               wpa_printf(MSG_INFO, "recvfrom[radius_server]: %s",
+                          strerror(errno));
                goto fail;
        }
 
@@ -971,6 +1288,140 @@ fail:
 }
 
 
+static void radius_server_receive_acct(int sock, void *eloop_ctx,
+                                      void *sock_ctx)
+{
+       struct radius_server_data *data = eloop_ctx;
+       u8 *buf = NULL;
+       union {
+               struct sockaddr_storage ss;
+               struct sockaddr_in sin;
+#ifdef CONFIG_IPV6
+               struct sockaddr_in6 sin6;
+#endif /* CONFIG_IPV6 */
+       } from;
+       socklen_t fromlen;
+       int len, res;
+       struct radius_client *client = NULL;
+       struct radius_msg *msg = NULL, *resp = NULL;
+       char abuf[50];
+       int from_port = 0;
+       struct radius_hdr *hdr;
+       struct wpabuf *rbuf;
+
+       buf = os_malloc(RADIUS_MAX_MSG_LEN);
+       if (buf == NULL) {
+               goto fail;
+       }
+
+       fromlen = sizeof(from);
+       len = recvfrom(sock, buf, RADIUS_MAX_MSG_LEN, 0,
+                      (struct sockaddr *) &from.ss, &fromlen);
+       if (len < 0) {
+               wpa_printf(MSG_INFO, "recvfrom[radius_server]: %s",
+                          strerror(errno));
+               goto fail;
+       }
+
+#ifdef CONFIG_IPV6
+       if (data->ipv6) {
+               if (inet_ntop(AF_INET6, &from.sin6.sin6_addr, abuf,
+                             sizeof(abuf)) == NULL)
+                       abuf[0] = '\0';
+               from_port = ntohs(from.sin6.sin6_port);
+               RADIUS_DEBUG("Received %d bytes from %s:%d",
+                            len, abuf, from_port);
+
+               client = radius_server_get_client(data,
+                                                 (struct in_addr *)
+                                                 &from.sin6.sin6_addr, 1);
+       }
+#endif /* CONFIG_IPV6 */
+
+       if (!data->ipv6) {
+               os_strlcpy(abuf, inet_ntoa(from.sin.sin_addr), sizeof(abuf));
+               from_port = ntohs(from.sin.sin_port);
+               RADIUS_DEBUG("Received %d bytes from %s:%d",
+                            len, abuf, from_port);
+
+               client = radius_server_get_client(data, &from.sin.sin_addr, 0);
+       }
+
+       RADIUS_DUMP("Received data", buf, len);
+
+       if (client == NULL) {
+               RADIUS_DEBUG("Unknown client %s - packet ignored", abuf);
+               data->counters.invalid_acct_requests++;
+               goto fail;
+       }
+
+       msg = radius_msg_parse(buf, len);
+       if (msg == NULL) {
+               RADIUS_DEBUG("Parsing incoming RADIUS frame failed");
+               data->counters.malformed_acct_requests++;
+               client->counters.malformed_acct_requests++;
+               goto fail;
+       }
+
+       os_free(buf);
+       buf = NULL;
+
+       if (wpa_debug_level <= MSG_MSGDUMP) {
+               radius_msg_dump(msg);
+       }
+
+       if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_REQUEST) {
+               RADIUS_DEBUG("Unexpected RADIUS code %d",
+                            radius_msg_get_hdr(msg)->code);
+               data->counters.unknown_acct_types++;
+               client->counters.unknown_acct_types++;
+               goto fail;
+       }
+
+       data->counters.acct_requests++;
+       client->counters.acct_requests++;
+
+       if (radius_msg_verify_acct_req(msg, (u8 *) client->shared_secret,
+                                      client->shared_secret_len)) {
+               RADIUS_DEBUG("Invalid Authenticator from %s", abuf);
+               data->counters.acct_bad_authenticators++;
+               client->counters.acct_bad_authenticators++;
+               goto fail;
+       }
+
+       /* TODO: Write accounting information to a file or database */
+
+       hdr = radius_msg_get_hdr(msg);
+
+       resp = radius_msg_new(RADIUS_CODE_ACCOUNTING_RESPONSE, hdr->identifier);
+       if (resp == NULL)
+               goto fail;
+
+       radius_msg_finish_acct_resp(resp, (u8 *) client->shared_secret,
+                                   client->shared_secret_len,
+                                   hdr->authenticator);
+
+       RADIUS_DEBUG("Reply to %s:%d", abuf, from_port);
+       if (wpa_debug_level <= MSG_MSGDUMP) {
+               radius_msg_dump(resp);
+       }
+       rbuf = radius_msg_get_buf(resp);
+       data->counters.acct_responses++;
+       client->counters.acct_responses++;
+       res = sendto(data->acct_sock, wpabuf_head(rbuf), wpabuf_len(rbuf), 0,
+                    (struct sockaddr *) &from.ss, fromlen);
+       if (res < 0) {
+               wpa_printf(MSG_INFO, "sendto[RADIUS SRV]: %s",
+                          strerror(errno));
+       }
+
+fail:
+       radius_msg_free(resp);
+       radius_msg_free(msg);
+       os_free(buf);
+}
+
+
 static int radius_server_disable_pmtu_discovery(int s)
 {
        int r = -1;
@@ -994,7 +1445,7 @@ static int radius_server_open_socket(int port)
 
        s = socket(PF_INET, SOCK_DGRAM, 0);
        if (s < 0) {
-               perror("socket");
+               wpa_printf(MSG_INFO, "RADIUS: socket: %s", strerror(errno));
                return -1;
        }
 
@@ -1004,7 +1455,7 @@ static int radius_server_open_socket(int port)
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-               perror("bind");
+               wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno));
                close(s);
                return -1;
        }
@@ -1021,7 +1472,8 @@ static int radius_server_open_socket6(int port)
 
        s = socket(PF_INET6, SOCK_DGRAM, 0);
        if (s < 0) {
-               perror("socket[IPv6]");
+               wpa_printf(MSG_INFO, "RADIUS: socket[IPv6]: %s",
+                          strerror(errno));
                return -1;
        }
 
@@ -1030,7 +1482,7 @@ static int radius_server_open_socket6(int port)
        os_memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any));
        addr.sin6_port = htons(port);
        if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-               perror("bind");
+               wpa_printf(MSG_INFO, "RADIUS: bind: %s", strerror(errno));
                close(s);
                return -1;
        }
@@ -1183,8 +1635,8 @@ radius_server_read_clients(const char *client_file, int ipv6)
                        break;
                }
                entry->shared_secret_len = os_strlen(entry->shared_secret);
-               entry->addr.s_addr = addr.s_addr;
                if (!ipv6) {
+                       entry->addr.s_addr = addr.s_addr;
                        val = 0;
                        for (i = 0; i < mask; i++)
                                val |= 1 << (31 - i);
@@ -1241,8 +1693,7 @@ radius_server_init(struct radius_server_conf *conf)
 
 #ifndef CONFIG_IPV6
        if (conf->ipv6) {
-               fprintf(stderr, "RADIUS server compiled without IPv6 "
-                       "support.\n");
+               wpa_printf(MSG_ERROR, "RADIUS server compiled without IPv6 support");
                return NULL;
        }
 #endif /* CONFIG_IPV6 */
@@ -1251,7 +1702,8 @@ radius_server_init(struct radius_server_conf *conf)
        if (data == NULL)
                return NULL;
 
-       os_get_time(&data->start_time);
+       dl_list_init(&data->erp_keys);
+       os_get_reltime(&data->start_time);
        data->conf_ctx = conf->conf_ctx;
        data->eap_sim_db_priv = conf->eap_sim_db_priv;
        data->ssl_ctx = conf->ssl_ctx;
@@ -1280,6 +1732,7 @@ radius_server_init(struct radius_server_conf *conf)
        data->tnc = conf->tnc;
        data->wps = conf->wps;
        data->pwd_group = conf->pwd_group;
+       data->server_id = conf->server_id;
        if (conf->eap_req_id_text) {
                data->eap_req_id_text = os_malloc(conf->eap_req_id_text_len);
                if (data->eap_req_id_text) {
@@ -1288,6 +1741,25 @@ radius_server_init(struct radius_server_conf *conf)
                        data->eap_req_id_text_len = conf->eap_req_id_text_len;
                }
        }
+       data->erp = conf->erp;
+       data->erp_domain = conf->erp_domain;
+
+       if (conf->subscr_remediation_url) {
+               data->subscr_remediation_url =
+                       os_strdup(conf->subscr_remediation_url);
+       }
+       data->subscr_remediation_method = conf->subscr_remediation_method;
+
+#ifdef CONFIG_SQLITE
+       if (conf->sqlite_file) {
+               if (sqlite3_open(conf->sqlite_file, &data->db)) {
+                       RADIUS_ERROR("Could not open SQLite file '%s'",
+                                    conf->sqlite_file);
+                       radius_server_deinit(data);
+                       return NULL;
+               }
+       }
+#endif /* CONFIG_SQLITE */
 
 #ifdef CONFIG_RADIUS_TEST
        if (conf->dump_msk_file)
@@ -1297,7 +1769,7 @@ radius_server_init(struct radius_server_conf *conf)
        data->clients = radius_server_read_clients(conf->client_file,
                                                   conf->ipv6);
        if (data->clients == NULL) {
-               printf("No RADIUS clients configured.\n");
+               wpa_printf(MSG_ERROR, "No RADIUS clients configured");
                radius_server_deinit(data);
                return NULL;
        }
@@ -1309,8 +1781,7 @@ radius_server_init(struct radius_server_conf *conf)
 #endif /* CONFIG_IPV6 */
        data->auth_sock = radius_server_open_socket(conf->auth_port);
        if (data->auth_sock < 0) {
-               printf("Failed to open UDP socket for RADIUS authentication "
-                      "server\n");
+               wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS authentication server");
                radius_server_deinit(data);
                return NULL;
        }
@@ -1321,11 +1792,52 @@ radius_server_init(struct radius_server_conf *conf)
                return NULL;
        }
 
+       if (conf->acct_port) {
+#ifdef CONFIG_IPV6
+               if (conf->ipv6)
+                       data->acct_sock = radius_server_open_socket6(
+                               conf->acct_port);
+               else
+#endif /* CONFIG_IPV6 */
+               data->acct_sock = radius_server_open_socket(conf->acct_port);
+               if (data->acct_sock < 0) {
+                       wpa_printf(MSG_ERROR, "Failed to open UDP socket for RADIUS accounting server");
+                       radius_server_deinit(data);
+                       return NULL;
+               }
+               if (eloop_register_read_sock(data->acct_sock,
+                                            radius_server_receive_acct,
+                                            data, NULL)) {
+                       radius_server_deinit(data);
+                       return NULL;
+               }
+       } else {
+               data->acct_sock = -1;
+       }
+
        return data;
 }
 
 
 /**
+ * radius_server_erp_flush - Flush all ERP keys
+ * @data: RADIUS server context from radius_server_init()
+ */
+void radius_server_erp_flush(struct radius_server_data *data)
+{
+       struct eap_server_erp_key *erp;
+
+       if (data == NULL)
+               return;
+       while ((erp = dl_list_first(&data->erp_keys, struct eap_server_erp_key,
+                                   list)) != NULL) {
+               dl_list_del(&erp->list);
+               bin_clear_free(erp, sizeof(*erp));
+       }
+}
+
+
+/**
  * radius_server_deinit - Deinitialize RADIUS server
  * @data: RADIUS server context from radius_server_init()
  */
@@ -1339,6 +1851,11 @@ void radius_server_deinit(struct radius_server_data *data)
                close(data->auth_sock);
        }
 
+       if (data->acct_sock >= 0) {
+               eloop_unregister_read_sock(data->acct_sock);
+               close(data->acct_sock);
+       }
+
        radius_server_free_clients(data, data->clients);
 
        os_free(data->pac_opaque_encr_key);
@@ -1348,6 +1865,15 @@ void radius_server_deinit(struct radius_server_data *data)
 #ifdef CONFIG_RADIUS_TEST
        os_free(data->dump_msk_file);
 #endif /* CONFIG_RADIUS_TEST */
+       os_free(data->subscr_remediation_url);
+
+#ifdef CONFIG_SQLITE
+       if (data->db)
+               sqlite3_close(data->db);
+#endif /* CONFIG_SQLITE */
+
+       radius_server_erp_flush(data);
+
        os_free(data);
 }
 
@@ -1365,7 +1891,7 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf,
        int ret, uptime;
        unsigned int idx;
        char *end, *pos;
-       struct os_time now;
+       struct os_reltime now;
        struct radius_client *cli;
 
        /* RFC 2619 - RADIUS Authentication Server MIB */
@@ -1376,7 +1902,7 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf,
        pos = buf;
        end = buf + buflen;
 
-       os_get_time(&now);
+       os_get_reltime(&now);
        uptime = (now.sec - data->start_time.sec) * 100 +
                ((now.usec - data->start_time.usec) / 10000) % 100;
        ret = os_snprintf(pos, end - pos,
@@ -1386,7 +1912,7 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf,
                          "radiusAuthServResetTime=0\n"
                          "radiusAuthServConfigReset=4\n",
                          uptime);
-       if (ret < 0 || ret >= end - pos) {
+       if (os_snprintf_error(end - pos, ret)) {
                *pos = '\0';
                return pos - buf;
        }
@@ -1402,7 +1928,13 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf,
                          "radiusAuthServTotalMalformedAccessRequests=%u\n"
                          "radiusAuthServTotalBadAuthenticators=%u\n"
                          "radiusAuthServTotalPacketsDropped=%u\n"
-                         "radiusAuthServTotalUnknownTypes=%u\n",
+                         "radiusAuthServTotalUnknownTypes=%u\n"
+                         "radiusAccServTotalRequests=%u\n"
+                         "radiusAccServTotalInvalidRequests=%u\n"
+                         "radiusAccServTotalResponses=%u\n"
+                         "radiusAccServTotalMalformedRequests=%u\n"
+                         "radiusAccServTotalBadAuthenticators=%u\n"
+                         "radiusAccServTotalUnknownTypes=%u\n",
                          data->counters.access_requests,
                          data->counters.invalid_requests,
                          data->counters.dup_access_requests,
@@ -1412,8 +1944,14 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf,
                          data->counters.malformed_access_requests,
                          data->counters.bad_authenticators,
                          data->counters.packets_dropped,
-                         data->counters.unknown_types);
-       if (ret < 0 || ret >= end - pos) {
+                         data->counters.unknown_types,
+                         data->counters.acct_requests,
+                         data->counters.invalid_acct_requests,
+                         data->counters.acct_responses,
+                         data->counters.malformed_acct_requests,
+                         data->counters.acct_bad_authenticators,
+                         data->counters.unknown_acct_types);
+       if (os_snprintf_error(end - pos, ret)) {
                *pos = '\0';
                return pos - buf;
        }
@@ -1426,7 +1964,7 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf,
                        if (inet_ntop(AF_INET6, &cli->addr6, abuf,
                                      sizeof(abuf)) == NULL)
                                abuf[0] = '\0';
-                       if (inet_ntop(AF_INET6, &cli->mask6, abuf,
+                       if (inet_ntop(AF_INET6, &cli->mask6, mbuf,
                                      sizeof(mbuf)) == NULL)
                                mbuf[0] = '\0';
                }
@@ -1447,7 +1985,13 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf,
                                  "radiusAuthServMalformedAccessRequests=%u\n"
                                  "radiusAuthServBadAuthenticators=%u\n"
                                  "radiusAuthServPacketsDropped=%u\n"
-                                 "radiusAuthServUnknownTypes=%u\n",
+                                 "radiusAuthServUnknownTypes=%u\n"
+                                 "radiusAccServTotalRequests=%u\n"
+                                 "radiusAccServTotalInvalidRequests=%u\n"
+                                 "radiusAccServTotalResponses=%u\n"
+                                 "radiusAccServTotalMalformedRequests=%u\n"
+                                 "radiusAccServTotalBadAuthenticators=%u\n"
+                                 "radiusAccServTotalUnknownTypes=%u\n",
                                  idx,
                                  abuf, mbuf,
                                  cli->counters.access_requests,
@@ -1458,8 +2002,14 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf,
                                  cli->counters.malformed_access_requests,
                                  cli->counters.bad_authenticators,
                                  cli->counters.packets_dropped,
-                                 cli->counters.unknown_types);
-               if (ret < 0 || ret >= end - pos) {
+                                 cli->counters.unknown_types,
+                                 cli->counters.acct_requests,
+                                 cli->counters.invalid_acct_requests,
+                                 cli->counters.acct_responses,
+                                 cli->counters.malformed_acct_requests,
+                                 cli->counters.acct_bad_authenticators,
+                                 cli->counters.unknown_acct_types);
+               if (os_snprintf_error(end - pos, ret)) {
                        *pos = '\0';
                        return pos - buf;
                }
@@ -1476,9 +2026,16 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity,
 {
        struct radius_session *sess = ctx;
        struct radius_server_data *data = sess->server;
-
-       return data->get_eap_user(data->conf_ctx, identity, identity_len,
-                                 phase2, user);
+       int ret;
+
+       ret = data->get_eap_user(data->conf_ctx, identity, identity_len,
+                                phase2, user);
+       if (ret == 0 && user) {
+               sess->accept_attr = user->accept_attr;
+               sess->remediation = user->remediation;
+               sess->macacl = user->macacl;
+       }
+       return ret;
 }
 
 
@@ -1491,10 +2048,64 @@ static const char * radius_server_get_eap_req_id_text(void *ctx, size_t *len)
 }
 
 
+static void radius_server_log_msg(void *ctx, const char *msg)
+{
+       struct radius_session *sess = ctx;
+       srv_log(sess, "EAP: %s", msg);
+}
+
+
+#ifdef CONFIG_ERP
+
+static const char * radius_server_get_erp_domain(void *ctx)
+{
+       struct radius_session *sess = ctx;
+       struct radius_server_data *data = sess->server;
+
+       return data->erp_domain;
+}
+
+
+static struct eap_server_erp_key *
+radius_server_erp_get_key(void *ctx, const char *keyname)
+{
+       struct radius_session *sess = ctx;
+       struct radius_server_data *data = sess->server;
+       struct eap_server_erp_key *erp;
+
+       dl_list_for_each(erp, &data->erp_keys, struct eap_server_erp_key,
+                        list) {
+               if (os_strcmp(erp->keyname_nai, keyname) == 0)
+                       return erp;
+       }
+
+       return NULL;
+}
+
+
+static int radius_server_erp_add_key(void *ctx, struct eap_server_erp_key *erp)
+{
+       struct radius_session *sess = ctx;
+       struct radius_server_data *data = sess->server;
+
+       dl_list_add(&data->erp_keys, &erp->list);
+       return 0;
+}
+
+#endif /* CONFIG_ERP */
+
+
 static struct eapol_callbacks radius_server_eapol_cb =
 {
        .get_eap_user = radius_server_get_eap_user,
        .get_eap_req_id_text = radius_server_get_eap_req_id_text,
+       .log_msg = radius_server_log_msg,
+#ifdef CONFIG_ERP
+       .get_erp_send_reauth_start = NULL,
+       .get_erp_domain = radius_server_get_erp_domain,
+       .erp_get_key = radius_server_erp_get_key,
+       .erp_add_key = radius_server_erp_add_key,
+#endif /* CONFIG_ERP */
 };
 
 
@@ -1521,8 +2132,6 @@ void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx)
                                sess = s;
                                break;
                        }
-                       if (sess)
-                               break;
                }
                if (sess)
                        break;
index 82466c3..ca4e38c 100644 (file)
@@ -22,6 +22,11 @@ struct radius_server_conf {
        int auth_port;
 
        /**
+        * acct_port - UDP port to listen to as an accounting server
+        */
+       int acct_port;
+
+       /**
         * client_file - RADIUS client configuration file
         *
         * This file contains the RADIUS clients and the shared secret to be
@@ -35,6 +40,11 @@ struct radius_server_conf {
        char *client_file;
 
        /**
+        * sqlite_file - SQLite database for storing debug log information
+        */
+       const char *sqlite_file;
+
+       /**
         * conf_ctx - Context pointer for callbacks
         *
         * This is used as the ctx argument in get_eap_user() calls.
@@ -144,6 +154,23 @@ struct radius_server_conf {
        u16 pwd_group;
 
        /**
+        * server_id - Server identity
+        */
+       const char *server_id;
+
+       /**
+        * erp - Whether EAP Re-authentication Protocol (ERP) is enabled
+        *
+        * This controls whether the authentication server derives ERP key
+        * hierarchy (rRK and rIK) from full EAP authentication and allows
+        * these keys to be used to perform ERP to derive rMSK instead of full
+        * EAP authentication to derive MSK.
+        */
+       int erp;
+
+       const char *erp_domain;
+
+       /**
         * wps - Wi-Fi Protected Setup context
         *
         * If WPS is used with an external RADIUS server (which is quite
@@ -199,12 +226,16 @@ struct radius_server_conf {
 #ifdef CONFIG_RADIUS_TEST
        const char *dump_msk_file;
 #endif /* CONFIG_RADIUS_TEST */
+
+       char *subscr_remediation_url;
+       u8 subscr_remediation_method;
 };
 
 
 struct radius_server_data *
 radius_server_init(struct radius_server_conf *conf);
 
+void radius_server_erp_flush(struct radius_server_data *data);
 void radius_server_deinit(struct radius_server_data *data);
 
 int radius_server_get_mib(struct radius_server_data *data, char *buf,
index 9c41962..adfd3df 100644 (file)
@@ -2,7 +2,7 @@ all:
        @echo Nothing to be made.
 
 clean:
-       rm -f *~ *.o *.d
+       rm -f *~ *.o *.d *.gcno *.gcda *.gcov
 
 install:
        @echo Nothing to be made.
old mode 100644 (file)
new mode 100755 (executable)
index 789ac25..79764d9
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - PeerKey for Direct Link Setup (DLS)
- * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -65,6 +65,7 @@ static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst,
 {
        size_t rlen;
        struct wpa_eapol_key *err;
+       struct wpa_eapol_key_192 *err192;
        struct rsn_error_kde error;
        u8 *rbuf, *pos;
        size_t kde_len;
@@ -79,6 +80,7 @@ static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst,
                                  (void *) &err);
        if (rbuf == NULL)
                return -1;
+       err192 = (struct wpa_eapol_key_192 *) err;
 
        err->type = EAPOL_KEY_TYPE_RSN;
        key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
@@ -112,8 +114,8 @@ static int wpa_supplicant_send_smk_error(struct wpa_sm *sm, const u8 *dst,
                           "(mui %d error_type %d)", mui, error_type);
        }
 
-       wpa_eapol_key_send(sm, sm->ptk.kck, ver, dst, ETH_P_EAPOL,
-                          rbuf, rlen, err->key_mic);
+       wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, dst,
+                          ETH_P_EAPOL, rbuf, rlen, err192->key_mic);
 
        return 0;
 }
@@ -126,6 +128,7 @@ static int wpa_supplicant_send_smk_m3(struct wpa_sm *sm,
 {
        size_t rlen;
        struct wpa_eapol_key *reply;
+       struct wpa_eapol_key_192 *reply192;
        u8 *rbuf, *pos;
        size_t kde_len;
        u16 key_info;
@@ -140,6 +143,7 @@ static int wpa_supplicant_send_smk_m3(struct wpa_sm *sm,
                                  (void *) &reply);
        if (rbuf == NULL)
                return -1;
+       reply192 = (struct wpa_eapol_key_192 *) reply;
 
        reply->type = EAPOL_KEY_TYPE_RSN;
        key_info = ver | WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_MIC |
@@ -164,8 +168,8 @@ static int wpa_supplicant_send_smk_m3(struct wpa_sm *sm,
        wpa_add_kde(pos, RSN_KEY_DATA_NONCE, peerkey->inonce, WPA_NONCE_LEN);
 
        wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key SMK M3");
-       wpa_eapol_key_send(sm, sm->ptk.kck, ver, src_addr, ETH_P_EAPOL,
-                          rbuf, rlen, reply->key_mic);
+       wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, src_addr,
+                          ETH_P_EAPOL, rbuf, rlen, reply192->key_mic);
 
        return 0;
 }
@@ -240,11 +244,7 @@ static int wpa_supplicant_process_smk_m2(
        os_memcpy(peerkey->rsnie_i, kde.rsn_ie, kde.rsn_ie_len);
        peerkey->rsnie_i_len = kde.rsn_ie_len;
        peerkey->cipher = cipher;
-#ifdef CONFIG_IEEE80211W
-       if (ie.key_mgmt & (WPA_KEY_MGMT_IEEE8021X_SHA256 |
-                          WPA_KEY_MGMT_PSK_SHA256))
-               peerkey->use_sha256 = 1;
-#endif /* CONFIG_IEEE80211W */
+       peerkey->akmp = ie.key_mgmt;
 
        if (random_get_bytes(peerkey->pnonce, WPA_NONCE_LEN)) {
                wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
@@ -288,14 +288,14 @@ static int wpa_supplicant_process_smk_m2(
  * @mac_p: Peer MAC address
  * @inonce: Initiator Nonce
  * @mac_i: Initiator MAC address
- * @use_sha256: Whether to use SHA256-based KDF
+ * @akmp: Negotiated AKM
  *
  * 8.5.1.4 Station to station (STK) key hierarchy
  * SMKID = HMAC-SHA1-128(SMK, "SMK Name" || PNonce || MAC_P || INonce || MAC_I)
  */
 static void rsn_smkid(const u8 *smk, const u8 *pnonce, const u8 *mac_p,
                      const u8 *inonce, const u8 *mac_i, u8 *smkid,
-                     int use_sha256)
+                     int akmp)
 {
        char *title = "SMK Name";
        const u8 *addr[5];
@@ -310,7 +310,7 @@ static void rsn_smkid(const u8 *smk, const u8 *pnonce, const u8 *mac_p,
        addr[4] = mac_i;
 
 #ifdef CONFIG_IEEE80211W
-       if (use_sha256)
+       if (wpa_key_mgmt_sha256(akmp))
                hmac_sha256_vector(smk, PMK_LEN, 5, addr, len, hash);
        else
 #endif /* CONFIG_IEEE80211W */
@@ -371,7 +371,7 @@ static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm,
 
        wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 1/4 to " MACSTR,
                   MAC2STR(peerkey->addr));
-       wpa_eapol_key_send(sm, NULL, ver, peerkey->addr, ETH_P_EAPOL,
+       wpa_eapol_key_send(sm, NULL, 0, ver, peerkey->addr, ETH_P_EAPOL,
                           mbuf, mlen, NULL);
 }
 
@@ -426,8 +426,9 @@ static void wpa_supplicant_send_stk_3_of_4(struct wpa_sm *sm,
 
        wpa_printf(MSG_DEBUG, "RSN: Sending EAPOL-Key STK 3/4 to " MACSTR,
                   MAC2STR(peerkey->addr));
-       wpa_eapol_key_send(sm, peerkey->stk.kck, ver, peerkey->addr,
-                          ETH_P_EAPOL, mbuf, mlen, msg->key_mic);
+       wpa_eapol_key_send(sm, peerkey->stk.kck, peerkey->stk.kck_len, ver,
+                          peerkey->addr, ETH_P_EAPOL, mbuf, mlen,
+                          msg->key_mic);
 }
 
 
@@ -516,7 +517,6 @@ static int wpa_supplicant_process_smk_m45(
        struct wpa_peerkey *peerkey;
        struct wpa_eapol_ie_parse kde;
        u32 lifetime;
-       struct os_time now;
 
        if (!sm->peerkey_enabled || sm->proto != WPA_PROTO_RSN) {
                wpa_printf(MSG_DEBUG, "RSN: SMK handshake not allowed for "
@@ -568,22 +568,20 @@ static int wpa_supplicant_process_smk_m45(
        lifetime = WPA_GET_BE32(kde.lifetime);
        wpa_printf(MSG_DEBUG, "RSN: SMK lifetime %u seconds", lifetime);
        if (lifetime > 1000000000)
-               lifetime = 1000000000; /* avoid overflowing expiration time */
+               lifetime = 1000000000; /* avoid overflowing eloop time */
        peerkey->lifetime = lifetime;
-       os_get_time(&now);
-       peerkey->expiration = now.sec + lifetime;
        eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout,
                               sm, peerkey);
 
        if (peerkey->initiator) {
                rsn_smkid(peerkey->smk, peerkey->pnonce, peerkey->addr,
                          peerkey->inonce, sm->own_addr, peerkey->smkid,
-                         peerkey->use_sha256);
+                         peerkey->akmp);
                wpa_supplicant_send_stk_1_of_4(sm, peerkey);
        } else {
                rsn_smkid(peerkey->smk, peerkey->pnonce, sm->own_addr,
                          peerkey->inonce, peerkey->addr, peerkey->smkid,
-                         peerkey->use_sha256);
+                         peerkey->akmp);
        }
        wpa_hexdump(MSG_DEBUG, "RSN: SMKID", peerkey->smkid, PMKID_LEN);
 
@@ -656,11 +654,11 @@ static int wpa_supplicant_process_smk_error(
 static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm,
                                              struct wpa_peerkey *peerkey,
                                              const struct wpa_eapol_key *key,
-                                             u16 ver)
+                                             u16 ver, const u8 *key_data,
+                                             size_t key_data_len)
 {
        struct wpa_eapol_ie_parse ie;
-       const u8 *kde;
-       size_t len, kde_buf_len;
+       size_t kde_buf_len;
        struct wpa_ptk *stk;
        u8 buf[8], *kde_buf, *pos;
        be32 lifetime;
@@ -671,14 +669,13 @@ static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm,
        os_memset(&ie, 0, sizeof(ie));
 
        /* RSN: msg 1/4 should contain SMKID for the selected SMK */
-       kde = (const u8 *) (key + 1);
-       len = WPA_GET_BE16(key->key_data_length);
-       wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", kde, len);
-       if (wpa_supplicant_parse_ies(kde, len, &ie) < 0 || ie.pmkid == NULL) {
+       wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", key_data, key_data_len);
+       if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0 ||
+           ie.pmkid == NULL) {
                wpa_printf(MSG_DEBUG, "RSN: No SMKID in STK 1/4");
                return;
        }
-       if (os_memcmp(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
+       if (os_memcmp_const(ie.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
                wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 1/4",
                            ie.pmkid, PMKID_LEN);
                return;
@@ -698,12 +695,11 @@ static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm,
        wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion",
                       sm->own_addr, peerkey->addr,
                       peerkey->pnonce, key->key_nonce,
-                      (u8 *) stk, sizeof(*stk),
-                      peerkey->use_sha256);
+                      stk, peerkey->akmp, peerkey->cipher);
        /* Supplicant: swap tx/rx Mic keys */
-       os_memcpy(buf, stk->u.auth.tx_mic_key, 8);
-       os_memcpy(stk->u.auth.tx_mic_key, stk->u.auth.rx_mic_key, 8);
-       os_memcpy(stk->u.auth.rx_mic_key, buf, 8);
+       os_memcpy(buf, &stk->tk[16], 8);
+       os_memcpy(&stk->tk[16], &stk->tk[24], 8);
+       os_memcpy(&stk->tk[24], buf, 8);
        peerkey->tstk_set = 1;
 
        kde_buf_len = peerkey->rsnie_p_len +
@@ -736,7 +732,6 @@ static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm,
                                               struct wpa_eapol_ie_parse *kde)
 {
        u32 lifetime;
-       struct os_time now;
 
        if (kde->lifetime == NULL || kde->lifetime_len < sizeof(lifetime))
                return;
@@ -755,8 +750,6 @@ static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm,
                   lifetime, peerkey->lifetime);
        peerkey->lifetime = lifetime;
 
-       os_get_time(&now);
-       peerkey->expiration = now.sec + lifetime;
        eloop_cancel_timeout(wpa_supplicant_smk_timeout, sm, peerkey);
        eloop_register_timeout(lifetime, 0, wpa_supplicant_smk_timeout,
                               sm, peerkey);
@@ -766,11 +759,10 @@ static void wpa_supplicant_update_smk_lifetime(struct wpa_sm *sm,
 static void wpa_supplicant_process_stk_2_of_4(struct wpa_sm *sm,
                                              struct wpa_peerkey *peerkey,
                                              const struct wpa_eapol_key *key,
-                                             u16 ver)
+                                             u16 ver, const u8 *key_data,
+                                             size_t key_data_len)
 {
        struct wpa_eapol_ie_parse kde;
-       const u8 *keydata;
-       size_t len;
 
        wpa_printf(MSG_DEBUG, "RSN: RX message 2 of STK 4-Way Handshake from "
                   MACSTR " (ver=%d)", MAC2STR(peerkey->addr), ver);
@@ -779,16 +771,14 @@ static void wpa_supplicant_process_stk_2_of_4(struct wpa_sm *sm,
 
        /* RSN: msg 2/4 should contain SMKID for the selected SMK and RSN IE
         * from the peer. It may also include Lifetime KDE. */
-       keydata = (const u8 *) (key + 1);
-       len = WPA_GET_BE16(key->key_data_length);
-       wpa_hexdump(MSG_DEBUG, "RSN: msg 2/4 key data", keydata, len);
-       if (wpa_supplicant_parse_ies(keydata, len, &kde) < 0 ||
+       wpa_hexdump(MSG_DEBUG, "RSN: msg 2/4 key data", key_data, key_data_len);
+       if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0 ||
            kde.pmkid == NULL || kde.rsn_ie == NULL) {
                wpa_printf(MSG_DEBUG, "RSN: No SMKID or RSN IE in STK 2/4");
                return;
        }
 
-       if (os_memcmp(kde.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
+       if (os_memcmp_const(kde.pmkid, peerkey->smkid, PMKID_LEN) != 0) {
                wpa_hexdump(MSG_DEBUG, "RSN: Unknown SMKID in STK 2/4",
                            kde.pmkid, PMKID_LEN);
                return;
@@ -815,11 +805,11 @@ static void wpa_supplicant_process_stk_2_of_4(struct wpa_sm *sm,
 static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm,
                                              struct wpa_peerkey *peerkey,
                                              const struct wpa_eapol_key *key,
-                                             u16 ver)
+                                             u16 ver, const u8 *key_data,
+                                             size_t key_data_len)
 {
        struct wpa_eapol_ie_parse kde;
-       const u8 *keydata;
-       size_t len, key_len;
+       size_t key_len;
        const u8 *_key;
        u8 key_buf[32], rsc[6];
 
@@ -830,10 +820,8 @@ static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm,
 
        /* RSN: msg 3/4 should contain Initiator RSN IE. It may also include
         * Lifetime KDE. */
-       keydata = (const u8 *) (key + 1);
-       len = WPA_GET_BE16(key->key_data_length);
-       wpa_hexdump(MSG_DEBUG, "RSN: msg 3/4 key data", keydata, len);
-       if (wpa_supplicant_parse_ies(keydata, len, &kde) < 0) {
+       wpa_hexdump(MSG_DEBUG, "RSN: msg 3/4 key data", key_data, key_data_len);
+       if (wpa_supplicant_parse_ies(key_data, key_data_len, &kde) < 0) {
                wpa_printf(MSG_DEBUG, "RSN: Failed to parse key data in "
                           "STK 3/4");
                return;
@@ -864,15 +852,15 @@ static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm,
 
        if (wpa_supplicant_send_4_of_4(sm, peerkey->addr, key, ver,
                                       WPA_GET_BE16(key->key_info),
-                                      NULL, 0, &peerkey->stk))
+                                      &peerkey->stk))
                return;
 
-       _key = (u8 *) peerkey->stk.tk1;
+       _key = peerkey->stk.tk;
        if (peerkey->cipher == WPA_CIPHER_TKIP) {
                /* Swap Tx/Rx keys for Michael MIC */
                os_memcpy(key_buf, _key, 16);
-               os_memcpy(key_buf + 16, peerkey->stk.u.auth.rx_mic_key, 8);
-               os_memcpy(key_buf + 24, peerkey->stk.u.auth.tx_mic_key, 8);
+               os_memcpy(key_buf + 16, _key + 24, 8);
+               os_memcpy(key_buf + 24, _key + 16, 8);
                _key = key_buf;
                key_len = 32;
        } else
@@ -881,10 +869,12 @@ static void wpa_supplicant_process_stk_3_of_4(struct wpa_sm *sm,
        os_memset(rsc, 0, 6);
        if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1,
                           rsc, sizeof(rsc), _key, key_len) < 0) {
+               os_memset(key_buf, 0, sizeof(key_buf));
                wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the "
                           "driver.");
                return;
        }
+       os_memset(key_buf, 0, sizeof(key_buf));
 }
 
 
@@ -900,7 +890,7 @@ static void wpa_supplicant_process_stk_4_of_4(struct wpa_sm *sm,
 
        os_memset(rsc, 0, 6);
        if (wpa_sm_set_key(sm, peerkey->cipher, peerkey->addr, 0, 1,
-                          rsc, sizeof(rsc), (u8 *) peerkey->stk.tk1,
+                          rsc, sizeof(rsc), peerkey->stk.tk,
                           peerkey->cipher == WPA_CIPHER_TKIP ? 32 : 16) < 0) {
                wpa_printf(MSG_WARNING, "RSN: Failed to set STK to the "
                           "driver.");
@@ -921,27 +911,27 @@ static void wpa_supplicant_process_stk_4_of_4(struct wpa_sm *sm,
  */
 int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
                                 struct wpa_peerkey *peerkey,
-                                struct wpa_eapol_key *key, u16 ver,
+                                struct wpa_eapol_key_192 *key, u16 ver,
                                 const u8 *buf, size_t len)
 {
-       u8 mic[16];
+       u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+       size_t mic_len = 16;
        int ok = 0;
 
        if (peerkey->initiator && !peerkey->stk_set) {
                wpa_pmk_to_ptk(peerkey->smk, PMK_LEN, "Peer key expansion",
                               sm->own_addr, peerkey->addr,
                               peerkey->inonce, key->key_nonce,
-                              (u8 *) &peerkey->stk, sizeof(peerkey->stk),
-                              peerkey->use_sha256);
+                              &peerkey->stk, peerkey->akmp, peerkey->cipher);
                peerkey->stk_set = 1;
        }
 
-       os_memcpy(mic, key->key_mic, 16);
+       os_memcpy(mic, key->key_mic, mic_len);
        if (peerkey->tstk_set) {
-               os_memset(key->key_mic, 0, 16);
-               wpa_eapol_key_mic(peerkey->tstk.kck, ver, buf, len,
-                                 key->key_mic);
-               if (os_memcmp(mic, key->key_mic, 16) != 0) {
+               os_memset(key->key_mic, 0, mic_len);
+               wpa_eapol_key_mic(peerkey->tstk.kck, peerkey->tstk.kck_len,
+                                 sm->key_mgmt, ver, buf, len, key->key_mic);
+               if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
                        wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
                                   "when using TSTK - ignoring TSTK");
                } else {
@@ -950,14 +940,15 @@ int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
                        peerkey->stk_set = 1;
                        os_memcpy(&peerkey->stk, &peerkey->tstk,
                                  sizeof(peerkey->stk));
+                       os_memset(&peerkey->tstk, 0, sizeof(peerkey->tstk));
                }
        }
 
        if (!ok && peerkey->stk_set) {
-               os_memset(key->key_mic, 0, 16);
-               wpa_eapol_key_mic(peerkey->stk.kck, ver, buf, len,
-                                 key->key_mic);
-               if (os_memcmp(mic, key->key_mic, 16) != 0) {
+               os_memset(key->key_mic, 0, mic_len);
+               wpa_eapol_key_mic(peerkey->stk.kck, peerkey->stk.kck_len,
+                                 sm->key_mgmt, ver, buf, len, key->key_mic);
+               if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
                        wpa_printf(MSG_WARNING, "RSN: Invalid EAPOL-Key MIC "
                                   "- dropping packet");
                        return -1;
@@ -1026,10 +1017,7 @@ int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
                return -1;
        peerkey->initiator = 1;
        os_memcpy(peerkey->addr, peer, ETH_ALEN);
-#ifdef CONFIG_IEEE80211W
-       if (wpa_key_mgmt_sha256(sm->key_mgmt))
-               peerkey->use_sha256 = 1;
-#endif /* CONFIG_IEEE80211W */
+       peerkey->akmp = sm->key_mgmt;
 
        /* SMK M1:
         * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce,
@@ -1096,8 +1084,8 @@ int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
 
        wpa_printf(MSG_INFO, "RSN: Sending EAPOL-Key SMK M1 Request (peer "
                   MACSTR ")", MAC2STR(peer));
-       wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL,
-                          rbuf, rlen, req->key_mic);
+       wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, bssid,
+                          ETH_P_EAPOL, rbuf, rlen, req->key_mic);
 
        peerkey->next = sm->peerkey;
        sm->peerkey = peerkey;
@@ -1116,28 +1104,32 @@ void peerkey_deinit(struct wpa_sm *sm)
        while (peerkey) {
                prev = peerkey;
                peerkey = peerkey->next;
-               os_free(prev);
+               wpa_supplicant_peerkey_free(sm, prev);
        }
        sm->peerkey = NULL;
 }
 
 
 void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
-                          struct wpa_eapol_key *key, u16 key_info, u16 ver)
+                          struct wpa_eapol_key *key, u16 key_info, u16 ver,
+                          const u8 *key_data, size_t key_data_len)
 {
        if ((key_info & (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) ==
            (WPA_KEY_INFO_MIC | WPA_KEY_INFO_ACK)) {
                /* 3/4 STK 4-Way Handshake */
-               wpa_supplicant_process_stk_3_of_4(sm, peerkey, key, ver);
+               wpa_supplicant_process_stk_3_of_4(sm, peerkey, key, ver,
+                                                 key_data, key_data_len);
        } else if (key_info & WPA_KEY_INFO_ACK) {
                /* 1/4 STK 4-Way Handshake */
-               wpa_supplicant_process_stk_1_of_4(sm, peerkey, key, ver);
+               wpa_supplicant_process_stk_1_of_4(sm, peerkey, key, ver,
+                                                 key_data, key_data_len);
        } else if (key_info & WPA_KEY_INFO_SECURE) {
                /* 4/4 STK 4-Way Handshake */
                wpa_supplicant_process_stk_4_of_4(sm, peerkey, key, ver);
        } else {
                /* 2/4 STK 4-Way Handshake */
-               wpa_supplicant_process_stk_2_of_4(sm, peerkey, key, ver);
+               wpa_supplicant_process_stk_2_of_4(sm, peerkey, key, ver,
+                                                 key_data, key_data_len);
        }
 }
 
old mode 100644 (file)
new mode 100755 (executable)
index b8845f7..6ccd948
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - PeerKey for Direct Link Setup (DLS)
- * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -24,11 +24,10 @@ struct wpa_peerkey {
        int smk_complete;
        u8 smkid[PMKID_LEN];
        u32 lifetime;
-       os_time_t expiration;
        int cipher; /* Selected cipher (WPA_CIPHER_*) */
        u8 replay_counter[WPA_REPLAY_COUNTER_LEN];
        int replay_counter_set;
-       int use_sha256; /* whether AKMP indicate SHA256-based derivations */
+       int akmp;
 
        struct wpa_ptk stk, tstk;
        int stk_set, tstk_set;
@@ -39,10 +38,11 @@ struct wpa_peerkey {
 
 int peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
                                 struct wpa_peerkey *peerkey,
-                                struct wpa_eapol_key *key, u16 ver,
+                                struct wpa_eapol_key_192 *key, u16 ver,
                                 const u8 *buf, size_t len);
 void peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
-                          struct wpa_eapol_key *key, u16 key_info, u16 ver);
+                          struct wpa_eapol_key *key, u16 key_info, u16 ver,
+                          const u8 *key_data, size_t key_data_len);
 void peerkey_rx_eapol_smk(struct wpa_sm *sm, const u8 *src_addr,
                          struct wpa_eapol_key *key, size_t extra_len,
                          u16 key_info, u16 ver);
@@ -61,7 +61,8 @@ peerkey_verify_eapol_key_mic(struct wpa_sm *sm,
 
 static inline void
 peerkey_rx_eapol_4way(struct wpa_sm *sm, struct wpa_peerkey *peerkey,
-                     struct wpa_eapol_key *key, u16 key_info, u16 ver)
+                     struct wpa_eapol_key *key, u16 key_info, u16 ver,
+                     const u8 *key_data, size_t key_data_len)
 {
 }
 
old mode 100644 (file)
new mode 100755 (executable)
index 33fa1a2..ef7b683
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - RSN PMKSA cache
- * Copyright (c) 2004-2009, 2011-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2009, 2011-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -35,7 +35,7 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
 
 static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
 {
-       os_free(entry);
+       bin_clear_free(entry, sizeof(*entry));
 }
 
 
@@ -53,9 +53,9 @@ static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
 static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
 {
        struct rsn_pmksa_cache *pmksa = eloop_ctx;
-       struct os_time now;
+       struct os_reltime now;
 
-       os_get_time(&now);
+       os_get_reltime(&now);
        while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
                struct rsn_pmksa_cache_entry *entry = pmksa->pmksa;
                pmksa->pmksa = entry->next;
@@ -80,13 +80,13 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
 {
        int sec;
        struct rsn_pmksa_cache_entry *entry;
-       struct os_time now;
+       struct os_reltime now;
 
        eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
        eloop_cancel_timeout(pmksa_cache_reauth, pmksa, NULL);
        if (pmksa->pmksa == NULL)
                return;
-       os_get_time(&now);
+       os_get_reltime(&now);
        sec = pmksa->pmksa->expiration - now.sec;
        if (sec < 0)
                sec = 0;
@@ -109,6 +109,8 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
  * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init()
  * @pmk: The new pairwise master key
  * @pmk_len: PMK length in bytes, usually PMK_LEN (32)
+ * @kck: Key confirmation key or %NULL if not yet derived
+ * @kck_len: KCK length in bytes
  * @aa: Authenticator address
  * @spa: Supplicant address
  * @network_ctx: Network configuration context for this PMK
@@ -122,22 +124,31 @@ static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
  */
 struct rsn_pmksa_cache_entry *
 pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
+               const u8 *kck, size_t kck_len,
                const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
 {
        struct rsn_pmksa_cache_entry *entry, *pos, *prev;
-       struct os_time now;
+       struct os_reltime now;
 
        if (pmk_len > PMK_LEN)
                return NULL;
 
+       if (wpa_key_mgmt_suite_b(akmp) && !kck)
+               return NULL;
+
        entry = os_zalloc(sizeof(*entry));
        if (entry == NULL)
                return NULL;
        os_memcpy(entry->pmk, pmk, pmk_len);
        entry->pmk_len = pmk_len;
-       rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
-                 wpa_key_mgmt_sha256(akmp));
-       os_get_time(&now);
+       if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+               rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid);
+       else if (wpa_key_mgmt_suite_b(akmp))
+               rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
+       else
+               rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid,
+                         wpa_key_mgmt_sha256(akmp));
+       os_get_reltime(&now);
        entry->expiration = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime;
        entry->reauth_time = now.sec + pmksa->sm->dot11RSNAConfigPMKLifetime *
                pmksa->sm->dot11RSNAConfigPMKReauthThreshold / 100;
@@ -152,9 +163,9 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
        while (pos) {
                if (os_memcmp(aa, pos->aa, ETH_ALEN) == 0) {
                        if (pos->pmk_len == pmk_len &&
-                           os_memcmp(pos->pmk, pmk, pmk_len) == 0 &&
-                           os_memcmp(pos->pmkid, entry->pmkid, PMKID_LEN) ==
-                           0) {
+                           os_memcmp_const(pos->pmk, pmk, pmk_len) == 0 &&
+                           os_memcmp_const(pos->pmkid, entry->pmkid,
+                                           PMKID_LEN) == 0) {
                                wpa_printf(MSG_DEBUG, "WPA: reusing previous "
                                           "PMKSA entry");
                                os_free(entry);
@@ -333,6 +344,7 @@ pmksa_cache_clone_entry(struct rsn_pmksa_cache *pmksa,
        struct rsn_pmksa_cache_entry *new_entry;
 
        new_entry = pmksa_cache_add(pmksa, old_entry->pmk, old_entry->pmk_len,
+                                   NULL, 0,
                                    aa, pmksa->sm->own_addr,
                                    old_entry->network_ctx, old_entry->akmp);
        if (new_entry == NULL)
@@ -466,13 +478,13 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
        int i, ret;
        char *pos = buf;
        struct rsn_pmksa_cache_entry *entry;
-       struct os_time now;
+       struct os_reltime now;
 
-       os_get_time(&now);
+       os_get_reltime(&now);
        ret = os_snprintf(pos, buf + len - pos,
                          "Index / AA / PMKID / expiration (in seconds) / "
                          "opportunistic\n");
-       if (ret < 0 || ret >= buf + len - pos)
+       if (os_snprintf_error(buf + len - pos, ret))
                return pos - buf;
        pos += ret;
        i = 0;
@@ -481,7 +493,7 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
                i++;
                ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
                                  i, MAC2STR(entry->aa));
-               if (ret < 0 || ret >= buf + len - pos)
+               if (os_snprintf_error(buf + len - pos, ret))
                        return pos - buf;
                pos += ret;
                pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
@@ -489,7 +501,7 @@ int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
                ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
                                  (int) (entry->expiration - now.sec),
                                  entry->opportunistic);
-               if (ret < 0 || ret >= buf + len - pos)
+               if (os_snprintf_error(buf + len - pos, ret))
                        return pos - buf;
                pos += ret;
                entry = entry->next;
old mode 100644 (file)
new mode 100755 (executable)
index 6cbf89a..f8e040e
@@ -57,6 +57,7 @@ struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa,
 int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
 struct rsn_pmksa_cache_entry *
 pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
+               const u8 *kck, size_t kck_len,
                const u8 *aa, const u8 *spa, void *network_ctx, int akmp);
 struct rsn_pmksa_cache_entry * pmksa_cache_get_current(struct wpa_sm *sm);
 void pmksa_cache_clear_current(struct wpa_sm *sm);
@@ -104,6 +105,7 @@ static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf,
 
 static inline struct rsn_pmksa_cache_entry *
 pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len,
+               const u8 *kck, size_t kck_len,
                const u8 *aa, const u8 *spa, void *network_ctx, int akmp)
 {
        return NULL;
old mode 100644 (file)
new mode 100755 (executable)
index c51620e..c6534af
@@ -1,6 +1,6 @@
 /*
  * RSN pre-authentication (supplicant)
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -70,13 +70,14 @@ static void rsn_preauth_receive(void *ctx, const u8 *src_addr,
 }
 
 
-static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success,
+static void rsn_preauth_eapol_cb(struct eapol_sm *eapol,
+                                enum eapol_supp_result result,
                                 void *ctx)
 {
        struct wpa_sm *sm = ctx;
        u8 pmk[PMK_LEN];
 
-       if (success) {
+       if (result == EAPOL_SUPP_RESULT_SUCCESS) {
                int res, pmk_len;
                pmk_len = PMK_LEN;
                res = eapol_sm_get_key(eapol, pmk, PMK_LEN);
@@ -93,6 +94,7 @@ static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success,
                                        pmk, pmk_len);
                        sm->pmk_len = pmk_len;
                        pmksa_cache_add(sm->pmksa, pmk, pmk_len,
+                                       NULL, 0,
                                        sm->preauth_bssid, sm->own_addr,
                                        sm->network_ctx,
                                        WPA_KEY_MGMT_IEEE8021X);
@@ -100,13 +102,14 @@ static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success,
                        wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
                                "RSN: failed to get master session key from "
                                "pre-auth EAPOL state machines");
-                       success = 0;
+                       result = EAPOL_SUPP_RESULT_FAILURE;
                }
        }
 
        wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with "
                MACSTR " %s", MAC2STR(sm->preauth_bssid),
-               success ? "completed successfully" : "failed");
+               result == EAPOL_SUPP_RESULT_SUCCESS ? "completed successfully" :
+               "failed");
 
        rsn_preauth_deinit(sm);
        rsn_preauth_candidate_process(sm);
@@ -169,6 +172,7 @@ int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst,
 {
        struct eapol_config eapol_conf;
        struct eapol_ctx *ctx;
+       int ret;
 
        if (sm->preauth_eapol)
                return -1;
@@ -194,14 +198,16 @@ int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst,
                        wpa_printf(MSG_WARNING, "RSN: Failed to initialize L2 "
                                   "packet processing (bridge) for "
                                   "pre-authentication");
-                       return -2;
+                       ret = -2;
+                       goto fail;
                }
        }
 
        ctx = os_zalloc(sizeof(*ctx));
        if (ctx == NULL) {
                wpa_printf(MSG_WARNING, "Failed to allocate EAPOL context.");
-               return -4;
+               ret = -4;
+               goto fail;
        }
        ctx->ctx = sm->ctx->ctx;
        ctx->msg_ctx = sm->ctx->ctx;
@@ -219,7 +225,8 @@ int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst,
                os_free(ctx);
                wpa_printf(MSG_WARNING, "RSN: Failed to initialize EAPOL "
                           "state machines for pre-authentication");
-               return -3;
+               ret = -3;
+               goto fail;
        }
        os_memset(&eapol_conf, 0, sizeof(eapol_conf));
        eapol_conf.accept_802_1x_keys = 0;
@@ -244,6 +251,15 @@ int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst,
                               rsn_preauth_timeout, sm, NULL);
 
        return 0;
+
+fail:
+       if (sm->l2_preauth_br) {
+               l2_packet_deinit(sm->l2_preauth_br);
+               sm->l2_preauth_br = NULL;
+       }
+       l2_packet_deinit(sm->l2_preauth);
+       sm->l2_preauth = NULL;
+       return ret;
 }
 
 
@@ -296,7 +312,9 @@ void rsn_preauth_candidate_process(struct wpa_sm *sm)
            sm->proto != WPA_PROTO_RSN ||
            wpa_sm_get_state(sm) != WPA_COMPLETED ||
            (sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X &&
-            sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SHA256)) {
+            sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SHA256 &&
+            sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SUITE_B &&
+            sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)) {
                wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: not in suitable "
                        "state for new pre-authentication");
                return; /* invalid state for new pre-auth */
@@ -389,6 +407,18 @@ void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid,
        dl_list_for_each(pos, &sm->pmksa_candidates,
                         struct rsn_pmksa_candidate, list) {
                if (cand->priority <= pos->priority) {
+                       if (!pos->list.prev) {
+                               /*
+                                * This cannot really happen in pracrice since
+                                * pos was fetched from the list and the prev
+                                * pointer must be set. It looks like clang
+                                * static analyzer gets confused with the
+                                * dl_list_del(&cand->list) call above and ends
+                                * up assuming pos->list.prev could be NULL.
+                                */
+                               os_free(cand);
+                               return;
+                       }
                        dl_list_add(pos->list.prev, &cand->list);
                        cand = NULL;
                        break;
@@ -485,7 +515,7 @@ int rsn_preauth_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
        if (sm->preauth_eapol) {
                ret = os_snprintf(pos, end - pos, "Pre-authentication "
                                  "EAPOL state machines:\n");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
                res = eapol_sm_get_status(sm->preauth_eapol,
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
index 539aa25..c1d7749
@@ -33,6 +33,7 @@
 #define TDLS_TESTING_NO_TPK_EXPIRATION BIT(8)
 #define TDLS_TESTING_DECLINE_RESP BIT(9)
 #define TDLS_TESTING_IGNORE_AP_PROHIBIT BIT(10)
+#define TDLS_TESTING_WRONG_MIC BIT(11)
 unsigned int tdls_testing = 0;
 #endif /* CONFIG_TDLS_TESTING */
 
@@ -81,6 +82,10 @@ struct wpa_tdls_frame {
 static u8 * wpa_add_tdls_timeoutie(u8 *pos, u8 *ie, size_t ie_len, u32 tsecs);
 static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx);
 static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer);
+static void wpa_tdls_disable_peer_link(struct wpa_sm *sm,
+                                      struct wpa_tdls_peer *peer);
+static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr,
+                                 u16 reason_code);
 
 
 #define TDLS_MAX_IE_LEN 80
@@ -107,6 +112,7 @@ struct wpa_tdls_peer {
        } tpk;
        int tpk_set;
        int tpk_success;
+       int tpk_in_progress;
 
        struct tpk_timer {
                u8 dest[ETH_ALEN];
@@ -115,6 +121,7 @@ struct wpa_tdls_peer {
                u8 action_code; /* TDLS frame type */
                u8 dialog_token;
                u16 status_code;
+               u32 peer_capab;
                int buf_len;    /* length of TPK message for retransmission */
                u8 *buf;        /* buffer for TPK message */
        } sm_tmr;
@@ -133,6 +140,17 @@ struct wpa_tdls_peer {
 
        u8 *ext_capab;
        size_t ext_capab_len;
+
+       u8 *supp_channels;
+       size_t supp_channels_len;
+
+       u8 *supp_oper_classes;
+       size_t supp_oper_classes_len;
+
+       u8 wmm_capable;
+
+       /* channel switch currently enabled */
+       int chan_switch_enabled;
 };
 
 
@@ -202,26 +220,30 @@ static int wpa_tdls_set_key(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
 
 static int wpa_tdls_send_tpk_msg(struct wpa_sm *sm, const u8 *dst,
                                 u8 action_code, u8 dialog_token,
-                                u16 status_code, const u8 *buf, size_t len)
+                                u16 status_code, u32 peer_capab,
+                                int initiator, const u8 *buf, size_t len)
 {
        return wpa_sm_send_tdls_mgmt(sm, dst, action_code, dialog_token,
-                                    status_code, buf, len);
+                                    status_code, peer_capab, initiator, buf,
+                                    len);
 }
 
 
 static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code,
-                            u8 dialog_token, u16 status_code,
-                            const u8 *msg, size_t msg_len)
+                            u8 dialog_token, u16 status_code, u32 peer_capab,
+                            int initiator, const u8 *msg, size_t msg_len)
 {
        struct wpa_tdls_peer *peer;
 
        wpa_printf(MSG_DEBUG, "TDLS: TPK send dest=" MACSTR " action_code=%u "
-                  "dialog_token=%u status_code=%u msg_len=%u",
+                  "dialog_token=%u status_code=%u peer_capab=%u initiator=%d "
+                  "msg_len=%u",
                   MAC2STR(dest), action_code, dialog_token, status_code,
-                  (unsigned int) msg_len);
+                  peer_capab, initiator, (unsigned int) msg_len);
 
        if (wpa_tdls_send_tpk_msg(sm, dest, action_code, dialog_token,
-                                 status_code, msg, msg_len)) {
+                                 status_code, peer_capab, initiator, msg,
+                                 msg_len)) {
                wpa_printf(MSG_INFO, "TDLS: Failed to send message "
                           "(action_code=%u)", action_code);
                return -1;
@@ -259,6 +281,7 @@ static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code,
        peer->sm_tmr.action_code = action_code;
        peer->sm_tmr.dialog_token = dialog_token;
        peer->sm_tmr.status_code = status_code;
+       peer->sm_tmr.peer_capab = peer_capab;
        peer->sm_tmr.buf_len = msg_len;
        os_free(peer->sm_tmr.buf);
        peer->sm_tmr.buf = os_malloc(msg_len);
@@ -276,21 +299,13 @@ static int wpa_tdls_tpk_send(struct wpa_sm *sm, const u8 *dest, u8 action_code,
 
 
 static int wpa_tdls_do_teardown(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
-                               u16 reason_code, int free_peer)
+                               u16 reason_code)
 {
        int ret;
 
-       if (sm->tdls_external_setup) {
-               ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code);
-
-               /* disable the link after teardown was sent */
-               wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
-       } else {
-               ret = wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr);
-       }
-
-       if (sm->tdls_external_setup || free_peer)
-               wpa_tdls_peer_free(sm, peer);
+       ret = wpa_tdls_send_teardown(sm, peer->addr, reason_code);
+       /* disable the link after teardown was sent */
+       wpa_tdls_disable_peer_link(sm, peer);
 
        return ret;
 }
@@ -323,6 +338,8 @@ static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx)
                                          peer->sm_tmr.action_code,
                                          peer->sm_tmr.dialog_token,
                                          peer->sm_tmr.status_code,
+                                         peer->sm_tmr.peer_capab,
+                                         peer->initiator,
                                          peer->sm_tmr.buf,
                                          peer->sm_tmr.buf_len)) {
                        wpa_printf(MSG_INFO, "TDLS: Failed to retry "
@@ -338,7 +355,7 @@ static void wpa_tdls_tpk_retry_timeout(void *eloop_ctx, void *timeout_ctx)
 
                wpa_printf(MSG_DEBUG, "TDLS: Sending Teardown Request");
                wpa_tdls_do_teardown(sm, peer,
-                                    WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, 1);
+                                    WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
        }
 }
 
@@ -554,7 +571,7 @@ static int wpa_supplicant_verify_tdls_mic(u8 trans_seq,
                wpa_tdls_ftie_mic(peer->tpk.kck, trans_seq, lnkid,
                                  peer->rsnie_p, timeoutie, (u8 *) ftie,
                                  mic);
-               if (os_memcmp(mic, ftie->mic, 16) != 0) {
+               if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
                        wpa_printf(MSG_INFO, "TDLS: Invalid MIC in FTIE - "
                                   "dropping packet");
                        wpa_hexdump(MSG_DEBUG, "TDLS: Received MIC",
@@ -581,7 +598,7 @@ static int wpa_supplicant_verify_tdls_mic_teardown(
        if (peer->tpk_set) {
                wpa_tdls_key_mic_teardown(peer->tpk.kck, trans_seq, rcode,
                                          dtoken, lnkid, (u8 *) ftie, mic);
-               if (os_memcmp(mic, ftie->mic, 16) != 0) {
+               if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
                        wpa_printf(MSG_INFO, "TDLS: Invalid MIC in Teardown - "
                                   "dropping packet");
                        return -1;
@@ -616,12 +633,38 @@ static void wpa_tdls_tpk_timeout(void *eloop_ctx, void *timeout_ctx)
                wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime expired for " MACSTR
                           " - tear down", MAC2STR(peer->addr));
                wpa_tdls_do_teardown(sm, peer,
-                                    WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, 1);
+                                    WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
        }
 }
 
 
-static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+static void wpa_tdls_peer_remove_from_list(struct wpa_sm *sm,
+                                          struct wpa_tdls_peer *peer)
+{
+       struct wpa_tdls_peer *cur, *prev;
+
+       cur = sm->tdls;
+       prev = NULL;
+       while (cur && cur != peer) {
+               prev = cur;
+               cur = cur->next;
+       }
+
+       if (cur != peer) {
+               wpa_printf(MSG_ERROR, "TDLS: Could not find peer " MACSTR
+                          " to remove it from the list",
+                          MAC2STR(peer->addr));
+               return;
+       }
+
+       if (prev)
+               prev->next = peer->next;
+       else
+               sm->tdls = peer->next;
+}
+
+
+static void wpa_tdls_peer_clear(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
 {
        wpa_printf(MSG_DEBUG, "TDLS: Clear state for peer " MACSTR,
                   MAC2STR(peer->addr));
@@ -629,6 +672,7 @@ static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
        eloop_cancel_timeout(wpa_tdls_tpk_retry_timeout, sm, peer);
        peer->reconfig_key = 0;
        peer->initiator = 0;
+       peer->tpk_in_progress = 0;
        os_free(peer->sm_tmr.buf);
        peer->sm_tmr.buf = NULL;
        os_free(peer->ht_capabilities);
@@ -637,15 +681,30 @@ static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
        peer->vht_capabilities = NULL;
        os_free(peer->ext_capab);
        peer->ext_capab = NULL;
+       os_free(peer->supp_channels);
+       peer->supp_channels = NULL;
+       os_free(peer->supp_oper_classes);
+       peer->supp_oper_classes = NULL;
        peer->rsnie_i_len = peer->rsnie_p_len = 0;
        peer->cipher = 0;
+       peer->qos_info = 0;
+       peer->wmm_capable = 0;
        peer->tpk_set = peer->tpk_success = 0;
+       peer->chan_switch_enabled = 0;
        os_memset(&peer->tpk, 0, sizeof(peer->tpk));
        os_memset(peer->inonce, 0, WPA_NONCE_LEN);
        os_memset(peer->rnonce, 0, WPA_NONCE_LEN);
 }
 
 
+static void wpa_tdls_peer_free(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
+{
+       wpa_tdls_peer_clear(sm, peer);
+       wpa_tdls_peer_remove_from_list(sm, peer);
+       os_free(peer);
+}
+
+
 static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
                            struct wpa_tdls_lnkid *lnkid)
 {
@@ -662,7 +721,8 @@ static void wpa_tdls_linkid(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
 }
 
 
-int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code)
+static int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr,
+                                 u16 reason_code)
 {
        struct wpa_tdls_peer *peer;
        struct wpa_tdls_ftie *ftie;
@@ -686,6 +746,13 @@ int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code)
                return 0;
        }
 
+       /* Cancel active channel switch before teardown */
+       if (peer->chan_switch_enabled) {
+               wpa_printf(MSG_DEBUG, "TDLS: First returning link with " MACSTR
+                          " to base channel", MAC2STR(addr));
+               wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
+       }
+
        dialog_token = peer->dtoken;
 
        wpa_printf(MSG_DEBUG, "TDLS: TDLS Teardown for " MACSTR,
@@ -741,12 +808,9 @@ skip_ies:
 
        /* request driver to send Teardown using this FTIE */
        wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_TEARDOWN, 0,
-                         reason_code, rbuf, pos - rbuf);
+                         reason_code, 0, peer->initiator, rbuf, pos - rbuf);
        os_free(rbuf);
 
-       /* clear the Peerkey statemachine */
-       wpa_tdls_peer_free(sm, peer);
-
        return 0;
 }
 
@@ -775,11 +839,19 @@ int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code)
                return -1;
        }
 
-       return wpa_tdls_do_teardown(sm, peer, reason_code, 0);
+       return wpa_tdls_do_teardown(sm, peer, reason_code);
+}
+
+
+static void wpa_tdls_disable_peer_link(struct wpa_sm *sm,
+                                      struct wpa_tdls_peer *peer)
+{
+       wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+       wpa_tdls_peer_free(sm, peer);
 }
 
 
-void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr)
+void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr)
 {
        struct wpa_tdls_peer *peer;
 
@@ -788,10 +860,49 @@ void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr)
                        break;
        }
 
-       if (peer) {
+       if (!peer || !peer->tpk_success) {
+               wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
+                          " not connected - cannot teardown unreachable link",
+                          MAC2STR(addr));
+               return;
+       }
+
+       if (wpa_tdls_is_external_setup(sm)) {
+               /*
+                * Get us on the base channel, disable the link, send a
+                * teardown packet through the AP, and then reset link data.
+                */
+               if (peer->chan_switch_enabled)
+                       wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
                wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, addr);
+               wpa_tdls_send_teardown(sm, addr,
+                                      WLAN_REASON_TDLS_TEARDOWN_UNREACHABLE);
                wpa_tdls_peer_free(sm, peer);
+       } else {
+               wpa_tdls_disable_peer_link(sm, peer);
+       }
+}
+
+
+const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr)
+{
+       struct wpa_tdls_peer *peer;
+
+       if (sm->tdls_disabled || !sm->tdls_supported)
+               return "disabled";
+
+       for (peer = sm->tdls; peer; peer = peer->next) {
+               if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+                       break;
        }
+
+       if (peer == NULL)
+               return "peer does not exist";
+
+       if (!peer->tpk_success)
+               return "peer not connected";
+
+       return "connected";
 }
 
 
@@ -828,10 +939,15 @@ static int wpa_tdls_recv_teardown(struct wpa_sm *sm, const u8 *src_addr,
                   " (reason code %u)", MAC2STR(src_addr), reason_code);
 
        ielen = len - (pos - buf); /* start of IE in buf */
-       if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) {
-               wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in Teardown");
-               return -1;
-       }
+
+       /*
+        * Don't reject the message if failing to parse IEs. The IEs we need are
+        * explicitly checked below. Some APs may add arbitrary padding to the
+        * end of short TDLS frames and that would look like invalid IEs.
+        */
+       if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0)
+               wpa_printf(MSG_DEBUG,
+                          "TDLS: Failed to parse IEs in Teardown - ignore as an interop workaround");
 
        if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
                wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TDLS "
@@ -864,11 +980,7 @@ skip_ftie:
         * Request the driver to disable the direct link and clear associated
         * keys.
         */
-       wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
-
-       /* clear the Peerkey statemachine */
-       wpa_tdls_peer_free(sm, peer);
-
+       wpa_tdls_disable_peer_link(sm, peer);
        return 0;
 }
 
@@ -878,17 +990,19 @@ skip_ftie:
  *     appropriate status code mentioning reason for error/failure.
  * @dst        - MAC addr of Peer station
  * @tdls_action - TDLS frame type for which error code is sent
+ * @initiator   - was this end the initiator of the connection
  * @status     - status code mentioning reason
  */
 
 static int wpa_tdls_send_error(struct wpa_sm *sm, const u8 *dst,
-                              u8 tdls_action, u8 dialog_token, u16 status)
+                              u8 tdls_action, u8 dialog_token, int initiator,
+                              u16 status)
 {
        wpa_printf(MSG_DEBUG, "TDLS: Sending error to " MACSTR
                   " (action=%u status=%u)",
                   MAC2STR(dst), tdls_action, status);
        return wpa_tdls_tpk_send(sm, dst, tdls_action, dialog_token, status,
-                                NULL, 0);
+                                0, initiator, NULL, 0);
 }
 
 
@@ -932,6 +1046,7 @@ static int wpa_tdls_send_tpk_m1(struct wpa_sm *sm,
        u8 *rbuf, *pos, *count_pos;
        u16 count;
        struct rsn_ie_hdr *hdr;
+       int status;
 
        if (!wpa_tdls_get_privacy(sm)) {
                wpa_printf(MSG_DEBUG, "TDLS: No security used on the link");
@@ -1092,11 +1207,11 @@ skip_ies:
                   "Handshake Message 1 (peer " MACSTR ")",
                   MAC2STR(peer->addr));
 
-       wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST, 1, 0,
-                         rbuf, pos - rbuf);
+       status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_SETUP_REQUEST,
+                                  1, 0, 0, peer->initiator, rbuf, pos - rbuf);
        os_free(rbuf);
 
-       return 0;
+       return status;
 }
 
 
@@ -1110,6 +1225,7 @@ static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm,
        u32 lifetime;
        struct wpa_tdls_timeoutie timeoutie;
        struct wpa_tdls_ftie *ftie;
+       int status;
 
        buf_len = 0;
        if (wpa_tdls_get_privacy(sm)) {
@@ -1173,13 +1289,20 @@ static int wpa_tdls_send_tpk_m2(struct wpa_sm *sm,
        /* compute MIC before sending */
        wpa_tdls_ftie_mic(peer->tpk.kck, 2, (u8 *) lnkid, peer->rsnie_p,
                          (u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
+#ifdef CONFIG_TDLS_TESTING
+       if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
+               wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
+               ftie->mic[0] ^= 0x01;
+       }
+#endif /* CONFIG_TDLS_TESTING */
 
 skip_ies:
-       wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0,
-                         rbuf, pos - rbuf);
+       status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE,
+                                  dtoken, 0, 0, peer->initiator, rbuf,
+                                  pos - rbuf);
        os_free(rbuf);
 
-       return 0;
+       return status;
 }
 
 
@@ -1193,6 +1316,8 @@ static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm,
        struct wpa_tdls_ftie *ftie;
        struct wpa_tdls_timeoutie timeoutie;
        u32 lifetime;
+       int status;
+       u32 peer_capab = 0;
 
        buf_len = 0;
        if (wpa_tdls_get_privacy(sm)) {
@@ -1254,13 +1379,28 @@ static int wpa_tdls_send_tpk_m3(struct wpa_sm *sm,
        /* compute MIC before sending */
        wpa_tdls_ftie_mic(peer->tpk.kck, 3, (u8 *) lnkid, peer->rsnie_p,
                          (u8 *) &timeoutie, (u8 *) ftie, ftie->mic);
+#ifdef CONFIG_TDLS_TESTING
+       if (tdls_testing & TDLS_TESTING_WRONG_MIC) {
+               wpa_printf(MSG_DEBUG, "TDLS: Testing - use wrong MIC");
+               ftie->mic[0] ^= 0x01;
+       }
+#endif /* CONFIG_TDLS_TESTING */
 
 skip_ies:
-       wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 0,
-                         rbuf, pos - rbuf);
+
+       if (peer->vht_capabilities)
+               peer_capab |= TDLS_PEER_VHT;
+       if (peer->ht_capabilities)
+               peer_capab |= TDLS_PEER_HT;
+       if (peer->wmm_capable)
+               peer_capab |= TDLS_PEER_WMM;
+
+       status = wpa_tdls_tpk_send(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM,
+                                  dtoken, 0, peer_capab, peer->initiator,
+                                  rbuf, pos - rbuf);
        os_free(rbuf);
 
-       return 0;
+       return status;
 }
 
 
@@ -1268,11 +1408,85 @@ static int wpa_tdls_send_discovery_response(struct wpa_sm *sm,
                                            struct wpa_tdls_peer *peer,
                                            u8 dialog_token)
 {
+       size_t buf_len = 0;
+       struct wpa_tdls_timeoutie timeoutie;
+       u16 rsn_capab;
+       u8 *rbuf, *pos, *count_pos;
+       u16 count;
+       struct rsn_ie_hdr *hdr;
+       int status;
+
        wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Discovery Response "
                   "(peer " MACSTR ")", MAC2STR(peer->addr));
+       if (!wpa_tdls_get_privacy(sm))
+               goto skip_rsn_ies;
 
-       return wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE,
-                                dialog_token, 0, NULL, 0);
+       /* Filling RSN IE */
+       hdr = (struct rsn_ie_hdr *) peer->rsnie_i;
+       hdr->elem_id = WLAN_EID_RSN;
+       WPA_PUT_LE16(hdr->version, RSN_VERSION);
+       pos = (u8 *) (hdr + 1);
+       RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NO_GROUP_ADDRESSED);
+       pos += RSN_SELECTOR_LEN;
+       count_pos = pos;
+       pos += 2;
+       count = 0;
+
+       /*
+       * AES-CCMP is the default encryption preferred for TDLS, so
+       * RSN IE is filled only with CCMP cipher suite.
+       * Note: TKIP is not used to encrypt TDLS link.
+       *
+       * Regardless of the cipher used on the AP connection, select CCMP
+       * here.
+       */
+       RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP);
+       pos += RSN_SELECTOR_LEN;
+       count++;
+       WPA_PUT_LE16(count_pos, count);
+       WPA_PUT_LE16(pos, 1);
+       pos += 2;
+       RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_TPK_HANDSHAKE);
+       pos += RSN_SELECTOR_LEN;
+
+       rsn_capab = WPA_CAPABILITY_PEERKEY_ENABLED;
+       rsn_capab |= RSN_NUM_REPLAY_COUNTERS_16 << 2;
+       WPA_PUT_LE16(pos, rsn_capab);
+       pos += 2;
+       hdr->len = (pos - (u8 *) hdr) - 2;
+       peer->rsnie_i_len = pos - peer->rsnie_i;
+
+       wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE for Discovery Response",
+                   (u8 *) hdr, hdr->len + 2);
+skip_rsn_ies:
+       buf_len = 0;
+       if (wpa_tdls_get_privacy(sm)) {
+               /* Peer RSN IE, Lifetime */
+               buf_len += peer->rsnie_i_len +
+                       sizeof(struct wpa_tdls_timeoutie);
+       }
+       rbuf = os_zalloc(buf_len + 1);
+       if (rbuf == NULL) {
+               wpa_tdls_peer_free(sm, peer);
+               return -1;
+       }
+       pos = rbuf;
+
+       if (!wpa_tdls_get_privacy(sm))
+               goto skip_ies;
+       /* Initiator RSN IE */
+       pos = wpa_add_ie(pos, peer->rsnie_i, peer->rsnie_i_len);
+       /* Lifetime */
+       peer->lifetime = TPK_LIFETIME;
+       pos = wpa_add_tdls_timeoutie(pos, (u8 *) &timeoutie,
+                                    sizeof(timeoutie), peer->lifetime);
+       wpa_printf(MSG_DEBUG, "TDLS: TPK lifetime %u seconds", peer->lifetime);
+skip_ies:
+       status = wpa_tdls_tpk_send(sm, peer->addr, WLAN_TDLS_DISCOVERY_RESPONSE,
+                                  dialog_token, 0, 0, 0, rbuf, pos - rbuf);
+       os_free(rbuf);
+
+       return status;
 }
 
 
@@ -1298,10 +1512,17 @@ wpa_tdls_process_discovery_request(struct wpa_sm *sm, const u8 *addr,
 
        dialog_token = buf[sizeof(struct wpa_tdls_frame)];
 
+       /*
+        * Some APs will tack on a weird IE to the end of a TDLS
+        * discovery request packet. This needn't fail the response,
+        * since the required IE are verified separately.
+        */
        if (wpa_supplicant_parse_ies(buf + sizeof(struct wpa_tdls_frame) + 1,
                                     len - (sizeof(struct wpa_tdls_frame) + 1),
-                                    &kde) < 0)
-               return -1;
+                                    &kde) < 0) {
+               wpa_printf(MSG_DEBUG,
+                          "TDLS: Failed to parse IEs in Discovery Request - ignore as an interop workaround");
+       }
 
        if (!kde.lnkid) {
                wpa_printf(MSG_DEBUG, "TDLS: Link ID not found in Discovery "
@@ -1333,7 +1554,7 @@ int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr)
        wpa_printf(MSG_DEBUG, "TDLS: Sending Discovery Request to peer "
                   MACSTR, MAC2STR(addr));
        return wpa_tdls_tpk_send(sm, addr, WLAN_TDLS_DISCOVERY_REQUEST,
-                                1, 0, NULL, 0);
+                                1, 0, 0, 1, NULL, 0);
 }
 
 
@@ -1433,6 +1654,100 @@ static int copy_peer_ext_capab(const struct wpa_eapol_ie_parse *kde,
 }
 
 
+static int copy_peer_wmm_capab(const struct wpa_eapol_ie_parse *kde,
+                              struct wpa_tdls_peer *peer)
+{
+       struct wmm_information_element *wmm;
+
+       if (!kde->wmm) {
+               wpa_printf(MSG_DEBUG, "TDLS: No supported WMM capabilities received");
+               return 0;
+       }
+
+       if (kde->wmm_len < sizeof(struct wmm_information_element)) {
+               wpa_printf(MSG_DEBUG, "TDLS: Invalid supported WMM capabilities received");
+               return -1;
+       }
+
+       wmm = (struct wmm_information_element *) kde->wmm;
+       peer->qos_info = wmm->qos_info;
+
+       peer->wmm_capable = 1;
+
+       wpa_printf(MSG_DEBUG, "TDLS: Peer WMM QOS Info 0x%x", peer->qos_info);
+       return 0;
+}
+
+
+static int copy_peer_supp_channels(const struct wpa_eapol_ie_parse *kde,
+                                  struct wpa_tdls_peer *peer)
+{
+       if (!kde->supp_channels) {
+               wpa_printf(MSG_DEBUG, "TDLS: No supported channels received");
+               return 0;
+       }
+
+       if (!peer->supp_channels ||
+           peer->supp_channels_len < kde->supp_channels_len) {
+               os_free(peer->supp_channels);
+               peer->supp_channels = os_zalloc(kde->supp_channels_len);
+               if (peer->supp_channels == NULL)
+                       return -1;
+       }
+
+       peer->supp_channels_len = kde->supp_channels_len;
+
+       os_memcpy(peer->supp_channels, kde->supp_channels,
+                 peer->supp_channels_len);
+       wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Channels",
+                   (u8 *) peer->supp_channels, peer->supp_channels_len);
+       return 0;
+}
+
+
+static int copy_peer_supp_oper_classes(const struct wpa_eapol_ie_parse *kde,
+                                      struct wpa_tdls_peer *peer)
+{
+       if (!kde->supp_oper_classes) {
+               wpa_printf(MSG_DEBUG, "TDLS: No supported operating classes received");
+               return 0;
+       }
+
+       if (!peer->supp_oper_classes ||
+           peer->supp_oper_classes_len < kde->supp_oper_classes_len) {
+               os_free(peer->supp_oper_classes);
+               peer->supp_oper_classes = os_zalloc(kde->supp_oper_classes_len);
+               if (peer->supp_oper_classes == NULL)
+                       return -1;
+       }
+
+       peer->supp_oper_classes_len = kde->supp_oper_classes_len;
+       os_memcpy(peer->supp_oper_classes, kde->supp_oper_classes,
+                 peer->supp_oper_classes_len);
+       wpa_hexdump(MSG_DEBUG, "TDLS: Peer Supported Operating Classes",
+                   (u8 *) peer->supp_oper_classes,
+                   peer->supp_oper_classes_len);
+       return 0;
+}
+
+
+static int wpa_tdls_addset_peer(struct wpa_sm *sm, struct wpa_tdls_peer *peer,
+                               int add)
+{
+       return wpa_sm_tdls_peer_addset(sm, peer->addr, add, peer->aid,
+                                      peer->capability,
+                                      peer->supp_rates, peer->supp_rates_len,
+                                      peer->ht_capabilities,
+                                      peer->vht_capabilities,
+                                      peer->qos_info, peer->wmm_capable,
+                                      peer->ext_capab, peer->ext_capab_len,
+                                      peer->supp_channels,
+                                      peer->supp_channels_len,
+                                      peer->supp_oper_classes,
+                                      peer->supp_oper_classes_len);
+}
+
+
 static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
                                   const u8 *buf, size_t len)
 {
@@ -1480,28 +1795,16 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
                        wpa_printf(MSG_DEBUG, "TDLS: TDLS Setup Request while "
                                   "direct link is enabled - tear down the "
                                   "old link first");
-#if 0
-                       /* TODO: Disabling the link would be more proper
-                        * operation here, but it seems to trigger a race with
-                        * some drivers handling the new request frame. */
-                       wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
-#else
-                       if (sm->tdls_external_setup)
-                               wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK,
-                                                src_addr);
-                       else
-                               wpa_tdls_del_key(sm, peer);
-#endif
-                       wpa_tdls_peer_free(sm, peer);
-               }
-
-               /*
-                * An entry is already present, so check if we already sent a
-                * TDLS Setup Request. If so, compare MAC addresses and let the
-                * STA with the lower MAC address continue as the initiator.
-                * The other negotiation is terminated.
-                */
-               if (peer->initiator) {
+                       wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+                       wpa_tdls_peer_clear(sm, peer);
+               } else if (peer->initiator) {
+                       /*
+                        * An entry is already present, so check if we already
+                        * sent a TDLS Setup Request. If so, compare MAC
+                        * addresses and let the STA with the lower MAC address
+                        * continue as the initiator. The other negotiation is
+                        * terminated.
+                        */
                        if (os_memcmp(sm->own_addr, src_addr, ETH_ALEN) < 0) {
                                wpa_printf(MSG_DEBUG, "TDLS: Discard request "
                                           "from peer with higher address "
@@ -1513,12 +1816,9 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
                                           MACSTR " (terminate previously "
                                           "initiated negotiation",
                                           MAC2STR(src_addr));
-                               if (sm->tdls_external_setup)
-                                       wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK,
-                                                        src_addr);
-                               else
-                                       wpa_tdls_del_key(sm, peer);
-                               wpa_tdls_peer_free(sm, peer);
+                               wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK,
+                                                peer->addr);
+                               wpa_tdls_peer_clear(sm, peer);
                        }
                }
        }
@@ -1528,10 +1828,15 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
        cpos += 2;
 
        ielen = len - (cpos - buf); /* start of IE in buf */
-       if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0) {
-               wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M1");
-               goto error;
-       }
+
+       /*
+        * Don't reject the message if failing to parse IEs. The IEs we need are
+        * explicitly checked below. Some APs may add arbitrary padding to the
+        * end of short TDLS frames and that would look like invalid IEs.
+        */
+       if (wpa_supplicant_parse_ies(cpos, ielen, &kde) < 0)
+               wpa_printf(MSG_DEBUG,
+                          "TDLS: Failed to parse IEs in TPK M1 - ignore as an interop workaround");
 
        if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
                wpa_printf(MSG_INFO, "TDLS: No valid Link Identifier IE in "
@@ -1543,7 +1848,7 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
        lnkid = (struct wpa_tdls_lnkid *) kde.lnkid;
        if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
                wpa_printf(MSG_INFO, "TDLS: TPK M1 from diff BSS");
-               status = WLAN_STATUS_NOT_IN_SAME_BSS;
+               status = WLAN_STATUS_REQUEST_DECLINED;
                goto error;
        }
 
@@ -1562,8 +1867,18 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
        if (copy_peer_ext_capab(&kde, peer) < 0)
                goto error;
 
+       if (copy_peer_supp_channels(&kde, peer) < 0)
+               goto error;
+
+       if (copy_peer_supp_oper_classes(&kde, peer) < 0)
+               goto error;
+
        peer->qos_info = kde.qosinfo;
 
+       /* Overwrite with the qos_info obtained in WMM IE */
+       if (copy_peer_wmm_capab(&kde, peer) < 0)
+               goto error;
+
        peer->aid = kde.aid;
 
 #ifdef CONFIG_TDLS_TESTING
@@ -1574,6 +1889,8 @@ static int wpa_tdls_process_tpk_m1(struct wpa_sm *sm, const u8 *src_addr,
                wpa_printf(MSG_DEBUG, "TDLS: Testing concurrent initiation of "
                           "TDLS setup - send own request");
                peer->initiator = 1;
+               wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL,
+                                       NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0);
                wpa_tdls_send_tpk_m1(sm, peer);
        }
 
@@ -1680,16 +1997,26 @@ skip_rsn:
        }
 
        ftie = (struct wpa_tdls_ftie *) kde.ftie;
-       os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN);
        os_memcpy(peer->rsnie_i, kde.rsn_ie, kde.rsn_ie_len);
        peer->rsnie_i_len = kde.rsn_ie_len;
        peer->cipher = cipher;
 
-       if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) {
-               wpa_msg(sm->ctx->ctx, MSG_WARNING,
-                       "TDLS: Failed to get random data for responder nonce");
-               wpa_tdls_peer_free(sm, peer);
-               goto error;
+       if (os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) != 0) {
+               /*
+                * There is no point in updating the RNonce for every obtained
+                * TPK M1 frame (e.g., retransmission due to timeout) with the
+                * same INonce (SNonce in FTIE). However, if the TPK M1 is
+                * retransmitted with a different INonce, update the RNonce
+                * since this is for a new TDLS session.
+                */
+               wpa_printf(MSG_DEBUG,
+                          "TDLS: New TPK M1 INonce - generate new RNonce");
+               os_memcpy(peer->inonce, ftie->Snonce, WPA_NONCE_LEN);
+               if (os_get_random(peer->rnonce, WPA_NONCE_LEN)) {
+                       wpa_msg(sm->ctx->ctx, MSG_WARNING,
+                               "TDLS: Failed to get random data for responder nonce");
+                       goto error;
+               }
        }
 
 #if 0
@@ -1742,21 +2069,33 @@ skip_rsn:
        wpa_tdls_generate_tpk(peer, sm->own_addr, sm->bssid);
 
 skip_rsn_check:
-       /* add the peer to the driver as a "setup in progress" peer */
-       wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, NULL, 0,
-                               NULL, 0);
+#ifdef CONFIG_TDLS_TESTING
+       if (tdls_testing & TDLS_TESTING_CONCURRENT_INIT)
+               goto skip_add_peer;
+#endif /* CONFIG_TDLS_TESTING */
+
+       /* add supported rates, capabilities, and qos_info to the TDLS peer */
+       if (wpa_tdls_addset_peer(sm, peer, 1) < 0)
+               goto error;
+
+#ifdef CONFIG_TDLS_TESTING
+skip_add_peer:
+#endif /* CONFIG_TDLS_TESTING */
+       peer->tpk_in_progress = 1;
 
        wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Response / TPK M2");
        if (wpa_tdls_send_tpk_m2(sm, src_addr, dtoken, lnkid, peer) < 0) {
-               wpa_tdls_disable_link(sm, peer->addr);
+               wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
                goto error;
        }
 
        return 0;
 
 error:
-       wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken,
+       wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_RESPONSE, dtoken, 0,
                            status);
+       if (peer)
+               wpa_tdls_peer_free(sm, peer);
        return -1;
 }
 
@@ -1764,6 +2103,7 @@ error:
 static int wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
 {
        peer->tpk_success = 1;
+       peer->tpk_in_progress = 0;
        eloop_cancel_timeout(wpa_tdls_tpk_timeout, sm, peer);
        if (wpa_tdls_get_privacy(sm)) {
                u32 lifetime = peer->lifetime;
@@ -1784,16 +2124,6 @@ static int wpa_tdls_enable_link(struct wpa_sm *sm, struct wpa_tdls_peer *peer)
 #endif /* CONFIG_TDLS_TESTING */
        }
 
-       /* add supported rates, capabilities, and qos_info to the TDLS peer */
-       if (wpa_sm_tdls_peer_addset(sm, peer->addr, 0, peer->aid,
-                                   peer->capability,
-                                   peer->supp_rates, peer->supp_rates_len,
-                                   peer->ht_capabilities,
-                                   peer->vht_capabilities,
-                                   peer->qos_info, peer->ext_capab,
-                                   peer->ext_capab_len) < 0)
-               return -1;
-
        if (peer->reconfig_key && wpa_tdls_set_key(sm, peer) < 0) {
                wpa_printf(MSG_INFO, "TDLS: Could not configure key to the "
                           "driver");
@@ -1820,7 +2150,7 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
        int ielen;
        u16 status;
        const u8 *pos;
-       int ret;
+       int ret = 0;
 
        wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Response / TPK M2 "
                   "(Peer " MACSTR ")", MAC2STR(src_addr));
@@ -1845,8 +2175,11 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
        }
        wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_REQUEST);
 
-       if (len < 3 + 2 + 1)
+       if (len < 3 + 2 + 1) {
+               wpa_tdls_disable_peer_link(sm, peer);
                return -1;
+       }
+
        pos = buf;
        pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
        status = WPA_GET_LE16(pos);
@@ -1855,8 +2188,7 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
        if (status != WLAN_STATUS_SUCCESS) {
                wpa_printf(MSG_INFO, "TDLS: Status code in TPK M2: %u",
                           status);
-               if (sm->tdls_external_setup)
-                       wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+               wpa_tdls_disable_peer_link(sm, peer);
                return -1;
        }
 
@@ -1867,18 +2199,25 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
 
        wpa_printf(MSG_DEBUG, "TDLS: Dialog Token in TPK M2 %d", dtoken);
 
-       if (len < 3 + 2 + 1 + 2)
+       if (len < 3 + 2 + 1 + 2) {
+               wpa_tdls_disable_peer_link(sm, peer);
                return -1;
+       }
 
        /* capability information */
        peer->capability = WPA_GET_LE16(pos);
        pos += 2;
 
        ielen = len - (pos - buf); /* start of IE in buf */
-       if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0) {
-               wpa_printf(MSG_INFO, "TDLS: Failed to parse IEs in TPK M2");
-               goto error;
-       }
+
+       /*
+        * Don't reject the message if failing to parse IEs. The IEs we need are
+        * explicitly checked below. Some APs may add arbitrary padding to the
+        * end of short TDLS frames and that would look like invalid IEs.
+        */
+       if (wpa_supplicant_parse_ies(pos, ielen, &kde) < 0)
+               wpa_printf(MSG_DEBUG,
+                          "TDLS: Failed to parse IEs in TPK M2 - ignore as an interop workaround");
 
 #ifdef CONFIG_TDLS_TESTING
        if (tdls_testing & TDLS_TESTING_DECLINE_RESP) {
@@ -1915,8 +2254,18 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
        if (copy_peer_ext_capab(&kde, peer) < 0)
                goto error;
 
+       if (copy_peer_supp_channels(&kde, peer) < 0)
+               goto error;
+
+       if (copy_peer_supp_oper_classes(&kde, peer) < 0)
+               goto error;
+
        peer->qos_info = kde.qosinfo;
 
+       /* Overwrite with the qos_info obtained in WMM IE */
+       if (copy_peer_wmm_capab(&kde, peer) < 0)
+               goto error;
+
        peer->aid = kde.aid;
 
        if (!wpa_tdls_get_privacy(sm)) {
@@ -1934,6 +2283,13 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
        wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M2",
                    kde.rsn_ie, kde.rsn_ie_len);
 
+       if (kde.rsn_ie_len > TDLS_MAX_IE_LEN) {
+               wpa_printf(MSG_INFO,
+                          "TDLS: Too long Responder RSN IE in TPK M2");
+               status = WLAN_STATUS_INVALID_RSNIE;
+               goto error;
+       }
+
        /*
         * FIX: bitwise comparison of RSN IE is not the correct way of
         * validation this. It can be different, but certain fields must
@@ -2009,9 +2365,7 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
                                           (u8 *) timeoutie, ftie) < 0) {
                /* Discard the frame */
                wpa_tdls_del_key(sm, peer);
-               wpa_tdls_peer_free(sm, peer);
-               if (sm->tdls_external_setup)
-                       wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+               wpa_tdls_disable_peer_link(sm, peer);
                return -1;
        }
 
@@ -2028,23 +2382,35 @@ static int wpa_tdls_process_tpk_m2(struct wpa_sm *sm, const u8 *src_addr,
 skip_rsn:
        peer->dtoken = dtoken;
 
+       /* add supported rates, capabilities, and qos_info to the TDLS peer */
+       if (wpa_tdls_addset_peer(sm, peer, 0) < 0)
+               goto error;
+
        wpa_printf(MSG_DEBUG, "TDLS: Sending TDLS Setup Confirm / "
                   "TPK Handshake Message 3");
-       wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer);
+       if (wpa_tdls_send_tpk_m3(sm, src_addr, dtoken, lnkid, peer) < 0)
+               goto error;
 
-       ret = wpa_tdls_enable_link(sm, peer);
-       if (ret < 0) {
-               wpa_printf(MSG_DEBUG, "TDLS: Could not enable link");
-               wpa_tdls_do_teardown(sm, peer,
-                                    WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, 1);
+       if (!peer->tpk_success) {
+               /*
+                * Enable Link only when tpk_success is 0, signifying that this
+                * processing of TPK M2 frame is not because of a retransmission
+                * during TDLS setup handshake.
+                */
+               ret = wpa_tdls_enable_link(sm, peer);
+               if (ret < 0) {
+                       wpa_printf(MSG_DEBUG, "TDLS: Could not enable link");
+                       wpa_tdls_do_teardown(
+                               sm, peer,
+                               WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+               }
        }
        return ret;
 
 error:
-       wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken,
+       wpa_tdls_send_error(sm, src_addr, WLAN_TDLS_SETUP_CONFIRM, dtoken, 1,
                            status);
-       if (sm->tdls_external_setup)
-               wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
+       wpa_tdls_disable_peer_link(sm, peer);
        return -1;
 }
 
@@ -2061,7 +2427,7 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
        u16 status;
        const u8 *pos;
        u32 lifetime;
-       int ret;
+       int ret = 0;
 
        wpa_printf(MSG_DEBUG, "TDLS: Received TDLS Setup Confirm / TPK M3 "
                   "(Peer " MACSTR ")", MAC2STR(src_addr));
@@ -2077,7 +2443,7 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
        wpa_tdls_tpk_retry_timeout_cancel(sm, peer, WLAN_TDLS_SETUP_RESPONSE);
 
        if (len < 3 + 3)
-               return -1;
+               goto error;
        pos = buf;
        pos += 1 /* pkt_type */ + 1 /* Category */ + 1 /* Action */;
 
@@ -2086,21 +2452,26 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
        if (status != 0) {
                wpa_printf(MSG_INFO, "TDLS: Status code in TPK M3: %u",
                           status);
-               if (sm->tdls_external_setup)
-                       wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
-               return -1;
+               goto error;
        }
        pos += 2 /* status code */ + 1 /* dialog token */;
 
        ielen = len - (pos - buf); /* start of IE in buf */
+
+       /*
+        * Don't reject the message if failing to parse IEs. The IEs we need are
+        * explicitly checked below. Some APs piggy-back broken IEs to the end
+        * of a TDLS Confirm packet, which will fail the link if we don't ignore
+        * this error.
+        */
        if (wpa_supplicant_parse_ies((const u8 *) pos, ielen, &kde) < 0) {
-               wpa_printf(MSG_INFO, "TDLS: Failed to parse KDEs in TPK M3");
-               return -1;
+               wpa_printf(MSG_DEBUG,
+                          "TDLS: Failed to parse KDEs in TPK M3 - ignore as an interop workaround");
        }
 
        if (kde.lnkid == NULL || kde.lnkid_len < 3 * ETH_ALEN) {
                wpa_printf(MSG_INFO, "TDLS: No Link Identifier IE in TPK M3");
-               return -1;
+               goto error;
        }
        wpa_hexdump(MSG_DEBUG, "TDLS: Link ID Received from TPK M3",
                    (u8 *) kde.lnkid, kde.lnkid_len);
@@ -2108,7 +2479,7 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
 
        if (os_memcmp(sm->bssid, lnkid->bssid, ETH_ALEN) != 0) {
                wpa_printf(MSG_INFO, "TDLS: TPK M3 from diff BSS");
-               return -1;
+               goto error;
        }
 
        if (!wpa_tdls_get_privacy(sm))
@@ -2116,7 +2487,7 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
 
        if (kde.ftie == NULL || kde.ftie_len < sizeof(*ftie)) {
                wpa_printf(MSG_INFO, "TDLS: No FTIE in TPK M3");
-               return -1;
+               goto error;
        }
        wpa_hexdump(MSG_DEBUG, "TDLS: FTIE Received from TPK M3",
                    kde.ftie, sizeof(*ftie));
@@ -2124,7 +2495,7 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
 
        if (kde.rsn_ie == NULL) {
                wpa_printf(MSG_INFO, "TDLS: No RSN IE in TPK M3");
-               return -1;
+               goto error;
        }
        wpa_hexdump(MSG_DEBUG, "TDLS: RSN IE Received from TPK M3",
                    kde.rsn_ie, kde.rsn_ie_len);
@@ -2132,24 +2503,24 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
            os_memcmp(kde.rsn_ie, peer->rsnie_p, peer->rsnie_p_len) != 0) {
                wpa_printf(MSG_INFO, "TDLS: RSN IE in TPK M3 does not match "
                           "with the one sent in TPK M2");
-               return -1;
+               goto error;
        }
 
        if (!os_memcmp(peer->rnonce, ftie->Anonce, WPA_NONCE_LEN) == 0) {
                wpa_printf(MSG_INFO, "TDLS: FTIE ANonce in TPK M3 does "
                           "not match with FTIE ANonce used in TPK M2");
-               return -1;
+               goto error;
        }
 
        if (!os_memcmp(peer->inonce, ftie->Snonce, WPA_NONCE_LEN) == 0) {
                wpa_printf(MSG_INFO, "TDLS: FTIE SNonce in TPK M3 does not "
                           "match with FTIE SNonce used in TPK M1");
-               return -1;
+               goto error;
        }
 
        if (kde.key_lifetime == NULL) {
                wpa_printf(MSG_INFO, "TDLS: No Key Lifetime IE in TPK M3");
-               return -1;
+               goto error;
        }
        timeoutie = (struct wpa_tdls_timeoutie *) kde.key_lifetime;
        wpa_hexdump(MSG_DEBUG, "TDLS: Timeout IE Received from TPK M3",
@@ -2160,16 +2531,13 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
        if (lifetime != peer->lifetime) {
                wpa_printf(MSG_INFO, "TDLS: Unexpected TPK lifetime %u in "
                           "TPK M3 (expected %u)", lifetime, peer->lifetime);
-               if (sm->tdls_external_setup)
-                       wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, src_addr);
-               return -1;
+               goto error;
        }
 
        if (wpa_supplicant_verify_tdls_mic(3, peer, (u8 *) lnkid,
                                           (u8 *) timeoutie, ftie) < 0) {
                wpa_tdls_del_key(sm, peer);
-               wpa_tdls_peer_free(sm, peer);
-               return -1;
+               goto error;
        }
 
        if (wpa_tdls_set_key(sm, peer) < 0) {
@@ -2183,13 +2551,26 @@ static int wpa_tdls_process_tpk_m3(struct wpa_sm *sm, const u8 *src_addr,
        }
 
 skip_rsn:
-       ret = wpa_tdls_enable_link(sm, peer);
-       if (ret < 0) {
-               wpa_printf(MSG_DEBUG, "TDLS: Could not enable link");
-               wpa_tdls_do_teardown(sm, peer,
-                                    WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, 1);
+       /* add supported rates, capabilities, and qos_info to the TDLS peer */
+       if (wpa_tdls_addset_peer(sm, peer, 0) < 0)
+               goto error;
+
+       if (!peer->tpk_success) {
+               /*
+                * Enable Link only when tpk_success is 0, signifying that this
+                * processing of TPK M3 frame is not because of a retransmission
+                * during TDLS setup handshake.
+                */
+               ret = wpa_tdls_enable_link(sm, peer);
+               if (ret < 0) {
+                       wpa_printf(MSG_DEBUG, "TDLS: Could not enable link");
+                       goto error;
+               }
        }
        return ret;
+error:
+       wpa_tdls_do_teardown(sm, peer, WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+       return -1;
 }
 
 
@@ -2243,14 +2624,24 @@ int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr)
        if (peer == NULL)
                return -1;
 
+       if (peer->tpk_in_progress) {
+               wpa_printf(MSG_DEBUG, "TDLS: Setup is already in progress with the peer");
+               return 0;
+       }
+
        peer->initiator = 1;
 
        /* add the peer to the driver as a "setup in progress" peer */
-       wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL, NULL, 0,
-                               NULL, 0);
+       if (wpa_sm_tdls_peer_addset(sm, peer->addr, 1, 0, 0, NULL, 0, NULL,
+                                   NULL, 0, 0, NULL, 0, NULL, 0, NULL, 0)) {
+               wpa_tdls_disable_peer_link(sm, peer);
+               return -1;
+       }
+
+       peer->tpk_in_progress = 1;
 
        if (wpa_tdls_send_tpk_m1(sm, peer) < 0) {
-               wpa_tdls_disable_link(sm, peer->addr);
+               wpa_tdls_disable_peer_link(sm, peer);
                return -1;
        }
 
@@ -2278,7 +2669,8 @@ void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr)
                 * Disable previous link to allow renegotiation to be completed
                 * on AP path.
                 */
-               wpa_sm_tdls_oper(sm, TDLS_DISABLE_LINK, peer->addr);
+               wpa_tdls_do_teardown(sm, peer,
+                                    WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
        }
 }
 
@@ -2378,7 +2770,8 @@ int wpa_tdls_init(struct wpa_sm *sm)
         * are assumed to perform everything internally
         */
        if (wpa_sm_tdls_get_capa(sm, &sm->tdls_supported,
-                                &sm->tdls_external_setup) < 0) {
+                                &sm->tdls_external_setup,
+                                &sm->tdls_chan_switch) < 0) {
                sm->tdls_supported = 1;
                sm->tdls_external_setup = 0;
        }
@@ -2387,6 +2780,8 @@ int wpa_tdls_init(struct wpa_sm *sm)
                   "driver", sm->tdls_supported ? "" : " not");
        wpa_printf(MSG_DEBUG, "TDLS: Driver uses %s link setup",
                   sm->tdls_external_setup ? "external" : "internal");
+       wpa_printf(MSG_DEBUG, "TDLS: Driver %s TDLS channel switching",
+                  sm->tdls_chan_switch ? "supports" : "does not support");
 
        return 0;
 }
@@ -2394,22 +2789,25 @@ int wpa_tdls_init(struct wpa_sm *sm)
 
 void wpa_tdls_teardown_peers(struct wpa_sm *sm)
 {
-       struct wpa_tdls_peer *peer;
+       struct wpa_tdls_peer *peer, *tmp;
 
+       if (!sm)
+               return;
        peer = sm->tdls;
 
        wpa_printf(MSG_DEBUG, "TDLS: Tear down peers");
 
        while (peer) {
+               tmp = peer->next;
                wpa_printf(MSG_DEBUG, "TDLS: Tear down peer " MACSTR,
                           MAC2STR(peer->addr));
                if (sm->tdls_external_setup)
-                       wpa_tdls_send_teardown(sm, peer->addr,
-                                              WLAN_REASON_DEAUTH_LEAVING);
+                       wpa_tdls_do_teardown(sm, peer,
+                                            WLAN_REASON_DEAUTH_LEAVING);
                else
                        wpa_sm_tdls_oper(sm, TDLS_TEARDOWN, peer->addr);
 
-               peer = peer->next;
+               peer = tmp;
        }
 }
 
@@ -2419,7 +2817,6 @@ static void wpa_tdls_remove_peers(struct wpa_sm *sm)
        struct wpa_tdls_peer *peer, *tmp;
 
        peer = sm->tdls;
-       sm->tdls = NULL;
 
        while (peer) {
                int res;
@@ -2428,7 +2825,6 @@ static void wpa_tdls_remove_peers(struct wpa_sm *sm)
                wpa_printf(MSG_DEBUG, "TDLS: Remove peer " MACSTR " (res=%d)",
                           MAC2STR(peer->addr), res);
                wpa_tdls_peer_free(sm, peer);
-               os_free(peer);
                peer = tmp;
        }
 }
@@ -2467,39 +2863,61 @@ void wpa_tdls_disassoc(struct wpa_sm *sm)
 }
 
 
-static int wpa_tdls_prohibited(const u8 *ies, size_t len)
+static int wpa_tdls_prohibited(struct wpa_eapol_ie_parse *elems)
 {
-       struct wpa_eapol_ie_parse elems;
-
-       if (ies == NULL)
-               return 0;
-
-       if (wpa_supplicant_parse_ies(ies, len, &elems) < 0)
-               return 0;
+       /* bit 38 - TDLS Prohibited */
+       return !!(elems->ext_capab[2 + 4] & 0x40);
+}
 
-       if (elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
-               return 0;
 
-        /* bit 38 - TDLS Prohibited */
-       return !!(elems.ext_capab[2 + 4] & 0x40);
+static int wpa_tdls_chan_switch_prohibited(struct wpa_eapol_ie_parse *elems)
+{
+       /* bit 39 - TDLS Channel Switch Prohibited */
+       return !!(elems->ext_capab[2 + 4] & 0x80);
 }
 
 
 void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
 {
-       sm->tdls_prohibited = wpa_tdls_prohibited(ies, len);
+       struct wpa_eapol_ie_parse elems;
+
+       sm->tdls_prohibited = 0;
+       sm->tdls_chan_switch_prohibited = 0;
+
+       if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 ||
+           elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
+               return;
+
+       sm->tdls_prohibited = wpa_tdls_prohibited(&elems);
        wpa_printf(MSG_DEBUG, "TDLS: TDLS is %s in the target BSS",
                   sm->tdls_prohibited ? "prohibited" : "allowed");
+       sm->tdls_chan_switch_prohibited =
+               wpa_tdls_chan_switch_prohibited(&elems);
+       wpa_printf(MSG_DEBUG, "TDLS: TDLS channel switch %s in the target BSS",
+                  sm->tdls_chan_switch_prohibited ? "prohibited" : "allowed");
 }
 
 
 void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len)
 {
-       if (!sm->tdls_prohibited && wpa_tdls_prohibited(ies, len)) {
+       struct wpa_eapol_ie_parse elems;
+
+       if (ies == NULL || wpa_supplicant_parse_ies(ies, len, &elems) < 0 ||
+           elems.ext_capab == NULL || elems.ext_capab_len < 2 + 5)
+               return;
+
+       if (!sm->tdls_prohibited && wpa_tdls_prohibited(&elems)) {
                wpa_printf(MSG_DEBUG, "TDLS: TDLS prohibited based on "
                           "(Re)Association Response IEs");
                sm->tdls_prohibited = 1;
        }
+
+       if (!sm->tdls_chan_switch_prohibited &&
+           wpa_tdls_chan_switch_prohibited(&elems)) {
+               wpa_printf(MSG_DEBUG,
+                          "TDLS: TDLS channel switch prohibited based on (Re)Association Response IEs");
+               sm->tdls_chan_switch_prohibited = 1;
+       }
 }
 
 
@@ -2514,3 +2932,78 @@ int wpa_tdls_is_external_setup(struct wpa_sm *sm)
 {
        return sm->tdls_external_setup;
 }
+
+
+int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr,
+                               u8 oper_class,
+                               struct hostapd_freq_params *freq_params)
+{
+       struct wpa_tdls_peer *peer;
+       int ret;
+
+       if (sm->tdls_disabled || !sm->tdls_supported)
+               return -1;
+
+       if (!sm->tdls_chan_switch) {
+               wpa_printf(MSG_DEBUG,
+                          "TDLS: Channel switching not supported by the driver");
+               return -1;
+       }
+
+       if (sm->tdls_chan_switch_prohibited) {
+               wpa_printf(MSG_DEBUG,
+                          "TDLS: Channel switching is prohibited in this BSS - reject request to switch channel");
+               return -1;
+       }
+
+       for (peer = sm->tdls; peer; peer = peer->next) {
+               if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+                       break;
+       }
+
+       if (peer == NULL || !peer->tpk_success) {
+               wpa_printf(MSG_ERROR, "TDLS: Peer " MACSTR
+                          " not found for channel switching", MAC2STR(addr));
+               return -1;
+       }
+
+       if (peer->chan_switch_enabled) {
+               wpa_printf(MSG_DEBUG, "TDLS: Peer " MACSTR
+                          " already has channel switching enabled",
+                          MAC2STR(addr));
+               return 0;
+       }
+
+       ret = wpa_sm_tdls_enable_channel_switch(sm, peer->addr,
+                                               oper_class, freq_params);
+       if (!ret)
+               peer->chan_switch_enabled = 1;
+
+       return ret;
+}
+
+
+int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr)
+{
+       struct wpa_tdls_peer *peer;
+
+       if (sm->tdls_disabled || !sm->tdls_supported)
+               return -1;
+
+       for (peer = sm->tdls; peer; peer = peer->next) {
+               if (os_memcmp(peer->addr, addr, ETH_ALEN) == 0)
+                       break;
+       }
+
+       if (!peer || !peer->chan_switch_enabled) {
+               wpa_printf(MSG_ERROR, "TDLS: Channel switching not enabled for "
+                          MACSTR, MAC2STR(addr));
+               return -1;
+       }
+
+       /* ignore the return value */
+       wpa_sm_tdls_disable_channel_switch(sm, peer->addr);
+
+       peer->chan_switch_enabled = 0;
+       return 0;
+}
old mode 100644 (file)
new mode 100755 (executable)
index 292255c..8adeef4
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - WPA state machine and EAPOL-Key processing
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -27,6 +27,7 @@
  * wpa_eapol_key_send - Send WPA/RSN EAPOL-Key message
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
  * @kck: Key Confirmation Key (KCK, part of PTK)
+ * @kck_len: KCK length in octets
  * @ver: Version field from Key Info
  * @dest: Destination address for the frame
  * @proto: Ethertype (usually ETH_P_EAPOL)
  * @msg_len: Length of message
  * @key_mic: Pointer to the buffer to which the EAPOL-Key MIC is written
  */
-void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck,
+void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
                        int ver, const u8 *dest, u16 proto,
                        u8 *msg, size_t msg_len, u8 *key_mic)
 {
+       size_t mic_len = wpa_mic_len(sm->key_mgmt);
+
        if (is_zero_ether_addr(dest) && is_zero_ether_addr(sm->bssid)) {
                /*
                 * Association event was not yet received; try to fetch
@@ -56,14 +59,15 @@ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck,
                }
        }
        if (key_mic &&
-           wpa_eapol_key_mic(kck, ver, msg, msg_len, key_mic)) {
+           wpa_eapol_key_mic(kck, kck_len, sm->key_mgmt, ver, msg, msg_len,
+                             key_mic)) {
                wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
-                       "WPA: Failed to generate EAPOL-Key "
-                       "version %d MIC", ver);
+                       "WPA: Failed to generate EAPOL-Key version %d key_mgmt 0x%x MIC",
+                       ver, sm->key_mgmt);
                goto out;
        }
-       wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, 16);
-       wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, 16);
+       wpa_hexdump_key(MSG_DEBUG, "WPA: KCK", kck, kck_len);
+       wpa_hexdump(MSG_DEBUG, "WPA: Derived Key MIC", key_mic, mic_len);
        wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len);
        wpa_sm_ether_send(sm, dest, proto, msg, msg_len);
        eapol_sm_notify_tx_eapol_key(sm->eapol);
@@ -84,12 +88,17 @@ out:
  */
 void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
 {
-       size_t rlen;
+       size_t mic_len, hdrlen, rlen;
        struct wpa_eapol_key *reply;
+       struct wpa_eapol_key_192 *reply192;
        int key_info, ver;
-       u8 bssid[ETH_ALEN], *rbuf;
+       u8 bssid[ETH_ALEN], *rbuf, *key_mic;
 
-       if (wpa_key_mgmt_ft(sm->key_mgmt) || wpa_key_mgmt_sha256(sm->key_mgmt))
+       if (sm->key_mgmt == WPA_KEY_MGMT_OSEN ||
+           wpa_key_mgmt_suite_b(sm->key_mgmt))
+               ver = WPA_KEY_INFO_TYPE_AKM_DEFINED;
+       else if (wpa_key_mgmt_ft(sm->key_mgmt) ||
+                wpa_key_mgmt_sha256(sm->key_mgmt))
                ver = WPA_KEY_INFO_TYPE_AES_128_CMAC;
        else if (sm->pairwise_cipher != WPA_CIPHER_TKIP)
                ver = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES;
@@ -102,12 +111,16 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
                return;
        }
 
+       mic_len = wpa_mic_len(sm->key_mgmt);
+       hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
        rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
-                                 sizeof(*reply), &rlen, (void *) &reply);
+                                 hdrlen, &rlen, (void *) &reply);
        if (rbuf == NULL)
                return;
+       reply192 = (struct wpa_eapol_key_192 *) reply;
 
-       reply->type = sm->proto == WPA_PROTO_RSN ?
+       reply->type = (sm->proto == WPA_PROTO_RSN ||
+                      sm->proto == WPA_PROTO_OSEN) ?
                EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
        key_info = WPA_KEY_INFO_REQUEST | ver;
        if (sm->ptk_set)
@@ -122,15 +135,39 @@ void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise)
                  WPA_REPLAY_COUNTER_LEN);
        inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN);
 
-       WPA_PUT_BE16(reply->key_data_length, 0);
+       if (mic_len == 24)
+               WPA_PUT_BE16(reply192->key_data_length, 0);
+       else
+               WPA_PUT_BE16(reply->key_data_length, 0);
+       if (!(key_info & WPA_KEY_INFO_MIC))
+               key_mic = NULL;
+       else
+               key_mic = reply192->key_mic; /* same offset in reply */
 
        wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
                "WPA: Sending EAPOL-Key Request (error=%d "
                "pairwise=%d ptk_set=%d len=%lu)",
                error, pairwise, sm->ptk_set, (unsigned long) rlen);
-       wpa_eapol_key_send(sm, sm->ptk.kck, ver, bssid, ETH_P_EAPOL,
-                          rbuf, rlen, key_info & WPA_KEY_INFO_MIC ?
-                          reply->key_mic : NULL);
+       wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, bssid,
+                          ETH_P_EAPOL, rbuf, rlen, key_mic);
+}
+
+
+static void wpa_supplicant_key_mgmt_set_pmk(struct wpa_sm *sm)
+{
+#ifdef CONFIG_IEEE80211R
+       if (sm->key_mgmt == WPA_KEY_MGMT_FT_IEEE8021X) {
+               if (wpa_sm_key_mgmt_set_pmk(sm, sm->xxkey, sm->xxkey_len))
+                       wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                               "RSN: Cannot set low order 256 bits of MSK for key management offload");
+       } else {
+#endif /* CONFIG_IEEE80211R */
+               if (wpa_sm_key_mgmt_set_pmk(sm, sm->pmk, sm->pmk_len))
+                       wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                               "RSN: Cannot set PMK for key management offload");
+#ifdef CONFIG_IEEE80211R
+       }
+#endif /* CONFIG_IEEE80211R */
 }
 
 
@@ -158,7 +195,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
        }
 
        if (pmkid && sm->cur_pmksa &&
-           os_memcmp(pmkid, sm->cur_pmksa->pmkid, PMKID_LEN) == 0) {
+           os_memcmp_const(pmkid, sm->cur_pmksa->pmkid, PMKID_LEN) == 0) {
                wpa_hexdump(MSG_DEBUG, "RSN: matched PMKID", pmkid, PMKID_LEN);
                wpa_sm_set_pmk_from_pmksa(sm);
                wpa_hexdump_key(MSG_DEBUG, "RSN: PMK from PMKSA cache",
@@ -194,10 +231,13 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
                        wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state "
                                        "machines", sm->pmk, pmk_len);
                        sm->pmk_len = pmk_len;
+                       wpa_supplicant_key_mgmt_set_pmk(sm);
                        if (sm->proto == WPA_PROTO_RSN &&
+                           !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
                            !wpa_key_mgmt_ft(sm->key_mgmt)) {
                                sa = pmksa_cache_add(sm->pmksa,
                                                     sm->pmk, pmk_len,
+                                                    NULL, 0,
                                                     src_addr, sm->own_addr,
                                                     sm->network_ctx,
                                                     sm->key_mgmt);
@@ -231,7 +271,9 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
        }
 
        if (abort_cached && wpa_key_mgmt_wpa_ieee8021x(sm->key_mgmt) &&
-           !wpa_key_mgmt_ft(sm->key_mgmt)) {
+           !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+           !wpa_key_mgmt_ft(sm->key_mgmt) && sm->key_mgmt != WPA_KEY_MGMT_OSEN)
+       {
                /* Send EAPOL-Start to trigger full EAP authentication. */
                u8 *buf;
                size_t buflen;
@@ -273,9 +315,10 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
                               const u8 *wpa_ie, size_t wpa_ie_len,
                               struct wpa_ptk *ptk)
 {
-       size_t rlen;
+       size_t mic_len, hdrlen, rlen;
        struct wpa_eapol_key *reply;
-       u8 *rbuf;
+       struct wpa_eapol_key_192 *reply192;
+       u8 *rbuf, *key_mic;
        u8 *rsn_ie_buf = NULL;
 
        if (wpa_ie == NULL) {
@@ -317,19 +360,23 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
 
        wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len);
 
+       mic_len = wpa_mic_len(sm->key_mgmt);
+       hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
        rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY,
-                                 NULL, sizeof(*reply) + wpa_ie_len,
+                                 NULL, hdrlen + wpa_ie_len,
                                  &rlen, (void *) &reply);
        if (rbuf == NULL) {
                os_free(rsn_ie_buf);
                return -1;
        }
+       reply192 = (struct wpa_eapol_key_192 *) reply;
 
-       reply->type = sm->proto == WPA_PROTO_RSN ?
+       reply->type = (sm->proto == WPA_PROTO_RSN ||
+                      sm->proto == WPA_PROTO_OSEN) ?
                EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
        WPA_PUT_BE16(reply->key_info,
                     ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC);
-       if (sm->proto == WPA_PROTO_RSN)
+       if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
                WPA_PUT_BE16(reply->key_length, 0);
        else
                os_memcpy(reply->key_length, key->key_length, 2);
@@ -338,47 +385,52 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
        wpa_hexdump(MSG_DEBUG, "WPA: Replay Counter", reply->replay_counter,
                    WPA_REPLAY_COUNTER_LEN);
 
-       WPA_PUT_BE16(reply->key_data_length, wpa_ie_len);
-       os_memcpy(reply + 1, wpa_ie, wpa_ie_len);
+       key_mic = reply192->key_mic; /* same offset for reply and reply192 */
+       if (mic_len == 24) {
+               WPA_PUT_BE16(reply192->key_data_length, wpa_ie_len);
+               os_memcpy(reply192 + 1, wpa_ie, wpa_ie_len);
+       } else {
+               WPA_PUT_BE16(reply->key_data_length, wpa_ie_len);
+               os_memcpy(reply + 1, wpa_ie, wpa_ie_len);
+       }
        os_free(rsn_ie_buf);
 
        os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN);
 
        wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/4");
-       wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL,
-                          rbuf, rlen, reply->key_mic);
+       wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL,
+                          rbuf, rlen, key_mic);
 
        return 0;
 }
 
 
 static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr,
-                         const struct wpa_eapol_key *key,
-                         struct wpa_ptk *ptk)
+                         const struct wpa_eapol_key *key, struct wpa_ptk *ptk)
 {
-       size_t ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64;
 #ifdef CONFIG_IEEE80211R
        if (wpa_key_mgmt_ft(sm->key_mgmt))
-               return wpa_derive_ptk_ft(sm, src_addr, key, ptk, ptk_len);
+               return wpa_derive_ptk_ft(sm, src_addr, key, ptk);
 #endif /* CONFIG_IEEE80211R */
 
-       wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion",
-                      sm->own_addr, sm->bssid, sm->snonce, key->key_nonce,
-                      (u8 *) ptk, ptk_len,
-                      wpa_key_mgmt_sha256(sm->key_mgmt));
-       return 0;
+       return wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion",
+                             sm->own_addr, sm->bssid, sm->snonce,
+                             key->key_nonce, ptk, sm->key_mgmt,
+                             sm->pairwise_cipher);
 }
 
 
 static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
                                          const unsigned char *src_addr,
                                          const struct wpa_eapol_key *key,
-                                         u16 ver)
+                                         u16 ver, const u8 *key_data,
+                                         size_t key_data_len)
 {
        struct wpa_eapol_ie_parse ie;
        struct wpa_ptk *ptk;
-       u8 buf[8];
        int res;
+       u8 *kde, *kde_buf = NULL;
+       size_t kde_len;
 
        if (wpa_sm_get_network_ctx(sm) == NULL) {
                wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: No SSID info "
@@ -392,12 +444,11 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
 
        os_memset(&ie, 0, sizeof(ie));
 
-       if (sm->proto == WPA_PROTO_RSN) {
+       if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
                /* RSN: msg 1/4 should contain PMKID for the selected PMK */
-               const u8 *_buf = (const u8 *) (key + 1);
-               size_t len = WPA_GET_BE16(key->key_data_length);
-               wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data", _buf, len);
-               if (wpa_supplicant_parse_ies(_buf, len, &ie) < 0)
+               wpa_hexdump(MSG_DEBUG, "RSN: msg 1/4 key data",
+                           key_data, key_data_len);
+               if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
                        goto failed;
                if (ie.pmkid) {
                        wpa_hexdump(MSG_DEBUG, "RSN: PMKID from "
@@ -429,21 +480,49 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
         * been verified when processing message 3/4. */
        ptk = &sm->tptk;
        wpa_derive_ptk(sm, src_addr, key, ptk);
-       /* Supplicant: swap tx/rx Mic keys */
-       os_memcpy(buf, ptk->u.auth.tx_mic_key, 8);
-       os_memcpy(ptk->u.auth.tx_mic_key, ptk->u.auth.rx_mic_key, 8);
-       os_memcpy(ptk->u.auth.rx_mic_key, buf, 8);
+       if (sm->pairwise_cipher == WPA_CIPHER_TKIP) {
+               u8 buf[8];
+               /* Supplicant: swap tx/rx Mic keys */
+               os_memcpy(buf, &ptk->tk[16], 8);
+               os_memcpy(&ptk->tk[16], &ptk->tk[24], 8);
+               os_memcpy(&ptk->tk[24], buf, 8);
+               os_memset(buf, 0, sizeof(buf));
+       }
        sm->tptk_set = 1;
 
+       kde = sm->assoc_wpa_ie;
+       kde_len = sm->assoc_wpa_ie_len;
+
+#ifdef CONFIG_P2P
+       if (sm->p2p) {
+               kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 1);
+               if (kde_buf) {
+                       u8 *pos;
+                       wpa_printf(MSG_DEBUG, "P2P: Add IP Address Request KDE "
+                                  "into EAPOL-Key 2/4");
+                       os_memcpy(kde_buf, kde, kde_len);
+                       kde = kde_buf;
+                       pos = kde + kde_len;
+                       *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+                       *pos++ = RSN_SELECTOR_LEN + 1;
+                       RSN_SELECTOR_PUT(pos, WFA_KEY_DATA_IP_ADDR_REQ);
+                       pos += RSN_SELECTOR_LEN;
+                       *pos++ = 0x01;
+                       kde_len = pos - kde;
+               }
+       }
+#endif /* CONFIG_P2P */
+
        if (wpa_supplicant_send_2_of_4(sm, sm->bssid, key, ver, sm->snonce,
-                                      sm->assoc_wpa_ie, sm->assoc_wpa_ie_len,
-                                      ptk))
+                                      kde, kde_len, ptk))
                goto failed;
 
+       os_free(kde_buf);
        os_memcpy(sm->anonce, key->key_nonce, WPA_NONCE_LEN);
        return;
 
 failed:
+       os_free(kde_buf);
        wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
 }
 
@@ -535,7 +614,7 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
        keylen = wpa_cipher_key_len(sm->pairwise_cipher);
        rsclen = wpa_cipher_rsc_len(sm->pairwise_cipher);
 
-       if (sm->proto == WPA_PROTO_RSN) {
+       if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
                key_rsc = null_rsc;
        } else {
                key_rsc = key->key_rsc;
@@ -543,7 +622,7 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
        }
 
        if (wpa_sm_set_key(sm, alg, sm->bssid, 0, 1, key_rsc, rsclen,
-                          (u8 *) sm->ptk.tk1, keylen) < 0) {
+                          sm->ptk.tk, keylen) < 0) {
                wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                        "WPA: Failed to set PTK to the "
                        "driver (alg=%d keylen=%d bssid=" MACSTR ")",
@@ -551,6 +630,9 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm,
                return -1;
        }
 
+       /* TK is not needed anymore in supplicant */
+       os_memset(sm->ptk.tk, 0, WPA_TK_MAX_LEN);
+
        if (sm->wpa_ptk_rekey) {
                eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
                eloop_register_timeout(sm->wpa_ptk_rekey, 0, wpa_sm_rekey_ptk,
@@ -623,6 +705,7 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
                        wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                                "WPA: Failed to set GTK to the driver "
                                "(Group only)");
+                       os_memset(gtk_buf, 0, sizeof(gtk_buf));
                        return -1;
                }
        } else if (wpa_sm_set_key(sm, gd->alg, broadcast_ether_addr,
@@ -632,8 +715,10 @@ static int wpa_supplicant_install_gtk(struct wpa_sm *sm,
                        "WPA: Failed to set GTK to "
                        "the driver (alg=%d keylen=%d keyidx=%d)",
                        gd->alg, gd->gtk_len, gd->keyidx);
+               os_memset(gtk_buf, 0, sizeof(gtk_buf));
                return -1;
        }
+       os_memset(gtk_buf, 0, sizeof(gtk_buf));
 
        return 0;
 }
@@ -688,14 +773,17 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,
        os_memcpy(gd.gtk, gtk, gtk_len);
        gd.gtk_len = gtk_len;
 
-       if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
-                                             gtk_len, gtk_len,
-                                             &gd.key_rsc_len, &gd.alg) ||
-           wpa_supplicant_install_gtk(sm, &gd, key->key_rsc)) {
+       if (sm->group_cipher != WPA_CIPHER_GTK_NOT_USED &&
+           (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
+                                              gtk_len, gtk_len,
+                                              &gd.key_rsc_len, &gd.alg) ||
+            wpa_supplicant_install_gtk(sm, &gd, key->key_rsc))) {
                wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
                        "RSN: Failed to install GTK");
+               os_memset(&gd, 0, sizeof(gd));
                return -1;
        }
+       os_memset(&gd, 0, sizeof(gd));
 
        wpa_supplicant_key_neg_complete(sm, sm->bssid,
                                        key_info & WPA_KEY_INFO_SECURE);
@@ -707,13 +795,15 @@ static int ieee80211w_set_keys(struct wpa_sm *sm,
                               struct wpa_eapol_ie_parse *ie)
 {
 #ifdef CONFIG_IEEE80211W
-       if (sm->mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC)
+       if (!wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher))
                return 0;
 
        if (ie->igtk) {
+               size_t len;
                const struct wpa_igtk_kde *igtk;
                u16 keyidx;
-               if (ie->igtk_len != sizeof(*igtk))
+               len = wpa_cipher_key_len(sm->mgmt_group_cipher);
+               if (ie->igtk_len != WPA_IGTK_KDE_PREFIX_LEN + len)
                        return -1;
                igtk = (const struct wpa_igtk_kde *) ie->igtk;
                keyidx = WPA_GET_LE16(igtk->keyid);
@@ -721,15 +811,16 @@ static int ieee80211w_set_keys(struct wpa_sm *sm,
                        "pn %02x%02x%02x%02x%02x%02x",
                        keyidx, MAC2STR(igtk->pn));
                wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK",
-                               igtk->igtk, WPA_IGTK_LEN);
+                               igtk->igtk, len);
                if (keyidx > 4095) {
                        wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                                "WPA: Invalid IGTK KeyID %d", keyidx);
                        return -1;
                }
-               if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr,
+               if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+                                  broadcast_ether_addr,
                                   keyidx, 0, igtk->pn, sizeof(igtk->pn),
-                                  igtk->igtk, WPA_IGTK_LEN) < 0) {
+                                  igtk->igtk, len) < 0) {
                        wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                                "WPA: Failed to configure IGTK to the driver");
                        return -1;
@@ -862,7 +953,8 @@ static int ft_validate_rsnie(struct wpa_sm *sm,
                return -1;
        }
 
-       if (os_memcmp(rsn.pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) {
+       if (os_memcmp_const(rsn.pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0)
+       {
                wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
                        "FT: PMKR1Name mismatch in "
                        "FT 4-way handshake message 3/4");
@@ -982,49 +1074,49 @@ static int wpa_supplicant_validate_ie(struct wpa_sm *sm,
  * @key: Pointer to the EAPOL-Key frame header
  * @ver: Version bits from EAPOL-Key Key Info
  * @key_info: Key Info
- * @kde: KDEs to include the EAPOL-Key frame
- * @kde_len: Length of KDEs
  * @ptk: PTK to use for keyed hash and encryption
  * Returns: 0 on success, -1 on failure
  */
 int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,
                               const struct wpa_eapol_key *key,
                               u16 ver, u16 key_info,
-                              const u8 *kde, size_t kde_len,
                               struct wpa_ptk *ptk)
 {
-       size_t rlen;
+       size_t mic_len, hdrlen, rlen;
        struct wpa_eapol_key *reply;
-       u8 *rbuf;
-
-       if (kde)
-               wpa_hexdump(MSG_DEBUG, "WPA: KDE for msg 4/4", kde, kde_len);
+       struct wpa_eapol_key_192 *reply192;
+       u8 *rbuf, *key_mic;
 
+       mic_len = wpa_mic_len(sm->key_mgmt);
+       hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
        rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
-                                 sizeof(*reply) + kde_len,
-                                 &rlen, (void *) &reply);
+                                 hdrlen, &rlen, (void *) &reply);
        if (rbuf == NULL)
                return -1;
+       reply192 = (struct wpa_eapol_key_192 *) reply;
 
-       reply->type = sm->proto == WPA_PROTO_RSN ?
+       reply->type = (sm->proto == WPA_PROTO_RSN ||
+                      sm->proto == WPA_PROTO_OSEN) ?
                EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
        key_info &= WPA_KEY_INFO_SECURE;
        key_info |= ver | WPA_KEY_INFO_KEY_TYPE | WPA_KEY_INFO_MIC;
        WPA_PUT_BE16(reply->key_info, key_info);
-       if (sm->proto == WPA_PROTO_RSN)
+       if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
                WPA_PUT_BE16(reply->key_length, 0);
        else
                os_memcpy(reply->key_length, key->key_length, 2);
        os_memcpy(reply->replay_counter, key->replay_counter,
                  WPA_REPLAY_COUNTER_LEN);
 
-       WPA_PUT_BE16(reply->key_data_length, kde_len);
-       if (kde)
-               os_memcpy(reply + 1, kde, kde_len);
+       key_mic = reply192->key_mic; /* same offset for reply and reply192 */
+       if (mic_len == 24)
+               WPA_PUT_BE16(reply192->key_data_length, 0);
+       else
+               WPA_PUT_BE16(reply->key_data_length, 0);
 
        wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 4/4");
-       wpa_eapol_key_send(sm, ptk->kck, ver, dst, ETH_P_EAPOL,
-                          rbuf, rlen, reply->key_mic);
+       wpa_eapol_key_send(sm, ptk->kck, ptk->kck_len, ver, dst, ETH_P_EAPOL,
+                          rbuf, rlen, key_mic);
 
        return 0;
 }
@@ -1032,10 +1124,10 @@ int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,
 
 static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
                                          const struct wpa_eapol_key *key,
-                                         u16 ver)
+                                         u16 ver, const u8 *key_data,
+                                         size_t key_data_len)
 {
-       u16 key_info, keylen, len;
-       const u8 *pos;
+       u16 key_info, keylen;
        struct wpa_eapol_ie_parse ie;
 
        wpa_sm_set_state(sm, WPA_4WAY_HANDSHAKE);
@@ -1044,10 +1136,8 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
 
        key_info = WPA_GET_BE16(key->key_info);
 
-       pos = (const u8 *) (key + 1);
-       len = WPA_GET_BE16(key->key_data_length);
-       wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", pos, len);
-       if (wpa_supplicant_parse_ies(pos, len, &ie) < 0)
+       wpa_hexdump(MSG_DEBUG, "WPA: IE KeyData", key_data, key_data_len);
+       if (wpa_supplicant_parse_ies(key_data, key_data_len, &ie) < 0)
                goto failed;
        if (ie.gtk && !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
                wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
@@ -1061,7 +1151,10 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
                goto failed;
        }
 
-       if (ie.igtk && ie.igtk_len != sizeof(struct wpa_igtk_kde)) {
+       if (ie.igtk &&
+           wpa_cipher_valid_mgmt_group(sm->mgmt_group_cipher) &&
+           ie.igtk_len != WPA_IGTK_KDE_PREFIX_LEN +
+           (unsigned int) wpa_cipher_key_len(sm->mgmt_group_cipher)) {
                wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                        "WPA: Invalid IGTK KDE length %lu",
                        (unsigned long) ie.igtk_len);
@@ -1089,8 +1182,16 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
                goto failed;
        }
 
+#ifdef CONFIG_P2P
+       if (ie.ip_addr_alloc) {
+               os_memcpy(sm->p2p_ip_addr, ie.ip_addr_alloc, 3 * 4);
+               wpa_hexdump(MSG_DEBUG, "P2P: IP address info",
+                           sm->p2p_ip_addr, sizeof(sm->p2p_ip_addr));
+       }
+#endif /* CONFIG_P2P */
+
        if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
-                                      NULL, 0, &sm->ptk)) {
+                                      &sm->ptk)) {
                goto failed;
        }
 
@@ -1112,7 +1213,10 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
        }
        wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
 
-       if (ie.gtk &&
+       if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) {
+               wpa_supplicant_key_neg_complete(sm, sm->bssid,
+                                               key_info & WPA_KEY_INFO_SECURE);
+       } else if (ie.gtk &&
            wpa_supplicant_pairwise_gtk(sm, key,
                                        ie.gtk, ie.gtk_len, key_info) < 0) {
                wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
@@ -1126,8 +1230,21 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
                goto failed;
        }
 
-       wpa_sm_set_rekey_offload(sm);
+       if (ie.gtk)
+               wpa_sm_set_rekey_offload(sm);
+
+       if (sm->proto == WPA_PROTO_RSN && wpa_key_mgmt_suite_b(sm->key_mgmt)) {
+               struct rsn_pmksa_cache_entry *sa;
+
+               sa = pmksa_cache_add(sm->pmksa, sm->pmk, sm->pmk_len,
+                                    sm->ptk.kck, sm->ptk.kck_len,
+                                    sm->bssid, sm->own_addr,
+                                    sm->network_ctx, sm->key_mgmt);
+               if (!sm->cur_pmksa)
+                       sm->cur_pmksa = sa;
+       }
 
+       sm->msg_3_of_4_ok = 1;
        return;
 
 failed:
@@ -1187,22 +1304,15 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm,
 
 static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm,
                                             const struct wpa_eapol_key *key,
-                                            size_t keydatalen, int key_info,
-                                            size_t extra_len, u16 ver,
-                                            struct wpa_gtk_data *gd)
+                                            const u8 *key_data,
+                                            size_t key_data_len, u16 key_info,
+                                            u16 ver, struct wpa_gtk_data *gd)
 {
        size_t maxkeylen;
-       u8 ek[32];
+       u16 gtk_len;
 
-       gd->gtk_len = WPA_GET_BE16(key->key_length);
-       maxkeylen = keydatalen;
-       if (keydatalen > extra_len) {
-               wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
-                       "WPA: Truncated EAPOL-Key packet: "
-                       "key_data_length=%lu > extra_len=%lu",
-                       (unsigned long) keydatalen, (unsigned long) extra_len);
-               return -1;
-       }
+       gtk_len = WPA_GET_BE16(key->key_length);
+       maxkeylen = key_data_len;
        if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
                if (maxkeylen < 8) {
                        wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
@@ -1213,45 +1323,50 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm,
                maxkeylen -= 8;
        }
 
-       if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
-                                             gd->gtk_len, maxkeylen,
+       if (gtk_len > maxkeylen ||
+           wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
+                                             gtk_len, maxkeylen,
                                              &gd->key_rsc_len, &gd->alg))
                return -1;
 
+       gd->gtk_len = gtk_len;
        gd->keyidx = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
                WPA_KEY_INFO_KEY_INDEX_SHIFT;
-       if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
-               os_memcpy(ek, key->key_iv, 16);
-               os_memcpy(ek + 16, sm->ptk.kek, 16);
-               if (keydatalen > sizeof(gd->gtk)) {
+       if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) {
+               u8 ek[32];
+               if (key_data_len > sizeof(gd->gtk)) {
                        wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                                "WPA: RC4 key data too long (%lu)",
-                               (unsigned long) keydatalen);
+                               (unsigned long) key_data_len);
                        return -1;
                }
-               os_memcpy(gd->gtk, key + 1, keydatalen);
-               if (rc4_skip(ek, 32, 256, gd->gtk, keydatalen)) {
+               os_memcpy(ek, key->key_iv, 16);
+               os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
+               os_memcpy(gd->gtk, key_data, key_data_len);
+               if (rc4_skip(ek, 32, 256, gd->gtk, key_data_len)) {
+                       os_memset(ek, 0, sizeof(ek));
                        wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
                                "WPA: RC4 failed");
                        return -1;
                }
+               os_memset(ek, 0, sizeof(ek));
        } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
-               if (keydatalen % 8) {
+               if (maxkeylen % 8) {
                        wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                                "WPA: Unsupported AES-WRAP len %lu",
-                               (unsigned long) keydatalen);
+                               (unsigned long) maxkeylen);
                        return -1;
                }
                if (maxkeylen > sizeof(gd->gtk)) {
                        wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                                "WPA: AES-WRAP key data "
                                "too long (keydatalen=%lu maxkeylen=%lu)",
-                               (unsigned long) keydatalen,
+                               (unsigned long) key_data_len,
                                (unsigned long) maxkeylen);
                        return -1;
                }
-               if (aes_unwrap(sm->ptk.kek, maxkeylen / 8,
-                              (const u8 *) (key + 1), gd->gtk)) {
+               if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, maxkeylen / 8,
+                              key_data, gd->gtk)) {
                        wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                                "WPA: AES unwrap failed - could not decrypt "
                                "GTK");
@@ -1272,32 +1387,41 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,
                                      const struct wpa_eapol_key *key,
                                      int ver, u16 key_info)
 {
-       size_t rlen;
+       size_t mic_len, hdrlen, rlen;
        struct wpa_eapol_key *reply;
-       u8 *rbuf;
+       struct wpa_eapol_key_192 *reply192;
+       u8 *rbuf, *key_mic;
 
+       mic_len = wpa_mic_len(sm->key_mgmt);
+       hdrlen = mic_len == 24 ? sizeof(*reply192) : sizeof(*reply);
        rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
-                                 sizeof(*reply), &rlen, (void *) &reply);
+                                 hdrlen, &rlen, (void *) &reply);
        if (rbuf == NULL)
                return -1;
+       reply192 = (struct wpa_eapol_key_192 *) reply;
 
-       reply->type = sm->proto == WPA_PROTO_RSN ?
+       reply->type = (sm->proto == WPA_PROTO_RSN ||
+                      sm->proto == WPA_PROTO_OSEN) ?
                EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA;
        key_info &= WPA_KEY_INFO_KEY_INDEX_MASK;
        key_info |= ver | WPA_KEY_INFO_MIC | WPA_KEY_INFO_SECURE;
        WPA_PUT_BE16(reply->key_info, key_info);
-       if (sm->proto == WPA_PROTO_RSN)
+       if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN)
                WPA_PUT_BE16(reply->key_length, 0);
        else
                os_memcpy(reply->key_length, key->key_length, 2);
        os_memcpy(reply->replay_counter, key->replay_counter,
                  WPA_REPLAY_COUNTER_LEN);
 
-       WPA_PUT_BE16(reply->key_data_length, 0);
+       key_mic = reply192->key_mic; /* same offset for reply and reply192 */
+       if (mic_len == 24)
+               WPA_PUT_BE16(reply192->key_data_length, 0);
+       else
+               WPA_PUT_BE16(reply->key_data_length, 0);
 
        wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
-       wpa_eapol_key_send(sm, sm->ptk.kck, ver, sm->bssid, ETH_P_EAPOL,
-                          rbuf, rlen, reply->key_mic);
+       wpa_eapol_key_send(sm, sm->ptk.kck, sm->ptk.kck_len, ver, sm->bssid,
+                          ETH_P_EAPOL, rbuf, rlen, key_mic);
 
        return 0;
 }
@@ -1306,12 +1430,19 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,
 static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
                                          const unsigned char *src_addr,
                                          const struct wpa_eapol_key *key,
-                                         int extra_len, u16 ver)
+                                         const u8 *key_data,
+                                         size_t key_data_len, u16 ver)
 {
-       u16 key_info, keydatalen;
+       u16 key_info;
        int rekey, ret;
        struct wpa_gtk_data gd;
 
+       if (!sm->msg_3_of_4_ok) {
+               wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+                       "WPA: Group Key Handshake started prior to completion of 4-way handshake");
+               goto failed;
+       }
+
        os_memset(&gd, 0, sizeof(gd));
 
        rekey = wpa_sm_get_state(sm) == WPA_COMPLETED;
@@ -1319,17 +1450,15 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
                "Handshake from " MACSTR " (ver=%d)", MAC2STR(src_addr), ver);
 
        key_info = WPA_GET_BE16(key->key_info);
-       keydatalen = WPA_GET_BE16(key->key_data_length);
 
-       if (sm->proto == WPA_PROTO_RSN) {
-               ret = wpa_supplicant_process_1_of_2_rsn(sm,
-                                                       (const u8 *) (key + 1),
-                                                       keydatalen, key_info,
+       if (sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) {
+               ret = wpa_supplicant_process_1_of_2_rsn(sm, key_data,
+                                                       key_data_len, key_info,
                                                        &gd);
        } else {
-               ret = wpa_supplicant_process_1_of_2_wpa(sm, key, keydatalen,
-                                                       key_info, extra_len,
-                                                       ver, &gd);
+               ret = wpa_supplicant_process_1_of_2_wpa(sm, key, key_data,
+                                                       key_data_len,
+                                                       key_info, ver, &gd);
        }
 
        wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
@@ -1340,6 +1469,7 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
        if (wpa_supplicant_install_gtk(sm, &gd, key->key_rsc) ||
            wpa_supplicant_send_2_of_2(sm, key, ver, key_info))
                goto failed;
+       os_memset(&gd, 0, sizeof(gd));
 
        if (rekey) {
                wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Group rekeying "
@@ -1347,34 +1477,37 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm,
                        MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher));
                wpa_sm_cancel_auth_timeout(sm);
                wpa_sm_set_state(sm, WPA_COMPLETED);
-
-               wpa_sm_set_rekey_offload(sm);
        } else {
                wpa_supplicant_key_neg_complete(sm, sm->bssid,
                                                key_info &
                                                WPA_KEY_INFO_SECURE);
        }
+
+       wpa_sm_set_rekey_offload(sm);
+
        return;
 
 failed:
+       os_memset(&gd, 0, sizeof(gd));
        wpa_sm_deauthenticate(sm, WLAN_REASON_UNSPECIFIED);
 }
 
 
 static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
-                                              struct wpa_eapol_key *key,
+                                              struct wpa_eapol_key_192 *key,
                                               u16 ver,
                                               const u8 *buf, size_t len)
 {
-       u8 mic[16];
+       u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
        int ok = 0;
+       size_t mic_len = wpa_mic_len(sm->key_mgmt);
 
-       os_memcpy(mic, key->key_mic, 16);
+       os_memcpy(mic, key->key_mic, mic_len);
        if (sm->tptk_set) {
-               os_memset(key->key_mic, 0, 16);
-               wpa_eapol_key_mic(sm->tptk.kck, ver, buf, len,
-                                 key->key_mic);
-               if (os_memcmp(mic, key->key_mic, 16) != 0) {
+               os_memset(key->key_mic, 0, mic_len);
+               wpa_eapol_key_mic(sm->tptk.kck, sm->tptk.kck_len, sm->key_mgmt,
+                                 ver, buf, len, key->key_mic);
+               if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
                        wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                                "WPA: Invalid EAPOL-Key MIC "
                                "when using TPTK - ignoring TPTK");
@@ -1383,14 +1516,15 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
                        sm->tptk_set = 0;
                        sm->ptk_set = 1;
                        os_memcpy(&sm->ptk, &sm->tptk, sizeof(sm->ptk));
+                       os_memset(&sm->tptk, 0, sizeof(sm->tptk));
                }
        }
 
        if (!ok && sm->ptk_set) {
-               os_memset(key->key_mic, 0, 16);
-               wpa_eapol_key_mic(sm->ptk.kck, ver, buf, len,
-                                 key->key_mic);
-               if (os_memcmp(mic, key->key_mic, 16) != 0) {
+               os_memset(key->key_mic, 0, mic_len);
+               wpa_eapol_key_mic(sm->ptk.kck, sm->ptk.kck_len, sm->key_mgmt,
+                                 ver, buf, len, key->key_mic);
+               if (os_memcmp_const(mic, key->key_mic, mic_len) != 0) {
                        wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                                "WPA: Invalid EAPOL-Key MIC - "
                                "dropping packet");
@@ -1415,12 +1549,11 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
 
 /* Decrypt RSN EAPOL-Key key data (RC4 or AES-WRAP) */
 static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
-                                          struct wpa_eapol_key *key, u16 ver)
+                                          struct wpa_eapol_key *key, u16 ver,
+                                          u8 *key_data, size_t *key_data_len)
 {
-       u16 keydatalen = WPA_GET_BE16(key->key_data_length);
-
        wpa_hexdump(MSG_DEBUG, "RSN: encrypted key data",
-                   (u8 *) (key + 1), keydatalen);
+                   key_data, *key_data_len);
        if (!sm->ptk_set) {
                wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                        "WPA: PTK not available, cannot decrypt EAPOL-Key Key "
@@ -1430,49 +1563,53 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
 
        /* Decrypt key data here so that this operation does not need
         * to be implemented separately for each message type. */
-       if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4) {
+       if (ver == WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 && sm->ptk.kek_len == 16) {
                u8 ek[32];
                os_memcpy(ek, key->key_iv, 16);
-               os_memcpy(ek + 16, sm->ptk.kek, 16);
-               if (rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen)) {
+               os_memcpy(ek + 16, sm->ptk.kek, sm->ptk.kek_len);
+               if (rc4_skip(ek, 32, 256, key_data, *key_data_len)) {
+                       os_memset(ek, 0, sizeof(ek));
                        wpa_msg(sm->ctx->msg_ctx, MSG_ERROR,
                                "WPA: RC4 failed");
                        return -1;
                }
+               os_memset(ek, 0, sizeof(ek));
        } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
-                  ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+                  ver == WPA_KEY_INFO_TYPE_AES_128_CMAC ||
+                  sm->key_mgmt == WPA_KEY_MGMT_OSEN ||
+                  wpa_key_mgmt_suite_b(sm->key_mgmt)) {
                u8 *buf;
-               if (keydatalen % 8) {
+               if (*key_data_len < 8 || *key_data_len % 8) {
                        wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
-                               "WPA: Unsupported AES-WRAP len %d",
-                               keydatalen);
+                               "WPA: Unsupported AES-WRAP len %u",
+                               (unsigned int) *key_data_len);
                        return -1;
                }
-               keydatalen -= 8; /* AES-WRAP adds 8 bytes */
-               buf = os_malloc(keydatalen);
+               *key_data_len -= 8; /* AES-WRAP adds 8 bytes */
+               buf = os_malloc(*key_data_len);
                if (buf == NULL) {
                        wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                                "WPA: No memory for AES-UNWRAP buffer");
                        return -1;
                }
-               if (aes_unwrap(sm->ptk.kek, keydatalen / 8,
-                              (u8 *) (key + 1), buf)) {
+               if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, *key_data_len / 8,
+                              key_data, buf)) {
                        os_free(buf);
                        wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                                "WPA: AES unwrap failed - "
                                "could not decrypt EAPOL-Key key data");
                        return -1;
                }
-               os_memcpy(key + 1, buf, keydatalen);
+               os_memcpy(key_data, buf, *key_data_len);
                os_free(buf);
-               WPA_PUT_BE16(key->key_data_length, keydatalen);
+               WPA_PUT_BE16(key->key_data_length, *key_data_len);
        } else {
                wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                        "WPA: Unsupported key_info type %d", ver);
                return -1;
        }
        wpa_hexdump_key(MSG_DEBUG, "WPA: decrypted EAPOL-Key key data",
-                       (u8 *) (key + 1), keydatalen);
+                       key_data, *key_data_len);
        return 0;
 }
 
@@ -1492,7 +1629,9 @@ void wpa_sm_aborted_cached(struct wpa_sm *sm)
 
 
 static void wpa_eapol_key_dump(struct wpa_sm *sm,
-                              const struct wpa_eapol_key *key)
+                              const struct wpa_eapol_key *key,
+                              unsigned int key_data_len,
+                              const u8 *mic, unsigned int mic_len)
 {
 #ifndef CONFIG_NO_STDOUT_DEBUG
        u16 key_info = WPA_GET_BE16(key->key_info);
@@ -1514,15 +1653,14 @@ static void wpa_eapol_key_dump(struct wpa_sm *sm,
                key_info & WPA_KEY_INFO_ENCR_KEY_DATA ? " Encr" : "");
        wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
                "  key_length=%u key_data_length=%u",
-               WPA_GET_BE16(key->key_length),
-               WPA_GET_BE16(key->key_data_length));
+               WPA_GET_BE16(key->key_length), key_data_len);
        wpa_hexdump(MSG_DEBUG, "  replay_counter",
                    key->replay_counter, WPA_REPLAY_COUNTER_LEN);
        wpa_hexdump(MSG_DEBUG, "  key_nonce", key->key_nonce, WPA_NONCE_LEN);
        wpa_hexdump(MSG_DEBUG, "  key_iv", key->key_iv, 16);
        wpa_hexdump(MSG_DEBUG, "  key_rsc", key->key_rsc, 8);
        wpa_hexdump(MSG_DEBUG, "  key_id (reserved)", key->key_id, 8);
-       wpa_hexdump(MSG_DEBUG, "  key_mic", key->key_mic, 16);
+       wpa_hexdump(MSG_DEBUG, "  key_mic", mic, mic_len);
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 }
 
@@ -1546,34 +1684,34 @@ static void wpa_eapol_key_dump(struct wpa_sm *sm,
 int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
                    const u8 *buf, size_t len)
 {
-       size_t plen, data_len, extra_len;
-       struct ieee802_1x_hdr *hdr;
+       size_t plen, data_len, key_data_len;
+       const struct ieee802_1x_hdr *hdr;
        struct wpa_eapol_key *key;
+       struct wpa_eapol_key_192 *key192;
        u16 key_info, ver;
-       u8 *tmp;
+       u8 *tmp = NULL;
        int ret = -1;
        struct wpa_peerkey *peerkey = NULL;
+       u8 *key_data;
+       size_t mic_len, keyhdrlen;
 
 #ifdef CONFIG_IEEE80211R
        sm->ft_completed = 0;
 #endif /* CONFIG_IEEE80211R */
 
-       if (len < sizeof(*hdr) + sizeof(*key)) {
+       mic_len = wpa_mic_len(sm->key_mgmt);
+       keyhdrlen = mic_len == 24 ? sizeof(*key192) : sizeof(*key);
+
+       if (len < sizeof(*hdr) + keyhdrlen) {
                wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
                        "WPA: EAPOL frame too short to be a WPA "
                        "EAPOL-Key (len %lu, expecting at least %lu)",
                        (unsigned long) len,
-                       (unsigned long) sizeof(*hdr) + sizeof(*key));
+                       (unsigned long) sizeof(*hdr) + keyhdrlen);
                return 0;
        }
 
-       tmp = os_malloc(len);
-       if (tmp == NULL)
-               return -1;
-       os_memcpy(tmp, buf, len);
-
-       hdr = (struct ieee802_1x_hdr *) tmp;
-       key = (struct wpa_eapol_key *) (hdr + 1);
+       hdr = (const struct ieee802_1x_hdr *) buf;
        plen = be_to_host16(hdr->length);
        data_len = plen + sizeof(*hdr);
        wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
@@ -1590,7 +1728,8 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
                ret = 0;
                goto out;
        }
-       if (plen > len - sizeof(*hdr) || plen < sizeof(*key)) {
+       wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", buf, len);
+       if (plen > len - sizeof(*hdr) || plen < keyhdrlen) {
                wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
                        "WPA: EAPOL frame payload size %lu "
                        "invalid (frame size %lu)",
@@ -1598,6 +1737,27 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
                ret = 0;
                goto out;
        }
+       if (data_len < len) {
+               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
+                       "WPA: ignoring %lu bytes after the IEEE 802.1X data",
+                       (unsigned long) len - data_len);
+       }
+
+       /*
+        * Make a copy of the frame since we need to modify the buffer during
+        * MAC validation and Key Data decryption.
+        */
+       tmp = os_malloc(data_len);
+       if (tmp == NULL)
+               goto out;
+       os_memcpy(tmp, buf, data_len);
+       key = (struct wpa_eapol_key *) (tmp + sizeof(struct ieee802_1x_hdr));
+       key192 = (struct wpa_eapol_key_192 *)
+               (tmp + sizeof(struct ieee802_1x_hdr));
+       if (mic_len == 24)
+               key_data = (u8 *) (key192 + 1);
+       else
+               key_data = (u8 *) (key + 1);
 
        if (key->type != EAPOL_KEY_TYPE_WPA && key->type != EAPOL_KEY_TYPE_RSN)
        {
@@ -1607,28 +1767,53 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
                ret = 0;
                goto out;
        }
-       wpa_eapol_key_dump(sm, key);
 
-       eapol_sm_notify_lower_layer_success(sm->eapol, 0);
-       wpa_hexdump(MSG_MSGDUMP, "WPA: RX EAPOL-Key", tmp, len);
-       if (data_len < len) {
-               wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG,
-                       "WPA: ignoring %lu bytes after the IEEE 802.1X data",
-                       (unsigned long) len - data_len);
+       if (mic_len == 24)
+               key_data_len = WPA_GET_BE16(key192->key_data_length);
+       else
+               key_data_len = WPA_GET_BE16(key->key_data_length);
+       wpa_eapol_key_dump(sm, key, key_data_len, key192->key_mic, mic_len);
+
+       if (key_data_len > plen - keyhdrlen) {
+               wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key "
+                       "frame - key_data overflow (%u > %u)",
+                       (unsigned int) key_data_len,
+                       (unsigned int) (plen - keyhdrlen));
+               goto out;
        }
+
+       eapol_sm_notify_lower_layer_success(sm->eapol, 0);
        key_info = WPA_GET_BE16(key->key_info);
        ver = key_info & WPA_KEY_INFO_TYPE_MASK;
        if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
 #if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W)
            ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
 #endif /* CONFIG_IEEE80211R || CONFIG_IEEE80211W */
-           ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+           ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
+           !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+           sm->key_mgmt != WPA_KEY_MGMT_OSEN) {
                wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
                        "WPA: Unsupported EAPOL-Key descriptor version %d",
                        ver);
                goto out;
        }
 
+       if (sm->key_mgmt == WPA_KEY_MGMT_OSEN &&
+           ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+               wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+                       "OSEN: Unsupported EAPOL-Key descriptor version %d",
+                       ver);
+               goto out;
+       }
+
+       if (wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+           ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
+               wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+                       "RSN: Unsupported EAPOL-Key descriptor version %d (expected AKM defined = 0)",
+                       ver);
+               goto out;
+       }
+
 #ifdef CONFIG_IEEE80211R
        if (wpa_key_mgmt_ft(sm->key_mgmt)) {
                /* IEEE 802.11r uses a new key_info type (AES-128-CMAC). */
@@ -1641,7 +1826,9 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_IEEE80211W
        if (wpa_key_mgmt_sha256(sm->key_mgmt)) {
-               if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+               if (ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
+                   sm->key_mgmt != WPA_KEY_MGMT_OSEN &&
+                   !wpa_key_mgmt_suite_b(sm->key_mgmt)) {
                        wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
                                "WPA: AP did not use the "
                                "negotiated AES-128-CMAC");
@@ -1650,6 +1837,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
        } else
 #endif /* CONFIG_IEEE80211W */
        if (sm->pairwise_cipher == WPA_CIPHER_CCMP &&
+           !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
            ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
                wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
                        "WPA: CCMP is used, but EAPOL-Key "
@@ -1663,11 +1851,14 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
                        wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
                                "WPA: Backwards compatibility: allow invalid "
                                "version for non-CCMP group keys");
+               } else if (ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+                       wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+                               "WPA: Interoperability workaround: allow incorrect (should have been HMAC-SHA1), but stronger (is AES-128-CMAC), descriptor version to be used");
                } else
                        goto out;
-       }
-       if (sm->pairwise_cipher == WPA_CIPHER_GCMP &&
-           ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
+       } else if (sm->pairwise_cipher == WPA_CIPHER_GCMP &&
+                  !wpa_key_mgmt_suite_b(sm->key_mgmt) &&
+                  ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
                wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
                        "WPA: GCMP is used, but EAPOL-Key "
                        "descriptor version (%d) is not 2", ver);
@@ -1737,31 +1928,21 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
        }
 
        if ((key_info & WPA_KEY_INFO_MIC) && !peerkey &&
-           wpa_supplicant_verify_eapol_key_mic(sm, key, ver, tmp, data_len))
+           wpa_supplicant_verify_eapol_key_mic(sm, key192, ver, tmp, data_len))
                goto out;
 
 #ifdef CONFIG_PEERKEY
        if ((key_info & WPA_KEY_INFO_MIC) && peerkey &&
-           peerkey_verify_eapol_key_mic(sm, peerkey, key, ver, tmp, data_len))
+           peerkey_verify_eapol_key_mic(sm, peerkey, key192, ver, tmp,
+                                        data_len))
                goto out;
 #endif /* CONFIG_PEERKEY */
 
-       extra_len = data_len - sizeof(*hdr) - sizeof(*key);
-
-       if (WPA_GET_BE16(key->key_data_length) > extra_len) {
-               wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key "
-                       "frame - key_data overflow (%d > %lu)",
-                       WPA_GET_BE16(key->key_data_length),
-                       (unsigned long) extra_len);
-               goto out;
-       }
-       extra_len = WPA_GET_BE16(key->key_data_length);
-
-       if (sm->proto == WPA_PROTO_RSN &&
+       if ((sm->proto == WPA_PROTO_RSN || sm->proto == WPA_PROTO_OSEN) &&
            (key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
-               if (wpa_supplicant_decrypt_key_data(sm, key, ver))
+               if (wpa_supplicant_decrypt_key_data(sm, key, ver, key_data,
+                                                   &key_data_len))
                        goto out;
-               extra_len = WPA_GET_BE16(key->key_data_length);
        }
 
        if (key_info & WPA_KEY_INFO_KEY_TYPE) {
@@ -1773,24 +1954,28 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
                }
                if (peerkey) {
                        /* PeerKey 4-Way Handshake */
-                       peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver);
+                       peerkey_rx_eapol_4way(sm, peerkey, key, key_info, ver,
+                                             key_data, key_data_len);
                } else if (key_info & WPA_KEY_INFO_MIC) {
                        /* 3/4 4-Way Handshake */
-                       wpa_supplicant_process_3_of_4(sm, key, ver);
+                       wpa_supplicant_process_3_of_4(sm, key, ver, key_data,
+                                                     key_data_len);
                } else {
                        /* 1/4 4-Way Handshake */
                        wpa_supplicant_process_1_of_4(sm, src_addr, key,
-                                                     ver);
+                                                     ver, key_data,
+                                                     key_data_len);
                }
        } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) {
                /* PeerKey SMK Handshake */
-               peerkey_rx_eapol_smk(sm, src_addr, key, extra_len, key_info,
+               peerkey_rx_eapol_smk(sm, src_addr, key, key_data_len, key_info,
                                     ver);
        } else {
                if (key_info & WPA_KEY_INFO_MIC) {
                        /* 1/2 Group Key Handshake */
                        wpa_supplicant_process_1_of_2(sm, src_addr, key,
-                                                     extra_len, ver);
+                                                     key_data, key_data_len,
+                                                     ver);
                } else {
                        wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
                                "WPA: EAPOL-Key (Group) without Mic bit - "
@@ -1801,7 +1986,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr,
        ret = 1;
 
 out:
-       os_free(tmp);
+       bin_clear_free(tmp, data_len);
        return ret;
 }
 
@@ -1811,7 +1996,8 @@ static u32 wpa_key_mgmt_suite(struct wpa_sm *sm)
 {
        switch (sm->key_mgmt) {
        case WPA_KEY_MGMT_IEEE8021X:
-               return (sm->proto == WPA_PROTO_RSN ?
+               return ((sm->proto == WPA_PROTO_RSN ||
+                        sm->proto == WPA_PROTO_OSEN) ?
                        RSN_AUTH_KEY_MGMT_UNSPEC_802_1X :
                        WPA_AUTH_KEY_MGMT_UNSPEC_802_1X);
        case WPA_KEY_MGMT_PSK:
@@ -1836,6 +2022,10 @@ static u32 wpa_key_mgmt_suite(struct wpa_sm *sm)
                        WPA_AUTH_KEY_MGMT_CCKM);
        case WPA_KEY_MGMT_WPA_NONE:
                return WPA_AUTH_KEY_MGMT_NONE;
+       case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
+               return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
+       case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
+               return RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
        default:
                return 0;
        }
@@ -1893,7 +2083,7 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
                          sm->dot11RSNAConfigPMKLifetime,
                          sm->dot11RSNAConfigPMKReauthThreshold,
                          sm->dot11RSNAConfigSATimeout);
-       if (ret < 0 || (size_t) ret >= buflen)
+       if (os_snprintf_error(buflen, ret))
                return 0;
        len = ret;
 
@@ -1920,7 +2110,7 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen)
                RSN_SUITE_ARG(wpa_cipher_to_suite(sm->proto,
                                                  sm->group_cipher)),
                sm->dot11RSNA4WayHandshakeFailures);
-       if (ret >= 0 && (size_t) ret < buflen)
+       if (!os_snprintf_error(buflen - len, ret))
                len += ret;
 
        return (int) len;
@@ -2018,6 +2208,7 @@ void wpa_sm_deinit(struct wpa_sm *sm)
        os_free(sm->assoc_wpa_ie);
        os_free(sm->ap_wpa_ie);
        os_free(sm->ap_rsn_ie);
+       wpa_sm_drop_sa(sm);
        os_free(sm->ctx);
        peerkey_deinit(sm);
 #ifdef CONFIG_IEEE80211R
@@ -2074,12 +2265,18 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
                 */
                wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Clear old PTK");
                sm->ptk_set = 0;
+               os_memset(&sm->ptk, 0, sizeof(sm->ptk));
                sm->tptk_set = 0;
+               os_memset(&sm->tptk, 0, sizeof(sm->tptk));
        }
 
 #ifdef CONFIG_TDLS
        wpa_tdls_assoc(sm);
 #endif /* CONFIG_TDLS */
+
+#ifdef CONFIG_P2P
+       os_memset(sm->p2p_ip_addr, 0, sizeof(sm->p2p_ip_addr));
+#endif /* CONFIG_P2P */
 }
 
 
@@ -2092,6 +2289,9 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
  */
 void wpa_sm_notify_disassoc(struct wpa_sm *sm)
 {
+       eloop_cancel_timeout(wpa_sm_start_preauth, sm, NULL);
+       eloop_cancel_timeout(wpa_sm_rekey_ptk, sm, NULL);
+       peerkey_deinit(sm);
        rsn_preauth_deinit(sm);
        pmksa_cache_clear_current(sm);
        if (wpa_sm_get_state(sm) == WPA_4WAY_HANDSHAKE)
@@ -2099,6 +2299,11 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm)
 #ifdef CONFIG_TDLS
        wpa_tdls_disassoc(sm);
 #endif /* CONFIG_TDLS */
+
+       /* Keys are not needed in the WPA state machine anymore */
+       wpa_sm_drop_sa(sm);
+
+       sm->msg_3_of_4_ok = 0;
 }
 
 
@@ -2107,10 +2312,12 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm)
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
  * @pmk: The new PMK
  * @pmk_len: The length of the new PMK in bytes
+ * @bssid: AA to add into PMKSA cache or %NULL to not cache the PMK
  *
  * Configure the PMK for WPA state machine.
  */
-void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len)
+void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
+                   const u8 *bssid)
 {
        if (sm == NULL)
                return;
@@ -2123,6 +2330,12 @@ void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len)
        sm->xxkey_len = pmk_len;
        os_memcpy(sm->xxkey, pmk, pmk_len);
 #endif /* CONFIG_IEEE80211R */
+
+       if (bssid) {
+               pmksa_cache_add(sm->pmksa, pmk, pmk_len, NULL, 0,
+                               bssid, sm->own_addr,
+                               sm->network_ctx, sm->key_mgmt);
+       }
 }
 
 
@@ -2202,6 +2415,7 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config)
                } else
                        sm->ssid_len = 0;
                sm->wpa_ptk_rekey = config->wpa_ptk_rekey;
+               sm->p2p = config->p2p;
        } else {
                sm->network_ctx = NULL;
                sm->peerkey_enabled = 0;
@@ -2211,6 +2425,7 @@ void wpa_sm_set_config(struct wpa_sm *sm, struct rsn_supp_config *config)
                sm->eap_conf_ctx = NULL;
                sm->ssid_len = 0;
                sm->wpa_ptk_rekey = 0;
+               sm->p2p = 0;
        }
 }
 
@@ -2321,44 +2536,6 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
 
 
 /**
- * wpa_sm_get_param - Get WPA state machine parameters
- * @sm: Pointer to WPA state machine data from wpa_sm_init()
- * @param: Parameter field
- * Returns: Parameter value
- */
-unsigned int wpa_sm_get_param(struct wpa_sm *sm, enum wpa_sm_conf_params param)
-{
-       if (sm == NULL)
-               return 0;
-
-       switch (param) {
-       case RSNA_PMK_LIFETIME:
-               return sm->dot11RSNAConfigPMKLifetime;
-       case RSNA_PMK_REAUTH_THRESHOLD:
-               return sm->dot11RSNAConfigPMKReauthThreshold;
-       case RSNA_SA_TIMEOUT:
-               return sm->dot11RSNAConfigSATimeout;
-       case WPA_PARAM_PROTO:
-               return sm->proto;
-       case WPA_PARAM_PAIRWISE:
-               return sm->pairwise_cipher;
-       case WPA_PARAM_GROUP:
-               return sm->group_cipher;
-       case WPA_PARAM_KEY_MGMT:
-               return sm->key_mgmt;
-#ifdef CONFIG_IEEE80211W
-       case WPA_PARAM_MGMT_GROUP:
-               return sm->mgmt_group_cipher;
-#endif /* CONFIG_IEEE80211W */
-       case WPA_PARAM_RSN_ENABLED:
-               return sm->rsn_enabled;
-       default:
-               return 0;
-       }
-}
-
-
-/**
  * wpa_sm_get_status - Get WPA state machine
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
  * @buf: Buffer for status information
@@ -2383,7 +2560,7 @@ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
                          wpa_cipher_txt(sm->pairwise_cipher),
                          wpa_cipher_txt(sm->group_cipher),
                          wpa_key_mgmt_txt(sm->key_mgmt, sm->proto));
-       if (ret < 0 || ret >= end - pos)
+       if (os_snprintf_error(end - pos, ret))
                return pos - buf;
        pos += ret;
 
@@ -2396,7 +2573,7 @@ int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
                        ret = os_snprintf(pos, end - pos, "pmf=%d\n",
                                          (rsn.capabilities &
                                           WPA_CAPABILITY_MFPR) ? 2 : 1);
-                       if (ret < 0 || ret >= end - pos)
+                       if (os_snprintf_error(end - pos, ret))
                                return pos - buf;
                        pos += ret;
                }
@@ -2607,6 +2784,11 @@ void wpa_sm_drop_sa(struct wpa_sm *sm)
        os_memset(sm->pmk, 0, sizeof(sm->pmk));
        os_memset(&sm->ptk, 0, sizeof(sm->ptk));
        os_memset(&sm->tptk, 0, sizeof(sm->tptk));
+#ifdef CONFIG_IEEE80211R
+       os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
+       os_memset(sm->pmk_r0, 0, sizeof(sm->pmk_r0));
+       os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1));
+#endif /* CONFIG_IEEE80211R */
 }
 
 
@@ -2633,29 +2815,22 @@ void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx)
 #ifdef CONFIG_WNM
 int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
 {
-       struct wpa_gtk_data gd;
-#ifdef CONFIG_IEEE80211W
-       struct wpa_igtk_kde igd;
-       u16 keyidx;
-#endif /* CONFIG_IEEE80211W */
        u16 keyinfo;
        u8 keylen;  /* plaintext key len */
        u8 *key_rsc;
 
-       os_memset(&gd, 0, sizeof(gd));
-#ifdef CONFIG_IEEE80211W
-       os_memset(&igd, 0, sizeof(igd));
-#endif /* CONFIG_IEEE80211W */
-
-       keylen = wpa_cipher_key_len(sm->group_cipher);
-       gd.key_rsc_len = wpa_cipher_rsc_len(sm->group_cipher);
-       gd.alg = wpa_cipher_to_alg(sm->group_cipher);
-       if (gd.alg == WPA_ALG_NONE) {
-               wpa_printf(MSG_DEBUG, "Unsupported group cipher suite");
-               return -1;
-       }
-
        if (subelem_id == WNM_SLEEP_SUBELEM_GTK) {
+               struct wpa_gtk_data gd;
+
+               os_memset(&gd, 0, sizeof(gd));
+               keylen = wpa_cipher_key_len(sm->group_cipher);
+               gd.key_rsc_len = wpa_cipher_rsc_len(sm->group_cipher);
+               gd.alg = wpa_cipher_to_alg(sm->group_cipher);
+               if (gd.alg == WPA_ALG_NONE) {
+                       wpa_printf(MSG_DEBUG, "Unsupported group cipher suite");
+                       return -1;
+               }
+
                key_rsc = buf + 5;
                keyinfo = WPA_GET_LE16(buf + 2);
                gd.gtk_len = keylen;
@@ -2673,27 +2848,37 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
                wpa_hexdump_key(MSG_DEBUG, "Install GTK (WNM SLEEP)",
                                gd.gtk, gd.gtk_len);
                if (wpa_supplicant_install_gtk(sm, &gd, key_rsc)) {
+                       os_memset(&gd, 0, sizeof(gd));
                        wpa_printf(MSG_DEBUG, "Failed to install the GTK in "
                                   "WNM mode");
                        return -1;
                }
+               os_memset(&gd, 0, sizeof(gd));
 #ifdef CONFIG_IEEE80211W
        } else if (subelem_id == WNM_SLEEP_SUBELEM_IGTK) {
+               struct wpa_igtk_kde igd;
+               u16 keyidx;
+
+               os_memset(&igd, 0, sizeof(igd));
+               keylen = wpa_cipher_key_len(sm->mgmt_group_cipher);
                os_memcpy(igd.keyid, buf + 2, 2);
                os_memcpy(igd.pn, buf + 4, 6);
 
                keyidx = WPA_GET_LE16(igd.keyid);
-               os_memcpy(igd.igtk, buf + 10, WPA_IGTK_LEN);
+               os_memcpy(igd.igtk, buf + 10, keylen);
 
                wpa_hexdump_key(MSG_DEBUG, "Install IGTK (WNM SLEEP)",
-                               igd.igtk, WPA_IGTK_LEN);
-               if (wpa_sm_set_key(sm, WPA_ALG_IGTK, broadcast_ether_addr,
+                               igd.igtk, keylen);
+               if (wpa_sm_set_key(sm, wpa_cipher_to_alg(sm->mgmt_group_cipher),
+                                  broadcast_ether_addr,
                                   keyidx, 0, igd.pn, sizeof(igd.pn),
-                                  igd.igtk, WPA_IGTK_LEN) < 0) {
+                                  igd.igtk, keylen) < 0) {
                        wpa_printf(MSG_DEBUG, "Failed to install the IGTK in "
                                   "WNM mode");
+                       os_memset(&igd, 0, sizeof(igd));
                        return -1;
                }
+               os_memset(&igd, 0, sizeof(igd));
 #endif /* CONFIG_IEEE80211W */
        } else {
                wpa_printf(MSG_DEBUG, "Unknown element id");
@@ -2703,3 +2888,67 @@ int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf)
        return 0;
 }
 #endif /* CONFIG_WNM */
+
+
+#ifdef CONFIG_PEERKEY
+int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr,
+                           const u8 *buf, size_t len)
+{
+       struct wpa_peerkey *peerkey;
+
+       for (peerkey = sm->peerkey; peerkey; peerkey = peerkey->next) {
+               if (os_memcmp(peerkey->addr, src_addr, ETH_ALEN) == 0)
+                       break;
+       }
+
+       if (!peerkey)
+               return 0;
+
+       wpa_sm_rx_eapol(sm, src_addr, buf, len);
+
+       return 1;
+}
+#endif /* CONFIG_PEERKEY */
+
+
+#ifdef CONFIG_P2P
+
+int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf)
+{
+       if (sm == NULL || WPA_GET_BE32(sm->p2p_ip_addr) == 0)
+               return -1;
+       os_memcpy(buf, sm->p2p_ip_addr, 3 * 4);
+       return 0;
+}
+
+#endif /* CONFIG_P2P */
+
+
+void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter)
+{
+       if (rx_replay_counter == NULL)
+               return;
+
+       os_memcpy(sm->rx_replay_counter, rx_replay_counter,
+                 WPA_REPLAY_COUNTER_LEN);
+       sm->rx_replay_counter_set = 1;
+       wpa_printf(MSG_DEBUG, "Updated key replay counter");
+}
+
+
+void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm,
+                           const u8 *ptk_kck, size_t ptk_kck_len,
+                           const u8 *ptk_kek, size_t ptk_kek_len)
+{
+       if (ptk_kck && ptk_kck_len <= WPA_KCK_MAX_LEN) {
+               os_memcpy(sm->ptk.kck, ptk_kck, ptk_kck_len);
+               sm->ptk.kck_len = ptk_kck_len;
+               wpa_printf(MSG_DEBUG, "Updated PTK KCK");
+       }
+       if (ptk_kek && ptk_kek_len <= WPA_KEK_MAX_LEN) {
+               os_memcpy(sm->ptk.kek, ptk_kek, ptk_kek_len);
+               sm->ptk.kek_len = ptk_kek_len;
+               wpa_printf(MSG_DEBUG, "Updated PTK KEK");
+       }
+       sm->ptk_set = 1;
+}
old mode 100644 (file)
new mode 100755 (executable)
index 26e9c6c..e163b70
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant - WPA definitions
- * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -17,6 +17,7 @@
 struct wpa_sm;
 struct eapol_sm;
 struct wpa_config_blob;
+struct hostapd_freq_params;
 
 struct wpa_sm_ctx {
        void *ctx; /* pointer to arbitrary upper level context */
@@ -51,21 +52,31 @@ struct wpa_sm_ctx {
        int (*mark_authenticated)(void *ctx, const u8 *target_ap);
 #ifdef CONFIG_TDLS
        int (*tdls_get_capa)(void *ctx, int *tdls_supported,
-                            int *tdls_ext_setup);
+                            int *tdls_ext_setup, int *tdls_chan_switch);
        int (*send_tdls_mgmt)(void *ctx, const u8 *dst,
                              u8 action_code, u8 dialog_token,
-                             u16 status_code, const u8 *buf, size_t len);
+                             u16 status_code, u32 peer_capab,
+                             int initiator, const u8 *buf, size_t len);
        int (*tdls_oper)(void *ctx, int oper, const u8 *peer);
        int (*tdls_peer_addset)(void *ctx, const u8 *addr, int add, u16 aid,
                                u16 capability, const u8 *supp_rates,
                                size_t supp_rates_len,
                                const struct ieee80211_ht_capabilities *ht_capab,
                                const struct ieee80211_vht_capabilities *vht_capab,
-                               u8 qosinfo, const u8 *ext_capab,
-                               size_t ext_capab_len);
+                               u8 qosinfo, int wmm, const u8 *ext_capab,
+                               size_t ext_capab_len, const u8 *supp_channels,
+                               size_t supp_channels_len,
+                               const u8 *supp_oper_classes,
+                               size_t supp_oper_classes_len);
+       int (*tdls_enable_channel_switch)(
+               void *ctx, const u8 *addr, u8 oper_class,
+               const struct hostapd_freq_params *params);
+       int (*tdls_disable_channel_switch)(void *ctx, const u8 *addr);
 #endif /* CONFIG_TDLS */
-       void (*set_rekey_offload)(void *ctx, const u8 *kek, const u8 *kck,
+       void (*set_rekey_offload)(void *ctx, const u8 *kek, size_t kek_len,
+                                 const u8 *kck, size_t kck_len,
                                  const u8 *replay_ctr);
+       int (*key_mgmt_set_pmk)(void *ctx, const u8 *pmk, size_t pmk_len);
 };
 
 
@@ -92,6 +103,7 @@ struct rsn_supp_config {
        const u8 *ssid;
        size_t ssid_len;
        int wpa_ptk_rekey;
+       int p2p;
 };
 
 #ifndef CONFIG_NO_WPA
@@ -100,7 +112,8 @@ struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx);
 void wpa_sm_deinit(struct wpa_sm *sm);
 void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid);
 void wpa_sm_notify_disassoc(struct wpa_sm *sm);
-void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len);
+void wpa_sm_set_pmk(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
+                   const u8 *bssid);
 void wpa_sm_set_pmk_from_pmksa(struct wpa_sm *sm);
 void wpa_sm_set_fast_reauth(struct wpa_sm *sm, int fast_reauth);
 void wpa_sm_set_scard_ctx(struct wpa_sm *sm, void *scard_ctx);
@@ -118,8 +131,6 @@ int wpa_sm_get_mib(struct wpa_sm *sm, char *buf, size_t buflen);
 
 int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
                     unsigned int value);
-unsigned int wpa_sm_get_param(struct wpa_sm *sm,
-                             enum wpa_sm_conf_params param);
 
 int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
                      int verbose);
@@ -142,6 +153,13 @@ void wpa_sm_update_replay_ctr(struct wpa_sm *sm, const u8 *replay_ctr);
 
 void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm, void *network_ctx);
 
+int wpa_sm_get_p2p_ip_addr(struct wpa_sm *sm, u8 *buf);
+
+void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm, const u8 *rx_replay_counter);
+void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm,
+                           const u8 *ptk_kck, size_t ptk_kck_len,
+                           const u8 *ptk_kek, size_t ptk_kek_len);
+
 #else /* CONFIG_NO_WPA */
 
 static inline struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx)
@@ -233,12 +251,6 @@ static inline int wpa_sm_set_param(struct wpa_sm *sm,
        return -1;
 }
 
-static inline unsigned int wpa_sm_get_param(struct wpa_sm *sm,
-                                           enum wpa_sm_conf_params param)
-{
-       return 0;
-}
-
 static inline int wpa_sm_get_status(struct wpa_sm *sm, char *buf,
                                    size_t buflen, int verbose)
 {
@@ -302,15 +314,33 @@ static inline void wpa_sm_pmksa_cache_flush(struct wpa_sm *sm,
 {
 }
 
+static inline void wpa_sm_set_rx_replay_ctr(struct wpa_sm *sm,
+                                           const u8 *rx_replay_counter)
+{
+}
+
+static inline void wpa_sm_set_ptk_kck_kek(struct wpa_sm *sm, const u8 *ptk_kck,
+                                         const u8 *ptk_kek)
+{
+}
+
 #endif /* CONFIG_NO_WPA */
 
 #ifdef CONFIG_PEERKEY
 int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer);
+int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr,
+                           const u8 *buf, size_t len);
 #else /* CONFIG_PEERKEY */
 static inline int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer)
 {
        return -1;
 }
+
+static inline int wpa_sm_rx_eapol_peerkey(struct wpa_sm *sm, const u8 *src_addr,
+                                         const u8 *buf, size_t len)
+{
+       return 0;
+}
 #endif /* CONFIG_PEERKEY */
 
 #ifdef CONFIG_IEEE80211R
@@ -372,15 +402,19 @@ void wpa_tdls_ap_ies(struct wpa_sm *sm, const u8 *ies, size_t len);
 void wpa_tdls_assoc_resp_ies(struct wpa_sm *sm, const u8 *ies, size_t len);
 int wpa_tdls_start(struct wpa_sm *sm, const u8 *addr);
 void wpa_tdls_remove(struct wpa_sm *sm, const u8 *addr);
-int wpa_tdls_send_teardown(struct wpa_sm *sm, const u8 *addr, u16 reason_code);
 int wpa_tdls_teardown_link(struct wpa_sm *sm, const u8 *addr, u16 reason_code);
 int wpa_tdls_send_discovery_request(struct wpa_sm *sm, const u8 *addr);
 int wpa_tdls_init(struct wpa_sm *sm);
 void wpa_tdls_teardown_peers(struct wpa_sm *sm);
 void wpa_tdls_deinit(struct wpa_sm *sm);
 void wpa_tdls_enable(struct wpa_sm *sm, int enabled);
-void wpa_tdls_disable_link(struct wpa_sm *sm, const u8 *addr);
+void wpa_tdls_disable_unreachable_link(struct wpa_sm *sm, const u8 *addr);
+const char * wpa_tdls_get_link_status(struct wpa_sm *sm, const u8 *addr);
 int wpa_tdls_is_external_setup(struct wpa_sm *sm);
+int wpa_tdls_enable_chan_switch(struct wpa_sm *sm, const u8 *addr,
+                               u8 oper_class,
+                               struct hostapd_freq_params *freq_params);
+int wpa_tdls_disable_chan_switch(struct wpa_sm *sm, const u8 *addr);
 
 int wpa_wnmsleep_install_key(struct wpa_sm *sm, u8 subelem_id, u8 *buf);
 
old mode 100644 (file)
new mode 100755 (executable)
index 3a40c96..06dea05
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - IEEE 802.11r - Fast BSS Transition
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -19,8 +19,7 @@
 #ifdef CONFIG_IEEE80211R
 
 int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
-                     const struct wpa_eapol_key *key,
-                     struct wpa_ptk *ptk, size_t ptk_len)
+                     const struct wpa_eapol_key *key, struct wpa_ptk *ptk)
 {
        u8 ptk_name[WPA_PMK_NAME_LEN];
        const u8 *anonce = key->key_nonce;
@@ -43,13 +42,9 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
        wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN);
        wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name,
                    WPA_PMK_NAME_LEN);
-       wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr,
-                         sm->bssid, sm->pmk_r1_name,
-                         (u8 *) ptk, ptk_len, ptk_name);
-       wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len);
-       wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
-
-       return 0;
+       return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr,
+                                sm->bssid, sm->pmk_r1_name, ptk, ptk_name,
+                                sm->key_mgmt, sm->pairwise_cipher);
 }
 
 
@@ -134,6 +129,7 @@ int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len)
  * @anonce: ANonce or %NULL if not yet available
  * @pmk_name: PMKR0Name or PMKR1Name to be added into the RSN IE PMKID List
  * @kck: 128-bit KCK for MIC or %NULL if no MIC is used
+ * @kck_len: KCK length in octets
  * @target_ap: Target AP address
  * @ric_ies: Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request or %NULL
  * @ric_ies_len: Length of ric_ies buffer in octets
@@ -144,7 +140,8 @@ int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len)
  */
 static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
                               const u8 *anonce, const u8 *pmk_name,
-                              const u8 *kck, const u8 *target_ap,
+                              const u8 *kck, size_t kck_len,
+                              const u8 *target_ap,
                               const u8 *ric_ies, size_t ric_ies_len,
                               const u8 *ap_mdie)
 {
@@ -207,6 +204,8 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X);
        else if (sm->key_mgmt == WPA_KEY_MGMT_FT_PSK)
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK);
+       else if (sm->key_mgmt == WPA_KEY_MGMT_FT_SAE)
+               RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
        else {
                wpa_printf(MSG_WARNING, "FT: Invalid key management type (%d)",
                           sm->key_mgmt);
@@ -296,7 +295,7 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
                /* Information element count */
                ftie->mic_control[1] = 3 + ieee802_11_ie_count(ric_ies,
                                                               ric_ies_len);
-               if (wpa_ft_mic(kck, sm->own_addr, target_ap, 5,
+               if (wpa_ft_mic(kck, kck_len, sm->own_addr, target_ap, 5,
                               ((u8 *) mdie) - 2, 2 + sizeof(*mdie),
                               ftie_pos, 2 + *ftie_len,
                               (u8 *) rsnie, 2 + rsnie->len, ric_ies,
@@ -331,7 +330,7 @@ static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid)
        keylen = wpa_cipher_key_len(sm->pairwise_cipher);
 
        if (wpa_sm_set_key(sm, alg, bssid, 0, 1, null_rsc,
-                          sizeof(null_rsc), (u8 *) sm->ptk.tk1, keylen) < 0) {
+                          sizeof(null_rsc), (u8 *) sm->ptk.tk, keylen) < 0) {
                wpa_printf(MSG_WARNING, "FT: Failed to set PTK to the driver");
                return -1;
        }
@@ -358,7 +357,7 @@ int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie)
        }
 
        ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name,
-                                   NULL, sm->bssid, NULL, 0, mdie);
+                                   NULL, 0, sm->bssid, NULL, 0, mdie);
        if (ft_ies) {
                wpa_sm_update_ft_ies(sm, sm->mobility_domain,
                                     ft_ies, ft_ies_len);
@@ -374,7 +373,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
                            const u8 *ric_ies, size_t ric_ies_len)
 {
        u8 *ft_ies;
-       size_t ft_ies_len, ptk_len;
+       size_t ft_ies_len;
        struct wpa_ft_ies parse;
        struct rsn_mdie *mdie;
        struct rsn_ftie *ftie;
@@ -400,8 +399,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
                }
        }
 
-       if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
-           sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) {
+       if (!wpa_key_mgmt_ft(sm->key_mgmt)) {
                wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not "
                           "enabled for this connection");
                return -1;
@@ -441,7 +439,8 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
        }
 
        if (parse.r0kh_id_len != sm->r0kh_id_len ||
-           os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) {
+           os_memcmp_const(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0)
+       {
                wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with "
                           "the current R0KH-ID");
                wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
@@ -457,7 +456,8 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
        }
 
        if (parse.rsn_pmkid == NULL ||
-           os_memcmp(parse.rsn_pmkid, sm->pmk_r0_name, WPA_PMK_NAME_LEN)) {
+           os_memcmp_const(parse.rsn_pmkid, sm->pmk_r0_name, WPA_PMK_NAME_LEN))
+       {
                wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name (PMKID) in "
                           "RSNIE");
                return -1;
@@ -475,16 +475,14 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len,
                    sm->pmk_r1_name, WPA_PMK_NAME_LEN);
 
        bssid = target_ap;
-       ptk_len = sm->pairwise_cipher != WPA_CIPHER_TKIP ? 48 : 64;
-       wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce, sm->own_addr,
-                         bssid, sm->pmk_r1_name,
-                         (u8 *) &sm->ptk, ptk_len, ptk_name);
-       wpa_hexdump_key(MSG_DEBUG, "FT: PTK",
-                       (u8 *) &sm->ptk, ptk_len);
-       wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN);
+       if (wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, ftie->anonce,
+                             sm->own_addr, bssid, sm->pmk_r1_name, &sm->ptk,
+                             ptk_name, sm->key_mgmt, sm->pairwise_cipher) < 0)
+               return -1;
 
        ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce,
-                                   sm->pmk_r1_name, sm->ptk.kck, bssid,
+                                   sm->pmk_r1_name,
+                                   sm->ptk.kck, sm->ptk.kck_len, bssid,
                                    ric_ies, ric_ies_len,
                                    parse.mdie ? parse.mdie - 2 : NULL);
        if (ft_ies) {
@@ -526,8 +524,7 @@ int wpa_ft_is_completed(struct wpa_sm *sm)
        if (sm == NULL)
                return 0;
 
-       if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
-           sm->key_mgmt != WPA_KEY_MGMT_FT_PSK)
+       if (!wpa_key_mgmt_ft(sm->key_mgmt))
                return 0;
 
        return sm->ft_completed;
@@ -564,7 +561,8 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem,
                return -1;
        }
        gtk_len = gtk_elem_len - 19;
-       if (aes_unwrap(sm->ptk.kek, gtk_len / 8, gtk_elem + 11, gtk)) {
+       if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, gtk_len / 8, gtk_elem + 11,
+                      gtk)) {
                wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not "
                           "decrypt GTK");
                return -1;
@@ -643,7 +641,8 @@ static int wpa_ft_process_igtk_subelem(struct wpa_sm *sm, const u8 *igtk_elem,
                return -1;
        }
 
-       if (aes_unwrap(sm->ptk.kek, WPA_IGTK_LEN / 8, igtk_elem + 9, igtk)) {
+       if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, WPA_IGTK_LEN / 8,
+                      igtk_elem + 9, igtk)) {
                wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not "
                           "decrypt IGTK");
                return -1;
@@ -674,12 +673,11 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
        struct rsn_mdie *mdie;
        struct rsn_ftie *ftie;
        unsigned int count;
-       u8 mic[16];
+       u8 mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
 
        wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len);
 
-       if (sm->key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X &&
-           sm->key_mgmt != WPA_KEY_MGMT_FT_PSK) {
+       if (!wpa_key_mgmt_ft(sm->key_mgmt)) {
                wpa_printf(MSG_DEBUG, "FT: Reject FT IEs since FT is not "
                           "enabled for this connection");
                return -1;
@@ -728,7 +726,8 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
        }
 
        if (parse.r0kh_id_len != sm->r0kh_id_len ||
-           os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) {
+           os_memcmp_const(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0)
+       {
                wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with "
                           "the current R0KH-ID");
                wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE",
@@ -743,14 +742,15 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
                return -1;
        }
 
-       if (os_memcmp(parse.r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN) != 0) {
+       if (os_memcmp_const(parse.r1kh_id, sm->r1kh_id, FT_R1KH_ID_LEN) != 0) {
                wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in "
                           "ReassocResp");
                return -1;
        }
 
        if (parse.rsn_pmkid == NULL ||
-           os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) {
+           os_memcmp_const(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN))
+       {
                wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in "
                           "RSNIE (pmkid=%d)", !!parse.rsn_pmkid);
                return -1;
@@ -766,7 +766,7 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
                return -1;
        }
 
-       if (wpa_ft_mic(sm->ptk.kck, sm->own_addr, src_addr, 6,
+       if (wpa_ft_mic(sm->ptk.kck, sm->ptk.kck_len, sm->own_addr, src_addr, 6,
                       parse.mdie - 2, parse.mdie_len + 2,
                       parse.ftie - 2, parse.ftie_len + 2,
                       parse.rsn - 2, parse.rsn_len + 2,
@@ -776,7 +776,7 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
                return -1;
        }
 
-       if (os_memcmp(mic, ftie->mic, 16) != 0) {
+       if (os_memcmp_const(mic, ftie->mic, 16) != 0) {
                wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE");
                wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16);
                wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16);
@@ -835,7 +835,7 @@ int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap,
        }
 
        ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name,
-                                   NULL, target_ap, NULL, 0, mdie);
+                                   NULL, 0, target_ap, NULL, 0, mdie);
        if (ft_ies) {
                sm->over_the_ds_in_progress = 1;
                os_memcpy(sm->target_ap, target_ap, ETH_ALEN);
old mode 100644 (file)
new mode 100755 (executable)
index 0e0d373..965a9c1
@@ -1,6 +1,6 @@
 /*
  * Internal WPA/RSN supplicant state machine definitions
- * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -23,6 +23,7 @@ struct wpa_sm {
        size_t pmk_len;
        struct wpa_ptk ptk, tptk;
        int ptk_set, tptk_set;
+       unsigned int msg_3_of_4_ok:1;
        u8 snonce[WPA_NONCE_LEN];
        u8 anonce[WPA_NONCE_LEN]; /* ANonce from the last 1/4 msg */
        int renew_snonce;
@@ -58,6 +59,7 @@ struct wpa_sm {
        u8 ssid[32];
        size_t ssid_len;
        int wpa_ptk_rekey;
+       int p2p;
 
        u8 own_addr[ETH_ALEN];
        const char *ifname;
@@ -91,6 +93,7 @@ struct wpa_sm {
 #ifdef CONFIG_TDLS
        struct wpa_tdls_peer *tdls;
        int tdls_prohibited;
+       int tdls_chan_switch_prohibited;
        int tdls_disabled;
 
        /* The driver supports TDLS */
@@ -101,6 +104,9 @@ struct wpa_sm {
         * to it via tdls_mgmt.
         */
        int tdls_external_setup;
+
+       /* The driver supports TDLS channel switching */
+       int tdls_chan_switch;
 #endif /* CONFIG_TDLS */
 
 #ifdef CONFIG_IEEE80211R
@@ -122,6 +128,10 @@ struct wpa_sm {
        u8 *assoc_resp_ies; /* MDIE and FTIE from (Re)Association Response */
        size_t assoc_resp_ies_len;
 #endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_P2P
+       u8 p2p_ip_addr[3 * 4];
+#endif /* CONFIG_P2P */
 };
 
 
@@ -245,30 +255,34 @@ static inline void wpa_sm_set_rekey_offload(struct wpa_sm *sm)
 {
        if (!sm->ctx->set_rekey_offload)
                return;
-       sm->ctx->set_rekey_offload(sm->ctx->ctx, sm->ptk.kek,
-                                  sm->ptk.kck, sm->rx_replay_counter);
+       sm->ctx->set_rekey_offload(sm->ctx->ctx, sm->ptk.kek, sm->ptk.kek_len,
+                                  sm->ptk.kck, sm->ptk.kck_len,
+                                  sm->rx_replay_counter);
 }
 
 #ifdef CONFIG_TDLS
 static inline int wpa_sm_tdls_get_capa(struct wpa_sm *sm,
                                       int *tdls_supported,
-                                      int *tdls_ext_setup)
+                                      int *tdls_ext_setup,
+                                      int *tdls_chan_switch)
 {
        if (sm->ctx->tdls_get_capa)
                return sm->ctx->tdls_get_capa(sm->ctx->ctx, tdls_supported,
-                                             tdls_ext_setup);
+                                             tdls_ext_setup, tdls_chan_switch);
        return -1;
 }
 
 static inline int wpa_sm_send_tdls_mgmt(struct wpa_sm *sm, const u8 *dst,
                                        u8 action_code, u8 dialog_token,
-                                       u16 status_code, const u8 *buf,
+                                       u16 status_code, u32 peer_capab,
+                                       int initiator, const u8 *buf,
                                        size_t len)
 {
        if (sm->ctx->send_tdls_mgmt)
                return sm->ctx->send_tdls_mgmt(sm->ctx->ctx, dst, action_code,
                                               dialog_token, status_code,
-                                              buf, len);
+                                              peer_capab, initiator, buf,
+                                              len);
        return -1;
 }
 
@@ -286,19 +300,56 @@ wpa_sm_tdls_peer_addset(struct wpa_sm *sm, const u8 *addr, int add,
                        size_t supp_rates_len,
                        const struct ieee80211_ht_capabilities *ht_capab,
                        const struct ieee80211_vht_capabilities *vht_capab,
-                       u8 qosinfo, const u8 *ext_capab, size_t ext_capab_len)
+                       u8 qosinfo, int wmm, const u8 *ext_capab,
+                       size_t ext_capab_len, const u8 *supp_channels,
+                       size_t supp_channels_len, const u8 *supp_oper_classes,
+                       size_t supp_oper_classes_len)
 {
        if (sm->ctx->tdls_peer_addset)
                return sm->ctx->tdls_peer_addset(sm->ctx->ctx, addr, add,
                                                 aid, capability, supp_rates,
                                                 supp_rates_len, ht_capab,
-                                                vht_capab, qosinfo,
-                                                ext_capab, ext_capab_len);
+                                                vht_capab, qosinfo, wmm,
+                                                ext_capab, ext_capab_len,
+                                                supp_channels,
+                                                supp_channels_len,
+                                                supp_oper_classes,
+                                                supp_oper_classes_len);
+       return -1;
+}
+
+static inline int
+wpa_sm_tdls_enable_channel_switch(struct wpa_sm *sm, const u8 *addr,
+                                 u8 oper_class,
+                                 const struct hostapd_freq_params *freq_params)
+{
+       if (sm->ctx->tdls_enable_channel_switch)
+               return sm->ctx->tdls_enable_channel_switch(sm->ctx->ctx, addr,
+                                                          oper_class,
+                                                          freq_params);
+       return -1;
+}
+
+static inline int
+wpa_sm_tdls_disable_channel_switch(struct wpa_sm *sm, const u8 *addr)
+{
+       if (sm->ctx->tdls_disable_channel_switch)
+               return sm->ctx->tdls_disable_channel_switch(sm->ctx->ctx, addr);
        return -1;
 }
 #endif /* CONFIG_TDLS */
 
-void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck,
+static inline int wpa_sm_key_mgmt_set_pmk(struct wpa_sm *sm,
+                                         const u8 *pmk, size_t pmk_len)
+{
+       if (!sm->proactive_key_caching)
+               return 0;
+       if (!sm->ctx->key_mgmt_set_pmk)
+               return -1;
+       return sm->ctx->key_mgmt_set_pmk(sm->ctx->ctx, pmk, pmk_len);
+}
+
+void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, size_t kck_len,
                        int ver, const u8 *dest, u16 proto,
                        u8 *msg, size_t msg_len, u8 *key_mic);
 int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
@@ -309,12 +360,10 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
 int wpa_supplicant_send_4_of_4(struct wpa_sm *sm, const unsigned char *dst,
                               const struct wpa_eapol_key *key,
                               u16 ver, u16 key_info,
-                              const u8 *kde, size_t kde_len,
                               struct wpa_ptk *ptk);
 
 int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr,
-                     const struct wpa_eapol_key *key,
-                     struct wpa_ptk *ptk, size_t ptk_len);
+                     const struct wpa_eapol_key *key, struct wpa_ptk *ptk);
 
 void wpa_tdls_assoc(struct wpa_sm *sm);
 void wpa_tdls_disassoc(struct wpa_sm *sm);
old mode 100644 (file)
new mode 100755 (executable)
index 50b9272..cb334df
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant - WPA/RSN IE and KDE processing
- * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -173,6 +173,10 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
        } else if (key_mgmt == WPA_KEY_MGMT_FT_SAE) {
                RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_SAE);
 #endif /* CONFIG_SAE */
+       } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+               RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192);
+       } else if (key_mgmt == WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+               RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SUITE_B);
        } else {
                wpa_printf(MSG_WARNING, "Invalid key management type (%d).",
                           key_mgmt);
@@ -201,7 +205,7 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
        }
 
 #ifdef CONFIG_IEEE80211W
-       if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
+       if (wpa_cipher_valid_mgmt_group(mgmt_group_cipher)) {
                if (!sm->cur_pmksa) {
                        /* PMKID Count */
                        WPA_PUT_LE16(pos, 0);
@@ -209,7 +213,8 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
                }
 
                /* Management Group Cipher Suite */
-               RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC);
+               RSN_SELECTOR_PUT(pos, wpa_cipher_to_suite(WPA_PROTO_RSN,
+                                                         mgmt_group_cipher));
                pos += RSN_SELECTOR_LEN;
        }
 #endif /* CONFIG_IEEE80211W */
@@ -222,6 +227,64 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
 }
 
 
+#ifdef CONFIG_HS20
+static int wpa_gen_wpa_ie_osen(u8 *wpa_ie, size_t wpa_ie_len,
+                              int pairwise_cipher, int group_cipher,
+                              int key_mgmt)
+{
+       u8 *pos, *len;
+       u32 suite;
+
+       if (wpa_ie_len < 2 + 4 + RSN_SELECTOR_LEN +
+           2 + RSN_SELECTOR_LEN + 2 + RSN_SELECTOR_LEN)
+               return -1;
+
+       pos = wpa_ie;
+       *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+       len = pos++; /* to be filled */
+       WPA_PUT_BE24(pos, OUI_WFA);
+       pos += 3;
+       *pos++ = HS20_OSEN_OUI_TYPE;
+
+       /* Group Data Cipher Suite */
+       suite = wpa_cipher_to_suite(WPA_PROTO_RSN, group_cipher);
+       if (suite == 0) {
+               wpa_printf(MSG_WARNING, "Invalid group cipher (%d).",
+                          group_cipher);
+               return -1;
+       }
+       RSN_SELECTOR_PUT(pos, suite);
+       pos += RSN_SELECTOR_LEN;
+
+       /* Pairwise Cipher Suite Count and List */
+       WPA_PUT_LE16(pos, 1);
+       pos += 2;
+       suite = wpa_cipher_to_suite(WPA_PROTO_RSN, pairwise_cipher);
+       if (suite == 0 ||
+           (!wpa_cipher_valid_pairwise(pairwise_cipher) &&
+            pairwise_cipher != WPA_CIPHER_NONE)) {
+               wpa_printf(MSG_WARNING, "Invalid pairwise cipher (%d).",
+                          pairwise_cipher);
+               return -1;
+       }
+       RSN_SELECTOR_PUT(pos, suite);
+       pos += RSN_SELECTOR_LEN;
+
+       /* AKM Suite Count and List */
+       WPA_PUT_LE16(pos, 1);
+       pos += 2;
+       RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_OSEN);
+       pos += RSN_SELECTOR_LEN;
+
+       *len = pos - len - 1;
+
+       WPA_ASSERT((size_t) (pos - wpa_ie) <= wpa_ie_len);
+
+       return pos - wpa_ie;
+}
+#endif /* CONFIG_HS20 */
+
+
 /**
  * wpa_gen_wpa_ie - Generate WPA/RSN IE based on current security policy
  * @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -237,6 +300,13 @@ int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len)
                                          sm->group_cipher,
                                          sm->key_mgmt, sm->mgmt_group_cipher,
                                          sm);
+#ifdef CONFIG_HS20
+       else if (sm->proto == WPA_PROTO_OSEN)
+               return wpa_gen_wpa_ie_osen(wpa_ie, wpa_ie_len,
+                                          sm->pairwise_cipher,
+                                          sm->group_cipher,
+                                          sm->key_mgmt);
+#endif /* CONFIG_HS20 */
        else
                return wpa_gen_wpa_ie_wpa(wpa_ie, wpa_ie_len,
                                          sm->pairwise_cipher,
@@ -246,6 +316,42 @@ int wpa_gen_wpa_ie(struct wpa_sm *sm, u8 *wpa_ie, size_t wpa_ie_len)
 
 
 /**
+ * wpa_parse_vendor_specific - Parse Vendor Specific IEs
+ * @pos: Pointer to the IE header
+ * @end: Pointer to the end of the Key Data buffer
+ * @ie: Pointer to parsed IE data
+ * Returns: 0 on success, 1 if end mark is found, -1 on failure
+ */
+static int wpa_parse_vendor_specific(const u8 *pos, const u8 *end,
+                                    struct wpa_eapol_ie_parse *ie)
+{
+       unsigned int oui;
+
+       if (pos[1] < 4) {
+               wpa_printf(MSG_MSGDUMP, "Too short vendor specific IE ignored (len=%u)",
+                          pos[1]);
+               return 1;
+       }
+
+       oui = WPA_GET_BE24(&pos[2]);
+       if (oui == OUI_MICROSOFT && pos[5] == WMM_OUI_TYPE && pos[1] > 4) {
+               if (pos[6] == WMM_OUI_SUBTYPE_INFORMATION_ELEMENT) {
+                       ie->wmm = &pos[2];
+                       ie->wmm_len = pos[1];
+                       wpa_hexdump(MSG_DEBUG, "WPA: WMM IE",
+                                   ie->wmm, ie->wmm_len);
+               } else if (pos[6] == WMM_OUI_SUBTYPE_PARAMETER_ELEMENT) {
+                       ie->wmm = &pos[2];
+                       ie->wmm_len = pos[1];
+                       wpa_hexdump(MSG_DEBUG, "WPA: WMM Parameter Element",
+                                   ie->wmm, ie->wmm_len);
+               }
+       }
+       return 0;
+}
+
+
+/**
  * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs
  * @pos: Pointer to the IE header
  * @end: Pointer to the end of the Key Data buffer
@@ -345,6 +451,25 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end,
        }
 #endif /* CONFIG_IEEE80211W */
 
+#ifdef CONFIG_P2P
+       if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
+           RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_REQ) {
+               ie->ip_addr_req = pos + 2 + RSN_SELECTOR_LEN;
+               wpa_hexdump(MSG_DEBUG, "WPA: IP Address Request in EAPOL-Key",
+                           ie->ip_addr_req, pos[1] - RSN_SELECTOR_LEN);
+               return 0;
+       }
+
+       if (pos[1] >= RSN_SELECTOR_LEN + 3 * 4 &&
+           RSN_SELECTOR_GET(pos + 2) == WFA_KEY_DATA_IP_ADDR_ALLOC) {
+               ie->ip_addr_alloc = pos + 2 + RSN_SELECTOR_LEN;
+               wpa_hexdump(MSG_DEBUG,
+                           "WPA: IP Address Allocation in EAPOL-Key",
+                           ie->ip_addr_alloc, pos[1] - RSN_SELECTOR_LEN);
+               return 0;
+       }
+#endif /* CONFIG_P2P */
+
        return 0;
 }
 
@@ -428,12 +553,26 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
                        ie->ht_capabilities_len = pos[1];
                } else if (*pos == WLAN_EID_VHT_AID) {
                        if (pos[1] >= 2)
-                               ie->aid = WPA_GET_LE16(pos + 2);
+                               ie->aid = WPA_GET_LE16(pos + 2) & 0x3fff;
                } else if (*pos == WLAN_EID_VHT_CAP) {
                        ie->vht_capabilities = pos + 2;
                        ie->vht_capabilities_len = pos[1];
                } else if (*pos == WLAN_EID_QOS && pos[1] >= 1) {
                        ie->qosinfo = pos[2];
+               } else if (*pos == WLAN_EID_SUPPORTED_CHANNELS) {
+                       ie->supp_channels = pos + 2;
+                       ie->supp_channels_len = pos[1];
+               } else if (*pos == WLAN_EID_SUPPORTED_OPERATING_CLASSES) {
+                       /*
+                        * The value of the Length field of the Supported
+                        * Operating Classes element is between 2 and 253.
+                        * Silently skip invalid elements to avoid interop
+                        * issues when trying to use the value.
+                        */
+                       if (pos[1] >= 2 && pos[1] <= 253) {
+                               ie->supp_oper_classes = pos + 2;
+                               ie->supp_oper_classes_len = pos[1];
+                       }
                } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
                        ret = wpa_parse_generic(pos, end, ie);
                        if (ret < 0)
@@ -442,6 +581,14 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
                                ret = 0;
                                break;
                        }
+
+                       ret = wpa_parse_vendor_specific(pos, end, ie);
+                       if (ret < 0)
+                               break;
+                       if (ret > 0) {
+                               ret = 0;
+                               break;
+                       }
                } else {
                        wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key "
                                    "Key Data IE", pos, 2 + pos[1]);
index 2c78801..0fc42cc 100644 (file)
@@ -53,8 +53,18 @@ struct wpa_eapol_ie_parse {
        size_t ht_capabilities_len;
        const u8 *vht_capabilities;
        size_t vht_capabilities_len;
+       const u8 *supp_channels;
+       size_t supp_channels_len;
+       const u8 *supp_oper_classes;
+       size_t supp_oper_classes_len;
        u8 qosinfo;
        u16 aid;
+       const u8 *wmm;
+       size_t wmm_len;
+#ifdef CONFIG_P2P
+       const u8 *ip_addr_req;
+       const u8 *ip_addr_alloc;
+#endif /* CONFIG_P2P */
 };
 
 int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
index 53acd53..cec1092 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * ASN.1 DER parsing
- * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 #include "common.h"
 #include "asn1.h"
 
+struct asn1_oid asn1_sha1_oid = {
+       .oid = { 1, 3, 14, 3, 2, 26 },
+       .len = 6
+};
+
+struct asn1_oid asn1_sha256_oid = {
+       .oid = { 2, 16, 840, 1, 101, 3, 4, 2, 1 },
+       .len = 9
+};
+
+
 int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr)
 {
        const u8 *pos, *end;
@@ -140,7 +151,7 @@ int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid,
 }
 
 
-void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len)
+void asn1_oid_to_str(const struct asn1_oid *oid, char *buf, size_t len)
 {
        char *pos = buf;
        size_t i;
@@ -155,7 +166,7 @@ void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len)
                ret = os_snprintf(pos, buf + len - pos,
                                  "%s%lu",
                                  i == 0 ? "" : ".", oid->oid[i]);
-               if (ret < 0 || ret >= buf + len - pos)
+               if (os_snprintf_error(buf + len - pos, ret))
                        break;
                pos += ret;
        }
@@ -204,3 +215,19 @@ unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len)
 
        return val;
 }
+
+
+int asn1_oid_equal(const struct asn1_oid *a, const struct asn1_oid *b)
+{
+       size_t i;
+
+       if (a->len != b->len)
+               return 0;
+
+       for (i = 0; i < a->len; i++) {
+               if (a->oid[i] != b->oid[i])
+                       return 0;
+       }
+
+       return 1;
+}
index 6342c4c..7475007 100644 (file)
@@ -60,7 +60,11 @@ int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr);
 int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid);
 int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid,
                 const u8 **next);
-void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len);
+void asn1_oid_to_str(const struct asn1_oid *oid, char *buf, size_t len);
 unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len);
+int asn1_oid_equal(const struct asn1_oid *a, const struct asn1_oid *b);
+
+extern struct asn1_oid asn1_sha1_oid;
+extern struct asn1_oid asn1_sha256_oid;
 
 #endif /* ASN1_H */
index b6fde5e..141ac50 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * PKCS #1 (RSA Encryption)
- * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -9,7 +9,9 @@
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/crypto.h"
 #include "rsa.h"
+#include "asn1.h"
 #include "pkcs1.h"
 
 
@@ -113,6 +115,11 @@ int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key,
                pos++;
        if (pos == end)
                return -1;
+       if (pos - out - 2 < 8) {
+               /* PKCS #1 v1.5, 8.1: At least eight octets long PS */
+               wpa_printf(MSG_INFO, "LibTomCrypt: Too short padding");
+               return -1;
+       }
        pos++;
 
        *outlen -= pos - out;
@@ -142,35 +149,26 @@ int pkcs1_decrypt_public_key(struct crypto_rsa_key *key,
         * BT = 00 or 01
         * PS = k-3-||D|| times (00 if BT=00) or (FF if BT=01)
         * k = length of modulus in octets
+        *
+        * Based on 10.1.3, "The block type shall be 01" for a signature.
         */
 
        if (len < 3 + 8 + 16 /* min hash len */ ||
-           plain[0] != 0x00 || (plain[1] != 0x00 && plain[1] != 0x01)) {
+           plain[0] != 0x00 || plain[1] != 0x01) {
                wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB "
                           "structure");
                return -1;
        }
 
        pos = plain + 3;
-       if (plain[1] == 0x00) {
-               /* BT = 00 */
-               if (plain[2] != 0x00) {
-                       wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature "
-                                  "PS (BT=00)");
-                       return -1;
-               }
-               while (pos + 1 < plain + len && *pos == 0x00 && pos[1] == 0x00)
-                       pos++;
-       } else {
-               /* BT = 01 */
-               if (plain[2] != 0xff) {
-                       wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature "
-                                  "PS (BT=01)");
-                       return -1;
-               }
-               while (pos < plain + len && *pos == 0xff)
-                       pos++;
+       /* BT = 01 */
+       if (plain[2] != 0xff) {
+               wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature "
+                          "PS (BT=01)");
+               return -1;
        }
+       while (pos < plain + len && *pos == 0xff)
+               pos++;
 
        if (pos - plain - 2 < 8) {
                /* PKCS #1 v1.5, 8.1: At least eight octets long PS */
@@ -193,3 +191,130 @@ int pkcs1_decrypt_public_key(struct crypto_rsa_key *key,
 
        return 0;
 }
+
+
+int pkcs1_v15_sig_ver(struct crypto_public_key *pk,
+                     const u8 *s, size_t s_len,
+                     const struct asn1_oid *hash_alg,
+                     const u8 *hash, size_t hash_len)
+{
+       int res;
+       u8 *decrypted;
+       size_t decrypted_len;
+       const u8 *pos, *end, *next, *da_end;
+       struct asn1_hdr hdr;
+       struct asn1_oid oid;
+
+       decrypted = os_malloc(s_len);
+       if (decrypted == NULL)
+               return -1;
+       decrypted_len = s_len;
+       res = crypto_public_key_decrypt_pkcs1(pk, s, s_len, decrypted,
+                                             &decrypted_len);
+       if (res < 0) {
+               wpa_printf(MSG_INFO, "PKCS #1: RSA decrypt failed");
+               os_free(decrypted);
+               return -1;
+       }
+       wpa_hexdump(MSG_DEBUG, "Decrypted(S)", decrypted, decrypted_len);
+
+       /*
+        * PKCS #1 v1.5, 10.1.2:
+        *
+        * DigestInfo ::= SEQUENCE {
+        *     digestAlgorithm DigestAlgorithmIdentifier,
+        *     digest Digest
+        * }
+        *
+        * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
+        *
+        * Digest ::= OCTET STRING
+        *
+        */
+       if (asn1_get_next(decrypted, decrypted_len, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG,
+                          "PKCS #1: Expected SEQUENCE (DigestInfo) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               os_free(decrypted);
+               return -1;
+       }
+
+       pos = hdr.payload;
+       end = pos + hdr.length;
+
+       /*
+        * X.509:
+        * AlgorithmIdentifier ::= SEQUENCE {
+        *     algorithm            OBJECT IDENTIFIER,
+        *     parameters           ANY DEFINED BY algorithm OPTIONAL
+        * }
+        */
+
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_SEQUENCE) {
+               wpa_printf(MSG_DEBUG,
+                          "PKCS #1: Expected SEQUENCE (AlgorithmIdentifier) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               os_free(decrypted);
+               return -1;
+       }
+       da_end = hdr.payload + hdr.length;
+
+       if (asn1_get_oid(hdr.payload, hdr.length, &oid, &next)) {
+               wpa_printf(MSG_DEBUG,
+                          "PKCS #1: Failed to parse digestAlgorithm");
+               os_free(decrypted);
+               return -1;
+       }
+
+       if (!asn1_oid_equal(&oid, hash_alg)) {
+               char txt[100], txt2[100];
+               asn1_oid_to_str(&oid, txt, sizeof(txt));
+               asn1_oid_to_str(hash_alg, txt2, sizeof(txt2));
+               wpa_printf(MSG_DEBUG,
+                          "PKCS #1: Hash alg OID mismatch: was %s, expected %s",
+                          txt, txt2);
+               os_free(decrypted);
+               return -1;
+       }
+
+       /* Digest ::= OCTET STRING */
+       pos = da_end;
+       end = decrypted + decrypted_len;
+
+       if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
+           hdr.class != ASN1_CLASS_UNIVERSAL ||
+           hdr.tag != ASN1_TAG_OCTETSTRING) {
+               wpa_printf(MSG_DEBUG,
+                          "PKCS #1: Expected OCTETSTRING (Digest) - found class %d tag 0x%x",
+                          hdr.class, hdr.tag);
+               os_free(decrypted);
+               return -1;
+       }
+       wpa_hexdump(MSG_MSGDUMP, "PKCS #1: Decrypted Digest",
+                   hdr.payload, hdr.length);
+
+       if (hdr.length != hash_len ||
+           os_memcmp_const(hdr.payload, hash, hdr.length) != 0) {
+               wpa_printf(MSG_INFO, "PKCS #1: Digest value does not match calculated hash");
+               os_free(decrypted);
+               return -1;
+       }
+
+       os_free(decrypted);
+
+       if (hdr.payload + hdr.length != end) {
+               wpa_printf(MSG_INFO,
+                          "PKCS #1: Extra data after signature - reject");
+
+               wpa_hexdump(MSG_DEBUG, "PKCS #1: Extra data",
+                           hdr.payload + hdr.length,
+                           end - hdr.payload - hdr.length);
+               return -1;
+       }
+
+       return 0;
+}
index ed64def..f37ebf3 100644 (file)
@@ -9,6 +9,9 @@
 #ifndef PKCS1_H
 #define PKCS1_H
 
+struct crypto_public_key;
+struct asn1_oid;
+
 int pkcs1_encrypt(int block_type, struct crypto_rsa_key *key,
                  int use_private, const u8 *in, size_t inlen,
                  u8 *out, size_t *outlen);
@@ -18,5 +21,9 @@ int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key,
 int pkcs1_decrypt_public_key(struct crypto_rsa_key *key,
                             const u8 *crypt, size_t crypt_len,
                             u8 *plain, size_t *plain_len);
+int pkcs1_v15_sig_ver(struct crypto_public_key *pk,
+                     const u8 *s, size_t s_len,
+                     const struct asn1_oid *hash_alg,
+                     const u8 *hash, size_t hash_len);
 
 #endif /* PKCS1_H */
index 125c420..0b7b530 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * RSA
- * Copyright (c) 2006, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -116,6 +116,29 @@ error:
 }
 
 
+struct crypto_rsa_key *
+crypto_rsa_import_public_key_parts(const u8 *n, size_t n_len,
+                                  const u8 *e, size_t e_len)
+{
+       struct crypto_rsa_key *key;
+
+       key = os_zalloc(sizeof(*key));
+       if (key == NULL)
+               return NULL;
+
+       key->n = bignum_init();
+       key->e = bignum_init();
+       if (key->n == NULL || key->e == NULL ||
+           bignum_set_unsigned_bin(key->n, n, n_len) < 0 ||
+           bignum_set_unsigned_bin(key->e, e, e_len) < 0) {
+               crypto_rsa_free(key);
+               return NULL;
+       }
+
+       return key;
+}
+
+
 /**
  * crypto_rsa_import_private_key - Import an RSA private key
  * @buf: Key buffer (DER encoded RSA private key)
index c236a9d..b65818e 100644 (file)
@@ -14,6 +14,9 @@ struct crypto_rsa_key;
 struct crypto_rsa_key *
 crypto_rsa_import_public_key(const u8 *buf, size_t len);
 struct crypto_rsa_key *
+crypto_rsa_import_public_key_parts(const u8 *n, size_t n_len,
+                                  const u8 *e, size_t e_len);
+struct crypto_rsa_key *
 crypto_rsa_import_private_key(const u8 *buf, size_t len);
 size_t crypto_rsa_get_modulus_len(struct crypto_rsa_key *key);
 int crypto_rsa_exptmod(const u8 *in, size_t inlen, u8 *out, size_t *outlen,
index 12148b6..facdd65 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -459,10 +459,15 @@ struct tlsv1_client * tlsv1_client_init(void)
 
        count = 0;
        suites = conn->cipher_suites;
+       suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA256;
        suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256;
+       suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
        suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+       suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA256;
        suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
+       suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
        suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+       suites[count++] = TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA;
        suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
        suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
        suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
@@ -565,8 +570,26 @@ int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
        case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
                cipher = "DES-CBC3-SHA";
                break;
-       case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
-               cipher = "ADH-AES-128-SHA256";
+       case TLS_DHE_RSA_WITH_DES_CBC_SHA:
+               cipher = "DHE-RSA-DES-CBC-SHA";
+               break;
+       case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+               cipher = "DHE-RSA-DES-CBC3-SHA";
+               break;
+       case TLS_DH_anon_WITH_RC4_128_MD5:
+               cipher = "ADH-RC4-MD5";
+               break;
+       case TLS_DH_anon_WITH_DES_CBC_SHA:
+               cipher = "ADH-DES-SHA";
+               break;
+       case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA:
+               cipher = "ADH-DES-CBC3-SHA";
+               break;
+       case TLS_RSA_WITH_AES_128_CBC_SHA:
+               cipher = "AES-128-SHA";
+               break;
+       case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+               cipher = "DHE-RSA-AES-128-SHA";
                break;
        case TLS_DH_anon_WITH_AES_128_CBC_SHA:
                cipher = "ADH-AES-128-SHA";
@@ -574,15 +597,30 @@ int tlsv1_client_get_cipher(struct tlsv1_client *conn, char *buf,
        case TLS_RSA_WITH_AES_256_CBC_SHA:
                cipher = "AES-256-SHA";
                break;
-       case TLS_RSA_WITH_AES_256_CBC_SHA256:
-               cipher = "AES-256-SHA256";
+       case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+               cipher = "DHE-RSA-AES-256-SHA";
                break;
-       case TLS_RSA_WITH_AES_128_CBC_SHA:
-               cipher = "AES-128-SHA";
+       case TLS_DH_anon_WITH_AES_256_CBC_SHA:
+               cipher = "ADH-AES-256-SHA";
                break;
        case TLS_RSA_WITH_AES_128_CBC_SHA256:
                cipher = "AES-128-SHA256";
                break;
+       case TLS_RSA_WITH_AES_256_CBC_SHA256:
+               cipher = "AES-256-SHA256";
+               break;
+       case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+               cipher = "DHE-RSA-AES-128-SHA256";
+               break;
+       case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+               cipher = "DHE-RSA-AES-256-SHA256";
+               break;
+       case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+               cipher = "ADH-AES-128-SHA256";
+               break;
+       case TLS_DH_anon_WITH_AES_256_CBC_SHA256:
+               cipher = "ADH-AES-256-SHA256";
+               break;
        default:
                return -1;
        }
old mode 100644 (file)
new mode 100755 (executable)
index 3269ecf..9ce9680
@@ -1,6 +1,6 @@
 /*
  * TLSv1 client - read handshake message
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -409,10 +409,38 @@ static int tls_process_certificate(struct tlsv1_client *conn, u8 ct,
 }
 
 
+static unsigned int count_bits(const u8 *val, size_t len)
+{
+       size_t i;
+       unsigned int bits;
+       u8 tmp;
+
+       for (i = 0; i < len; i++) {
+               if (val[i])
+                       break;
+       }
+       if (i == len)
+               return 0;
+
+       bits = (len - i - 1) * 8;
+       tmp = val[i];
+       while (tmp) {
+               bits++;
+               tmp >>= 1;
+       }
+
+       return bits;
+}
+
+
 static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
-                                       const u8 *buf, size_t len)
+                                       const u8 *buf, size_t len,
+                                       tls_key_exchange key_exchange)
 {
-       const u8 *pos, *end;
+       const u8 *pos, *end, *server_params, *server_params_end;
+       u8 alert;
+       unsigned int bits;
+       u16 val;
 
        tlsv1_client_free_dh(conn);
 
@@ -421,11 +449,20 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
 
        if (end - pos < 3)
                goto fail;
-       conn->dh_p_len = WPA_GET_BE16(pos);
+       server_params = pos;
+       val = WPA_GET_BE16(pos);
        pos += 2;
-       if (conn->dh_p_len == 0 || end - pos < (int) conn->dh_p_len) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %lu",
-                          (unsigned long) conn->dh_p_len);
+       if (val == 0 || val > (size_t) (end - pos)) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Invalid dh_p length %u", val);
+               goto fail;
+       }
+       conn->dh_p_len = val;
+       bits = count_bits(pos, conn->dh_p_len);
+       if (bits < 768) {
+               wpa_printf(MSG_INFO, "TLSv1: Reject under 768-bit DH prime (insecure; only %u bits)",
+                          bits);
+               wpa_hexdump(MSG_DEBUG, "TLSv1: Rejected DH prime",
+                           pos, conn->dh_p_len);
                goto fail;
        }
        conn->dh_p = os_malloc(conn->dh_p_len);
@@ -438,10 +475,11 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
 
        if (end - pos < 3)
                goto fail;
-       conn->dh_g_len = WPA_GET_BE16(pos);
+       val = WPA_GET_BE16(pos);
        pos += 2;
-       if (conn->dh_g_len == 0 || end - pos < (int) conn->dh_g_len)
+       if (val == 0 || val > (size_t) (end - pos))
                goto fail;
+       conn->dh_g_len = val;
        conn->dh_g = os_malloc(conn->dh_g_len);
        if (conn->dh_g == NULL)
                goto fail;
@@ -454,10 +492,11 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
 
        if (end - pos < 3)
                goto fail;
-       conn->dh_ys_len = WPA_GET_BE16(pos);
+       val = WPA_GET_BE16(pos);
        pos += 2;
-       if (conn->dh_ys_len == 0 || end - pos < (int) conn->dh_ys_len)
+       if (val == 0 || val > (size_t) (end - pos))
                goto fail;
+       conn->dh_ys_len = val;
        conn->dh_ys = os_malloc(conn->dh_ys_len);
        if (conn->dh_ys == NULL)
                goto fail;
@@ -465,6 +504,59 @@ static int tlsv1_process_diffie_hellman(struct tlsv1_client *conn,
        pos += conn->dh_ys_len;
        wpa_hexdump(MSG_DEBUG, "TLSv1: DH Ys (server's public value)",
                    conn->dh_ys, conn->dh_ys_len);
+       server_params_end = pos;
+
+       if (key_exchange == TLS_KEY_X_DHE_RSA) {
+               u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
+               int hlen;
+
+               if (conn->rl.tls_version == TLS_VERSION_1_2) {
+#ifdef CONFIG_TLSV12
+                       /*
+                        * RFC 5246, 4.7:
+                        * TLS v1.2 adds explicit indication of the used
+                        * signature and hash algorithms.
+                        *
+                        * struct {
+                        *   HashAlgorithm hash;
+                        *   SignatureAlgorithm signature;
+                        * } SignatureAndHashAlgorithm;
+                        */
+                       if (end - pos < 2)
+                               goto fail;
+                       if (pos[0] != TLS_HASH_ALG_SHA256 ||
+                           pos[1] != TLS_SIGN_ALG_RSA) {
+                               wpa_printf(MSG_DEBUG, "TLSv1.2: Unsupported hash(%u)/signature(%u) algorithm",
+                                          pos[0], pos[1]);
+                               goto fail;
+                       }
+                       pos += 2;
+
+                       hlen = tlsv12_key_x_server_params_hash(
+                               conn->rl.tls_version, conn->client_random,
+                               conn->server_random, server_params,
+                               server_params_end - server_params, hash);
+#else /* CONFIG_TLSV12 */
+                       goto fail;
+#endif /* CONFIG_TLSV12 */
+               } else {
+                       hlen = tls_key_x_server_params_hash(
+                               conn->rl.tls_version, conn->client_random,
+                               conn->server_random, server_params,
+                               server_params_end - server_params, hash);
+               }
+
+               if (hlen < 0)
+                       goto fail;
+               wpa_hexdump(MSG_MSGDUMP, "TLSv1: ServerKeyExchange hash",
+                           hash, hlen);
+
+               if (tls_verify_signature(conn->rl.tls_version,
+                                        conn->server_rsa_key,
+                                        hash, hlen, pos, end - pos,
+                                        &alert) < 0)
+                       goto fail;
+       }
 
        return 0;
 
@@ -543,8 +635,10 @@ static int tls_process_server_key_exchange(struct tlsv1_client *conn, u8 ct,
 
        wpa_hexdump(MSG_DEBUG, "TLSv1: ServerKeyExchange", pos, len);
        suite = tls_get_cipher_suite(conn->rl.cipher_suite);
-       if (suite && suite->key_exchange == TLS_KEY_X_DH_anon) {
-               if (tlsv1_process_diffie_hellman(conn, pos, len) < 0) {
+       if (suite && (suite->key_exchange == TLS_KEY_X_DH_anon ||
+                     suite->key_exchange == TLS_KEY_X_DHE_RSA)) {
+               if (tlsv1_process_diffie_hellman(conn, pos, len,
+                                                suite->key_exchange) < 0) {
                        tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                  TLS_ALERT_DECODE_ERROR);
                        return -1;
@@ -871,8 +965,10 @@ static int tls_process_server_finished(struct tlsv1_client *conn, u8 ct,
        wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
                        verify_data, TLS_VERIFY_DATA_LEN);
 
-       if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
+       if (os_memcmp_const(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
                wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data");
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_DECRYPT_ERROR);
                return -1;
        }
 
index d789efb..d192f44 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * TLSv1 client - write handshake message
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -205,7 +205,7 @@ static int tls_write_client_certificate(struct tlsv1_client *conn,
 }
 
 
-static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end)
+static int tlsv1_key_x_dh(struct tlsv1_client *conn, u8 **pos, u8 *end)
 {
        /* ClientDiffieHellmanPublic */
        u8 *csecret, *csecret_start, *dh_yc, *shared;
@@ -399,8 +399,8 @@ static int tls_write_client_key_exchange(struct tlsv1_client *conn,
        hs_length = pos;
        pos += 3;
        /* body - ClientKeyExchange */
-       if (keyx == TLS_KEY_X_DH_anon) {
-               if (tlsv1_key_x_anon_dh(conn, &pos, end) < 0)
+       if (keyx == TLS_KEY_X_DH_anon || keyx == TLS_KEY_X_DHE_RSA) {
+               if (tlsv1_key_x_dh(conn, &pos, end) < 0)
                        return -1;
        } else {
                if (tlsv1_key_x_rsa(conn, &pos, end) < 0)
@@ -432,7 +432,6 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
        u8 *pos, *rhdr, *hs_start, *hs_length, *signed_start;
        size_t rlen, hlen, clen;
        u8 hash[100], *hpos;
-       enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
 
        pos = *msgpos;
 
@@ -505,21 +504,17 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
        } else {
 #endif /* CONFIG_TLSV12 */
 
-       if (alg == SIGN_ALG_RSA) {
-               hlen = MD5_MAC_LEN;
-               if (conn->verify.md5_cert == NULL ||
-                   crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0)
-               {
-                       tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
-                                 TLS_ALERT_INTERNAL_ERROR);
-                       conn->verify.md5_cert = NULL;
-                       crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
-                       conn->verify.sha1_cert = NULL;
-                       return -1;
-               }
-               hpos += MD5_MAC_LEN;
-       } else
-               crypto_hash_finish(conn->verify.md5_cert, NULL, NULL);
+       hlen = MD5_MAC_LEN;
+       if (conn->verify.md5_cert == NULL ||
+           crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) {
+               tls_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                         TLS_ALERT_INTERNAL_ERROR);
+               conn->verify.md5_cert = NULL;
+               crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
+               conn->verify.sha1_cert = NULL;
+               return -1;
+       }
+       hpos += MD5_MAC_LEN;
 
        conn->verify.md5_cert = NULL;
        hlen = SHA1_MAC_LEN;
@@ -532,8 +527,7 @@ static int tls_write_client_certificate_verify(struct tlsv1_client *conn,
        }
        conn->verify.sha1_cert = NULL;
 
-       if (alg == SIGN_ALG_RSA)
-               hlen += MD5_MAC_LEN;
+       hlen += MD5_MAC_LEN;
 
 #ifdef CONFIG_TLSV12
        }
index d212862..dabc12a 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * TLSv1 common routines
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -9,6 +9,7 @@
 #include "includes.h"
 
 #include "common.h"
+#include "crypto/md5.h"
 #include "crypto/sha1.h"
 #include "crypto/sha256.h"
 #include "x509v3.h"
@@ -33,6 +34,10 @@ static const struct tls_cipher_suite tls_cipher_suites[] = {
          TLS_HASH_SHA },
        { TLS_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_RSA,
          TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
+       { TLS_DHE_RSA_WITH_DES_CBC_SHA, TLS_KEY_X_DHE_RSA, TLS_CIPHER_DES_CBC,
+         TLS_HASH_SHA},
+       { TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, TLS_KEY_X_DHE_RSA,
+         TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
        { TLS_DH_anon_WITH_RC4_128_MD5, TLS_KEY_X_DH_anon,
          TLS_CIPHER_RC4_128, TLS_HASH_MD5 },
        { TLS_DH_anon_WITH_DES_CBC_SHA, TLS_KEY_X_DH_anon,
@@ -41,24 +46,31 @@ static const struct tls_cipher_suite tls_cipher_suites[] = {
          TLS_CIPHER_3DES_EDE_CBC, TLS_HASH_SHA },
        { TLS_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_128_CBC,
          TLS_HASH_SHA },
+       { TLS_DHE_RSA_WITH_AES_128_CBC_SHA, TLS_KEY_X_DHE_RSA,
+         TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA },
        { TLS_DH_anon_WITH_AES_128_CBC_SHA, TLS_KEY_X_DH_anon,
          TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA },
        { TLS_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_RSA, TLS_CIPHER_AES_256_CBC,
          TLS_HASH_SHA },
+       { TLS_DHE_RSA_WITH_AES_256_CBC_SHA, TLS_KEY_X_DHE_RSA,
+         TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA },
        { TLS_DH_anon_WITH_AES_256_CBC_SHA, TLS_KEY_X_DH_anon,
          TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA },
        { TLS_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_RSA,
          TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
        { TLS_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_RSA,
          TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 },
+       { TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DHE_RSA,
+         TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
+       { TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DHE_RSA,
+         TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 },
        { TLS_DH_anon_WITH_AES_128_CBC_SHA256, TLS_KEY_X_DH_anon,
          TLS_CIPHER_AES_128_CBC, TLS_HASH_SHA256 },
        { TLS_DH_anon_WITH_AES_256_CBC_SHA256, TLS_KEY_X_DH_anon,
          TLS_CIPHER_AES_256_CBC, TLS_HASH_SHA256 }
 };
 
-#define NUM_ELEMS(a) (sizeof(a) / sizeof((a)[0]))
-#define NUM_TLS_CIPHER_SUITES NUM_ELEMS(tls_cipher_suites)
+#define NUM_TLS_CIPHER_SUITES ARRAY_SIZE(tls_cipher_suites)
 
 
 static const struct tls_cipher_data tls_ciphers[] = {
@@ -84,7 +96,7 @@ static const struct tls_cipher_data tls_ciphers[] = {
          CRYPTO_CIPHER_ALG_AES }
 };
 
-#define NUM_TLS_CIPHER_DATA NUM_ELEMS(tls_ciphers)
+#define NUM_TLS_CIPHER_DATA ARRAY_SIZE(tls_ciphers)
 
 
 /**
@@ -320,3 +332,161 @@ int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label,
        return tls_prf_sha1_md5(secret, secret_len, label, seed, seed_len, out,
                                outlen);
 }
+
+
+#ifdef CONFIG_TLSV12
+int tlsv12_key_x_server_params_hash(u16 tls_version,
+                                   const u8 *client_random,
+                                   const u8 *server_random,
+                                   const u8 *server_params,
+                                   size_t server_params_len, u8 *hash)
+{
+       size_t hlen;
+       struct crypto_hash *ctx;
+
+       ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA256, NULL, 0);
+       if (ctx == NULL)
+               return -1;
+       crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN);
+       crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN);
+       crypto_hash_update(ctx, server_params, server_params_len);
+       hlen = SHA256_MAC_LEN;
+       if (crypto_hash_finish(ctx, hash, &hlen) < 0)
+               return -1;
+
+       return hlen;
+}
+#endif /* CONFIG_TLSV12 */
+
+
+int tls_key_x_server_params_hash(u16 tls_version, const u8 *client_random,
+                                const u8 *server_random,
+                                const u8 *server_params,
+                                size_t server_params_len, u8 *hash)
+{
+       u8 *hpos;
+       size_t hlen;
+       struct crypto_hash *ctx;
+
+       hpos = hash;
+
+       ctx = crypto_hash_init(CRYPTO_HASH_ALG_MD5, NULL, 0);
+       if (ctx == NULL)
+               return -1;
+       crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN);
+       crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN);
+       crypto_hash_update(ctx, server_params, server_params_len);
+       hlen = MD5_MAC_LEN;
+       if (crypto_hash_finish(ctx, hash, &hlen) < 0)
+               return -1;
+       hpos += hlen;
+
+       ctx = crypto_hash_init(CRYPTO_HASH_ALG_SHA1, NULL, 0);
+       if (ctx == NULL)
+               return -1;
+       crypto_hash_update(ctx, client_random, TLS_RANDOM_LEN);
+       crypto_hash_update(ctx, server_random, TLS_RANDOM_LEN);
+       crypto_hash_update(ctx, server_params, server_params_len);
+       hlen = hash + sizeof(hash) - hpos;
+       if (crypto_hash_finish(ctx, hpos, &hlen) < 0)
+               return -1;
+       hpos += hlen;
+       return hpos - hash;
+}
+
+
+int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk,
+                        const u8 *data, size_t data_len,
+                        const u8 *pos, size_t len, u8 *alert)
+{
+       u8 *buf;
+       const u8 *end = pos + len;
+       const u8 *decrypted;
+       u16 slen;
+       size_t buflen;
+
+       if (end - pos < 2) {
+               *alert = TLS_ALERT_DECODE_ERROR;
+               return -1;
+       }
+       slen = WPA_GET_BE16(pos);
+       pos += 2;
+       if (end - pos < slen) {
+               *alert = TLS_ALERT_DECODE_ERROR;
+               return -1;
+       }
+       if (end - pos > slen) {
+               wpa_hexdump(MSG_MSGDUMP, "Additional data after Signature",
+                           pos + slen, end - pos - slen);
+               end = pos + slen;
+       }
+
+       wpa_hexdump(MSG_MSGDUMP, "TLSv1: Signature", pos, end - pos);
+       if (pk == NULL) {
+               wpa_printf(MSG_DEBUG, "TLSv1: No public key to verify signature");
+               *alert = TLS_ALERT_INTERNAL_ERROR;
+               return -1;
+       }
+
+       buflen = end - pos;
+       buf = os_malloc(end - pos);
+       if (buf == NULL) {
+               *alert = TLS_ALERT_INTERNAL_ERROR;
+               return -1;
+       }
+       if (crypto_public_key_decrypt_pkcs1(pk, pos, end - pos, buf, &buflen) <
+           0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt signature");
+               os_free(buf);
+               *alert = TLS_ALERT_DECRYPT_ERROR;
+               return -1;
+       }
+       decrypted = buf;
+
+       wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature",
+                       decrypted, buflen);
+
+#ifdef CONFIG_TLSV12
+       if (tls_version >= TLS_VERSION_1_2) {
+               /*
+                * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5
+                *
+                * DigestInfo ::= SEQUENCE {
+                *   digestAlgorithm DigestAlgorithm,
+                *   digest OCTET STRING
+                * }
+                *
+                * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11}
+                *
+                * DER encoded DigestInfo for SHA256 per RFC 3447:
+                * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 ||
+                * H
+                */
+               if (buflen >= 19 + 32 &&
+                   os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01"
+                             "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0)
+               {
+                       wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = SHA-256");
+                       decrypted = buf + 19;
+                       buflen -= 19;
+               } else {
+                       wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized DigestInfo");
+                       os_free(buf);
+                       *alert = TLS_ALERT_DECRYPT_ERROR;
+                       return -1;
+               }
+       }
+#endif /* CONFIG_TLSV12 */
+
+       if (buflen != data_len ||
+           os_memcmp_const(decrypted, data, data_len) != 0) {
+               wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in CertificateVerify - did not match calculated hash");
+               os_free(buf);
+               *alert = TLS_ALERT_DECRYPT_ERROR;
+               return -1;
+       }
+
+       os_free(buf);
+
+       return 0;
+}
index f28c0cd..26e68af 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * TLSv1 common definitions
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -257,5 +257,16 @@ int tls_version_ok(u16 ver);
 const char * tls_version_str(u16 ver);
 int tls_prf(u16 ver, const u8 *secret, size_t secret_len, const char *label,
            const u8 *seed, size_t seed_len, u8 *out, size_t outlen);
+int tlsv12_key_x_server_params_hash(u16 tls_version, const u8 *client_random,
+                                   const u8 *server_random,
+                                   const u8 *server_params,
+                                   size_t server_params_len, u8 *hash);
+int tls_key_x_server_params_hash(u16 tls_version, const u8 *client_random,
+                                const u8 *server_random,
+                                const u8 *server_params,
+                                size_t server_params_len, u8 *hash);
+int tls_verify_signature(u16 tls_version, struct crypto_public_key *pk,
+                        const u8 *data, size_t data_len,
+                        const u8 *pos, size_t len, u8 *alert);
 
 #endif /* TLSV1_COMMON_H */
index 3bec3be..0c6897a 100644 (file)
@@ -456,7 +456,7 @@ int tlsv1_record_receive(struct tlsv1_record_layer *rl,
                        return -1;
                }
                if (hlen != rl->hash_size ||
-                   os_memcmp(hash, out_data + plen, hlen) != 0 ||
+                   os_memcmp_const(hash, out_data + plen, hlen) != 0 ||
                    force_mac_error) {
                        wpa_printf(MSG_DEBUG, "TLSv1: Invalid HMAC value in "
                                   "received message (force_mac_error=%d)",
index 2880309..93ae488 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246)
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
  */
 
 
+void tlsv1_server_log(struct tlsv1_server *conn, const char *fmt, ...)
+{
+       va_list ap;
+       char *buf;
+       int buflen;
+
+       va_start(ap, fmt);
+       buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+       va_end(ap);
+
+       buf = os_malloc(buflen);
+       if (buf == NULL)
+               return;
+       va_start(ap, fmt);
+       vsnprintf(buf, buflen, fmt, ap);
+       va_end(ap);
+
+       wpa_printf(MSG_DEBUG, "TLSv1: %s", buf);
+       if (conn->log_cb)
+               conn->log_cb(conn->log_cb_ctx, buf);
+
+       os_free(buf);
+}
+
+
 void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description)
 {
        conn->alert_level = level;
@@ -250,8 +275,7 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn,
                used = tlsv1_record_receive(&conn->rl, pos, in_end - pos,
                                            out_pos, &olen, &alert);
                if (used < 0) {
-                       wpa_printf(MSG_DEBUG, "TLSv1: Record layer processing "
-                                  "failed");
+                       tlsv1_server_log(conn, "Record layer processing failed");
                        tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
                        return -1;
                }
@@ -265,14 +289,13 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn,
 
                if (ct == TLS_CONTENT_TYPE_ALERT) {
                        if (olen < 2) {
-                               wpa_printf(MSG_DEBUG, "TLSv1: Alert "
-                                          "underflow");
+                               tlsv1_server_log(conn, "Alert underflow");
                                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                                   TLS_ALERT_DECODE_ERROR);
                                return -1;
                        }
-                       wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
-                                  out_pos[0], out_pos[1]);
+                       tlsv1_server_log(conn, "Received alert %d:%d",
+                                        out_pos[0], out_pos[1]);
                        if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) {
                                /* Continue processing */
                                pos += used;
@@ -285,13 +308,23 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn,
                }
 
                if (ct != TLS_CONTENT_TYPE_APPLICATION_DATA) {
-                       wpa_printf(MSG_DEBUG, "TLSv1: Unexpected content type "
-                                  "0x%x", pos[0]);
+                       tlsv1_server_log(conn, "Unexpected content type 0x%x",
+                                        pos[0]);
                        tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                           TLS_ALERT_UNEXPECTED_MESSAGE);
                        return -1;
                }
 
+#ifdef CONFIG_TESTING_OPTIONS
+               if ((conn->test_flags &
+                    (TLS_BREAK_VERIFY_DATA | TLS_BREAK_SRV_KEY_X_HASH |
+                     TLS_BREAK_SRV_KEY_X_SIGNATURE)) &&
+                   !conn->test_failure_reported) {
+                       tlsv1_server_log(conn, "TEST-FAILURE: Client ApplData received after invalid handshake");
+                       conn->test_failure_reported = 1;
+               }
+#endif /* CONFIG_TESTING_OPTIONS */
+
                out_pos += olen;
                if (out_pos > out_end) {
                        wpa_printf(MSG_DEBUG, "TLSv1: Buffer not large enough "
@@ -361,8 +394,15 @@ struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred)
 
        count = 0;
        suites = conn->cipher_suites;
+       suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA256;
+       suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA256;
+       suites[count++] = TLS_DHE_RSA_WITH_AES_256_CBC_SHA;
        suites[count++] = TLS_RSA_WITH_AES_256_CBC_SHA;
+       suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA256;
+       suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA256;
+       suites[count++] = TLS_DHE_RSA_WITH_AES_128_CBC_SHA;
        suites[count++] = TLS_RSA_WITH_AES_128_CBC_SHA;
+       suites[count++] = TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA;
        suites[count++] = TLS_RSA_WITH_3DES_EDE_CBC_SHA;
        suites[count++] = TLS_RSA_WITH_RC4_128_SHA;
        suites[count++] = TLS_RSA_WITH_RC4_128_MD5;
@@ -476,14 +516,56 @@ int tlsv1_server_get_cipher(struct tlsv1_server *conn, char *buf,
        case TLS_RSA_WITH_3DES_EDE_CBC_SHA:
                cipher = "DES-CBC3-SHA";
                break;
+       case TLS_DHE_RSA_WITH_DES_CBC_SHA:
+               cipher = "DHE-RSA-DES-CBC-SHA";
+               break;
+       case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+               cipher = "DHE-RSA-DES-CBC3-SHA";
+               break;
+       case TLS_DH_anon_WITH_RC4_128_MD5:
+               cipher = "ADH-RC4-MD5";
+               break;
+       case TLS_DH_anon_WITH_DES_CBC_SHA:
+               cipher = "ADH-DES-SHA";
+               break;
+       case TLS_DH_anon_WITH_3DES_EDE_CBC_SHA:
+               cipher = "ADH-DES-CBC3-SHA";
+               break;
+       case TLS_RSA_WITH_AES_128_CBC_SHA:
+               cipher = "AES-128-SHA";
+               break;
+       case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+               cipher = "DHE-RSA-AES-128-SHA";
+               break;
        case TLS_DH_anon_WITH_AES_128_CBC_SHA:
                cipher = "ADH-AES-128-SHA";
                break;
        case TLS_RSA_WITH_AES_256_CBC_SHA:
                cipher = "AES-256-SHA";
                break;
-       case TLS_RSA_WITH_AES_128_CBC_SHA:
-               cipher = "AES-128-SHA";
+       case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+               cipher = "DHE-RSA-AES-256-SHA";
+               break;
+       case TLS_DH_anon_WITH_AES_256_CBC_SHA:
+               cipher = "ADH-AES-256-SHA";
+               break;
+       case TLS_RSA_WITH_AES_128_CBC_SHA256:
+               cipher = "AES-128-SHA256";
+               break;
+       case TLS_RSA_WITH_AES_256_CBC_SHA256:
+               cipher = "AES-256-SHA256";
+               break;
+       case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+               cipher = "DHE-RSA-AES-128-SHA256";
+               break;
+       case TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+               cipher = "DHE-RSA-AES-256-SHA256";
+               break;
+       case TLS_DH_anon_WITH_AES_128_CBC_SHA256:
+               cipher = "ADH-AES-128-SHA256";
+               break;
+       case TLS_DH_anon_WITH_AES_256_CBC_SHA256:
+               cipher = "ADH-AES-256-SHA256";
                break;
        default:
                return -1;
@@ -618,3 +700,125 @@ void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn,
        conn->session_ticket_cb = cb;
        conn->session_ticket_cb_ctx = ctx;
 }
+
+
+void tlsv1_server_set_log_cb(struct tlsv1_server *conn,
+                            void (*cb)(void *ctx, const char *msg), void *ctx)
+{
+       conn->log_cb = cb;
+       conn->log_cb_ctx = ctx;
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags)
+{
+       conn->test_flags = flags;
+}
+
+
+static const u8 test_tls_prime15[1] = {
+       15
+};
+
+static const u8 test_tls_prime511b[64] = {
+       0x50, 0xfb, 0xf1, 0xae, 0x01, 0xf1, 0xfe, 0xe6,
+       0xe1, 0xae, 0xdc, 0x1e, 0xbe, 0xfb, 0x9e, 0x58,
+       0x9a, 0xd7, 0x54, 0x9d, 0x6b, 0xb3, 0x78, 0xe2,
+       0x39, 0x7f, 0x30, 0x01, 0x25, 0xa1, 0xf9, 0x7c,
+       0x55, 0x0e, 0xa1, 0x15, 0xcc, 0x36, 0x34, 0xbb,
+       0x6c, 0x8b, 0x64, 0x45, 0x15, 0x7f, 0xd3, 0xe7,
+       0x31, 0xc8, 0x8e, 0x56, 0x8e, 0x95, 0xdc, 0xea,
+       0x9e, 0xdf, 0xf7, 0x56, 0xdd, 0xb0, 0x34, 0xdb
+};
+
+static const u8 test_tls_prime767b[96] = {
+       0x4c, 0xdc, 0xb8, 0x21, 0x20, 0x9d, 0xe8, 0xa3,
+       0x53, 0xd9, 0x1c, 0x18, 0xc1, 0x3a, 0x58, 0x67,
+       0xa7, 0x85, 0xf9, 0x28, 0x9b, 0xce, 0xc0, 0xd1,
+       0x05, 0x84, 0x61, 0x97, 0xb2, 0x86, 0x1c, 0xd0,
+       0xd1, 0x96, 0x23, 0x29, 0x8c, 0xc5, 0x30, 0x68,
+       0x3e, 0xf9, 0x05, 0xba, 0x60, 0xeb, 0xdb, 0xee,
+       0x2d, 0xdf, 0x84, 0x65, 0x49, 0x87, 0x90, 0x2a,
+       0xc9, 0x8e, 0x34, 0x63, 0x6d, 0x9a, 0x2d, 0x32,
+       0x1c, 0x46, 0xd5, 0x4e, 0x20, 0x20, 0x90, 0xac,
+       0xd5, 0x48, 0x79, 0x99, 0x0c, 0xe6, 0xed, 0xbf,
+       0x79, 0xc2, 0x47, 0x50, 0x95, 0x38, 0x38, 0xbc,
+       0xde, 0xb0, 0xd2, 0xe8, 0x97, 0xcb, 0x22, 0xbb
+};
+
+static const u8 test_tls_prime58[128] = {
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+       0x03, 0xc1, 0xba, 0xc8, 0x25, 0xbe, 0x2d, 0xf3
+};
+
+static const u8 test_tls_non_prime[] = {
+       /*
+        * This is not a prime and the value has the following factors:
+        * 13736783488716579923 * 16254860191773456563 * 18229434976173670763 *
+        * 11112313018289079419 * 10260802278580253339 * 12394009491575311499 *
+        * 12419059668711064739 * 14317973192687985827 * 10498605410533203179 *
+        * 16338688760390249003 * 11128963991123878883 * 12990532258280301419 *
+        * 3
+        */
+       0x0C, 0x8C, 0x36, 0x9C, 0x6F, 0x71, 0x2E, 0xA7,
+       0xAB, 0x32, 0xD3, 0x0F, 0x68, 0x3D, 0xB2, 0x6D,
+       0x81, 0xDD, 0xC4, 0x84, 0x0D, 0x9C, 0x6E, 0x36,
+       0x29, 0x70, 0xF3, 0x1E, 0x9A, 0x42, 0x0B, 0x67,
+       0x82, 0x6B, 0xB1, 0xF2, 0xAF, 0x55, 0x28, 0xE7,
+       0xDB, 0x67, 0x6C, 0xF7, 0x6B, 0xAC, 0xAC, 0xE5,
+       0xF7, 0x9F, 0xD4, 0x63, 0x55, 0x70, 0x32, 0x7C,
+       0x70, 0xFB, 0xAF, 0xB8, 0xEB, 0x37, 0xCF, 0x3F,
+       0xFE, 0x94, 0x73, 0xF9, 0x7A, 0xC7, 0x12, 0x2E,
+       0x9B, 0xB4, 0x7D, 0x08, 0x60, 0x83, 0x43, 0x52,
+       0x83, 0x1E, 0xA5, 0xFC, 0xFA, 0x87, 0x12, 0xF4,
+       0x64, 0xE2, 0xCE, 0x71, 0x17, 0x72, 0xB6, 0xAB
+};
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+void tlsv1_server_get_dh_p(struct tlsv1_server *conn, const u8 **dh_p,
+                          size_t *dh_p_len)
+{
+       *dh_p = conn->cred->dh_p;
+       *dh_p_len = conn->cred->dh_p_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+       if (conn->test_flags & TLS_DHE_PRIME_511B) {
+               tlsv1_server_log(conn, "TESTING: Use short 511-bit prime with DHE");
+               *dh_p = test_tls_prime511b;
+               *dh_p_len = sizeof(test_tls_prime511b);
+       } else if (conn->test_flags & TLS_DHE_PRIME_767B) {
+               tlsv1_server_log(conn, "TESTING: Use short 767-bit prime with DHE");
+               *dh_p = test_tls_prime767b;
+               *dh_p_len = sizeof(test_tls_prime767b);
+       } else if (conn->test_flags & TLS_DHE_PRIME_15) {
+               tlsv1_server_log(conn, "TESTING: Use bogus 15 \"prime\" with DHE");
+               *dh_p = test_tls_prime15;
+               *dh_p_len = sizeof(test_tls_prime15);
+       } else if (conn->test_flags & TLS_DHE_PRIME_58B) {
+               tlsv1_server_log(conn, "TESTING: Use short 58-bit prime in long container with DHE");
+               *dh_p = test_tls_prime58;
+               *dh_p_len = sizeof(test_tls_prime58);
+       } else if (conn->test_flags & TLS_DHE_NON_PRIME) {
+               tlsv1_server_log(conn, "TESTING: Use claim non-prime as the DHE prime");
+               *dh_p = test_tls_non_prime;
+               *dh_p_len = sizeof(test_tls_non_prime);
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+}
index a18c69e..b2b28d1 100644 (file)
@@ -45,4 +45,9 @@ void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn,
                                        tlsv1_server_session_ticket_cb cb,
                                        void *ctx);
 
+void tlsv1_server_set_log_cb(struct tlsv1_server *conn,
+                            void (*cb)(void *ctx, const char *msg), void *ctx);
+
+void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags);
+
 #endif /* TLSV1_SERVER_H */
index 1f61533..96d79b3 100644 (file)
@@ -51,13 +51,24 @@ struct tlsv1_server {
        tlsv1_server_session_ticket_cb session_ticket_cb;
        void *session_ticket_cb_ctx;
 
+       void (*log_cb)(void *ctx, const char *msg);
+       void *log_cb_ctx;
+
        int use_session_ticket;
 
        u8 *dh_secret;
        size_t dh_secret_len;
+
+#ifdef CONFIG_TESTING_OPTIONS
+       u32 test_flags;
+       int test_failure_reported;
+#endif /* CONFIG_TESTING_OPTIONS */
 };
 
 
+void tlsv1_server_log(struct tlsv1_server *conn, const char *fmt, ...)
+PRINTF_FORMAT(2, 3);
+
 void tlsv1_server_alert(struct tlsv1_server *conn, u8 level, u8 description);
 int tlsv1_server_derive_keys(struct tlsv1_server *conn,
                             const u8 *pre_master_secret,
@@ -67,5 +78,7 @@ u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level,
                             u8 description, size_t *out_len);
 int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct,
                                   const u8 *buf, size_t *len);
+void tlsv1_server_get_dh_p(struct tlsv1_server *conn, const u8 **dh_p,
+                          size_t *dh_p_len);
 
 #endif /* TLSV1_SERVER_I_H */
index 6f6539b..0f237ba 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * TLSv1 server - read handshake message
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -27,6 +27,25 @@ static int tls_process_change_cipher_spec(struct tlsv1_server *conn,
                                          size_t *in_len);
 
 
+static int testing_cipher_suite_filter(struct tlsv1_server *conn, u16 suite)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+       if ((conn->test_flags &
+            (TLS_BREAK_SRV_KEY_X_HASH | TLS_BREAK_SRV_KEY_X_SIGNATURE |
+             TLS_DHE_PRIME_511B | TLS_DHE_PRIME_767B | TLS_DHE_PRIME_15 |
+             TLS_DHE_PRIME_58B | TLS_DHE_NON_PRIME)) &&
+           suite != TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 &&
+           suite != TLS_DHE_RSA_WITH_AES_256_CBC_SHA &&
+           suite != TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 &&
+           suite != TLS_DHE_RSA_WITH_AES_128_CBC_SHA &&
+           suite != TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA)
+               return 1;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+       return 0;
+}
+
+
 static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
                                    const u8 *in_data, size_t *in_len)
 {
@@ -38,8 +57,8 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
        u16 ext_type, ext_len;
 
        if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
-                          "received content type 0x%x", ct);
+               tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x",
+                                ct);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_UNEXPECTED_MESSAGE);
                return -1;
@@ -53,13 +72,13 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
 
        /* HandshakeType msg_type */
        if (*pos != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
-                          "message %d (expected ClientHello)", *pos);
+               tlsv1_server_log(conn, "Received unexpected handshake message %d (expected ClientHello)",
+                                *pos);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_UNEXPECTED_MESSAGE);
                return -1;
        }
-       wpa_printf(MSG_DEBUG, "TLSv1: Received ClientHello");
+       tlsv1_server_log(conn, "Received ClientHello");
        pos++;
        /* uint24 length */
        len = WPA_GET_BE24(pos);
@@ -78,13 +97,13 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
        if (end - pos < 2)
                goto decode_error;
        conn->client_version = WPA_GET_BE16(pos);
-       wpa_printf(MSG_DEBUG, "TLSv1: Client version %d.%d",
-                  conn->client_version >> 8, conn->client_version & 0xff);
+       tlsv1_server_log(conn, "Client version %d.%d",
+                        conn->client_version >> 8,
+                        conn->client_version & 0xff);
        if (conn->client_version < TLS_VERSION_1) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected protocol version in "
-                          "ClientHello %u.%u",
-                          conn->client_version >> 8,
-                          conn->client_version & 0xff);
+               tlsv1_server_log(conn, "Unexpected protocol version in ClientHello %u.%u",
+                                conn->client_version >> 8,
+                                conn->client_version & 0xff);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_PROTOCOL_VERSION);
                return -1;
@@ -101,8 +120,8 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
                conn->rl.tls_version = TLS_VERSION_1_1;
        else
                conn->rl.tls_version = conn->client_version;
-       wpa_printf(MSG_DEBUG, "TLSv1: Using TLS v%s",
-                  tls_version_str(conn->rl.tls_version));
+       tlsv1_server_log(conn, "Using TLS v%s",
+                        tls_version_str(conn->rl.tls_version));
 
        /* Random random */
        if (end - pos < TLS_RANDOM_LEN)
@@ -137,6 +156,8 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
 
        cipher_suite = 0;
        for (i = 0; !cipher_suite && i < conn->num_cipher_suites; i++) {
+               if (testing_cipher_suite_filter(conn, conn->cipher_suites[i]))
+                       continue;
                c = pos;
                for (j = 0; j < num_suites; j++) {
                        u16 tmp = WPA_GET_BE16(c);
@@ -149,8 +170,7 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
        }
        pos += num_suites * 2;
        if (!cipher_suite) {
-               wpa_printf(MSG_INFO, "TLSv1: No supported cipher suite "
-                          "available");
+               tlsv1_server_log(conn, "No supported cipher suite available");
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_ILLEGAL_PARAMETER);
                return -1;
@@ -180,16 +200,15 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
                        compr_null_found = 1;
        }
        if (!compr_null_found) {
-               wpa_printf(MSG_INFO, "TLSv1: Client does not accept NULL "
-                          "compression");
+               tlsv1_server_log(conn, "Client does not accept NULL compression");
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_ILLEGAL_PARAMETER);
                return -1;
        }
 
        if (end - pos == 1) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected extra octet in the "
-                           "end of ClientHello: 0x%02x", *pos);
+               tlsv1_server_log(conn, "Unexpected extra octet in the end of ClientHello: 0x%02x",
+                                *pos);
                goto decode_error;
        }
 
@@ -198,12 +217,11 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
                ext_len = WPA_GET_BE16(pos);
                pos += 2;
 
-               wpa_printf(MSG_DEBUG, "TLSv1: %u bytes of ClientHello "
-                          "extensions", ext_len);
+               tlsv1_server_log(conn, "%u bytes of ClientHello extensions",
+                                ext_len);
                if (end - pos != ext_len) {
-                       wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientHello "
-                                  "extension list length %u (expected %u)",
-                                  ext_len, (unsigned int) (end - pos));
+                       tlsv1_server_log(conn, "Invalid ClientHello extension list length %u (expected %u)",
+                                        ext_len, (unsigned int) (end - pos));
                        goto decode_error;
                }
 
@@ -216,8 +234,7 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
 
                while (pos < end) {
                        if (end - pos < 2) {
-                               wpa_printf(MSG_DEBUG, "TLSv1: Invalid "
-                                          "extension_type field");
+                               tlsv1_server_log(conn, "Invalid extension_type field");
                                goto decode_error;
                        }
 
@@ -225,8 +242,7 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
                        pos += 2;
 
                        if (end - pos < 2) {
-                               wpa_printf(MSG_DEBUG, "TLSv1: Invalid "
-                                          "extension_data length field");
+                               tlsv1_server_log(conn, "Invalid extension_data length field");
                                goto decode_error;
                        }
 
@@ -234,13 +250,12 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
                        pos += 2;
 
                        if (end - pos < ext_len) {
-                               wpa_printf(MSG_DEBUG, "TLSv1: Invalid "
-                                          "extension_data field");
+                               tlsv1_server_log(conn, "Invalid extension_data field");
                                goto decode_error;
                        }
 
-                       wpa_printf(MSG_DEBUG, "TLSv1: ClientHello Extension "
-                                  "type %u", ext_type);
+                       tlsv1_server_log(conn, "ClientHello Extension type %u",
+                                        ext_type);
                        wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientHello "
                                    "Extension data", pos, ext_len);
 
@@ -260,14 +275,13 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
 
        *in_len = end - in_data;
 
-       wpa_printf(MSG_DEBUG, "TLSv1: ClientHello OK - proceed to "
-                  "ServerHello");
+       tlsv1_server_log(conn, "ClientHello OK - proceed to ServerHello");
        conn->state = SERVER_HELLO;
 
        return 0;
 
 decode_error:
-       wpa_printf(MSG_DEBUG, "TLSv1: Failed to decode ClientHello");
+       tlsv1_server_log(conn, "Failed to decode ClientHello");
        tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                           TLS_ALERT_DECODE_ERROR);
        return -1;
@@ -284,8 +298,8 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct,
        int reason;
 
        if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
-                          "received content type 0x%x", ct);
+               tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x",
+                                ct);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_UNEXPECTED_MESSAGE);
                return -1;
@@ -295,8 +309,8 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct,
        left = *in_len;
 
        if (left < 4) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate message "
-                          "(len=%lu)", (unsigned long) left);
+               tlsv1_server_log(conn, "Too short Certificate message (len=%lu)",
+                                (unsigned long) left);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_DECODE_ERROR);
                return -1;
@@ -308,9 +322,8 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct,
        left -= 4;
 
        if (len > left) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected Certificate message "
-                          "length (len=%lu != left=%lu)",
-                          (unsigned long) len, (unsigned long) left);
+               tlsv1_server_log(conn, "Unexpected Certificate message length (len=%lu != left=%lu)",
+                                (unsigned long) len, (unsigned long) left);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_DECODE_ERROR);
                return -1;
@@ -318,8 +331,7 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct,
 
        if (type == TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) {
                if (conn->verify_peer) {
-                       wpa_printf(MSG_DEBUG, "TLSv1: Client did not include "
-                                  "Certificate");
+                       tlsv1_server_log(conn, "Client did not include Certificate");
                        tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                           TLS_ALERT_UNEXPECTED_MESSAGE);
                        return -1;
@@ -329,17 +341,15 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct,
                                                       in_len);
        }
        if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
-                          "message %d (expected Certificate/"
-                          "ClientKeyExchange)", type);
+               tlsv1_server_log(conn, "Received unexpected handshake message %d (expected Certificate/ClientKeyExchange)",
+                                type);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_UNEXPECTED_MESSAGE);
                return -1;
        }
 
-       wpa_printf(MSG_DEBUG,
-                  "TLSv1: Received Certificate (certificate_list len %lu)",
-                  (unsigned long) len);
+       tlsv1_server_log(conn, "Received Certificate (certificate_list len %lu)",
+                        (unsigned long) len);
 
        /*
         * opaque ASN.1Cert<2^24-1>;
@@ -352,8 +362,8 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct,
        end = pos + len;
 
        if (end - pos < 3) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Too short Certificate "
-                          "(left=%lu)", (unsigned long) left);
+               tlsv1_server_log(conn, "Too short Certificate (left=%lu)",
+                                (unsigned long) left);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_DECODE_ERROR);
                return -1;
@@ -363,10 +373,9 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct,
        pos += 3;
 
        if ((size_t) (end - pos) != list_len) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate_list "
-                          "length (len=%lu left=%lu)",
-                          (unsigned long) list_len,
-                          (unsigned long) (end - pos));
+               tlsv1_server_log(conn, "Unexpected certificate_list length (len=%lu left=%lu)",
+                                (unsigned long) list_len,
+                                (unsigned long) (end - pos));
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_DECODE_ERROR);
                return -1;
@@ -375,8 +384,7 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct,
        idx = 0;
        while (pos < end) {
                if (end - pos < 3) {
-                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
-                                  "certificate_list");
+                       tlsv1_server_log(conn, "Failed to parse certificate_list");
                        tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                           TLS_ALERT_DECODE_ERROR);
                        x509_certificate_chain_free(chain);
@@ -387,25 +395,23 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct,
                pos += 3;
 
                if ((size_t) (end - pos) < cert_len) {
-                       wpa_printf(MSG_DEBUG, "TLSv1: Unexpected certificate "
-                                  "length (len=%lu left=%lu)",
-                                  (unsigned long) cert_len,
-                                  (unsigned long) (end - pos));
+                       tlsv1_server_log(conn, "Unexpected certificate length (len=%lu left=%lu)",
+                                        (unsigned long) cert_len,
+                                        (unsigned long) (end - pos));
                        tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                           TLS_ALERT_DECODE_ERROR);
                        x509_certificate_chain_free(chain);
                        return -1;
                }
 
-               wpa_printf(MSG_DEBUG, "TLSv1: Certificate %lu (len %lu)",
-                          (unsigned long) idx, (unsigned long) cert_len);
+               tlsv1_server_log(conn, "Certificate %lu (len %lu)",
+                                (unsigned long) idx, (unsigned long) cert_len);
 
                if (idx == 0) {
                        crypto_public_key_free(conn->client_rsa_key);
                        if (tls_parse_cert(pos, cert_len,
                                           &conn->client_rsa_key)) {
-                               wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
-                                          "the certificate");
+                               tlsv1_server_log(conn, "Failed to parse the certificate");
                                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                                   TLS_ALERT_BAD_CERTIFICATE);
                                x509_certificate_chain_free(chain);
@@ -415,8 +421,7 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct,
 
                cert = x509_certificate_parse(pos, cert_len);
                if (cert == NULL) {
-                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to parse "
-                                  "the certificate");
+                       tlsv1_server_log(conn, "Failed to parse the certificate");
                        tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                           TLS_ALERT_BAD_CERTIFICATE);
                        x509_certificate_chain_free(chain);
@@ -436,8 +441,8 @@ static int tls_process_certificate(struct tlsv1_server *conn, u8 ct,
        if (x509_certificate_chain_validate(conn->cred->trusted_certs, chain,
                                            &reason, 0) < 0) {
                int tls_reason;
-               wpa_printf(MSG_DEBUG, "TLSv1: Server certificate chain "
-                          "validation failed (reason=%d)", reason);
+               tlsv1_server_log(conn, "Server certificate chain validation failed (reason=%d)",
+                                reason);
                switch (reason) {
                case X509_VALIDATE_BAD_CERTIFICATE:
                        tls_reason = TLS_ALERT_BAD_CERTIFICATE;
@@ -494,9 +499,8 @@ static int tls_process_client_key_exchange_rsa(
        encr_len = WPA_GET_BE16(pos);
        pos += 2;
        if (pos + encr_len > end) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Invalid ClientKeyExchange "
-                          "format: encr_len=%u left=%u",
-                          encr_len, (unsigned int) (end - pos));
+               tlsv1_server_log(conn, "Invalid ClientKeyExchange format: encr_len=%u left=%u",
+                                encr_len, (unsigned int) (end - pos));
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_DECODE_ERROR);
                return -1;
@@ -539,15 +543,13 @@ static int tls_process_client_key_exchange_rsa(
        }
 
        if (!use_random && outlen != TLS_PRE_MASTER_SECRET_LEN) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected PreMasterSecret "
-                          "length %lu", (unsigned long) outlen);
+               tlsv1_server_log(conn, "Unexpected PreMasterSecret length %lu",
+                                (unsigned long) outlen);
                use_random = 1;
        }
 
        if (!use_random && WPA_GET_BE16(out) != conn->client_version) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Client version in "
-                          "ClientKeyExchange does not match with version in "
-                          "ClientHello");
+               tlsv1_server_log(conn, "Client version in ClientKeyExchange does not match with version in ClientHello");
                use_random = 1;
        }
 
@@ -582,7 +584,7 @@ static int tls_process_client_key_exchange_rsa(
 }
 
 
-static int tls_process_client_key_exchange_dh_anon(
+static int tls_process_client_key_exchange_dh(
        struct tlsv1_server *conn, const u8 *pos, const u8 *end)
 {
        const u8 *dh_yc;
@@ -590,6 +592,8 @@ static int tls_process_client_key_exchange_dh_anon(
        u8 *shared;
        size_t shared_len;
        int res;
+       const u8 *dh_p;
+       size_t dh_p_len;
 
        /*
         * struct {
@@ -600,6 +604,7 @@ static int tls_process_client_key_exchange_dh_anon(
         * } ClientDiffieHellmanPublic;
         */
 
+       tlsv1_server_log(conn, "ClientDiffieHellmanPublic received");
        wpa_hexdump(MSG_MSGDUMP, "TLSv1: ClientDiffieHellmanPublic",
                    pos, end - pos);
 
@@ -612,8 +617,7 @@ static int tls_process_client_key_exchange_dh_anon(
        }
 
        if (end - pos < 3) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Invalid client public value "
-                          "length");
+               tlsv1_server_log(conn, "Invalid client public value length");
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_DECODE_ERROR);
                return -1;
@@ -622,9 +626,9 @@ static int tls_process_client_key_exchange_dh_anon(
        dh_yc_len = WPA_GET_BE16(pos);
        dh_yc = pos + 2;
 
-       if (dh_yc + dh_yc_len > end) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Client public value overflow "
-                          "(length %d)", dh_yc_len);
+       if (dh_yc_len > end - dh_yc) {
+               tlsv1_server_log(conn, "Client public value overflow (length %d)",
+                                dh_yc_len);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_DECODE_ERROR);
                return -1;
@@ -641,7 +645,9 @@ static int tls_process_client_key_exchange_dh_anon(
                return -1;
        }
 
-       shared_len = conn->cred->dh_p_len;
+       tlsv1_server_get_dh_p(conn, &dh_p, &dh_p_len);
+
+       shared_len = dh_p_len;
        shared = os_malloc(shared_len);
        if (shared == NULL) {
                wpa_printf(MSG_DEBUG, "TLSv1: Could not allocate memory for "
@@ -653,8 +659,7 @@ static int tls_process_client_key_exchange_dh_anon(
 
        /* shared = Yc^secret mod p */
        if (crypto_mod_exp(dh_yc, dh_yc_len, conn->dh_secret,
-                          conn->dh_secret_len,
-                          conn->cred->dh_p, conn->cred->dh_p_len,
+                          conn->dh_secret_len, dh_p, dh_p_len,
                           shared, &shared_len)) {
                os_free(shared);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -695,8 +700,8 @@ static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct,
        const struct tls_cipher_suite *suite;
 
        if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
-                          "received content type 0x%x", ct);
+               tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x",
+                                ct);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_UNEXPECTED_MESSAGE);
                return -1;
@@ -706,8 +711,8 @@ static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct,
        left = *in_len;
 
        if (left < 4) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Too short ClientKeyExchange "
-                          "(Left=%lu)", (unsigned long) left);
+               tlsv1_server_log(conn, "Too short ClientKeyExchange (Left=%lu)",
+                                (unsigned long) left);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_DECODE_ERROR);
                return -1;
@@ -719,9 +724,8 @@ static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct,
        left -= 4;
 
        if (len > left) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Mismatch in ClientKeyExchange "
-                          "length (len=%lu != left=%lu)",
-                          (unsigned long) len, (unsigned long) left);
+               tlsv1_server_log(conn, "Mismatch in ClientKeyExchange length (len=%lu != left=%lu)",
+                                (unsigned long) len, (unsigned long) left);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_DECODE_ERROR);
                return -1;
@@ -730,14 +734,14 @@ static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct,
        end = pos + len;
 
        if (type != TLS_HANDSHAKE_TYPE_CLIENT_KEY_EXCHANGE) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
-                          "message %d (expected ClientKeyExchange)", type);
+               tlsv1_server_log(conn, "Received unexpected handshake message %d (expected ClientKeyExchange)",
+                                type);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_UNEXPECTED_MESSAGE);
                return -1;
        }
 
-       wpa_printf(MSG_DEBUG, "TLSv1: Received ClientKeyExchange");
+       tlsv1_server_log(conn, "Received ClientKeyExchange");
 
        wpa_hexdump(MSG_DEBUG, "TLSv1: ClientKeyExchange", pos, len);
 
@@ -747,11 +751,11 @@ static int tls_process_client_key_exchange(struct tlsv1_server *conn, u8 ct,
        else
                keyx = suite->key_exchange;
 
-       if (keyx == TLS_KEY_X_DH_anon &&
-           tls_process_client_key_exchange_dh_anon(conn, pos, end) < 0)
+       if ((keyx == TLS_KEY_X_DH_anon || keyx == TLS_KEY_X_DHE_RSA) &&
+           tls_process_client_key_exchange_dh(conn, pos, end) < 0)
                return -1;
 
-       if (keyx != TLS_KEY_X_DH_anon &&
+       if (keyx != TLS_KEY_X_DH_anon && keyx != TLS_KEY_X_DHE_RSA &&
            tls_process_client_key_exchange_rsa(conn, pos, end) < 0)
                return -1;
 
@@ -769,15 +773,13 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
        const u8 *pos, *end;
        size_t left, len;
        u8 type;
-       size_t hlen, buflen;
-       u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos, *buf;
-       enum { SIGN_ALG_RSA, SIGN_ALG_DSA } alg = SIGN_ALG_RSA;
-       u16 slen;
+       size_t hlen;
+       u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN], *hpos;
+       u8 alert;
 
        if (ct == TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
                if (conn->verify_peer) {
-                       wpa_printf(MSG_DEBUG, "TLSv1: Client did not include "
-                                  "CertificateVerify");
+                       tlsv1_server_log(conn, "Client did not include CertificateVerify");
                        tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                           TLS_ALERT_UNEXPECTED_MESSAGE);
                        return -1;
@@ -788,8 +790,8 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
        }
 
        if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Expected Handshake; "
-                          "received content type 0x%x", ct);
+               tlsv1_server_log(conn, "Expected Handshake; received content type 0x%x",
+                                ct);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_UNEXPECTED_MESSAGE);
                return -1;
@@ -799,8 +801,8 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
        left = *in_len;
 
        if (left < 4) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Too short CertificateVerify "
-                          "message (len=%lu)", (unsigned long) left);
+               tlsv1_server_log(conn, "Too short CertificateVerify message (len=%lu)",
+                                (unsigned long) left);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_DECODE_ERROR);
                return -1;
@@ -812,9 +814,8 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
        left -= 4;
 
        if (len > left) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected CertificateVerify "
-                          "message length (len=%lu != left=%lu)",
-                          (unsigned long) len, (unsigned long) left);
+               tlsv1_server_log(conn, "Unexpected CertificateVerify message length (len=%lu != left=%lu)",
+                                (unsigned long) len, (unsigned long) left);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_DECODE_ERROR);
                return -1;
@@ -823,14 +824,14 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
        end = pos + len;
 
        if (type != TLS_HANDSHAKE_TYPE_CERTIFICATE_VERIFY) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Received unexpected handshake "
-                          "message %d (expected CertificateVerify)", type);
+               tlsv1_server_log(conn, "Received unexpected handshake message %d (expected CertificateVerify)",
+                                type);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_UNEXPECTED_MESSAGE);
                return -1;
        }
 
-       wpa_printf(MSG_DEBUG, "TLSv1: Received CertificateVerify");
+       tlsv1_server_log(conn, "Received CertificateVerify");
 
        /*
         * struct {
@@ -881,21 +882,17 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
        } else {
 #endif /* CONFIG_TLSV12 */
 
-       if (alg == SIGN_ALG_RSA) {
-               hlen = MD5_MAC_LEN;
-               if (conn->verify.md5_cert == NULL ||
-                   crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0)
-               {
-                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
-                                          TLS_ALERT_INTERNAL_ERROR);
-                       conn->verify.md5_cert = NULL;
-                       crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
-                       conn->verify.sha1_cert = NULL;
-                       return -1;
-               }
-               hpos += MD5_MAC_LEN;
-       } else
-               crypto_hash_finish(conn->verify.md5_cert, NULL, NULL);
+       hlen = MD5_MAC_LEN;
+       if (conn->verify.md5_cert == NULL ||
+           crypto_hash_finish(conn->verify.md5_cert, hpos, &hlen) < 0) {
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                  TLS_ALERT_INTERNAL_ERROR);
+               conn->verify.md5_cert = NULL;
+               crypto_hash_finish(conn->verify.sha1_cert, NULL, NULL);
+               conn->verify.sha1_cert = NULL;
+               return -1;
+       }
+       hpos += MD5_MAC_LEN;
 
        conn->verify.md5_cert = NULL;
        hlen = SHA1_MAC_LEN;
@@ -908,8 +905,7 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
        }
        conn->verify.sha1_cert = NULL;
 
-       if (alg == SIGN_ALG_RSA)
-               hlen += MD5_MAC_LEN;
+       hlen += MD5_MAC_LEN;
 
 #ifdef CONFIG_TLSV12
        }
@@ -917,90 +913,13 @@ static int tls_process_certificate_verify(struct tlsv1_server *conn, u8 ct,
 
        wpa_hexdump(MSG_MSGDUMP, "TLSv1: CertificateVerify hash", hash, hlen);
 
-       if (end - pos < 2) {
-               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
-                                  TLS_ALERT_DECODE_ERROR);
-               return -1;
-       }
-       slen = WPA_GET_BE16(pos);
-       pos += 2;
-       if (end - pos < slen) {
-               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
-                                  TLS_ALERT_DECODE_ERROR);
+       if (tls_verify_signature(conn->rl.tls_version, conn->client_rsa_key,
+                                hash, hlen, pos, end - pos, &alert) < 0) {
+               tlsv1_server_log(conn, "Invalid Signature in CertificateVerify");
+               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
                return -1;
        }
 
-       wpa_hexdump(MSG_MSGDUMP, "TLSv1: Signature", pos, end - pos);
-       if (conn->client_rsa_key == NULL) {
-               wpa_printf(MSG_DEBUG, "TLSv1: No client public key to verify "
-                          "signature");
-               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
-                                  TLS_ALERT_INTERNAL_ERROR);
-               return -1;
-       }
-
-       buflen = end - pos;
-       buf = os_malloc(end - pos);
-       if (crypto_public_key_decrypt_pkcs1(conn->client_rsa_key,
-                                           pos, end - pos, buf, &buflen) < 0)
-       {
-               wpa_printf(MSG_DEBUG, "TLSv1: Failed to decrypt signature");
-               os_free(buf);
-               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
-                                  TLS_ALERT_DECRYPT_ERROR);
-               return -1;
-       }
-
-       wpa_hexdump_key(MSG_MSGDUMP, "TLSv1: Decrypted Signature",
-                       buf, buflen);
-
-#ifdef CONFIG_TLSV12
-       if (conn->rl.tls_version >= TLS_VERSION_1_2) {
-               /*
-                * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5
-                *
-                * DigestInfo ::= SEQUENCE {
-                *   digestAlgorithm DigestAlgorithm,
-                *   digest OCTET STRING
-                * }
-                *
-                * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11}
-                *
-                * DER encoded DigestInfo for SHA256 per RFC 3447:
-                * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00 04 20 ||
-                * H
-                */
-               if (buflen >= 19 + 32 &&
-                   os_memcmp(buf, "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01"
-                             "\x65\x03\x04\x02\x01\x05\x00\x04\x20", 19) == 0)
-               {
-                       wpa_printf(MSG_DEBUG, "TLSv1.2: DigestAlgorithn = "
-                                  "SHA-256");
-                       os_memmove(buf, buf + 19, buflen - 19);
-                       buflen -= 19;
-               } else {
-                       wpa_printf(MSG_DEBUG, "TLSv1.2: Unrecognized "
-                                  "DigestInfo");
-                       os_free(buf);
-                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
-                                          TLS_ALERT_DECRYPT_ERROR);
-                       return -1;
-               }
-       }
-#endif /* CONFIG_TLSV12 */
-
-       if (buflen != hlen || os_memcmp(buf, hash, buflen) != 0) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Invalid Signature in "
-                          "CertificateVerify - did not match with calculated "
-                          "hash");
-               os_free(buf);
-               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
-                                  TLS_ALERT_DECRYPT_ERROR);
-               return -1;
-       }
-
-       os_free(buf);
-
        *in_len = end - in_data;
 
        conn->state = CHANGE_CIPHER_SPEC;
@@ -1017,8 +936,8 @@ static int tls_process_change_cipher_spec(struct tlsv1_server *conn,
        size_t left;
 
        if (ct != TLS_CONTENT_TYPE_CHANGE_CIPHER_SPEC) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
-                          "received content type 0x%x", ct);
+               tlsv1_server_log(conn, "Expected ChangeCipherSpec; received content type 0x%x",
+                                ct);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_UNEXPECTED_MESSAGE);
                return -1;
@@ -1028,21 +947,21 @@ static int tls_process_change_cipher_spec(struct tlsv1_server *conn,
        left = *in_len;
 
        if (left < 1) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Too short ChangeCipherSpec");
+               tlsv1_server_log(conn, "Too short ChangeCipherSpec");
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_DECODE_ERROR);
                return -1;
        }
 
        if (*pos != TLS_CHANGE_CIPHER_SPEC) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Expected ChangeCipherSpec; "
-                          "received data 0x%x", *pos);
+               tlsv1_server_log(conn, "Expected ChangeCipherSpec; received data 0x%x",
+                                *pos);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_UNEXPECTED_MESSAGE);
                return -1;
        }
 
-       wpa_printf(MSG_DEBUG, "TLSv1: Received ChangeCipherSpec");
+       tlsv1_server_log(conn, "Received ChangeCipherSpec");
        if (tlsv1_record_change_read_cipher(&conn->rl) < 0) {
                wpa_printf(MSG_DEBUG, "TLSv1: Failed to change read cipher "
                           "for record layer");
@@ -1067,9 +986,48 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct,
        u8 verify_data[TLS_VERIFY_DATA_LEN];
        u8 hash[MD5_MAC_LEN + SHA1_MAC_LEN];
 
+#ifdef CONFIG_TESTING_OPTIONS
+       if ((conn->test_flags &
+            (TLS_BREAK_SRV_KEY_X_HASH | TLS_BREAK_SRV_KEY_X_SIGNATURE)) &&
+           !conn->test_failure_reported) {
+               tlsv1_server_log(conn, "TEST-FAILURE: Client Finished received after invalid ServerKeyExchange");
+               conn->test_failure_reported = 1;
+       }
+
+       if ((conn->test_flags & TLS_DHE_PRIME_15) &&
+           !conn->test_failure_reported) {
+               tlsv1_server_log(conn, "TEST-FAILURE: Client Finished received after bogus DHE \"prime\" 15");
+               conn->test_failure_reported = 1;
+       }
+
+       if ((conn->test_flags & TLS_DHE_PRIME_58B) &&
+           !conn->test_failure_reported) {
+               tlsv1_server_log(conn, "TEST-FAILURE: Client Finished received after short 58-bit DHE prime in long container");
+               conn->test_failure_reported = 1;
+       }
+
+       if ((conn->test_flags & TLS_DHE_PRIME_511B) &&
+           !conn->test_failure_reported) {
+               tlsv1_server_log(conn, "TEST-WARNING: Client Finished received after short 511-bit DHE prime (insecure)");
+               conn->test_failure_reported = 1;
+       }
+
+       if ((conn->test_flags & TLS_DHE_PRIME_767B) &&
+           !conn->test_failure_reported) {
+               tlsv1_server_log(conn, "TEST-NOTE: Client Finished received after 767-bit DHE prime (relatively insecure)");
+               conn->test_failure_reported = 1;
+       }
+
+       if ((conn->test_flags & TLS_DHE_NON_PRIME) &&
+           !conn->test_failure_reported) {
+               tlsv1_server_log(conn, "TEST-NOTE: Client Finished received after non-prime claimed as DHE prime");
+               conn->test_failure_reported = 1;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
        if (ct != TLS_CONTENT_TYPE_HANDSHAKE) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Expected Finished; "
-                          "received content type 0x%x", ct);
+               tlsv1_server_log(conn, "Expected Finished; received content type 0x%x",
+                                ct);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_UNEXPECTED_MESSAGE);
                return -1;
@@ -1079,9 +1037,8 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct,
        left = *in_len;
 
        if (left < 4) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Too short record (left=%lu) for "
-                          "Finished",
-                          (unsigned long) left);
+               tlsv1_server_log(conn, "Too short record (left=%lu) forFinished",
+                                (unsigned long) left);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_DECODE_ERROR);
                return -1;
@@ -1101,18 +1058,16 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct,
        left -= 4;
 
        if (len > left) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Too short buffer for Finished "
-                          "(len=%lu > left=%lu)",
-                          (unsigned long) len, (unsigned long) left);
+               tlsv1_server_log(conn, "Too short buffer for Finished (len=%lu > left=%lu)",
+                                (unsigned long) len, (unsigned long) left);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_DECODE_ERROR);
                return -1;
        }
        end = pos + len;
        if (len != TLS_VERIFY_DATA_LEN) {
-               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected verify_data length "
-                          "in Finished: %lu (expected %d)",
-                          (unsigned long) len, TLS_VERIFY_DATA_LEN);
+               tlsv1_server_log(conn, "Unexpected verify_data length in Finished: %lu (expected %d)",
+                                (unsigned long) len, TLS_VERIFY_DATA_LEN);
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_DECODE_ERROR);
                return -1;
@@ -1174,19 +1129,18 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct,
        wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (client)",
                        verify_data, TLS_VERIFY_DATA_LEN);
 
-       if (os_memcmp(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
-               wpa_printf(MSG_INFO, "TLSv1: Mismatch in verify_data");
+       if (os_memcmp_const(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
+               tlsv1_server_log(conn, "Mismatch in verify_data");
                return -1;
        }
 
-       wpa_printf(MSG_DEBUG, "TLSv1: Received Finished");
+       tlsv1_server_log(conn, "Received Finished");
 
        *in_len = end - in_data;
 
        if (conn->use_session_ticket) {
                /* Abbreviated handshake using session ticket; RFC 4507 */
-               wpa_printf(MSG_DEBUG, "TLSv1: Abbreviated handshake completed "
-                          "successfully");
+               tlsv1_server_log(conn, "Abbreviated handshake completed successfully");
                conn->state = ESTABLISHED;
        } else {
                /* Full handshake */
@@ -1202,13 +1156,12 @@ int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct,
 {
        if (ct == TLS_CONTENT_TYPE_ALERT) {
                if (*len < 2) {
-                       wpa_printf(MSG_DEBUG, "TLSv1: Alert underflow");
+                       tlsv1_server_log(conn, "Alert underflow");
                        tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                           TLS_ALERT_DECODE_ERROR);
                        return -1;
                }
-               wpa_printf(MSG_DEBUG, "TLSv1: Received alert %d:%d",
-                          buf[0], buf[1]);
+               tlsv1_server_log(conn, "Received alert %d:%d", buf[0], buf[1]);
                *len = 2;
                conn->state = FAILED;
                return -1;
@@ -1240,9 +1193,8 @@ int tlsv1_server_process_handshake(struct tlsv1_server *conn, u8 ct,
                        return -1;
                break;
        default:
-               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d "
-                          "while processing received message",
-                          conn->state);
+               tlsv1_server_log(conn, "Unexpected state %d while processing received message",
+                                conn->state);
                return -1;
        }
 
index 6d8e55e..15e6692 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * TLSv1 server - write handshake message
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -48,7 +48,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn,
 
        pos = *msgpos;
 
-       wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHello");
+       tlsv1_server_log(conn, "Send ServerHello");
        rhdr = pos;
        pos += TLS_RECORD_HEADER_LEN;
 
@@ -104,8 +104,7 @@ static int tls_write_server_hello(struct tlsv1_server *conn,
                        conn->client_random, conn->server_random,
                        conn->master_secret);
                if (res < 0) {
-                       wpa_printf(MSG_DEBUG, "TLSv1: SessionTicket callback "
-                                  "indicated failure");
+                       tlsv1_server_log(conn, "SessionTicket callback indicated failure");
                        tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                           TLS_ALERT_HANDSHAKE_FAILURE);
                        return -1;
@@ -170,7 +169,7 @@ static int tls_write_server_certificate(struct tlsv1_server *conn,
 
        pos = *msgpos;
 
-       wpa_printf(MSG_DEBUG, "TLSv1: Send Certificate");
+       tlsv1_server_log(conn, "Send Certificate");
        rhdr = pos;
        pos += TLS_RECORD_HEADER_LEN;
 
@@ -245,10 +244,12 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn,
 {
        tls_key_exchange keyx;
        const struct tls_cipher_suite *suite;
-       u8 *pos, *rhdr, *hs_start, *hs_length;
+       u8 *pos, *rhdr, *hs_start, *hs_length, *server_params;
        size_t rlen;
        u8 *dh_ys;
        size_t dh_ys_len;
+       const u8 *dh_p;
+       size_t dh_p_len;
 
        suite = tls_get_cipher_suite(conn->rl.cipher_suite);
        if (suite == NULL)
@@ -261,8 +262,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn,
                return 0;
        }
 
-       if (keyx != TLS_KEY_X_DH_anon) {
-               /* TODO? */
+       if (keyx != TLS_KEY_X_DH_anon && keyx != TLS_KEY_X_DHE_RSA) {
                wpa_printf(MSG_DEBUG, "TLSv1: ServerKeyExchange not yet "
                           "supported with key exchange type %d", keyx);
                return -1;
@@ -275,8 +275,10 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn,
                return -1;
        }
 
+       tlsv1_server_get_dh_p(conn, &dh_p, &dh_p_len);
+
        os_free(conn->dh_secret);
-       conn->dh_secret_len = conn->cred->dh_p_len;
+       conn->dh_secret_len = dh_p_len;
        conn->dh_secret = os_malloc(conn->dh_secret_len);
        if (conn->dh_secret == NULL) {
                wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate "
@@ -295,8 +297,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn,
                return -1;
        }
 
-       if (os_memcmp(conn->dh_secret, conn->cred->dh_p, conn->dh_secret_len) >
-           0)
+       if (os_memcmp(conn->dh_secret, dh_p, conn->dh_secret_len) > 0)
                conn->dh_secret[0] = 0; /* make sure secret < p */
 
        pos = conn->dh_secret;
@@ -311,7 +312,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn,
                        conn->dh_secret, conn->dh_secret_len);
 
        /* Ys = g^secret mod p */
-       dh_ys_len = conn->cred->dh_p_len;
+       dh_ys_len = dh_p_len;
        dh_ys = os_malloc(dh_ys_len);
        if (dh_ys == NULL) {
                wpa_printf(MSG_DEBUG, "TLSv1: Failed to allocate memory for "
@@ -322,8 +323,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn,
        }
        if (crypto_mod_exp(conn->cred->dh_g, conn->cred->dh_g_len,
                           conn->dh_secret, conn->dh_secret_len,
-                          conn->cred->dh_p, conn->cred->dh_p_len,
-                          dh_ys, &dh_ys_len)) {
+                          dh_p, dh_p_len, dh_ys, &dh_ys_len)) {
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
                                   TLS_ALERT_INTERNAL_ERROR);
                os_free(dh_ys);
@@ -354,7 +354,7 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn,
 
        pos = *msgpos;
 
-       wpa_printf(MSG_DEBUG, "TLSv1: Send ServerKeyExchange");
+       tlsv1_server_log(conn, "Send ServerKeyExchange");
        rhdr = pos;
        pos += TLS_RECORD_HEADER_LEN;
 
@@ -369,8 +369,9 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn,
        pos += 3;
 
        /* body - ServerDHParams */
+       server_params = pos;
        /* dh_p */
-       if (pos + 2 + conn->cred->dh_p_len > end) {
+       if (pos + 2 + dh_p_len > end) {
                wpa_printf(MSG_DEBUG, "TLSv1: Not enough buffer space for "
                           "dh_p");
                tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
@@ -378,10 +379,10 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn,
                os_free(dh_ys);
                return -1;
        }
-       WPA_PUT_BE16(pos, conn->cred->dh_p_len);
+       WPA_PUT_BE16(pos, dh_p_len);
        pos += 2;
-       os_memcpy(pos, conn->cred->dh_p, conn->cred->dh_p_len);
-       pos += conn->cred->dh_p_len;
+       os_memcpy(pos, dh_p, dh_p_len);
+       pos += dh_p_len;
 
        /* dh_g */
        if (pos + 2 + conn->cred->dh_g_len > end) {
@@ -412,6 +413,138 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn,
        pos += dh_ys_len;
        os_free(dh_ys);
 
+       /*
+        * select (SignatureAlgorithm)
+        * {   case anonymous: struct { };
+        *     case rsa:
+        *         digitally-signed struct {
+        *             opaque md5_hash[16];
+        *             opaque sha_hash[20];
+        *         };
+        *     case dsa:
+        *         digitally-signed struct {
+        *             opaque sha_hash[20];
+        *         };
+        * } Signature;
+        *
+        * md5_hash
+        *     MD5(ClientHello.random + ServerHello.random + ServerParams);
+        *
+        * sha_hash
+        *     SHA(ClientHello.random + ServerHello.random + ServerParams);
+        */
+
+       if (keyx == TLS_KEY_X_DHE_RSA) {
+               u8 hash[100];
+               u8 *signed_start;
+               size_t clen;
+               int hlen;
+
+               if (conn->rl.tls_version >= TLS_VERSION_1_2) {
+#ifdef CONFIG_TLSV12
+                       hlen = tlsv12_key_x_server_params_hash(
+                               conn->rl.tls_version, conn->client_random,
+                               conn->server_random, server_params,
+                               pos - server_params, hash + 19);
+
+                       /*
+                        * RFC 5246, 4.7:
+                        * TLS v1.2 adds explicit indication of the used
+                        * signature and hash algorithms.
+                        *
+                        * struct {
+                        *   HashAlgorithm hash;
+                        *   SignatureAlgorithm signature;
+                        * } SignatureAndHashAlgorithm;
+                        */
+                       if (hlen < 0 || pos + 2 > end) {
+                               tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                                  TLS_ALERT_INTERNAL_ERROR);
+                               return -1;
+                       }
+                       *pos++ = TLS_HASH_ALG_SHA256;
+                       *pos++ = TLS_SIGN_ALG_RSA;
+
+                       /*
+                        * RFC 3447, A.2.4 RSASSA-PKCS1-v1_5
+                        *
+                        * DigestInfo ::= SEQUENCE {
+                        *   digestAlgorithm DigestAlgorithm,
+                        *   digest OCTET STRING
+                        * }
+                        *
+                        * SHA-256 OID: sha256WithRSAEncryption ::= {pkcs-1 11}
+                        *
+                        * DER encoded DigestInfo for SHA256 per RFC 3447:
+                        * 30 31 30 0d 06 09 60 86 48 01 65 03 04 02 01 05 00
+                        * 04 20 || H
+                        */
+                       hlen += 19;
+                       os_memcpy(hash,
+                                 "\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65"
+                                 "\x03\x04\x02\x01\x05\x00\x04\x20", 19);
+
+#else /* CONFIG_TLSV12 */
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_INTERNAL_ERROR);
+                       return -1;
+#endif /* CONFIG_TLSV12 */
+               } else {
+                       hlen = tls_key_x_server_params_hash(
+                               conn->rl.tls_version, conn->client_random,
+                               conn->server_random, server_params,
+                               pos - server_params, hash);
+               }
+
+               if (hlen < 0) {
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_INTERNAL_ERROR);
+                       return -1;
+               }
+
+               wpa_hexdump(MSG_MSGDUMP, "TLS: ServerKeyExchange signed_params hash",
+                           hash, hlen);
+#ifdef CONFIG_TESTING_OPTIONS
+               if (conn->test_flags & TLS_BREAK_SRV_KEY_X_HASH) {
+                       tlsv1_server_log(conn, "TESTING: Break ServerKeyExchange signed params hash");
+                       hash[hlen - 1] ^= 0x80;
+               }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+               /*
+                * RFC 2246, 4.7:
+                * In digital signing, one-way hash functions are used as input
+                * for a signing algorithm. A digitally-signed element is
+                * encoded as an opaque vector <0..2^16-1>, where the length is
+                * specified by the signing algorithm and key.
+                *
+                * In RSA signing, a 36-byte structure of two hashes (one SHA
+                * and one MD5) is signed (encrypted with the private key). It
+                * is encoded with PKCS #1 block type 0 or type 1 as described
+                * in [PKCS1].
+                */
+               signed_start = pos; /* length to be filled */
+               pos += 2;
+               clen = end - pos;
+               if (conn->cred == NULL ||
+                   crypto_private_key_sign_pkcs1(conn->cred->key, hash, hlen,
+                                                 pos, &clen) < 0) {
+                       wpa_printf(MSG_DEBUG, "TLSv1: Failed to sign hash (PKCS #1)");
+                       tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+                                          TLS_ALERT_INTERNAL_ERROR);
+                       return -1;
+               }
+               WPA_PUT_BE16(signed_start, clen);
+#ifdef CONFIG_TESTING_OPTIONS
+               if (conn->test_flags & TLS_BREAK_SRV_KEY_X_SIGNATURE) {
+                       tlsv1_server_log(conn, "TESTING: Break ServerKeyExchange signed params signature");
+                       pos[clen - 1] ^= 0x80;
+               }
+#endif /* CONFIG_TESTING_OPTIONS */
+
+               pos += clen;
+       }
+
        WPA_PUT_BE24(hs_length, pos - hs_length - 3);
 
        if (tlsv1_record_send(&conn->rl, TLS_CONTENT_TYPE_HANDSHAKE,
@@ -445,7 +578,7 @@ static int tls_write_server_certificate_request(struct tlsv1_server *conn,
 
        pos = *msgpos;
 
-       wpa_printf(MSG_DEBUG, "TLSv1: Send CertificateRequest");
+       tlsv1_server_log(conn, "Send CertificateRequest");
        rhdr = pos;
        pos += TLS_RECORD_HEADER_LEN;
 
@@ -505,7 +638,7 @@ static int tls_write_server_hello_done(struct tlsv1_server *conn,
        size_t rlen;
        u8 payload[4];
 
-       wpa_printf(MSG_DEBUG, "TLSv1: Send ServerHelloDone");
+       tlsv1_server_log(conn, "Send ServerHelloDone");
 
        /* opaque fragment[TLSPlaintext.length] */
 
@@ -541,7 +674,7 @@ static int tls_write_server_change_cipher_spec(struct tlsv1_server *conn,
        size_t rlen;
        u8 payload[1];
 
-       wpa_printf(MSG_DEBUG, "TLSv1: Send ChangeCipherSpec");
+       tlsv1_server_log(conn, "Send ChangeCipherSpec");
 
        payload[0] = TLS_CHANGE_CIPHER_SPEC;
 
@@ -578,7 +711,7 @@ static int tls_write_server_finished(struct tlsv1_server *conn,
 
        pos = *msgpos;
 
-       wpa_printf(MSG_DEBUG, "TLSv1: Send Finished");
+       tlsv1_server_log(conn, "Send Finished");
 
        /* Encrypted Handshake Message: Finished */
 
@@ -635,6 +768,12 @@ static int tls_write_server_finished(struct tlsv1_server *conn,
        }
        wpa_hexdump_key(MSG_DEBUG, "TLSv1: verify_data (server)",
                        verify_data + 1 + 3, TLS_VERIFY_DATA_LEN);
+#ifdef CONFIG_TESTING_OPTIONS
+       if (conn->test_flags & TLS_BREAK_VERIFY_DATA) {
+               tlsv1_server_log(conn, "TESTING: Break verify_data (server)");
+               verify_data[1 + 3 + 1] ^= 0x80;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
 
        /* Handshake */
        pos = hs_start = verify_data;
@@ -736,7 +875,7 @@ static u8 * tls_send_change_cipher_spec(struct tlsv1_server *conn,
 
        *out_len = pos - msg;
 
-       wpa_printf(MSG_DEBUG, "TLSv1: Handshake completed successfully");
+       tlsv1_server_log(conn, "Handshake completed successfully");
        conn->state = ESTABLISHED;
 
        return msg;
@@ -755,8 +894,8 @@ u8 * tlsv1_server_handshake_write(struct tlsv1_server *conn, size_t *out_len)
                        /* Abbreviated handshake was already completed. */
                        return NULL;
                }
-               wpa_printf(MSG_DEBUG, "TLSv1: Unexpected state %d while "
-                          "generating reply", conn->state);
+               tlsv1_server_log(conn, "Unexpected state %d while generating reply",
+                                conn->state);
                return NULL;
        }
 }
@@ -767,7 +906,7 @@ u8 * tlsv1_server_send_alert(struct tlsv1_server *conn, u8 level,
 {
        u8 *alert, *pos, *length;
 
-       wpa_printf(MSG_DEBUG, "TLSv1: Send Alert(%d:%d)", level, description);
+       tlsv1_server_log(conn, "Send Alert(%d:%d)", level, description);
        *out_len = 0;
 
        alert = os_malloc(10);
index 06540bf..742af32 100644 (file)
@@ -512,7 +512,7 @@ void x509_name_string(struct x509_name *name, char *buf, size_t len)
                ret = os_snprintf(pos, end - pos, "%s=%s, ",
                                  x509_name_attr_str(name->attr[i].type),
                                  name->attr[i].value);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        goto done;
                pos += ret;
        }
@@ -527,7 +527,7 @@ void x509_name_string(struct x509_name *name, char *buf, size_t len)
        if (name->email) {
                ret = os_snprintf(pos, end - pos, "/emailAddress=%s",
                                  name->email);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        goto done;
                pos += ret;
        }
@@ -1348,7 +1348,8 @@ static int x509_parse_tbs_certificate(const u8 *buf, size_t len,
                wpa_printf(MSG_DEBUG, "X509: issuerUniqueID");
                /* TODO: parse UniqueIdentifier ::= BIT STRING */
 
-               if (hdr.payload + hdr.length == end)
+               pos = hdr.payload + hdr.length;
+               if (pos == end)
                        return 0;
 
                if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
@@ -1366,7 +1367,8 @@ static int x509_parse_tbs_certificate(const u8 *buf, size_t len,
                wpa_printf(MSG_DEBUG, "X509: subjectUniqueID");
                /* TODO: parse UniqueIdentifier ::= BIT STRING */
 
-               if (hdr.payload + hdr.length == end)
+               pos = hdr.payload + hdr.length;
+               if (pos == end)
                        return 0;
 
                if (asn1_get_next(pos, end - pos, &hdr) < 0 ||
@@ -1774,13 +1776,22 @@ skip_digest_oid:
        }
 
        if (hdr.length != hash_len ||
-           os_memcmp(hdr.payload, hash, hdr.length) != 0) {
+           os_memcmp_const(hdr.payload, hash, hdr.length) != 0) {
                wpa_printf(MSG_INFO, "X509: Certificate Digest does not match "
                           "with calculated tbsCertificate hash");
                os_free(data);
                return -1;
        }
 
+       if (hdr.payload + hdr.length < data + data_len) {
+               wpa_hexdump(MSG_INFO,
+                           "X509: Extra data after certificate signature hash",
+                           hdr.payload + hdr.length,
+                           data + data_len - hdr.payload - hdr.length);
+               os_free(data);
+               return -1;
+       }
+
        os_free(data);
 
        wpa_printf(MSG_DEBUG, "X509: Certificate Digest matches with "
index 940b4d8..8aad813 100644 (file)
@@ -1,7 +1,7 @@
 all: libutils.a
 
 clean:
-       rm -f *~ *.o *.d libutils.a
+       rm -f *~ *.o *.d *.gcno *.gcda *.gcov libutils.a
 
 install:
        @echo Nothing to be made.
@@ -11,6 +11,7 @@ include ../lib.rules
 
 #CFLAGS += -DWPA_TRACE
 CFLAGS += -DCONFIG_IPV6
+CFLAGS += -DCONFIG_DEBUG_FILE
 
 LIB_OBJS= \
        base64.o \
index af1307f..d44f290 100644 (file)
@@ -48,9 +48,11 @@ unsigned char * base64_encode(const unsigned char *src, size_t len,
        pos = out;
        line_len = 0;
        while (end - in >= 3) {
-               *pos++ = base64_table[in[0] >> 2];
-               *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
-               *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
+               *pos++ = base64_table[(in[0] >> 2) & 0x3f];
+               *pos++ = base64_table[(((in[0] & 0x03) << 4) |
+                                      (in[1] >> 4)) & 0x3f];
+               *pos++ = base64_table[(((in[1] & 0x0f) << 2) |
+                                      (in[2] >> 6)) & 0x3f];
                *pos++ = base64_table[in[2] & 0x3f];
                in += 3;
                line_len += 4;
@@ -61,14 +63,14 @@ unsigned char * base64_encode(const unsigned char *src, size_t len,
        }
 
        if (end - in) {
-               *pos++ = base64_table[in[0] >> 2];
+               *pos++ = base64_table[(in[0] >> 2) & 0x3f];
                if (end - in == 1) {
-                       *pos++ = base64_table[(in[0] & 0x03) << 4];
+                       *pos++ = base64_table[((in[0] & 0x03) << 4) & 0x3f];
                        *pos++ = '=';
                } else {
-                       *pos++ = base64_table[((in[0] & 0x03) << 4) |
-                                             (in[1] >> 4)];
-                       *pos++ = base64_table[(in[1] & 0x0f) << 2];
+                       *pos++ = base64_table[(((in[0] & 0x03) << 4) |
+                                              (in[1] >> 4)) & 0x3f];
+                       *pos++ = base64_table[((in[1] & 0x0f) << 2) & 0x3f];
                }
                *pos++ = '=';
                line_len += 4;
index f90e4be..8dcec39 100644 (file)
@@ -76,11 +76,11 @@ static int first_zero(u8 val)
 int bitfield_get_first_zero(struct bitfield *bf)
 {
        size_t i;
-       for (i = 0; i <= (bf->max_bits + 7) / 8; i++) {
+       for (i = 0; i < (bf->max_bits + 7) / 8; i++) {
                if (bf->bits[i] != 0xff)
                        break;
        }
-       if (i > (bf->max_bits + 7) / 8)
+       if (i == (bf->max_bits + 7) / 8)
                return -1;
        i = i * 8 + first_zero(bf->bits[i]);
        if (i >= bf->max_bits)
diff --git a/src/utils/browser-android.c b/src/utils/browser-android.c
new file mode 100644 (file)
index 0000000..9ce1a5c
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Hotspot 2.0 client - Web browser using Android browser
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "wps/http_server.h"
+#include "browser.h"
+
+
+struct browser_data {
+       int success;
+};
+
+
+static void browser_timeout(void *eloop_data, void *user_ctx)
+{
+       wpa_printf(MSG_INFO, "Timeout on waiting browser interaction to "
+                  "complete");
+       eloop_terminate();
+}
+
+
+static void http_req(void *ctx, struct http_request *req)
+{
+       struct browser_data *data = ctx;
+       struct wpabuf *resp;
+       const char *url;
+       int done = 0;
+
+       url = http_request_get_uri(req);
+       wpa_printf(MSG_INFO, "Browser response received: %s", url);
+
+       if (os_strcmp(url, "/") == 0) {
+               data->success = 1;
+               done = 1;
+       } else if (os_strncmp(url, "/osu/", 5) == 0) {
+               data->success = atoi(url + 5);
+               done = 1;
+       }
+
+       resp = wpabuf_alloc(1);
+       if (resp == NULL) {
+               http_request_deinit(req);
+               if (done)
+                       eloop_terminate();
+               return;
+       }
+
+       if (done) {
+               eloop_cancel_timeout(browser_timeout, NULL, NULL);
+               eloop_register_timeout(0, 500000, browser_timeout, &data, NULL);
+       }
+
+       http_request_send_and_deinit(req, resp);
+}
+
+
+int hs20_web_browser(const char *url)
+{
+       struct http_server *http;
+       struct in_addr addr;
+       struct browser_data data;
+       pid_t pid;
+
+       wpa_printf(MSG_INFO, "Launching Android browser to %s", url);
+
+       os_memset(&data, 0, sizeof(data));
+
+       if (eloop_init() < 0) {
+               wpa_printf(MSG_ERROR, "eloop_init failed");
+               return -1;
+       }
+       addr.s_addr = htonl((127 << 24) | 1);
+       http = http_server_init(&addr, 12345, http_req, &data);
+       if (http == NULL) {
+               wpa_printf(MSG_ERROR, "http_server_init failed");
+               eloop_destroy();
+               return -1;
+       }
+
+       pid = fork();
+       if (pid < 0) {
+               wpa_printf(MSG_ERROR, "fork: %s", strerror(errno));
+               http_server_deinit(http);
+               eloop_destroy();
+               return -1;
+       }
+
+       if (pid == 0) {
+               /* run the external command in the child process */
+               char *argv[9];
+
+               argv[0] = "browser-android";
+               argv[1] = "start";
+               argv[2] = "-a";
+               argv[3] = "android.intent.action.VIEW";
+               argv[4] = "-d";
+               argv[5] = (void *) url;
+               argv[6] = "-n";
+               argv[7] = "com.android.browser/.BrowserActivity";
+               argv[8] = NULL;
+
+               execv("/system/bin/am", argv);
+               wpa_printf(MSG_ERROR, "execv: %s", strerror(errno));
+               exit(0);
+               return -1;
+       }
+
+       eloop_register_timeout(30, 0, browser_timeout, &data, NULL);
+       eloop_run();
+       eloop_cancel_timeout(browser_timeout, &data, NULL);
+       http_server_deinit(http);
+       eloop_destroy();
+
+       wpa_printf(MSG_INFO, "Closing Android browser");
+       if (system("/system/bin/input keyevent KEYCODE_HOME") != 0) {
+               wpa_printf(MSG_INFO, "Failed to inject keyevent");
+       }
+
+       return data.success;
+}
diff --git a/src/utils/browser-system.c b/src/utils/browser-system.c
new file mode 100644 (file)
index 0000000..aed3970
--- /dev/null
@@ -0,0 +1,119 @@
+/*
+ * Hotspot 2.0 client - Web browser using system browser
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "wps/http_server.h"
+#include "browser.h"
+
+
+struct browser_data {
+       int success;
+};
+
+
+static void browser_timeout(void *eloop_data, void *user_ctx)
+{
+       wpa_printf(MSG_INFO, "Timeout on waiting browser interaction to "
+                  "complete");
+       eloop_terminate();
+}
+
+
+static void http_req(void *ctx, struct http_request *req)
+{
+       struct browser_data *data = ctx;
+       struct wpabuf *resp;
+       const char *url;
+       int done = 0;
+
+       url = http_request_get_uri(req);
+       wpa_printf(MSG_INFO, "Browser response received: %s", url);
+
+       if (os_strcmp(url, "/") == 0) {
+               data->success = 1;
+               done = 1;
+       } else if (os_strncmp(url, "/osu/", 5) == 0) {
+               data->success = atoi(url + 5);
+               done = 1;
+       }
+
+       resp = wpabuf_alloc(1);
+       if (resp == NULL) {
+               http_request_deinit(req);
+               if (done)
+                       eloop_terminate();
+               return;
+       }
+
+       if (done) {
+               eloop_cancel_timeout(browser_timeout, NULL, NULL);
+               eloop_register_timeout(0, 500000, browser_timeout, &data, NULL);
+       }
+
+       http_request_send_and_deinit(req, resp);
+}
+
+
+int hs20_web_browser(const char *url)
+{
+       struct http_server *http;
+       struct in_addr addr;
+       struct browser_data data;
+       pid_t pid;
+
+       wpa_printf(MSG_INFO, "Launching system browser to %s", url);
+
+       os_memset(&data, 0, sizeof(data));
+
+       if (eloop_init() < 0) {
+               wpa_printf(MSG_ERROR, "eloop_init failed");
+               return -1;
+       }
+       addr.s_addr = htonl((127 << 24) | 1);
+       http = http_server_init(&addr, 12345, http_req, &data);
+       if (http == NULL) {
+               wpa_printf(MSG_ERROR, "http_server_init failed");
+               eloop_destroy();
+               return -1;
+       }
+
+       pid = fork();
+       if (pid < 0) {
+               wpa_printf(MSG_ERROR, "fork: %s", strerror(errno));
+               http_server_deinit(http);
+               eloop_destroy();
+               return -1;
+       }
+
+       if (pid == 0) {
+               /* run the external command in the child process */
+               char *argv[3];
+
+               argv[0] = "browser-system";
+               argv[1] = (void *) url;
+               argv[2] = NULL;
+
+               execv("/usr/bin/x-www-browser", argv);
+               wpa_printf(MSG_ERROR, "execv: %s", strerror(errno));
+               exit(0);
+               return -1;
+       }
+
+       eloop_register_timeout(120, 0, browser_timeout, &data, NULL);
+       eloop_run();
+       eloop_cancel_timeout(browser_timeout, &data, NULL);
+       http_server_deinit(http);
+       eloop_destroy();
+
+       /* TODO: Close browser */
+
+       return data.success;
+}
diff --git a/src/utils/browser-wpadebug.c b/src/utils/browser-wpadebug.c
new file mode 100644 (file)
index 0000000..5fc40fa
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Hotspot 2.0 client - Web browser using wpadebug on Android
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "utils/eloop.h"
+#include "wps/http_server.h"
+#include "browser.h"
+
+
+struct browser_data {
+       int success;
+};
+
+
+static void browser_timeout(void *eloop_data, void *user_ctx)
+{
+       wpa_printf(MSG_INFO, "Timeout on waiting browser interaction to "
+                  "complete");
+       eloop_terminate();
+}
+
+
+static void http_req(void *ctx, struct http_request *req)
+{
+       struct browser_data *data = ctx;
+       struct wpabuf *resp;
+       const char *url;
+       int done = 0;
+
+       url = http_request_get_uri(req);
+       wpa_printf(MSG_INFO, "Browser response received: %s", url);
+
+       if (os_strcmp(url, "/") == 0) {
+               data->success = 1;
+               done = 1;
+       } else if (os_strncmp(url, "/osu/", 5) == 0) {
+               data->success = atoi(url + 5);
+               done = 1;
+       }
+
+       resp = wpabuf_alloc(100);
+       if (resp == NULL) {
+               http_request_deinit(req);
+               if (done)
+                       eloop_terminate();
+               return;
+       }
+       wpabuf_put_str(resp, "User input completed");
+
+       if (done) {
+               eloop_cancel_timeout(browser_timeout, NULL, NULL);
+               eloop_register_timeout(0, 500000, browser_timeout, &data, NULL);
+       }
+
+       http_request_send_and_deinit(req, resp);
+}
+
+
+int hs20_web_browser(const char *url)
+{
+       struct http_server *http;
+       struct in_addr addr;
+       struct browser_data data;
+       pid_t pid;
+
+       wpa_printf(MSG_INFO, "Launching wpadebug browser to %s", url);
+
+       os_memset(&data, 0, sizeof(data));
+
+       if (eloop_init() < 0) {
+               wpa_printf(MSG_ERROR, "eloop_init failed");
+               return -1;
+       }
+       addr.s_addr = htonl((127 << 24) | 1);
+       http = http_server_init(&addr, 12345, http_req, &data);
+       if (http == NULL) {
+               wpa_printf(MSG_ERROR, "http_server_init failed");
+               eloop_destroy();
+               return -1;
+       }
+
+       pid = fork();
+       if (pid < 0) {
+               wpa_printf(MSG_ERROR, "fork: %s", strerror(errno));
+               http_server_deinit(http);
+               eloop_destroy();
+               return -1;
+       }
+
+       if (pid == 0) {
+               /* run the external command in the child process */
+               char *argv[12];
+
+               argv[0] = "browser-wpadebug";
+               argv[1] = "start";
+               argv[2] = "-a";
+               argv[3] = "android.action.MAIN";
+               argv[4] = "-c";
+               argv[5] = "android.intent.category.LAUNCHER";
+               argv[6] = "-n";
+               argv[7] = "w1.fi.wpadebug/.WpaWebViewActivity";
+               argv[8] = "-e";
+               argv[9] = "w1.fi.wpadebug.URL";
+               argv[10] = (void *) url;
+               argv[11] = NULL;
+
+               execv("/system/bin/am", argv);
+               wpa_printf(MSG_ERROR, "execv: %s", strerror(errno));
+               exit(0);
+               return -1;
+       }
+
+       eloop_register_timeout(300, 0, browser_timeout, &data, NULL);
+       eloop_run();
+       eloop_cancel_timeout(browser_timeout, &data, NULL);
+       http_server_deinit(http);
+       eloop_destroy();
+
+       wpa_printf(MSG_INFO, "Closing Android browser");
+       if (os_exec("/system/bin/am",
+                   "start -a android.action.MAIN "
+                   "-c android.intent.category.LAUNCHER "
+                   "-n w1.fi.wpadebug/.WpaWebViewActivity "
+                   "-e w1.fi.wpadebug.URL FINISH", 1) != 0) {
+               wpa_printf(MSG_INFO, "Failed to close wpadebug browser");
+       }
+
+       return data.success;
+}
diff --git a/src/utils/browser.c b/src/utils/browser.c
new file mode 100644 (file)
index 0000000..9cf6152
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Hotspot 2.0 client - Web browser using WebKit
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <webkit/webkit.h>
+
+#include "common.h"
+#include "browser.h"
+
+
+struct browser_context {
+       GtkWidget *win;
+       int success;
+       int progress;
+       char *hover_link;
+       char *title;
+};
+
+static void win_cb_destroy(GtkWidget *win, struct browser_context *ctx)
+{
+       wpa_printf(MSG_DEBUG, "BROWSER:%s", __func__);
+       gtk_main_quit();
+}
+
+
+static void browser_update_title(struct browser_context *ctx)
+{
+       char buf[100];
+
+       if (ctx->hover_link) {
+               gtk_window_set_title(GTK_WINDOW(ctx->win), ctx->hover_link);
+               return;
+       }
+
+       if (ctx->progress == 100) {
+               gtk_window_set_title(GTK_WINDOW(ctx->win),
+                                    ctx->title ? ctx->title :
+                                    "Hotspot 2.0 client");
+               return;
+       }
+
+       snprintf(buf, sizeof(buf), "[%d%%] %s", ctx->progress,
+                ctx->title ? ctx->title : "Hotspot 2.0 client");
+       gtk_window_set_title(GTK_WINDOW(ctx->win), buf);
+}
+
+
+static void view_cb_notify_progress(WebKitWebView *view, GParamSpec *pspec,
+                                   struct browser_context *ctx)
+{
+       ctx->progress = 100 * webkit_web_view_get_progress(view);
+       wpa_printf(MSG_DEBUG, "BROWSER:%s progress=%d", __func__,
+                  ctx->progress);
+       browser_update_title(ctx);
+}
+
+
+static void view_cb_notify_load_status(WebKitWebView *view, GParamSpec *pspec,
+                                      struct browser_context *ctx)
+{
+       int status = webkit_web_view_get_load_status(view);
+       wpa_printf(MSG_DEBUG, "BROWSER:%s load-status=%d uri=%s",
+                  __func__, status, webkit_web_view_get_uri(view));
+}
+
+
+static void view_cb_resource_request_starting(WebKitWebView *view,
+                                             WebKitWebFrame *frame,
+                                             WebKitWebResource *res,
+                                             WebKitNetworkRequest *req,
+                                             WebKitNetworkResponse *resp,
+                                             struct browser_context *ctx)
+{
+       const gchar *uri = webkit_network_request_get_uri(req);
+       wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri);
+       if (g_str_has_suffix(uri, "/favicon.ico"))
+               webkit_network_request_set_uri(req, "about:blank");
+       if (g_str_has_prefix(uri, "osu://")) {
+               ctx->success = atoi(uri + 6);
+               gtk_main_quit();
+       }
+       if (g_str_has_prefix(uri, "http://localhost:12345")) {
+               /*
+                * This is used as a special trigger to indicate that the
+                * user exchange has been completed.
+                */
+               ctx->success = 1;
+               gtk_main_quit();
+       }
+}
+
+
+static gboolean view_cb_mime_type_policy_decision(
+       WebKitWebView *view, WebKitWebFrame *frame, WebKitNetworkRequest *req,
+       gchar *mime, WebKitWebPolicyDecision *policy,
+       struct browser_context *ctx)
+{
+       wpa_printf(MSG_DEBUG, "BROWSER:%s mime=%s", __func__, mime);
+
+       if (!webkit_web_view_can_show_mime_type(view, mime)) {
+               webkit_web_policy_decision_download(policy);
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+static gboolean view_cb_download_requested(WebKitWebView *view,
+                                          WebKitDownload *dl,
+                                          struct browser_context *ctx)
+{
+       const gchar *uri;
+       uri = webkit_download_get_uri(dl);
+       wpa_printf(MSG_DEBUG, "BROWSER:%s uri=%s", __func__, uri);
+       return FALSE;
+}
+
+
+static void view_cb_hovering_over_link(WebKitWebView *view, gchar *title,
+                                      gchar *uri, struct browser_context *ctx)
+{
+       wpa_printf(MSG_DEBUG, "BROWSER:%s title=%s uri=%s", __func__, title,
+                  uri);
+       os_free(ctx->hover_link);
+       if (uri)
+               ctx->hover_link = os_strdup(uri);
+       else
+               ctx->hover_link = NULL;
+
+       browser_update_title(ctx);
+}
+
+
+static void view_cb_title_changed(WebKitWebView *view, WebKitWebFrame *frame,
+                                 const char *title,
+                                 struct browser_context *ctx)
+{
+       wpa_printf(MSG_DEBUG, "BROWSER:%s title=%s", __func__, title);
+       os_free(ctx->title);
+       ctx->title = os_strdup(title);
+       browser_update_title(ctx);
+}
+
+
+int hs20_web_browser(const char *url)
+{
+       GtkWidget *scroll;
+       SoupSession *s;
+       WebKitWebView *view;
+       WebKitWebSettings *settings;
+       struct browser_context ctx;
+
+       memset(&ctx, 0, sizeof(ctx));
+       if (!gtk_init_check(NULL, NULL))
+               return -1;
+
+       s = webkit_get_default_session();
+       g_object_set(G_OBJECT(s), "ssl-ca-file",
+                    "/etc/ssl/certs/ca-certificates.crt", NULL);
+       g_object_set(G_OBJECT(s), "ssl-strict", FALSE, NULL);
+
+       ctx.win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
+       gtk_window_set_wmclass(GTK_WINDOW(ctx.win), "Hotspot 2.0 client",
+                              "Hotspot 2.0 client");
+       gtk_window_set_default_size(GTK_WINDOW(ctx.win), 800, 600);
+
+       scroll = gtk_scrolled_window_new(NULL, NULL);
+       gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scroll),
+                                      GTK_POLICY_NEVER, GTK_POLICY_NEVER);
+
+       g_signal_connect(G_OBJECT(ctx.win), "destroy",
+                        G_CALLBACK(win_cb_destroy), &ctx);
+
+       view = WEBKIT_WEB_VIEW(webkit_web_view_new());
+       g_signal_connect(G_OBJECT(view), "notify::progress",
+                        G_CALLBACK(view_cb_notify_progress), &ctx);
+       g_signal_connect(G_OBJECT(view), "notify::load-status",
+                        G_CALLBACK(view_cb_notify_load_status), &ctx);
+       g_signal_connect(G_OBJECT(view), "resource-request-starting",
+                        G_CALLBACK(view_cb_resource_request_starting), &ctx);
+       g_signal_connect(G_OBJECT(view), "mime-type-policy-decision-requested",
+                        G_CALLBACK(view_cb_mime_type_policy_decision), &ctx);
+       g_signal_connect(G_OBJECT(view), "download-requested",
+                        G_CALLBACK(view_cb_download_requested), &ctx);
+       g_signal_connect(G_OBJECT(view), "hovering-over-link",
+                        G_CALLBACK(view_cb_hovering_over_link), &ctx);
+       g_signal_connect(G_OBJECT(view), "title-changed",
+                        G_CALLBACK(view_cb_title_changed), &ctx);
+
+       gtk_container_add(GTK_CONTAINER(scroll), GTK_WIDGET(view));
+       gtk_container_add(GTK_CONTAINER(ctx.win), GTK_WIDGET(scroll));
+
+       gtk_widget_grab_focus(GTK_WIDGET(view));
+       gtk_widget_show_all(ctx.win);
+
+       settings = webkit_web_view_get_settings(view);
+       g_object_set(G_OBJECT(settings), "user-agent",
+                    "Mozilla/5.0 (X11; U; Unix; en-US) "
+                    "AppleWebKit/537.15 (KHTML, like Gecko) "
+                    "hs20-client/1.0", NULL);
+       g_object_set(G_OBJECT(settings), "auto-load-images", TRUE, NULL);
+
+       webkit_web_view_load_uri(view, url);
+
+       gtk_main();
+       gtk_widget_destroy(ctx.win);
+       while (gtk_events_pending())
+               gtk_main_iteration();
+
+       free(ctx.hover_link);
+       free(ctx.title);
+       return ctx.success;
+}
diff --git a/src/utils/browser.h b/src/utils/browser.h
new file mode 100644 (file)
index 0000000..aaa0eed
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * Hotspot 2.0 client - Web browser
+ * Copyright (c) 2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef BROWSER_H
+#define BROWSER_H
+
+#ifdef CONFIG_NO_BROWSER
+static inline int hs20_web_browser(const char *url)
+{
+       return -1;
+}
+#else /* CONFIG_NO_BROWSER */
+int hs20_web_browser(const char *url);
+#endif /* CONFIG_NO_BROWSER */
+
+#endif /* BROWSER_H */
index f947388..c6f4e43 100644 (file)
 #endif /* USE_INTERNAL_CRYPTO */
 #endif /* CONFIG_WIN32_DEFAULTS */
 
-#ifdef CONFIG_XCODE_DEFAULTS
-#define CONFIG_DRIVER_OSX
-#define CONFIG_BACKEND_FILE
-#define IEEE8021X_EAPOL
-#define PKCS12_FUNCS
-#define CONFIG_CTRL_IFACE
-#define CONFIG_CTRL_IFACE_UNIX
-#define CONFIG_DEBUG_FILE
-#define EAP_MD5
-#define EAP_TLS
-#define EAP_MSCHAPv2
-#define EAP_PEAP
-#define EAP_TTLS
-#define EAP_GTC
-#define EAP_OTP
-#define EAP_LEAP
-#define EAP_TNC
-#define CONFIG_WPS
-#define EAP_WSC
-
-#ifdef USE_INTERNAL_CRYPTO
-#define CONFIG_TLS_INTERNAL_CLIENT
-#define CONFIG_INTERNAL_LIBTOMMATH
-#define CONFIG_CRYPTO_INTERNAL
-#endif /* USE_INTERNAL_CRYPTO */
-#endif /* CONFIG_XCODE_DEFAULTS */
-
 #endif /* BUILD_CONFIG_H */
index bf326cd..5fd795f 100644 (file)
@@ -36,6 +36,25 @@ int hex2byte(const char *hex)
 }
 
 
+static const char * hwaddr_parse(const char *txt, u8 *addr)
+{
+       size_t i;
+
+       for (i = 0; i < ETH_ALEN; i++) {
+               int a;
+
+               a = hex2byte(txt);
+               if (a < 0)
+                       return NULL;
+               txt += 2;
+               addr[i] = a;
+               if (i < ETH_ALEN - 1 && *txt++ != ':')
+                       return NULL;
+       }
+       return txt;
+}
+
+
 /**
  * hwaddr_aton - Convert ASCII string to MAC address (colon-delimited format)
  * @txt: MAC address as a string (e.g., "00:11:22:33:44:55")
@@ -44,25 +63,46 @@ int hex2byte(const char *hex)
  */
 int hwaddr_aton(const char *txt, u8 *addr)
 {
-       int i;
+       return hwaddr_parse(txt, addr) ? 0 : -1;
+}
 
-       for (i = 0; i < 6; i++) {
-               int a, b;
 
-               a = hex2num(*txt++);
-               if (a < 0)
-                       return -1;
-               b = hex2num(*txt++);
-               if (b < 0)
-                       return -1;
-               *addr++ = (a << 4) | b;
-               if (i < 5 && *txt++ != ':')
+/**
+ * hwaddr_masked_aton - Convert ASCII string with optional mask to MAC address (colon-delimited format)
+ * @txt: MAC address with optional mask as a string (e.g., "00:11:22:33:44:55/ff:ff:ff:ff:00:00")
+ * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes)
+ * @mask: Buffer for the MAC address mask (ETH_ALEN = 6 bytes)
+ * @maskable: Flag to indicate whether a mask is allowed
+ * Returns: 0 on success, -1 on failure (e.g., string not a MAC address)
+ */
+int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable)
+{
+       const char *r;
+
+       /* parse address part */
+       r = hwaddr_parse(txt, addr);
+       if (!r)
+               return -1;
+
+       /* check for optional mask */
+       if (*r == '\0' || isspace(*r)) {
+               /* no mask specified, assume default */
+               os_memset(mask, 0xff, ETH_ALEN);
+       } else if (maskable && *r == '/') {
+               /* mask specified and allowed */
+               r = hwaddr_parse(r + 1, mask);
+               /* parser error? */
+               if (!r)
                        return -1;
+       } else {
+               /* mask specified but not allowed or trailing garbage */
+               return -1;
        }
 
        return 0;
 }
 
+
 /**
  * hwaddr_compact_aton - Convert ASCII string to MAC address (no colon delimitors format)
  * @txt: MAC address as a string (e.g., "001122334455")
@@ -144,6 +184,30 @@ int hexstr2bin(const char *hex, u8 *buf, size_t len)
 }
 
 
+int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask)
+{
+       size_t i;
+       int print_mask = 0;
+       int res;
+
+       for (i = 0; i < ETH_ALEN; i++) {
+               if (mask[i] != 0xff) {
+                       print_mask = 1;
+                       break;
+               }
+       }
+
+       if (print_mask)
+               res = os_snprintf(buf, len, MACSTR "/" MACSTR,
+                                 MAC2STR(addr), MAC2STR(mask));
+       else
+               res = os_snprintf(buf, len, MACSTR, MAC2STR(addr));
+       if (os_snprintf_error(len, res))
+               return -1;
+       return res;
+}
+
+
 /**
  * inc_byte_array - Increment arbitrary length byte array by one
  * @counter: Pointer to byte array
@@ -183,6 +247,35 @@ void wpa_get_ntp_timestamp(u8 *buf)
        os_memcpy(buf + 4, (u8 *) &tmp, 4);
 }
 
+/**
+ * wpa_scnprintf - Simpler-to-use snprintf function
+ * @buf: Output buffer
+ * @size: Buffer size
+ * @fmt: format
+ *
+ * Simpler snprintf version that doesn't require further error checks - the
+ * return value only indicates how many bytes were actually written, excluding
+ * the NULL byte (i.e., 0 on error, size-1 if buffer is not big enough).
+ */
+int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...)
+{
+       va_list ap;
+       int ret;
+
+       if (!size)
+               return 0;
+
+       va_start(ap, fmt);
+       ret = vsnprintf(buf, size, fmt, ap);
+       va_end(ap);
+
+       if (ret < 0)
+               return 0;
+       if ((size_t) ret >= size)
+               return size - 1;
+
+       return ret;
+}
 
 static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data,
                                    size_t len, int uppercase)
@@ -195,7 +288,7 @@ static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data,
        for (i = 0; i < len; i++) {
                ret = os_snprintf(pos, end - pos, uppercase ? "%02X" : "%02x",
                                  data[i]);
-               if (ret < 0 || ret >= end - pos) {
+               if (os_snprintf_error(end - pos, ret)) {
                        end[-1] = '\0';
                        return pos - buf;
                }
@@ -350,7 +443,7 @@ void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len)
        size_t i;
 
        for (i = 0; i < len; i++) {
-               if (txt + 4 > end)
+               if (txt + 4 >= end)
                        break;
 
                switch (data[i]) {
@@ -362,7 +455,7 @@ void printf_encode(char *txt, size_t maxlen, const u8 *data, size_t len)
                        *txt++ = '\\';
                        *txt++ = '\\';
                        break;
-               case '\e':
+               case '\033':
                        *txt++ = '\\';
                        *txt++ = 'e';
                        break;
@@ -400,7 +493,7 @@ size_t printf_decode(u8 *buf, size_t maxlen, const char *str)
        int val;
 
        while (*pos) {
-               if (len == maxlen)
+               if (len + 1 >= maxlen)
                        break;
                switch (*pos) {
                case '\\':
@@ -427,7 +520,7 @@ size_t printf_decode(u8 *buf, size_t maxlen, const char *str)
                                pos++;
                                break;
                        case 'e':
-                               buf[len++] = '\e';
+                               buf[len++] = '\033';
                                pos++;
                                break;
                        case 'x':
@@ -468,6 +561,8 @@ size_t printf_decode(u8 *buf, size_t maxlen, const char *str)
                        break;
                }
        }
+       if (maxlen > len)
+               buf[len] = '\0';
 
        return len;
 }
@@ -622,3 +717,348 @@ char * dup_binstr(const void *src, size_t len)
 
        return res;
 }
+
+
+int freq_range_list_parse(struct wpa_freq_range_list *res, const char *value)
+{
+       struct wpa_freq_range *freq = NULL, *n;
+       unsigned int count = 0;
+       const char *pos, *pos2, *pos3;
+
+       /*
+        * Comma separated list of frequency ranges.
+        * For example: 2412-2432,2462,5000-6000
+        */
+       pos = value;
+       while (pos && pos[0]) {
+               n = os_realloc_array(freq, count + 1,
+                                    sizeof(struct wpa_freq_range));
+               if (n == NULL) {
+                       os_free(freq);
+                       return -1;
+               }
+               freq = n;
+               freq[count].min = atoi(pos);
+               pos2 = os_strchr(pos, '-');
+               pos3 = os_strchr(pos, ',');
+               if (pos2 && (!pos3 || pos2 < pos3)) {
+                       pos2++;
+                       freq[count].max = atoi(pos2);
+               } else
+                       freq[count].max = freq[count].min;
+               pos = pos3;
+               if (pos)
+                       pos++;
+               count++;
+       }
+
+       os_free(res->range);
+       res->range = freq;
+       res->num = count;
+
+       return 0;
+}
+
+
+int freq_range_list_includes(const struct wpa_freq_range_list *list,
+                            unsigned int freq)
+{
+       unsigned int i;
+
+       if (list == NULL)
+               return 0;
+
+       for (i = 0; i < list->num; i++) {
+               if (freq >= list->range[i].min && freq <= list->range[i].max)
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+char * freq_range_list_str(const struct wpa_freq_range_list *list)
+{
+       char *buf, *pos, *end;
+       size_t maxlen;
+       unsigned int i;
+       int res;
+
+       if (list->num == 0)
+               return NULL;
+
+       maxlen = list->num * 30;
+       buf = os_malloc(maxlen);
+       if (buf == NULL)
+               return NULL;
+       pos = buf;
+       end = buf + maxlen;
+
+       for (i = 0; i < list->num; i++) {
+               struct wpa_freq_range *range = &list->range[i];
+
+               if (range->min == range->max)
+                       res = os_snprintf(pos, end - pos, "%s%u",
+                                         i == 0 ? "" : ",", range->min);
+               else
+                       res = os_snprintf(pos, end - pos, "%s%u-%u",
+                                         i == 0 ? "" : ",",
+                                         range->min, range->max);
+               if (os_snprintf_error(end - pos, res)) {
+                       os_free(buf);
+                       return NULL;
+               }
+               pos += res;
+       }
+
+       return buf;
+}
+
+
+int int_array_len(const int *a)
+{
+       int i;
+       for (i = 0; a && a[i]; i++)
+               ;
+       return i;
+}
+
+
+void int_array_concat(int **res, const int *a)
+{
+       int reslen, alen, i;
+       int *n;
+
+       reslen = int_array_len(*res);
+       alen = int_array_len(a);
+
+       n = os_realloc_array(*res, reslen + alen + 1, sizeof(int));
+       if (n == NULL) {
+               os_free(*res);
+               *res = NULL;
+               return;
+       }
+       for (i = 0; i <= alen; i++)
+               n[reslen + i] = a[i];
+       *res = n;
+}
+
+
+static int freq_cmp(const void *a, const void *b)
+{
+       int _a = *(int *) a;
+       int _b = *(int *) b;
+
+       if (_a == 0)
+               return 1;
+       if (_b == 0)
+               return -1;
+       return _a - _b;
+}
+
+
+void int_array_sort_unique(int *a)
+{
+       int alen;
+       int i, j;
+
+       if (a == NULL)
+               return;
+
+       alen = int_array_len(a);
+       qsort(a, alen, sizeof(int), freq_cmp);
+
+       i = 0;
+       j = 1;
+       while (a[i] && a[j]) {
+               if (a[i] == a[j]) {
+                       j++;
+                       continue;
+               }
+               a[++i] = a[j++];
+       }
+       if (a[i])
+               i++;
+       a[i] = 0;
+}
+
+
+void int_array_add_unique(int **res, int a)
+{
+       int reslen;
+       int *n;
+
+       for (reslen = 0; *res && (*res)[reslen]; reslen++) {
+               if ((*res)[reslen] == a)
+                       return; /* already in the list */
+       }
+
+       n = os_realloc_array(*res, reslen + 2, sizeof(int));
+       if (n == NULL) {
+               os_free(*res);
+               *res = NULL;
+               return;
+       }
+
+       n[reslen] = a;
+       n[reslen + 1] = 0;
+
+       *res = n;
+}
+
+
+void str_clear_free(char *str)
+{
+       if (str) {
+               size_t len = os_strlen(str);
+               os_memset(str, 0, len);
+               os_free(str);
+       }
+}
+
+
+void bin_clear_free(void *bin, size_t len)
+{
+       if (bin) {
+               os_memset(bin, 0, len);
+               os_free(bin);
+       }
+}
+
+
+int random_mac_addr(u8 *addr)
+{
+       if (os_get_random(addr, ETH_ALEN) < 0)
+               return -1;
+       addr[0] &= 0xfe; /* unicast */
+       addr[0] |= 0x02; /* locally administered */
+       return 0;
+}
+
+
+int random_mac_addr_keep_oui(u8 *addr)
+{
+       if (os_get_random(addr + 3, 3) < 0)
+               return -1;
+       addr[0] &= 0xfe; /* unicast */
+       addr[0] |= 0x02; /* locally administered */
+       return 0;
+}
+
+
+/**
+ * str_token - Get next token from a string
+ * @buf: String to tokenize. Note that the string might be modified.
+ * @delim: String of delimiters
+ * @context: Pointer to save our context. Should be initialized with
+ *     NULL on the first call, and passed for any further call.
+ * Returns: The next token, NULL if there are no more valid tokens.
+ */
+char * str_token(char *str, const char *delim, char **context)
+{
+       char *end, *pos = str;
+
+       if (*context)
+               pos = *context;
+
+       while (*pos && os_strchr(delim, *pos))
+               pos++;
+       if (!*pos)
+               return NULL;
+
+       end = pos + 1;
+       while (*end && !os_strchr(delim, *end))
+               end++;
+
+       if (*end)
+               *end++ = '\0';
+
+       *context = end;
+       return pos;
+}
+
+
+size_t utf8_unescape(const char *inp, size_t in_size,
+                    char *outp, size_t out_size)
+{
+       size_t res_size = 0;
+
+       if (!inp || !outp)
+               return 0;
+
+       if (!in_size)
+               in_size = os_strlen(inp);
+
+       /* Advance past leading single quote */
+       if (*inp == '\'' && in_size) {
+               inp++;
+               in_size--;
+       }
+
+       while (in_size--) {
+               if (res_size >= out_size)
+                       return 0;
+
+               switch (*inp) {
+               case '\'':
+                       /* Terminate on bare single quote */
+                       *outp = '\0';
+                       return res_size;
+
+               case '\\':
+                       if (!in_size--)
+                               return 0;
+                       inp++;
+                       /* fall through */
+
+               default:
+                       *outp++ = *inp++;
+                       res_size++;
+               }
+       }
+
+       /* NUL terminate if space allows */
+       if (res_size < out_size)
+               *outp = '\0';
+
+       return res_size;
+}
+
+
+size_t utf8_escape(const char *inp, size_t in_size,
+                  char *outp, size_t out_size)
+{
+       size_t res_size = 0;
+
+       if (!inp || !outp)
+               return 0;
+
+       /* inp may or may not be NUL terminated, but must be if 0 size
+        * is specified */
+       if (!in_size)
+               in_size = os_strlen(inp);
+
+       while (in_size--) {
+               if (res_size++ >= out_size)
+                       return 0;
+
+               switch (*inp) {
+               case '\\':
+               case '\'':
+                       if (res_size++ >= out_size)
+                               return 0;
+                       *outp++ = '\\';
+                       /* fall through */
+
+               default:
+                       *outp++ = *inp++;
+                       break;
+               }
+       }
+
+       /* NUL terminate if space allows */
+       if (res_size < out_size)
+               *outp = '\0';
+
+       return res_size;
+}
index e4f7031..576e8e7 100644 (file)
@@ -164,6 +164,7 @@ static inline unsigned int wpa_swap_32(unsigned int v)
 #define be_to_host16(n) wpa_swap_16(n)
 #define host_to_be16(n) wpa_swap_16(n)
 #define le_to_host32(n) (n)
+#define host_to_le32(n) (n)
 #define be_to_host32(n) wpa_swap_32(n)
 #define host_to_be32(n) wpa_swap_32(n)
 
@@ -205,6 +206,7 @@ static inline unsigned int wpa_swap_32(unsigned int v)
 #define be_to_host16(n) (n)
 #define host_to_be16(n) (n)
 #define le_to_host32(n) bswap_32(n)
+#define host_to_le32(n) bswap_32(n)
 #define be_to_host32(n) (n)
 #define host_to_be32(n) (n)
 #define le_to_host64(n) bswap_64(n)
@@ -328,6 +330,9 @@ static inline void WPA_PUT_LE64(u8 *a, u64 val)
 #ifndef ETH_ALEN
 #define ETH_ALEN 6
 #endif
+#ifndef ETH_HLEN
+#define ETH_HLEN 14
+#endif
 #ifndef IFNAMSIZ
 #define IFNAMSIZ 16
 #endif
@@ -458,17 +463,29 @@ typedef u64 __bitwise le64;
 #endif /* __GNUC__ */
 #endif /* __must_check */
 
+#ifndef __maybe_unused
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+#define __maybe_unused __attribute__((unused))
+#else
+#define __maybe_unused
+#endif /* __GNUC__ */
+#endif /* __must_check */
+
 int hwaddr_aton(const char *txt, u8 *addr);
+int hwaddr_masked_aton(const char *txt, u8 *addr, u8 *mask, u8 maskable);
 int hwaddr_compact_aton(const char *txt, u8 *addr);
 int hwaddr_aton2(const char *txt, u8 *addr);
 int hex2byte(const char *hex);
 int hexstr2bin(const char *hex, u8 *buf, size_t len);
 void inc_byte_array(u8 *counter, size_t len);
 void wpa_get_ntp_timestamp(u8 *buf);
+int wpa_scnprintf(char *buf, size_t size, const char *fmt, ...);
 int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len);
 int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data,
                               size_t len);
 
+int hwaddr_mask_txt(char *buf, size_t len, const u8 *addr, const u8 *mask);
+
 #ifdef CONFIG_NATIVE_WINDOWS
 void wpa_unicode2ascii_inplace(TCHAR *str);
 TCHAR * wpa_strdup_tchar(const char *str);
@@ -504,6 +521,39 @@ static inline int is_broadcast_ether_addr(const u8 *a)
 #include "wpa_debug.h"
 
 
+struct wpa_freq_range_list {
+       struct wpa_freq_range {
+               unsigned int min;
+               unsigned int max;
+       } *range;
+       unsigned int num;
+};
+
+int freq_range_list_parse(struct wpa_freq_range_list *res, const char *value);
+int freq_range_list_includes(const struct wpa_freq_range_list *list,
+                            unsigned int freq);
+char * freq_range_list_str(const struct wpa_freq_range_list *list);
+
+int int_array_len(const int *a);
+void int_array_concat(int **res, const int *a);
+void int_array_sort_unique(int *a);
+void int_array_add_unique(int **res, int a);
+
+#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
+
+void str_clear_free(char *str);
+void bin_clear_free(void *bin, size_t len);
+
+int random_mac_addr(u8 *addr);
+int random_mac_addr_keep_oui(u8 *addr);
+
+char * str_token(char *str, const char *delim, char **context);
+size_t utf8_escape(const char *inp, size_t in_size,
+                  char *outp, size_t out_size);
+size_t utf8_unescape(const char *inp, size_t in_size,
+                    char *outp, size_t out_size);
+
+
 /*
  * gcc 4.4 ends up generating strict-aliasing warnings about some very common
  * networking socket uses that do not really result in a real problem and
index 177ecf4..d340bfa 100644 (file)
@@ -14,7 +14,7 @@
 #include "list.h"
 #include "edit.h"
 
-#define CMD_BUF_LEN 256
+#define CMD_BUF_LEN 4096
 static char cmdbuf[CMD_BUF_LEN];
 static int cmdbuf_pos = 0;
 static int cmdbuf_len = 0;
index a095ea6..13173cb 100644 (file)
@@ -13,7 +13,7 @@
 #include "edit.h"
 
 
-#define CMD_BUF_LEN 256
+#define CMD_BUF_LEN 4096
 static char cmdbuf[CMD_BUF_LEN];
 static int cmdbuf_pos = 0;
 static const char *ps2 = NULL;
index f62e2b7..4a565eb 100644 (file)
@@ -7,17 +7,28 @@
  */
 
 #include "includes.h"
+#include <assert.h>
 
 #include "common.h"
 #include "trace.h"
 #include "list.h"
 #include "eloop.h"
 
+#if defined(CONFIG_ELOOP_POLL) && defined(CONFIG_ELOOP_EPOLL)
+#error Do not define both of poll and epoll
+#endif
+
+#if !defined(CONFIG_ELOOP_POLL) && !defined(CONFIG_ELOOP_EPOLL)
+#define CONFIG_ELOOP_SELECT
+#endif
+
 #ifdef CONFIG_ELOOP_POLL
-#include <assert.h>
 #include <poll.h>
 #endif /* CONFIG_ELOOP_POLL */
 
+#ifdef CONFIG_ELOOP_EPOLL
+#include <sys/epoll.h>
+#endif /* CONFIG_ELOOP_EPOLL */
 
 struct eloop_sock {
        int sock;
@@ -31,7 +42,7 @@ struct eloop_sock {
 
 struct eloop_timeout {
        struct dl_list list;
-       struct os_time time;
+       struct os_reltime time;
        void *eloop_data;
        void *user_data;
        eloop_timeout_handler handler;
@@ -50,7 +61,11 @@ struct eloop_signal {
 struct eloop_sock_table {
        int count;
        struct eloop_sock *table;
+#ifdef CONFIG_ELOOP_EPOLL
+       eloop_event_type type;
+#else /* CONFIG_ELOOP_EPOLL */
        int changed;
+#endif /* CONFIG_ELOOP_EPOLL */
 };
 
 struct eloop_data {
@@ -63,6 +78,13 @@ struct eloop_data {
        struct pollfd *pollfds;
        struct pollfd **pollfds_map;
 #endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_EPOLL
+       int epollfd;
+       int epoll_max_event_num;
+       int epoll_max_fd;
+       struct eloop_sock *epoll_table;
+       struct epoll_event *epoll_events;
+#endif /* CONFIG_ELOOP_EPOLL */
        struct eloop_sock_table readers;
        struct eloop_sock_table writers;
        struct eloop_sock_table exceptions;
@@ -75,7 +97,6 @@ struct eloop_data {
        int pending_terminate;
 
        int terminate;
-       int reader_table_changed;
 };
 
 static struct eloop_data eloop;
@@ -128,6 +149,17 @@ int eloop_init(void)
 {
        os_memset(&eloop, 0, sizeof(eloop));
        dl_list_init(&eloop.timeout);
+#ifdef CONFIG_ELOOP_EPOLL
+       eloop.epollfd = epoll_create1(0);
+       if (eloop.epollfd < 0) {
+               wpa_printf(MSG_ERROR, "%s: epoll_create1 failed. %s\n",
+                          __func__, strerror(errno));
+               return -1;
+       }
+       eloop.readers.type = EVENT_TYPE_READ;
+       eloop.writers.type = EVENT_TYPE_WRITE;
+       eloop.exceptions.type = EVENT_TYPE_EXCEPTION;
+#endif /* CONFIG_ELOOP_EPOLL */
 #ifdef WPA_TRACE
        signal(SIGSEGV, eloop_sigsegv_handler);
 #endif /* WPA_TRACE */
@@ -139,6 +171,11 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
                                      int sock, eloop_sock_handler handler,
                                      void *eloop_data, void *user_data)
 {
+#ifdef CONFIG_ELOOP_EPOLL
+       struct eloop_sock *temp_table;
+       struct epoll_event ev, *temp_events;
+       int next;
+#endif /* CONFIG_ELOOP_EPOLL */
        struct eloop_sock *tmp;
        int new_max_sock;
 
@@ -174,12 +211,41 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
                eloop.pollfds = n;
        }
 #endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_EPOLL
+       if (new_max_sock >= eloop.epoll_max_fd) {
+               next = eloop.epoll_max_fd == 0 ? 16 : eloop.epoll_max_fd * 2;
+               temp_table = os_realloc_array(eloop.epoll_table, next,
+                                             sizeof(struct eloop_sock));
+               if (temp_table == NULL)
+                       return -1;
+
+               eloop.epoll_max_fd = next;
+               eloop.epoll_table = temp_table;
+       }
+
+       if (eloop.count + 1 > eloop.epoll_max_event_num) {
+               next = eloop.epoll_max_event_num == 0 ? 8 :
+                       eloop.epoll_max_event_num * 2;
+               temp_events = os_realloc_array(eloop.epoll_events, next,
+                                              sizeof(struct epoll_event));
+               if (temp_events == NULL) {
+                       wpa_printf(MSG_ERROR, "%s: malloc for epoll failed. "
+                                  "%s\n", __func__, strerror(errno));
+                       return -1;
+               }
+
+               eloop.epoll_max_event_num = next;
+               eloop.epoll_events = temp_events;
+       }
+#endif /* CONFIG_ELOOP_EPOLL */
 
        eloop_trace_sock_remove_ref(table);
        tmp = os_realloc_array(table->table, table->count + 1,
                               sizeof(struct eloop_sock));
-       if (tmp == NULL)
+       if (tmp == NULL) {
+               eloop_trace_sock_add_ref(table);
                return -1;
+       }
 
        tmp[table->count].sock = sock;
        tmp[table->count].eloop_data = eloop_data;
@@ -190,9 +256,38 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
        table->table = tmp;
        eloop.max_sock = new_max_sock;
        eloop.count++;
+#ifndef CONFIG_ELOOP_EPOLL
        table->changed = 1;
+#endif /* CONFIG_ELOOP_EPOLL */
        eloop_trace_sock_add_ref(table);
 
+#ifdef CONFIG_ELOOP_EPOLL
+       os_memset(&ev, 0, sizeof(ev));
+       switch (table->type) {
+       case EVENT_TYPE_READ:
+               ev.events = EPOLLIN;
+               break;
+       case EVENT_TYPE_WRITE:
+               ev.events = EPOLLOUT;
+               break;
+       /*
+        * Exceptions are always checked when using epoll, but I suppose it's
+        * possible that someone registered a socket *only* for exception
+        * handling.
+        */
+       case EVENT_TYPE_EXCEPTION:
+               ev.events = EPOLLERR | EPOLLHUP;
+               break;
+       }
+       ev.data.fd = sock;
+       if (epoll_ctl(eloop.epollfd, EPOLL_CTL_ADD, sock, &ev) < 0) {
+               wpa_printf(MSG_ERROR, "%s: epoll_ctl(ADD) for fd=%d "
+                          "failed. %s\n", __func__, sock, strerror(errno));
+               return -1;
+       }
+       os_memcpy(&eloop.epoll_table[sock], &table->table[table->count - 1],
+                 sizeof(struct eloop_sock));
+#endif /* CONFIG_ELOOP_EPOLL */
        return 0;
 }
 
@@ -219,8 +314,18 @@ static void eloop_sock_table_remove_sock(struct eloop_sock_table *table,
        }
        table->count--;
        eloop.count--;
+#ifndef CONFIG_ELOOP_EPOLL
        table->changed = 1;
+#endif /* CONFIG_ELOOP_EPOLL */
        eloop_trace_sock_add_ref(table);
+#ifdef CONFIG_ELOOP_EPOLL
+       if (epoll_ctl(eloop.epollfd, EPOLL_CTL_DEL, sock, NULL) < 0) {
+               wpa_printf(MSG_ERROR, "%s: epoll_ctl(DEL) for fd=%d "
+                          "failed. %s\n", __func__, sock, strerror(errno));
+               return;
+       }
+       os_memset(&eloop.epoll_table[sock], 0, sizeof(struct eloop_sock));
+#endif /* CONFIG_ELOOP_EPOLL */
 }
 
 
@@ -362,7 +467,9 @@ static void eloop_sock_table_dispatch(struct eloop_sock_table *readers,
                                        max_pollfd_map, POLLERR | POLLHUP);
 }
 
-#else /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_POLL */
+
+#ifdef CONFIG_ELOOP_SELECT
 
 static void eloop_sock_table_set_fds(struct eloop_sock_table *table,
                                     fd_set *fds)
@@ -374,8 +481,10 @@ static void eloop_sock_table_set_fds(struct eloop_sock_table *table,
        if (table->table == NULL)
                return;
 
-       for (i = 0; i < table->count; i++)
+       for (i = 0; i < table->count; i++) {
+               assert(table->table[i].sock >= 0);
                FD_SET(table->table[i].sock, fds);
+       }
 }
 
 
@@ -399,7 +508,24 @@ static void eloop_sock_table_dispatch(struct eloop_sock_table *table,
        }
 }
 
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_SELECT */
+
+
+#ifdef CONFIG_ELOOP_EPOLL
+static void eloop_sock_table_dispatch(struct epoll_event *events, int nfds)
+{
+       struct eloop_sock *table;
+       int i;
+
+       for (i = 0; i < nfds; i++) {
+               table = &eloop.epoll_table[events[i].data.fd];
+               if (table->handler == NULL)
+                       continue;
+               table->handler(table->sock, table->eloop_data,
+                              table->user_data);
+       }
+}
+#endif /* CONFIG_ELOOP_EPOLL */
 
 
 static void eloop_sock_table_destroy(struct eloop_sock_table *table)
@@ -459,6 +585,7 @@ int eloop_register_sock(int sock, eloop_event_type type,
 {
        struct eloop_sock_table *table;
 
+       assert(sock >= 0);
        table = eloop_get_sock_table(type);
        return eloop_sock_table_add_sock(table, sock, handler,
                                         eloop_data, user_data);
@@ -484,7 +611,7 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs,
        timeout = os_zalloc(sizeof(*timeout));
        if (timeout == NULL)
                return -1;
-       if (os_get_time(&timeout->time) < 0) {
+       if (os_get_reltime(&timeout->time) < 0) {
                os_free(timeout);
                return -1;
        }
@@ -514,7 +641,7 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs,
 
        /* Maintain timeouts in order of increasing time */
        dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
-               if (os_time_before(&timeout->time, &tmp->time)) {
+               if (os_reltime_before(&timeout->time, &tmp->time)) {
                        dl_list_add(tmp->list.prev, &timeout->list);
                        return 0;
                }
@@ -558,13 +685,13 @@ int eloop_cancel_timeout(eloop_timeout_handler handler,
 
 int eloop_cancel_timeout_one(eloop_timeout_handler handler,
                             void *eloop_data, void *user_data,
-                            struct os_time *remaining)
+                            struct os_reltime *remaining)
 {
        struct eloop_timeout *timeout, *prev;
        int removed = 0;
-       struct os_time now;
+       struct os_reltime now;
 
-       os_get_time(&now);
+       os_get_reltime(&now);
        remaining->sec = remaining->usec = 0;
 
        dl_list_for_each_safe(timeout, prev, &eloop.timeout,
@@ -573,8 +700,8 @@ int eloop_cancel_timeout_one(eloop_timeout_handler handler,
                    (timeout->eloop_data == eloop_data) &&
                    (timeout->user_data == user_data)) {
                        removed = 1;
-                       if (os_time_before(&now, &timeout->time))
-                               os_time_sub(&timeout->time, &now, remaining);
+                       if (os_reltime_before(&now, &timeout->time))
+                               os_reltime_sub(&timeout->time, &now, remaining);
                        eloop_remove_timeout(timeout);
                        break;
                }
@@ -599,6 +726,70 @@ int eloop_is_timeout_registered(eloop_timeout_handler handler,
 }
 
 
+int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs,
+                         eloop_timeout_handler handler, void *eloop_data,
+                         void *user_data)
+{
+       struct os_reltime now, requested, remaining;
+       struct eloop_timeout *tmp;
+
+       dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+               if (tmp->handler == handler &&
+                   tmp->eloop_data == eloop_data &&
+                   tmp->user_data == user_data) {
+                       requested.sec = req_secs;
+                       requested.usec = req_usecs;
+                       os_get_reltime(&now);
+                       os_reltime_sub(&tmp->time, &now, &remaining);
+                       if (os_reltime_before(&requested, &remaining)) {
+                               eloop_cancel_timeout(handler, eloop_data,
+                                                    user_data);
+                               eloop_register_timeout(requested.sec,
+                                                      requested.usec,
+                                                      handler, eloop_data,
+                                                      user_data);
+                               return 1;
+                       }
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+
+int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs,
+                           eloop_timeout_handler handler, void *eloop_data,
+                           void *user_data)
+{
+       struct os_reltime now, requested, remaining;
+       struct eloop_timeout *tmp;
+
+       dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+               if (tmp->handler == handler &&
+                   tmp->eloop_data == eloop_data &&
+                   tmp->user_data == user_data) {
+                       requested.sec = req_secs;
+                       requested.usec = req_usecs;
+                       os_get_reltime(&now);
+                       os_reltime_sub(&tmp->time, &now, &remaining);
+                       if (os_reltime_before(&remaining, &requested)) {
+                               eloop_cancel_timeout(handler, eloop_data,
+                                                    user_data);
+                               eloop_register_timeout(requested.sec,
+                                                      requested.usec,
+                                                      handler, eloop_data,
+                                                      user_data);
+                               return 1;
+                       }
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+
 #ifndef CONFIG_NATIVE_WINDOWS
 static void eloop_handle_alarm(int sig)
 {
@@ -709,20 +900,24 @@ void eloop_run(void)
 #ifdef CONFIG_ELOOP_POLL
        int num_poll_fds;
        int timeout_ms = 0;
-#else /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
        fd_set *rfds, *wfds, *efds;
        struct timeval _tv;
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+       int timeout_ms = -1;
+#endif /* CONFIG_ELOOP_EPOLL */
        int res;
-       struct os_time tv, now;
+       struct os_reltime tv, now;
 
-#ifndef CONFIG_ELOOP_POLL
+#ifdef CONFIG_ELOOP_SELECT
        rfds = os_malloc(sizeof(*rfds));
        wfds = os_malloc(sizeof(*wfds));
        efds = os_malloc(sizeof(*efds));
        if (rfds == NULL || wfds == NULL || efds == NULL)
                goto out;
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_SELECT */
 
        while (!eloop.terminate &&
               (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 ||
@@ -731,17 +926,18 @@ void eloop_run(void)
                timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
                                        list);
                if (timeout) {
-                       os_get_time(&now);
-                       if (os_time_before(&now, &timeout->time))
-                               os_time_sub(&timeout->time, &now, &tv);
+                       os_get_reltime(&now);
+                       if (os_reltime_before(&now, &timeout->time))
+                               os_reltime_sub(&timeout->time, &now, &tv);
                        else
                                tv.sec = tv.usec = 0;
-#ifdef CONFIG_ELOOP_POLL
+#if defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL)
                        timeout_ms = tv.sec * 1000 + tv.usec / 1000;
-#else /* CONFIG_ELOOP_POLL */
+#endif /* defined(CONFIG_ELOOP_POLL) || defined(CONFIG_ELOOP_EPOLL) */
+#ifdef CONFIG_ELOOP_SELECT
                        _tv.tv_sec = tv.sec;
                        _tv.tv_usec = tv.usec;
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_SELECT */
                }
 
 #ifdef CONFIG_ELOOP_POLL
@@ -751,30 +947,44 @@ void eloop_run(void)
                        eloop.max_pollfd_map);
                res = poll(eloop.pollfds, num_poll_fds,
                           timeout ? timeout_ms : -1);
-
-               if (res < 0 && errno != EINTR && errno != 0) {
-                       perror("poll");
-                       goto out;
-               }
-#else /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
                eloop_sock_table_set_fds(&eloop.readers, rfds);
                eloop_sock_table_set_fds(&eloop.writers, wfds);
                eloop_sock_table_set_fds(&eloop.exceptions, efds);
                res = select(eloop.max_sock + 1, rfds, wfds, efds,
                             timeout ? &_tv : NULL);
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+               if (eloop.count == 0) {
+                       res = 0;
+               } else {
+                       res = epoll_wait(eloop.epollfd, eloop.epoll_events,
+                                        eloop.count, timeout_ms);
+               }
+#endif /* CONFIG_ELOOP_EPOLL */
                if (res < 0 && errno != EINTR && errno != 0) {
-                       perror("select");
+                       wpa_printf(MSG_ERROR, "eloop: %s: %s",
+#ifdef CONFIG_ELOOP_POLL
+                                  "poll"
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
+                                  "select"
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+                                  "epoll"
+#endif /* CONFIG_ELOOP_EPOLL */
+                                  , strerror(errno));
                        goto out;
                }
-#endif /* CONFIG_ELOOP_POLL */
                eloop_process_pending_signals();
 
                /* check if some registered timeouts have occurred */
                timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
                                        list);
                if (timeout) {
-                       os_get_time(&now);
-                       if (!os_time_before(&now, &timeout->time)) {
+                       os_get_reltime(&now);
+                       if (!os_reltime_before(&now, &timeout->time)) {
                                void *eloop_data = timeout->eloop_data;
                                void *user_data = timeout->user_data;
                                eloop_timeout_handler handler =
@@ -792,20 +1002,24 @@ void eloop_run(void)
                eloop_sock_table_dispatch(&eloop.readers, &eloop.writers,
                                          &eloop.exceptions, eloop.pollfds_map,
                                          eloop.max_pollfd_map);
-#else /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_SELECT
                eloop_sock_table_dispatch(&eloop.readers, rfds);
                eloop_sock_table_dispatch(&eloop.writers, wfds);
                eloop_sock_table_dispatch(&eloop.exceptions, efds);
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_SELECT */
+#ifdef CONFIG_ELOOP_EPOLL
+               eloop_sock_table_dispatch(eloop.epoll_events, res);
+#endif /* CONFIG_ELOOP_EPOLL */
        }
 
        eloop.terminate = 0;
 out:
-#ifndef CONFIG_ELOOP_POLL
+#ifdef CONFIG_ELOOP_SELECT
        os_free(rfds);
        os_free(wfds);
        os_free(efds);
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_SELECT */
        return;
 }
 
@@ -819,9 +1033,9 @@ void eloop_terminate(void)
 void eloop_destroy(void)
 {
        struct eloop_timeout *timeout, *prev;
-       struct os_time now;
+       struct os_reltime now;
 
-       os_get_time(&now);
+       os_get_reltime(&now);
        dl_list_for_each_safe(timeout, prev, &eloop.timeout,
                              struct eloop_timeout, list) {
                int sec, usec;
@@ -849,6 +1063,11 @@ void eloop_destroy(void)
        os_free(eloop.pollfds);
        os_free(eloop.pollfds_map);
 #endif /* CONFIG_ELOOP_POLL */
+#ifdef CONFIG_ELOOP_EPOLL
+       os_free(eloop.epoll_table);
+       os_free(eloop.epoll_events);
+       close(eloop.epollfd);
+#endif /* CONFIG_ELOOP_EPOLL */
 }
 
 
@@ -871,7 +1090,13 @@ void eloop_wait_for_read_sock(int sock)
        pfd.events = POLLIN;
 
        poll(&pfd, 1, -1);
-#else /* CONFIG_ELOOP_POLL */
+#endif /* CONFIG_ELOOP_POLL */
+#if defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL)
+       /*
+        * We can use epoll() here. But epoll() requres 4 system calls.
+        * epoll_create1(), epoll_ctl() for ADD, epoll_wait, and close() for
+        * epoll fd. So select() is better for performance here.
+        */
        fd_set rfds;
 
        if (sock < 0)
@@ -880,5 +1105,9 @@ void eloop_wait_for_read_sock(int sock)
        FD_ZERO(&rfds);
        FD_SET(sock, &rfds);
        select(sock + 1, &rfds, NULL, NULL, NULL);
-#endif /* CONFIG_ELOOP_POLL */
+#endif /* defined(CONFIG_ELOOP_SELECT) || defined(CONFIG_ELOOP_EPOLL) */
 }
+
+#ifdef CONFIG_ELOOP_SELECT
+#undef CONFIG_ELOOP_SELECT
+#endif /* CONFIG_ELOOP_SELECT */
index 0037c63..07b8c0d 100644 (file)
@@ -207,7 +207,7 @@ int eloop_cancel_timeout(eloop_timeout_handler handler,
  */
 int eloop_cancel_timeout_one(eloop_timeout_handler handler,
                             void *eloop_data, void *user_data,
-                            struct os_time *remaining);
+                            struct os_reltime *remaining);
 
 /**
  * eloop_is_timeout_registered - Check if a timeout is already registered
@@ -223,6 +223,40 @@ int eloop_is_timeout_registered(eloop_timeout_handler handler,
                                void *eloop_data, void *user_data);
 
 /**
+ * eloop_deplete_timeout - Deplete a timeout that is already registered
+ * @req_secs: Requested number of seconds to the timeout
+ * @req_usecs: Requested number of microseconds to the timeout
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data
+ * @user_data: Matching user_data
+ * Returns: 1 if the timeout is depleted, 0 if no change is made, -1 if no
+ * timeout matched
+ *
+ * Find a registered matching <handler,eloop_data,user_data> timeout. If found,
+ * deplete the timeout if remaining time is more than the requested time.
+ */
+int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs,
+                         eloop_timeout_handler handler, void *eloop_data,
+                         void *user_data);
+
+/**
+ * eloop_replenish_timeout - Replenish a timeout that is already registered
+ * @req_secs: Requested number of seconds to the timeout
+ * @req_usecs: Requested number of microseconds to the timeout
+ * @handler: Matching callback function
+ * @eloop_data: Matching eloop_data
+ * @user_data: Matching user_data
+ * Returns: 1 if the timeout is replenished, 0 if no change is made, -1 if no
+ * timeout matched
+ *
+ * Find a registered matching <handler,eloop_data,user_data> timeout. If found,
+ * replenish the timeout if remaining time is less than the requested time.
+ */
+int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs,
+                           eloop_timeout_handler handler, void *eloop_data,
+                           void *user_data);
+
+/**
  * eloop_register_signal - Register handler for signals
  * @sig: Signal number (e.g., SIGHUP)
  * @handler: Callback function to be called when the signal is received
index eda412f..de47fb2 100644 (file)
@@ -31,7 +31,7 @@ struct eloop_event {
 
 struct eloop_timeout {
        struct dl_list list;
-       struct os_time time;
+       struct os_reltime time;
        void *eloop_data;
        void *user_data;
        eloop_timeout_handler handler;
@@ -244,7 +244,7 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs,
        timeout = os_zalloc(sizeof(*timeout));
        if (timeout == NULL)
                return -1;
-       if (os_get_time(&timeout->time) < 0) {
+       if (os_get_reltime(&timeout->time) < 0) {
                os_free(timeout);
                return -1;
        }
@@ -271,7 +271,7 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs,
 
        /* Maintain timeouts in order of increasing time */
        dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
-               if (os_time_before(&timeout->time, &tmp->time)) {
+               if (os_reltime_before(&timeout->time, &tmp->time)) {
                        dl_list_add(tmp->list.prev, &timeout->list);
                        return 0;
                }
@@ -313,13 +313,13 @@ int eloop_cancel_timeout(eloop_timeout_handler handler,
 
 int eloop_cancel_timeout_one(eloop_timeout_handler handler,
                             void *eloop_data, void *user_data,
-                            struct os_time *remaining)
+                            struct os_reltime *remaining)
 {
        struct eloop_timeout *timeout, *prev;
        int removed = 0;
-       struct os_time now;
+       struct os_reltime now;
 
-       os_get_time(&now);
+       os_get_reltime(&now);
        remaining->sec = remaining->usec = 0;
 
        dl_list_for_each_safe(timeout, prev, &eloop.timeout,
@@ -328,8 +328,8 @@ int eloop_cancel_timeout_one(eloop_timeout_handler handler,
                    (timeout->eloop_data == eloop_data) &&
                    (timeout->user_data == user_data)) {
                        removed = 1;
-                       if (os_time_before(&now, &timeout->time))
-                               os_time_sub(&timeout->time, &now, remaining);
+                       if (os_reltime_before(&now, &timeout->time))
+                               os_reltime_sub(&timeout->time, &now, remaining);
                        eloop_remove_timeout(timeout);
                        break;
                }
@@ -354,6 +354,70 @@ int eloop_is_timeout_registered(eloop_timeout_handler handler,
 }
 
 
+int eloop_deplete_timeout(unsigned int req_secs, unsigned int req_usecs,
+                         eloop_timeout_handler handler, void *eloop_data,
+                         void *user_data)
+{
+       struct os_reltime now, requested, remaining;
+       struct eloop_timeout *tmp;
+
+       dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+               if (tmp->handler == handler &&
+                   tmp->eloop_data == eloop_data &&
+                   tmp->user_data == user_data) {
+                       requested.sec = req_secs;
+                       requested.usec = req_usecs;
+                       os_get_reltime(&now);
+                       os_reltime_sub(&tmp->time, &now, &remaining);
+                       if (os_reltime_before(&requested, &remaining)) {
+                               eloop_cancel_timeout(handler, eloop_data,
+                                                    user_data);
+                               eloop_register_timeout(requested.sec,
+                                                      requested.usec,
+                                                      handler, eloop_data,
+                                                      user_data);
+                               return 1;
+                       }
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+
+int eloop_replenish_timeout(unsigned int req_secs, unsigned int req_usecs,
+                           eloop_timeout_handler handler, void *eloop_data,
+                           void *user_data)
+{
+       struct os_reltime now, requested, remaining;
+       struct eloop_timeout *tmp;
+
+       dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) {
+               if (tmp->handler == handler &&
+                   tmp->eloop_data == eloop_data &&
+                   tmp->user_data == user_data) {
+                       requested.sec = req_secs;
+                       requested.usec = req_usecs;
+                       os_get_reltime(&now);
+                       os_reltime_sub(&tmp->time, &now, &remaining);
+                       if (os_reltime_before(&remaining, &requested)) {
+                               eloop_cancel_timeout(handler, eloop_data,
+                                                    user_data);
+                               eloop_register_timeout(requested.sec,
+                                                      requested.usec,
+                                                      handler, eloop_data,
+                                                      user_data);
+                               return 1;
+                       }
+                       return 0;
+               }
+       }
+
+       return -1;
+}
+
+
 /* TODO: replace with suitable signal handler */
 #if 0
 static void eloop_handle_signal(int sig)
@@ -468,7 +532,7 @@ int eloop_register_signal_reconfig(eloop_signal_handler handler,
 
 void eloop_run(void)
 {
-       struct os_time tv, now;
+       struct os_reltime tv, now;
        DWORD count, ret, timeout_val, err;
        size_t i;
 
@@ -480,9 +544,9 @@ void eloop_run(void)
                timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
                                        list);
                if (timeout) {
-                       os_get_time(&now);
-                       if (os_time_before(&now, &timeout->time))
-                               os_time_sub(&timeout->time, &now, &tv);
+                       os_get_reltime(&now);
+                       if (os_reltime_before(&now, &timeout->time))
+                               os_reltime_sub(&timeout->time, &now, &tv);
                }
 
                count = 0;
@@ -521,8 +585,8 @@ void eloop_run(void)
                timeout = dl_list_first(&eloop.timeout, struct eloop_timeout,
                                        list);
                if (timeout) {
-                       os_get_time(&now);
-                       if (!os_time_before(&now, &timeout->time)) {
+                       os_get_reltime(&now);
+                       if (!os_reltime_before(&now, &timeout->time)) {
                                void *eloop_data = timeout->eloop_data;
                                void *user_data = timeout->user_data;
                                eloop_timeout_handler handler =
index 3801bb8..b3a4552 100644 (file)
@@ -36,7 +36,7 @@ static void ext_password_test_deinit(void *ctx)
 {
        struct ext_password_test_data *data = ctx;
 
-       os_free(data->params);
+       str_clear_free(data->params);
        os_free(data);
 }
 
diff --git a/src/utils/http-utils.h b/src/utils/http-utils.h
new file mode 100644 (file)
index 0000000..8d4399a
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * HTTP wrapper
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef HTTP_UTILS_H
+#define HTTP_UTILS_H
+
+struct http_ctx;
+
+struct http_othername {
+       char *oid;
+       u8 *data;
+       size_t len;
+};
+
+#define HTTP_MAX_CERT_LOGO_HASH 32
+
+struct http_logo {
+       char *alg_oid;
+       u8 *hash;
+       size_t hash_len;
+       char *uri;
+};
+
+struct http_cert {
+       char **dnsname;
+       unsigned int num_dnsname;
+       struct http_othername *othername;
+       unsigned int num_othername;
+       struct http_logo *logo;
+       unsigned int num_logo;
+};
+
+int soap_init_client(struct http_ctx *ctx, const char *address,
+                    const char *ca_fname, const char *username,
+                    const char *password, const char *client_cert,
+                    const char *client_key);
+int soap_reinit_client(struct http_ctx *ctx);
+xml_node_t * soap_send_receive(struct http_ctx *ctx, xml_node_t *node);
+
+struct http_ctx * http_init_ctx(void *upper_ctx, struct xml_node_ctx *xml_ctx);
+void http_ocsp_set(struct http_ctx *ctx, int val);
+void http_deinit_ctx(struct http_ctx *ctx);
+
+int http_download_file(struct http_ctx *ctx, const char *url,
+                      const char *fname, const char *ca_fname);
+char * http_post(struct http_ctx *ctx, const char *url, const char *data,
+                const char *content_type, const char *ext_hdr,
+                const char *ca_fname,
+                const char *username, const char *password,
+                const char *client_cert, const char *client_key,
+                size_t *resp_len);
+void http_set_cert_cb(struct http_ctx *ctx,
+                     int (*cb)(void *ctx, struct http_cert *cert),
+                     void *cb_ctx);
+const char * http_get_err(struct http_ctx *ctx);
+void http_parse_x509_certificate(struct http_ctx *ctx, const char *fname);
+
+#endif /* HTTP_UTILS_H */
diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c
new file mode 100644 (file)
index 0000000..b38cf79
--- /dev/null
@@ -0,0 +1,1641 @@
+/*
+ * HTTP wrapper for libcurl
+ * Copyright (c) 2012-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#include <curl/curl.h>
+#ifdef EAP_TLS_OPENSSL
+#include <openssl/ssl.h>
+#include <openssl/asn1.h>
+#include <openssl/asn1t.h>
+#include <openssl/x509v3.h>
+
+#ifdef SSL_set_tlsext_status_type
+#ifndef OPENSSL_NO_TLSEXT
+#define HAVE_OCSP
+#include <openssl/err.h>
+#include <openssl/ocsp.h>
+#endif /* OPENSSL_NO_TLSEXT */
+#endif /* SSL_set_tlsext_status_type */
+#endif /* EAP_TLS_OPENSSL */
+
+#include "common.h"
+#include "xml-utils.h"
+#include "http-utils.h"
+
+
+struct http_ctx {
+       void *ctx;
+       struct xml_node_ctx *xml;
+       CURL *curl;
+       struct curl_slist *curl_hdr;
+       char *svc_address;
+       char *svc_ca_fname;
+       char *svc_username;
+       char *svc_password;
+       char *svc_client_cert;
+       char *svc_client_key;
+       char *curl_buf;
+       size_t curl_buf_len;
+
+       int (*cert_cb)(void *ctx, struct http_cert *cert);
+       void *cert_cb_ctx;
+
+       enum {
+               NO_OCSP, OPTIONAL_OCSP, MANDATORY_OCSP
+       } ocsp;
+       X509 *peer_cert;
+       X509 *peer_issuer;
+       X509 *peer_issuer_issuer;
+
+       const char *last_err;
+};
+
+
+static void clear_curl(struct http_ctx *ctx)
+{
+       if (ctx->curl) {
+               curl_easy_cleanup(ctx->curl);
+               ctx->curl = NULL;
+       }
+       if (ctx->curl_hdr) {
+               curl_slist_free_all(ctx->curl_hdr);
+               ctx->curl_hdr = NULL;
+       }
+}
+
+
+static void clone_str(char **dst, const char *src)
+{
+       os_free(*dst);
+       if (src)
+               *dst = os_strdup(src);
+       else
+               *dst = NULL;
+}
+
+
+static void debug_dump(struct http_ctx *ctx, const char *title,
+                      const char *buf, size_t len)
+{
+       char *txt;
+       size_t i;
+
+       for (i = 0; i < len; i++) {
+               if (buf[i] < 32 && buf[i] != '\t' && buf[i] != '\n' &&
+                   buf[i] != '\r') {
+                       wpa_hexdump_ascii(MSG_MSGDUMP, title, buf, len);
+                       return;
+               }
+       }
+
+       txt = os_malloc(len + 1);
+       if (txt == NULL)
+               return;
+       os_memcpy(txt, buf, len);
+       txt[len] = '\0';
+       while (len > 0) {
+               len--;
+               if (txt[len] == '\n' || txt[len] == '\r')
+                       txt[len] = '\0';
+               else
+                       break;
+       }
+       wpa_printf(MSG_MSGDUMP, "%s[%s]", title, txt);
+       os_free(txt);
+}
+
+
+static int curl_cb_debug(CURL *curl, curl_infotype info, char *buf, size_t len,
+                        void *userdata)
+{
+       struct http_ctx *ctx = userdata;
+       switch (info) {
+       case CURLINFO_TEXT:
+               debug_dump(ctx, "CURLINFO_TEXT", buf, len);
+               break;
+       case CURLINFO_HEADER_IN:
+               debug_dump(ctx, "CURLINFO_HEADER_IN", buf, len);
+               break;
+       case CURLINFO_HEADER_OUT:
+               debug_dump(ctx, "CURLINFO_HEADER_OUT", buf, len);
+               break;
+       case CURLINFO_DATA_IN:
+               debug_dump(ctx, "CURLINFO_DATA_IN", buf, len);
+               break;
+       case CURLINFO_DATA_OUT:
+               debug_dump(ctx, "CURLINFO_DATA_OUT", buf, len);
+               break;
+       case CURLINFO_SSL_DATA_IN:
+               wpa_printf(MSG_DEBUG, "debug - CURLINFO_SSL_DATA_IN - %d",
+                          (int) len);
+               break;
+       case CURLINFO_SSL_DATA_OUT:
+               wpa_printf(MSG_DEBUG, "debug - CURLINFO_SSL_DATA_OUT - %d",
+                          (int) len);
+               break;
+       case CURLINFO_END:
+               wpa_printf(MSG_DEBUG, "debug - CURLINFO_END - %d",
+                          (int) len);
+               break;
+       }
+       return 0;
+}
+
+
+static size_t curl_cb_write(void *ptr, size_t size, size_t nmemb,
+                           void *userdata)
+{
+       struct http_ctx *ctx = userdata;
+       char *n;
+       n = os_realloc(ctx->curl_buf, ctx->curl_buf_len + size * nmemb + 1);
+       if (n == NULL)
+               return 0;
+       ctx->curl_buf = n;
+       os_memcpy(n + ctx->curl_buf_len, ptr, size * nmemb);
+       n[ctx->curl_buf_len + size * nmemb] = '\0';
+       ctx->curl_buf_len += size * nmemb;
+       return size * nmemb;
+}
+
+
+#ifdef EAP_TLS_OPENSSL
+
+static void debug_dump_cert(const char *title, X509 *cert)
+{
+       BIO *out;
+       char *txt;
+       size_t rlen;
+
+       out = BIO_new(BIO_s_mem());
+       if (!out)
+               return;
+
+       X509_print_ex(out, cert, XN_FLAG_COMPAT, X509_FLAG_COMPAT);
+       rlen = BIO_ctrl_pending(out);
+       txt = os_malloc(rlen + 1);
+       if (txt) {
+               int res = BIO_read(out, txt, rlen);
+               if (res > 0) {
+                       txt[res] = '\0';
+                       wpa_printf(MSG_MSGDUMP, "%s:\n%s", title, txt);
+               }
+               os_free(txt);
+       }
+       BIO_free(out);
+}
+
+
+static void add_alt_name_othername(struct http_ctx *ctx, struct http_cert *cert,
+                                  OTHERNAME *o)
+{
+       char txt[100];
+       int res;
+       struct http_othername *on;
+       ASN1_TYPE *val;
+
+       on = os_realloc_array(cert->othername, cert->num_othername + 1,
+                             sizeof(struct http_othername));
+       if (on == NULL)
+               return;
+       cert->othername = on;
+       on = &on[cert->num_othername];
+       os_memset(on, 0, sizeof(*on));
+
+       res = OBJ_obj2txt(txt, sizeof(txt), o->type_id, 1);
+       if (res < 0 || res >= (int) sizeof(txt))
+               return;
+
+       on->oid = os_strdup(txt);
+       if (on->oid == NULL)
+               return;
+
+       val = o->value;
+       on->data = val->value.octet_string->data;
+       on->len = val->value.octet_string->length;
+
+       cert->num_othername++;
+}
+
+
+static void add_alt_name_dns(struct http_ctx *ctx, struct http_cert *cert,
+                            ASN1_STRING *name)
+{
+       char *buf;
+       char **n;
+
+       buf = NULL;
+       if (ASN1_STRING_to_UTF8((unsigned char **) &buf, name) < 0)
+               return;
+
+       n = os_realloc_array(cert->dnsname, cert->num_dnsname + 1,
+                            sizeof(char *));
+       if (n == NULL)
+               return;
+
+       cert->dnsname = n;
+       n[cert->num_dnsname] = buf;
+       cert->num_dnsname++;
+}
+
+
+static void add_alt_name(struct http_ctx *ctx, struct http_cert *cert,
+                        const GENERAL_NAME *name)
+{
+       switch (name->type) {
+       case GEN_OTHERNAME:
+               add_alt_name_othername(ctx, cert, name->d.otherName);
+               break;
+       case GEN_DNS:
+               add_alt_name_dns(ctx, cert, name->d.dNSName);
+               break;
+       }
+}
+
+
+static void add_alt_names(struct http_ctx *ctx, struct http_cert *cert,
+                         GENERAL_NAMES *names)
+{
+       int num, i;
+
+       num = sk_GENERAL_NAME_num(names);
+       for (i = 0; i < num; i++) {
+               const GENERAL_NAME *name;
+               name = sk_GENERAL_NAME_value(names, i);
+               add_alt_name(ctx, cert, name);
+       }
+}
+
+
+/* RFC 3709 */
+
+typedef struct {
+       X509_ALGOR *hashAlg;
+       ASN1_OCTET_STRING *hashValue;
+} HashAlgAndValue;
+
+typedef struct {
+       STACK_OF(HashAlgAndValue) *refStructHash;
+       STACK_OF(ASN1_IA5STRING) *refStructURI;
+} LogotypeReference;
+
+typedef struct {
+       ASN1_IA5STRING *mediaType;
+       STACK_OF(HashAlgAndValue) *logotypeHash;
+       STACK_OF(ASN1_IA5STRING) *logotypeURI;
+} LogotypeDetails;
+
+typedef struct {
+       int type;
+       union {
+               ASN1_INTEGER *numBits;
+               ASN1_INTEGER *tableSize;
+       } d;
+} LogotypeImageResolution;
+
+typedef struct {
+       ASN1_INTEGER *type; /* LogotypeImageType ::= INTEGER */
+       ASN1_INTEGER *fileSize;
+       ASN1_INTEGER *xSize;
+       ASN1_INTEGER *ySize;
+       LogotypeImageResolution *resolution;
+       ASN1_IA5STRING *language;
+} LogotypeImageInfo;
+
+typedef struct {
+       LogotypeDetails *imageDetails;
+       LogotypeImageInfo *imageInfo;
+} LogotypeImage;
+
+typedef struct {
+       ASN1_INTEGER *fileSize;
+       ASN1_INTEGER *playTime;
+       ASN1_INTEGER *channels;
+       ASN1_INTEGER *sampleRate;
+       ASN1_IA5STRING *language;
+} LogotypeAudioInfo;
+
+typedef struct {
+       LogotypeDetails *audioDetails;
+       LogotypeAudioInfo *audioInfo;
+} LogotypeAudio;
+
+typedef struct {
+       STACK_OF(LogotypeImage) *image;
+       STACK_OF(LogotypeAudio) *audio;
+} LogotypeData;
+
+typedef struct {
+       int type;
+       union {
+               LogotypeData *direct;
+               LogotypeReference *indirect;
+       } d;
+} LogotypeInfo;
+
+typedef struct {
+       ASN1_OBJECT *logotypeType;
+       LogotypeInfo *info;
+} OtherLogotypeInfo;
+
+typedef struct {
+       STACK_OF(LogotypeInfo) *communityLogos;
+       LogotypeInfo *issuerLogo;
+       LogotypeInfo *subjectLogo;
+       STACK_OF(OtherLogotypeInfo) *otherLogos;
+} LogotypeExtn;
+
+ASN1_SEQUENCE(HashAlgAndValue) = {
+       ASN1_SIMPLE(HashAlgAndValue, hashAlg, X509_ALGOR),
+       ASN1_SIMPLE(HashAlgAndValue, hashValue, ASN1_OCTET_STRING)
+} ASN1_SEQUENCE_END(HashAlgAndValue);
+
+ASN1_SEQUENCE(LogotypeReference) = {
+       ASN1_SEQUENCE_OF(LogotypeReference, refStructHash, HashAlgAndValue),
+       ASN1_SEQUENCE_OF(LogotypeReference, refStructURI, ASN1_IA5STRING)
+} ASN1_SEQUENCE_END(LogotypeReference);
+
+ASN1_SEQUENCE(LogotypeDetails) = {
+       ASN1_SIMPLE(LogotypeDetails, mediaType, ASN1_IA5STRING),
+       ASN1_SEQUENCE_OF(LogotypeDetails, logotypeHash, HashAlgAndValue),
+       ASN1_SEQUENCE_OF(LogotypeDetails, logotypeURI, ASN1_IA5STRING)
+} ASN1_SEQUENCE_END(LogotypeDetails);
+
+ASN1_CHOICE(LogotypeImageResolution) = {
+       ASN1_IMP(LogotypeImageResolution, d.numBits, ASN1_INTEGER, 1),
+       ASN1_IMP(LogotypeImageResolution, d.tableSize, ASN1_INTEGER, 2)
+} ASN1_CHOICE_END(LogotypeImageResolution);
+
+ASN1_SEQUENCE(LogotypeImageInfo) = {
+       ASN1_IMP_OPT(LogotypeImageInfo, type, ASN1_INTEGER, 0),
+       ASN1_SIMPLE(LogotypeImageInfo, fileSize, ASN1_INTEGER),
+       ASN1_SIMPLE(LogotypeImageInfo, xSize, ASN1_INTEGER),
+       ASN1_SIMPLE(LogotypeImageInfo, ySize, ASN1_INTEGER),
+       ASN1_OPT(LogotypeImageInfo, resolution, LogotypeImageResolution),
+       ASN1_IMP_OPT(LogotypeImageInfo, language, ASN1_IA5STRING, 4),
+} ASN1_SEQUENCE_END(LogotypeImageInfo);
+
+ASN1_SEQUENCE(LogotypeImage) = {
+       ASN1_SIMPLE(LogotypeImage, imageDetails, LogotypeDetails),
+       ASN1_OPT(LogotypeImage, imageInfo, LogotypeImageInfo)
+} ASN1_SEQUENCE_END(LogotypeImage);
+
+ASN1_SEQUENCE(LogotypeAudioInfo) = {
+       ASN1_SIMPLE(LogotypeAudioInfo, fileSize, ASN1_INTEGER),
+       ASN1_SIMPLE(LogotypeAudioInfo, playTime, ASN1_INTEGER),
+       ASN1_SIMPLE(LogotypeAudioInfo, channels, ASN1_INTEGER),
+       ASN1_IMP_OPT(LogotypeAudioInfo, sampleRate, ASN1_INTEGER, 3),
+       ASN1_IMP_OPT(LogotypeAudioInfo, language, ASN1_IA5STRING, 4)
+} ASN1_SEQUENCE_END(LogotypeAudioInfo);
+
+ASN1_SEQUENCE(LogotypeAudio) = {
+       ASN1_SIMPLE(LogotypeAudio, audioDetails, LogotypeDetails),
+       ASN1_OPT(LogotypeAudio, audioInfo, LogotypeAudioInfo)
+} ASN1_SEQUENCE_END(LogotypeAudio);
+
+ASN1_SEQUENCE(LogotypeData) = {
+       ASN1_SEQUENCE_OF_OPT(LogotypeData, image, LogotypeImage),
+       ASN1_IMP_SEQUENCE_OF_OPT(LogotypeData, audio, LogotypeAudio, 1)
+} ASN1_SEQUENCE_END(LogotypeData);
+
+ASN1_CHOICE(LogotypeInfo) = {
+       ASN1_IMP(LogotypeInfo, d.direct, LogotypeData, 0),
+       ASN1_IMP(LogotypeInfo, d.indirect, LogotypeReference, 1)
+} ASN1_CHOICE_END(LogotypeInfo);
+
+ASN1_SEQUENCE(OtherLogotypeInfo) = {
+       ASN1_SIMPLE(OtherLogotypeInfo, logotypeType, ASN1_OBJECT),
+       ASN1_SIMPLE(OtherLogotypeInfo, info, LogotypeInfo)
+} ASN1_SEQUENCE_END(OtherLogotypeInfo);
+
+ASN1_SEQUENCE(LogotypeExtn) = {
+       ASN1_EXP_SEQUENCE_OF_OPT(LogotypeExtn, communityLogos, LogotypeInfo, 0),
+       ASN1_EXP_OPT(LogotypeExtn, issuerLogo, LogotypeInfo, 1),
+       ASN1_EXP_OPT(LogotypeExtn, issuerLogo, LogotypeInfo, 2),
+       ASN1_EXP_SEQUENCE_OF_OPT(LogotypeExtn, otherLogos, OtherLogotypeInfo, 3)
+} ASN1_SEQUENCE_END(LogotypeExtn);
+
+IMPLEMENT_ASN1_FUNCTIONS(LogotypeExtn);
+
+#define sk_LogotypeInfo_num(st) SKM_sk_num(LogotypeInfo, (st))
+#define sk_LogotypeInfo_value(st, i) SKM_sk_value(LogotypeInfo, (st), (i))
+#define sk_LogotypeImage_num(st) SKM_sk_num(LogotypeImage, (st))
+#define sk_LogotypeImage_value(st, i) SKM_sk_value(LogotypeImage, (st), (i))
+#define sk_LogotypeAudio_num(st) SKM_sk_num(LogotypeAudio, (st))
+#define sk_LogotypeAudio_value(st, i) SKM_sk_value(LogotypeAudio, (st), (i))
+#define sk_HashAlgAndValue_num(st) SKM_sk_num(HashAlgAndValue, (st))
+#define sk_HashAlgAndValue_value(st, i) SKM_sk_value(HashAlgAndValue, (st), (i))
+#define sk_ASN1_IA5STRING_num(st) SKM_sk_num(ASN1_IA5STRING, (st))
+#define sk_ASN1_IA5STRING_value(st, i) SKM_sk_value(ASN1_IA5STRING, (st), (i))
+
+
+static void add_logo(struct http_ctx *ctx, struct http_cert *hcert,
+                    HashAlgAndValue *hash, ASN1_IA5STRING *uri)
+{
+       char txt[100];
+       int res, len;
+       struct http_logo *n;
+
+       if (hash == NULL || uri == NULL)
+               return;
+
+       res = OBJ_obj2txt(txt, sizeof(txt), hash->hashAlg->algorithm, 1);
+       if (res < 0 || res >= (int) sizeof(txt))
+               return;
+
+       n = os_realloc_array(hcert->logo, hcert->num_logo + 1,
+                            sizeof(struct http_logo));
+       if (n == NULL)
+               return;
+       hcert->logo = n;
+       n = &hcert->logo[hcert->num_logo];
+       os_memset(n, 0, sizeof(*n));
+
+       n->alg_oid = os_strdup(txt);
+       if (n->alg_oid == NULL)
+               return;
+
+       n->hash_len = ASN1_STRING_length(hash->hashValue);
+       n->hash = os_malloc(n->hash_len);
+       if (n->hash == NULL) {
+               os_free(n->alg_oid);
+               return;
+       }
+       os_memcpy(n->hash, ASN1_STRING_data(hash->hashValue), n->hash_len);
+
+       len = ASN1_STRING_length(uri);
+       n->uri = os_malloc(len + 1);
+       if (n->uri == NULL) {
+               os_free(n->alg_oid);
+               os_free(n->hash);
+               return;
+       }
+       os_memcpy(n->uri, ASN1_STRING_data(uri), len);
+       n->uri[len] = '\0';
+
+       hcert->num_logo++;
+}
+
+
+static void add_logo_direct(struct http_ctx *ctx, struct http_cert *hcert,
+                           LogotypeData *data)
+{
+       int i, num;
+
+       if (data->image == NULL)
+               return;
+
+       num = sk_LogotypeImage_num(data->image);
+       for (i = 0; i < num; i++) {
+               LogotypeImage *image;
+               LogotypeDetails *details;
+               int j, hash_num, uri_num;
+               HashAlgAndValue *found_hash = NULL;
+
+               image = sk_LogotypeImage_value(data->image, i);
+               if (image == NULL)
+                       continue;
+
+               details = image->imageDetails;
+               if (details == NULL)
+                       continue;
+
+               hash_num = sk_HashAlgAndValue_num(details->logotypeHash);
+               for (j = 0; j < hash_num; j++) {
+                       HashAlgAndValue *hash;
+                       char txt[100];
+                       int res;
+                       hash = sk_HashAlgAndValue_value(details->logotypeHash,
+                                                       j);
+                       if (hash == NULL)
+                               continue;
+                       res = OBJ_obj2txt(txt, sizeof(txt),
+                                         hash->hashAlg->algorithm, 1);
+                       if (res < 0 || res >= (int) sizeof(txt))
+                               continue;
+                       if (os_strcmp(txt, "2.16.840.1.101.3.4.2.1") == 0) {
+                               found_hash = hash;
+                               break;
+                       }
+               }
+
+               if (!found_hash) {
+                       wpa_printf(MSG_DEBUG, "OpenSSL: No SHA256 hash found for the logo");
+                       continue;
+               }
+
+               uri_num = sk_ASN1_IA5STRING_num(details->logotypeURI);
+               for (j = 0; j < uri_num; j++) {
+                       ASN1_IA5STRING *uri;
+                       uri = sk_ASN1_IA5STRING_value(details->logotypeURI, j);
+                       add_logo(ctx, hcert, found_hash, uri);
+               }
+       }
+}
+
+
+static void add_logo_indirect(struct http_ctx *ctx, struct http_cert *hcert,
+                             LogotypeReference *ref)
+{
+       int j, hash_num, uri_num;
+
+       hash_num = sk_HashAlgAndValue_num(ref->refStructHash);
+       uri_num = sk_ASN1_IA5STRING_num(ref->refStructURI);
+       if (hash_num != uri_num) {
+               wpa_printf(MSG_INFO, "Unexpected LogotypeReference array size difference %d != %d",
+                          hash_num, uri_num);
+               return;
+       }
+
+       for (j = 0; j < hash_num; j++) {
+               HashAlgAndValue *hash;
+               ASN1_IA5STRING *uri;
+               hash = sk_HashAlgAndValue_value(ref->refStructHash, j);
+               uri = sk_ASN1_IA5STRING_value(ref->refStructURI, j);
+               add_logo(ctx, hcert, hash, uri);
+       }
+}
+
+
+static void i2r_HashAlgAndValue(HashAlgAndValue *hash, BIO *out, int indent)
+{
+       int i;
+       const unsigned char *data;
+
+       BIO_printf(out, "%*shashAlg: ", indent, "");
+       i2a_ASN1_OBJECT(out, hash->hashAlg->algorithm);
+       BIO_printf(out, "\n");
+
+       BIO_printf(out, "%*shashValue: ", indent, "");
+       data = hash->hashValue->data;
+       for (i = 0; i < hash->hashValue->length; i++)
+               BIO_printf(out, "%s%02x", i > 0 ? ":" : "", data[i]);
+       BIO_printf(out, "\n");
+}
+
+static void i2r_LogotypeDetails(LogotypeDetails *details, BIO *out, int indent)
+{
+       int i, num;
+
+       BIO_printf(out, "%*sLogotypeDetails\n", indent, "");
+       if (details->mediaType) {
+               BIO_printf(out, "%*smediaType: ", indent, "");
+               ASN1_STRING_print(out, details->mediaType);
+               BIO_printf(out, "\n");
+       }
+
+       num = details->logotypeHash ?
+               sk_HashAlgAndValue_num(details->logotypeHash) : 0;
+       for (i = 0; i < num; i++) {
+               HashAlgAndValue *hash;
+               hash = sk_HashAlgAndValue_value(details->logotypeHash, i);
+               i2r_HashAlgAndValue(hash, out, indent);
+       }
+
+       num = details->logotypeURI ?
+               sk_ASN1_IA5STRING_num(details->logotypeURI) : 0;
+       for (i = 0; i < num; i++) {
+               ASN1_IA5STRING *uri;
+               uri = sk_ASN1_IA5STRING_value(details->logotypeURI, i);
+               BIO_printf(out, "%*slogotypeURI: ", indent, "");
+               ASN1_STRING_print(out, uri);
+               BIO_printf(out, "\n");
+       }
+}
+
+static void i2r_LogotypeImageInfo(LogotypeImageInfo *info, BIO *out, int indent)
+{
+       long val;
+
+       BIO_printf(out, "%*sLogotypeImageInfo\n", indent, "");
+       if (info->type) {
+               val = ASN1_INTEGER_get(info->type);
+               BIO_printf(out, "%*stype: %ld\n", indent, "", val);
+       } else {
+               BIO_printf(out, "%*stype: default (1)\n", indent, "");
+       }
+       val = ASN1_INTEGER_get(info->xSize);
+       BIO_printf(out, "%*sxSize: %ld\n", indent, "", val);
+       val = ASN1_INTEGER_get(info->ySize);
+       BIO_printf(out, "%*sySize: %ld\n", indent, "", val);
+       if (info->resolution) {
+               BIO_printf(out, "%*sresolution\n", indent, "");
+               /* TODO */
+       }
+       if (info->language) {
+               BIO_printf(out, "%*slanguage: ", indent, "");
+               ASN1_STRING_print(out, info->language);
+               BIO_printf(out, "\n");
+       }
+}
+
+static void i2r_LogotypeImage(LogotypeImage *image, BIO *out, int indent)
+{
+       BIO_printf(out, "%*sLogotypeImage\n", indent, "");
+       if (image->imageDetails) {
+               i2r_LogotypeDetails(image->imageDetails, out, indent + 4);
+       }
+       if (image->imageInfo) {
+               i2r_LogotypeImageInfo(image->imageInfo, out, indent + 4);
+       }
+}
+
+static void i2r_LogotypeData(LogotypeData *data, const char *title, BIO *out,
+                            int indent)
+{
+       int i, num;
+
+       BIO_printf(out, "%*s%s - LogotypeData\n", indent, "", title);
+
+       num = data->image ? sk_LogotypeImage_num(data->image) : 0;
+       for (i = 0; i < num; i++) {
+               LogotypeImage *image = sk_LogotypeImage_value(data->image, i);
+               i2r_LogotypeImage(image, out, indent + 4);
+       }
+
+       num = data->audio ? sk_LogotypeAudio_num(data->audio) : 0;
+       for (i = 0; i < num; i++) {
+               BIO_printf(out, "%*saudio: TODO\n", indent, "");
+       }
+}
+
+static void i2r_LogotypeReference(LogotypeReference *ref, const char *title,
+                                 BIO *out, int indent)
+{
+       int i, hash_num, uri_num;
+
+       BIO_printf(out, "%*s%s - LogotypeReference\n", indent, "", title);
+
+       hash_num = ref->refStructHash ?
+               sk_HashAlgAndValue_num(ref->refStructHash) : 0;
+       uri_num = ref->refStructURI ?
+               sk_ASN1_IA5STRING_num(ref->refStructURI) : 0;
+       if (hash_num != uri_num) {
+               BIO_printf(out, "%*sUnexpected LogotypeReference array size difference %d != %d\n",
+                          indent, "", hash_num, uri_num);
+               return;
+       }
+
+       for (i = 0; i < hash_num; i++) {
+               HashAlgAndValue *hash;
+               ASN1_IA5STRING *uri;
+
+               hash = sk_HashAlgAndValue_value(ref->refStructHash, i);
+               i2r_HashAlgAndValue(hash, out, indent);
+
+               uri = sk_ASN1_IA5STRING_value(ref->refStructURI, i);
+               BIO_printf(out, "%*srefStructURI: ", indent, "");
+               ASN1_STRING_print(out, uri);
+               BIO_printf(out, "\n");
+       }
+}
+
+static void i2r_LogotypeInfo(LogotypeInfo *info, const char *title, BIO *out,
+                            int indent)
+{
+       switch (info->type) {
+       case 0:
+               i2r_LogotypeData(info->d.direct, title, out, indent);
+               break;
+       case 1:
+               i2r_LogotypeReference(info->d.indirect, title, out, indent);
+               break;
+       }
+}
+
+static void debug_print_logotypeext(LogotypeExtn *logo)
+{
+       BIO *out;
+       int i, num;
+       int indent = 0;
+
+       out = BIO_new_fp(stdout, BIO_NOCLOSE);
+       if (out == NULL)
+               return;
+
+       if (logo->communityLogos) {
+               num = sk_LogotypeInfo_num(logo->communityLogos);
+               for (i = 0; i < num; i++) {
+                       LogotypeInfo *info;
+                       info = sk_LogotypeInfo_value(logo->communityLogos, i);
+                       i2r_LogotypeInfo(info, "communityLogo", out, indent);
+               }
+       }
+
+       if (logo->issuerLogo) {
+               i2r_LogotypeInfo(logo->issuerLogo, "issuerLogo", out, indent );
+       }
+
+       if (logo->subjectLogo) {
+               i2r_LogotypeInfo(logo->subjectLogo, "subjectLogo", out, indent);
+       }
+
+       if (logo->otherLogos) {
+               BIO_printf(out, "%*sotherLogos - TODO\n", indent, "");
+       }
+
+       BIO_free(out);
+}
+
+
+static void add_logotype_ext(struct http_ctx *ctx, struct http_cert *hcert,
+                            X509 *cert)
+{
+       ASN1_OBJECT *obj;
+       int pos;
+       X509_EXTENSION *ext;
+       ASN1_OCTET_STRING *os;
+       LogotypeExtn *logo;
+       const unsigned char *data;
+       int i, num;
+
+       obj = OBJ_txt2obj("1.3.6.1.5.5.7.1.12", 0);
+       if (obj == NULL)
+               return;
+
+       pos = X509_get_ext_by_OBJ(cert, obj, -1);
+       if (pos < 0) {
+               wpa_printf(MSG_INFO, "No logotype extension included");
+               return;
+       }
+
+       wpa_printf(MSG_INFO, "Parsing logotype extension");
+       ext = X509_get_ext(cert, pos);
+       if (!ext) {
+               wpa_printf(MSG_INFO, "Could not get logotype extension");
+               return;
+       }
+
+       os = X509_EXTENSION_get_data(ext);
+       if (os == NULL) {
+               wpa_printf(MSG_INFO, "Could not get logotype extension data");
+               return;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "logotypeExtn",
+                   ASN1_STRING_data(os), ASN1_STRING_length(os));
+
+       data = ASN1_STRING_data(os);
+       logo = d2i_LogotypeExtn(NULL, &data, ASN1_STRING_length(os));
+       if (logo == NULL) {
+               wpa_printf(MSG_INFO, "Failed to parse logotypeExtn");
+               return;
+       }
+
+       if (wpa_debug_level < MSG_INFO)
+               debug_print_logotypeext(logo);
+
+       if (!logo->communityLogos) {
+               wpa_printf(MSG_INFO, "No communityLogos included");
+               LogotypeExtn_free(logo);
+               return;
+       }
+
+       num = sk_LogotypeInfo_num(logo->communityLogos);
+       for (i = 0; i < num; i++) {
+               LogotypeInfo *info;
+               info = sk_LogotypeInfo_value(logo->communityLogos, i);
+               switch (info->type) {
+               case 0:
+                       add_logo_direct(ctx, hcert, info->d.direct);
+                       break;
+               case 1:
+                       add_logo_indirect(ctx, hcert, info->d.indirect);
+                       break;
+               }
+       }
+
+       LogotypeExtn_free(logo);
+}
+
+
+static void parse_cert(struct http_ctx *ctx, struct http_cert *hcert,
+                      X509 *cert, GENERAL_NAMES **names)
+{
+       os_memset(hcert, 0, sizeof(*hcert));
+
+       *names = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL);
+       if (*names)
+               add_alt_names(ctx, hcert, *names);
+
+       add_logotype_ext(ctx, hcert, cert);
+}
+
+
+static void parse_cert_free(struct http_cert *hcert, GENERAL_NAMES *names)
+{
+       unsigned int i;
+
+       for (i = 0; i < hcert->num_dnsname; i++)
+               OPENSSL_free(hcert->dnsname[i]);
+       os_free(hcert->dnsname);
+
+       for (i = 0; i < hcert->num_othername; i++)
+               os_free(hcert->othername[i].oid);
+       os_free(hcert->othername);
+
+       for (i = 0; i < hcert->num_logo; i++) {
+               os_free(hcert->logo[i].alg_oid);
+               os_free(hcert->logo[i].hash);
+               os_free(hcert->logo[i].uri);
+       }
+       os_free(hcert->logo);
+
+       sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
+}
+
+
+static int validate_server_cert(struct http_ctx *ctx, X509 *cert)
+{
+       GENERAL_NAMES *names;
+       struct http_cert hcert;
+       int ret;
+
+       if (ctx->cert_cb == NULL)
+               return 0;
+
+       if (0) {
+               BIO *out;
+               out = BIO_new_fp(stdout, BIO_NOCLOSE);
+               X509_print_ex(out, cert, XN_FLAG_COMPAT, X509_FLAG_COMPAT);
+               BIO_free(out);
+       }
+
+       parse_cert(ctx, &hcert, cert, &names);
+       ret = ctx->cert_cb(ctx->cert_cb_ctx, &hcert);
+       parse_cert_free(&hcert, names);
+
+       return ret;
+}
+
+
+void http_parse_x509_certificate(struct http_ctx *ctx, const char *fname)
+{
+       BIO *in, *out;
+       X509 *cert;
+       GENERAL_NAMES *names;
+       struct http_cert hcert;
+       unsigned int i;
+
+       in = BIO_new_file(fname, "r");
+       if (in == NULL) {
+               wpa_printf(MSG_ERROR, "Could not read '%s'", fname);
+               return;
+       }
+
+       cert = d2i_X509_bio(in, NULL);
+       BIO_free(in);
+
+       if (cert == NULL) {
+               wpa_printf(MSG_ERROR, "Could not parse certificate");
+               return;
+       }
+
+       out = BIO_new_fp(stdout, BIO_NOCLOSE);
+       if (out) {
+               X509_print_ex(out, cert, XN_FLAG_COMPAT,
+                             X509_FLAG_COMPAT);
+               BIO_free(out);
+       }
+
+       wpa_printf(MSG_INFO, "Additional parsing information:");
+       parse_cert(ctx, &hcert, cert, &names);
+       for (i = 0; i < hcert.num_othername; i++) {
+               if (os_strcmp(hcert.othername[i].oid,
+                             "1.3.6.1.4.1.40808.1.1.1") == 0) {
+                       char *name = os_zalloc(hcert.othername[i].len + 1);
+                       if (name) {
+                               os_memcpy(name, hcert.othername[i].data,
+                                         hcert.othername[i].len);
+                               wpa_printf(MSG_INFO,
+                                          "id-wfa-hotspot-friendlyName: %s",
+                                          name);
+                               os_free(name);
+                       }
+                       wpa_hexdump_ascii(MSG_INFO,
+                                         "id-wfa-hotspot-friendlyName",
+                                         hcert.othername[i].data,
+                                         hcert.othername[i].len);
+               } else {
+                       wpa_printf(MSG_INFO, "subjAltName[othername]: oid=%s",
+                                  hcert.othername[i].oid);
+                       wpa_hexdump_ascii(MSG_INFO, "unknown othername",
+                                         hcert.othername[i].data,
+                                         hcert.othername[i].len);
+               }
+       }
+       parse_cert_free(&hcert, names);
+
+       X509_free(cert);
+}
+
+
+static int curl_cb_ssl_verify(int preverify_ok, X509_STORE_CTX *x509_ctx)
+{
+       struct http_ctx *ctx;
+       X509 *cert;
+       int err, depth;
+       char buf[256];
+       X509_NAME *name;
+       const char *err_str;
+       SSL *ssl;
+       SSL_CTX *ssl_ctx;
+
+       ssl = X509_STORE_CTX_get_ex_data(x509_ctx,
+                                        SSL_get_ex_data_X509_STORE_CTX_idx());
+       ssl_ctx = ssl->ctx;
+       ctx = SSL_CTX_get_app_data(ssl_ctx);
+
+       wpa_printf(MSG_DEBUG, "curl_cb_ssl_verify");
+
+       err = X509_STORE_CTX_get_error(x509_ctx);
+       err_str = X509_verify_cert_error_string(err);
+       depth = X509_STORE_CTX_get_error_depth(x509_ctx);
+       cert = X509_STORE_CTX_get_current_cert(x509_ctx);
+       if (!cert) {
+               wpa_printf(MSG_INFO, "No server certificate available");
+               ctx->last_err = "No server certificate available";
+               return 0;
+       }
+
+       if (depth == 0)
+               ctx->peer_cert = cert;
+       else if (depth == 1)
+               ctx->peer_issuer = cert;
+       else if (depth == 2)
+               ctx->peer_issuer_issuer = cert;
+
+       name = X509_get_subject_name(cert);
+       X509_NAME_oneline(name, buf, sizeof(buf));
+       wpa_printf(MSG_INFO, "Server certificate chain - depth=%d err=%d (%s) subject=%s",
+                  depth, err, err_str, buf);
+       debug_dump_cert("Server certificate chain - certificate", cert);
+
+       if (depth == 0 && preverify_ok && validate_server_cert(ctx, cert) < 0)
+               return 0;
+
+       if (!preverify_ok)
+               ctx->last_err = "TLS validation failed";
+
+       return preverify_ok;
+}
+
+
+#ifdef HAVE_OCSP
+
+static void ocsp_debug_print_resp(OCSP_RESPONSE *rsp)
+{
+       BIO *out;
+       size_t rlen;
+       char *txt;
+       int res;
+
+       out = BIO_new(BIO_s_mem());
+       if (!out)
+               return;
+
+       OCSP_RESPONSE_print(out, rsp, 0);
+       rlen = BIO_ctrl_pending(out);
+       txt = os_malloc(rlen + 1);
+       if (!txt) {
+               BIO_free(out);
+               return;
+       }
+
+       res = BIO_read(out, txt, rlen);
+       if (res > 0) {
+               txt[res] = '\0';
+               wpa_printf(MSG_MSGDUMP, "OpenSSL: OCSP Response\n%s", txt);
+       }
+       os_free(txt);
+       BIO_free(out);
+}
+
+
+static void tls_show_errors(const char *func, const char *txt)
+{
+       unsigned long err;
+
+       wpa_printf(MSG_DEBUG, "OpenSSL: %s - %s %s",
+                  func, txt, ERR_error_string(ERR_get_error(), NULL));
+
+       while ((err = ERR_get_error())) {
+               wpa_printf(MSG_DEBUG, "OpenSSL: pending error: %s",
+                          ERR_error_string(err, NULL));
+       }
+}
+
+
+static int ocsp_resp_cb(SSL *s, void *arg)
+{
+       struct http_ctx *ctx = arg;
+       const unsigned char *p;
+       int len, status, reason;
+       OCSP_RESPONSE *rsp;
+       OCSP_BASICRESP *basic;
+       OCSP_CERTID *id;
+       ASN1_GENERALIZEDTIME *produced_at, *this_update, *next_update;
+       X509_STORE *store;
+       STACK_OF(X509) *certs = NULL;
+
+       len = SSL_get_tlsext_status_ocsp_resp(s, &p);
+       if (!p) {
+               wpa_printf(MSG_DEBUG, "OpenSSL: No OCSP response received");
+               if (ctx->ocsp == MANDATORY_OCSP)
+                       ctx->last_err = "No OCSP response received";
+               return (ctx->ocsp == MANDATORY_OCSP) ? 0 : 1;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "OpenSSL: OCSP response", p, len);
+
+       rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
+       if (!rsp) {
+               wpa_printf(MSG_INFO, "OpenSSL: Failed to parse OCSP response");
+               ctx->last_err = "Failed to parse OCSP response";
+               return 0;
+       }
+
+       ocsp_debug_print_resp(rsp);
+
+       status = OCSP_response_status(rsp);
+       if (status != OCSP_RESPONSE_STATUS_SUCCESSFUL) {
+               wpa_printf(MSG_INFO, "OpenSSL: OCSP responder error %d (%s)",
+                          status, OCSP_response_status_str(status));
+               ctx->last_err = "OCSP responder error";
+               return 0;
+       }
+
+       basic = OCSP_response_get1_basic(rsp);
+       if (!basic) {
+               wpa_printf(MSG_INFO, "OpenSSL: Could not find BasicOCSPResponse");
+               ctx->last_err = "Could not find BasicOCSPResponse";
+               return 0;
+       }
+
+       store = SSL_CTX_get_cert_store(s->ctx);
+       if (ctx->peer_issuer) {
+               wpa_printf(MSG_DEBUG, "OpenSSL: Add issuer");
+               debug_dump_cert("OpenSSL: Issuer certificate",
+                               ctx->peer_issuer);
+
+               if (X509_STORE_add_cert(store, ctx->peer_issuer) != 1) {
+                       tls_show_errors(__func__,
+                                       "OpenSSL: Could not add issuer to certificate store");
+               }
+               certs = sk_X509_new_null();
+               if (certs) {
+                       X509 *cert;
+                       cert = X509_dup(ctx->peer_issuer);
+                       if (cert && !sk_X509_push(certs, cert)) {
+                               tls_show_errors(
+                                       __func__,
+                                       "OpenSSL: Could not add issuer to OCSP responder trust store");
+                               X509_free(cert);
+                               sk_X509_free(certs);
+                               certs = NULL;
+                       }
+                       if (certs && ctx->peer_issuer_issuer) {
+                               cert = X509_dup(ctx->peer_issuer_issuer);
+                               if (cert && !sk_X509_push(certs, cert)) {
+                                       tls_show_errors(
+                                               __func__,
+                                               "OpenSSL: Could not add issuer's issuer to OCSP responder trust store");
+                                       X509_free(cert);
+                               }
+                       }
+               }
+       }
+
+       status = OCSP_basic_verify(basic, certs, store, OCSP_TRUSTOTHER);
+       sk_X509_pop_free(certs, X509_free);
+       if (status <= 0) {
+               tls_show_errors(__func__,
+                               "OpenSSL: OCSP response failed verification");
+               OCSP_BASICRESP_free(basic);
+               OCSP_RESPONSE_free(rsp);
+               ctx->last_err = "OCSP response failed verification";
+               return 0;
+       }
+
+       wpa_printf(MSG_DEBUG, "OpenSSL: OCSP response verification succeeded");
+
+       if (!ctx->peer_cert) {
+               wpa_printf(MSG_DEBUG, "OpenSSL: Peer certificate not available for OCSP status check");
+               OCSP_BASICRESP_free(basic);
+               OCSP_RESPONSE_free(rsp);
+               ctx->last_err = "Peer certificate not available for OCSP status check";
+               return 0;
+       }
+
+       if (!ctx->peer_issuer) {
+               wpa_printf(MSG_DEBUG, "OpenSSL: Peer issuer certificate not available for OCSP status check");
+               OCSP_BASICRESP_free(basic);
+               OCSP_RESPONSE_free(rsp);
+               ctx->last_err = "Peer issuer certificate not available for OCSP status check";
+               return 0;
+       }
+
+       id = OCSP_cert_to_id(NULL, ctx->peer_cert, ctx->peer_issuer);
+       if (!id) {
+               wpa_printf(MSG_DEBUG, "OpenSSL: Could not create OCSP certificate identifier");
+               OCSP_BASICRESP_free(basic);
+               OCSP_RESPONSE_free(rsp);
+               ctx->last_err = "Could not create OCSP certificate identifier";
+               return 0;
+       }
+
+       if (!OCSP_resp_find_status(basic, id, &status, &reason, &produced_at,
+                                  &this_update, &next_update)) {
+               wpa_printf(MSG_INFO, "OpenSSL: Could not find current server certificate from OCSP response%s",
+                          (ctx->ocsp == MANDATORY_OCSP) ? "" :
+                          " (OCSP not required)");
+               OCSP_BASICRESP_free(basic);
+               OCSP_RESPONSE_free(rsp);
+               if (ctx->ocsp == MANDATORY_OCSP)
+
+                       ctx->last_err = "Could not find current server certificate from OCSP response";
+               return (ctx->ocsp == MANDATORY_OCSP) ? 0 : 1;
+       }
+
+       if (!OCSP_check_validity(this_update, next_update, 5 * 60, -1)) {
+               tls_show_errors(__func__, "OpenSSL: OCSP status times invalid");
+               OCSP_BASICRESP_free(basic);
+               OCSP_RESPONSE_free(rsp);
+               ctx->last_err = "OCSP status times invalid";
+               return 0;
+       }
+
+       OCSP_BASICRESP_free(basic);
+       OCSP_RESPONSE_free(rsp);
+
+       wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status for server certificate: %s",
+                  OCSP_cert_status_str(status));
+
+       if (status == V_OCSP_CERTSTATUS_GOOD)
+               return 1;
+       if (status == V_OCSP_CERTSTATUS_REVOKED) {
+               ctx->last_err = "Server certificate has been revoked";
+               return 0;
+       }
+       if (ctx->ocsp == MANDATORY_OCSP) {
+               wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP required");
+               ctx->last_err = "OCSP status unknown";
+               return 0;
+       }
+       wpa_printf(MSG_DEBUG, "OpenSSL: OCSP status unknown, but OCSP was not required, so allow connection to continue");
+       return 1;
+}
+
+
+static SSL_METHOD patch_ssl_method;
+static const SSL_METHOD *real_ssl_method;
+
+static int curl_patch_ssl_new(SSL *s)
+{
+       SSL_CTX *ssl = s->ctx;
+       int ret;
+
+       ssl->method = real_ssl_method;
+       s->method = real_ssl_method;
+
+       ret = s->method->ssl_new(s);
+       SSL_set_tlsext_status_type(s, TLSEXT_STATUSTYPE_ocsp);
+
+       return ret;
+}
+
+#endif /* HAVE_OCSP */
+
+
+static CURLcode curl_cb_ssl(CURL *curl, void *sslctx, void *parm)
+{
+       struct http_ctx *ctx = parm;
+       SSL_CTX *ssl = sslctx;
+
+       wpa_printf(MSG_DEBUG, "curl_cb_ssl");
+       SSL_CTX_set_app_data(ssl, ctx);
+       SSL_CTX_set_verify(ssl, SSL_VERIFY_PEER, curl_cb_ssl_verify);
+
+#ifdef HAVE_OCSP
+       if (ctx->ocsp != NO_OCSP) {
+               SSL_CTX_set_tlsext_status_cb(ssl, ocsp_resp_cb);
+               SSL_CTX_set_tlsext_status_arg(ssl, ctx);
+
+               /*
+                * Use a temporary SSL_METHOD to get a callback on SSL_new()
+                * from libcurl since there is no proper callback registration
+                * available for this.
+                */
+               os_memset(&patch_ssl_method, 0, sizeof(patch_ssl_method));
+               patch_ssl_method.ssl_new = curl_patch_ssl_new;
+               real_ssl_method = ssl->method;
+               ssl->method = &patch_ssl_method;
+       }
+#endif /* HAVE_OCSP */
+
+       return CURLE_OK;
+}
+
+#endif /* EAP_TLS_OPENSSL */
+
+
+static CURL * setup_curl_post(struct http_ctx *ctx, const char *address,
+                             const char *ca_fname, const char *username,
+                             const char *password, const char *client_cert,
+                             const char *client_key)
+{
+       CURL *curl;
+
+       wpa_printf(MSG_DEBUG, "Start HTTP client: address=%s ca_fname=%s "
+                  "username=%s", address, ca_fname, username);
+
+       curl = curl_easy_init();
+       if (curl == NULL)
+               return NULL;
+
+       curl_easy_setopt(curl, CURLOPT_URL, address);
+       curl_easy_setopt(curl, CURLOPT_POST, 1L);
+       if (ca_fname) {
+               curl_easy_setopt(curl, CURLOPT_CAINFO, ca_fname);
+               curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
+#ifdef EAP_TLS_OPENSSL
+               curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, curl_cb_ssl);
+               curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, ctx);
+#endif /* EAP_TLS_OPENSSL */
+       } else {
+               curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
+       }
+       if (client_cert && client_key) {
+               curl_easy_setopt(curl, CURLOPT_SSLCERT, client_cert);
+               curl_easy_setopt(curl, CURLOPT_SSLKEY, client_key);
+       }
+       /* TODO: use curl_easy_getinfo() with CURLINFO_CERTINFO to fetch
+        * information about the server certificate */
+       curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L);
+       curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curl_cb_debug);
+       curl_easy_setopt(curl, CURLOPT_DEBUGDATA, ctx);
+       curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_cb_write);
+       curl_easy_setopt(curl, CURLOPT_WRITEDATA, ctx);
+       curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+       if (username) {
+               curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_ANYSAFE);
+               curl_easy_setopt(curl, CURLOPT_USERNAME, username);
+               curl_easy_setopt(curl, CURLOPT_PASSWORD, password);
+       }
+
+       return curl;
+}
+
+
+static int post_init_client(struct http_ctx *ctx, const char *address,
+                           const char *ca_fname, const char *username,
+                           const char *password, const char *client_cert,
+                           const char *client_key)
+{
+       char *pos;
+       int count;
+
+       clone_str(&ctx->svc_address, address);
+       clone_str(&ctx->svc_ca_fname, ca_fname);
+       clone_str(&ctx->svc_username, username);
+       clone_str(&ctx->svc_password, password);
+       clone_str(&ctx->svc_client_cert, client_cert);
+       clone_str(&ctx->svc_client_key, client_key);
+
+       /*
+        * Workaround for Apache "Hostname 'FOO' provided via SNI and hostname
+        * 'foo' provided via HTTP are different.
+        */
+       for (count = 0, pos = ctx->svc_address; count < 3 && pos && *pos;
+            pos++) {
+               if (*pos == '/')
+                       count++;
+               *pos = tolower(*pos);
+       }
+
+       ctx->curl = setup_curl_post(ctx, ctx->svc_address, ca_fname, username,
+                                   password, client_cert, client_key);
+       if (ctx->curl == NULL)
+               return -1;
+
+       return 0;
+}
+
+
+int soap_init_client(struct http_ctx *ctx, const char *address,
+                    const char *ca_fname, const char *username,
+                    const char *password, const char *client_cert,
+                    const char *client_key)
+{
+       if (post_init_client(ctx, address, ca_fname, username, password,
+                            client_cert, client_key) < 0)
+               return -1;
+
+       ctx->curl_hdr = curl_slist_append(ctx->curl_hdr,
+                                         "Content-Type: application/soap+xml");
+       ctx->curl_hdr = curl_slist_append(ctx->curl_hdr, "SOAPAction: ");
+       ctx->curl_hdr = curl_slist_append(ctx->curl_hdr, "Expect:");
+       curl_easy_setopt(ctx->curl, CURLOPT_HTTPHEADER, ctx->curl_hdr);
+
+       return 0;
+}
+
+
+int soap_reinit_client(struct http_ctx *ctx)
+{
+       char *address = NULL;
+       char *ca_fname = NULL;
+       char *username = NULL;
+       char *password = NULL;
+       char *client_cert = NULL;
+       char *client_key = NULL;
+       int ret;
+
+       clear_curl(ctx);
+
+       clone_str(&address, ctx->svc_address);
+       clone_str(&ca_fname, ctx->svc_ca_fname);
+       clone_str(&username, ctx->svc_username);
+       clone_str(&password, ctx->svc_password);
+       clone_str(&client_cert, ctx->svc_client_cert);
+       clone_str(&client_key, ctx->svc_client_key);
+
+       ret = soap_init_client(ctx, address, ca_fname, username, password,
+                              client_cert, client_key);
+       os_free(address);
+       os_free(ca_fname);
+       str_clear_free(username);
+       str_clear_free(password);
+       os_free(client_cert);
+       os_free(client_key);
+       return ret;
+}
+
+
+static void free_curl_buf(struct http_ctx *ctx)
+{
+       os_free(ctx->curl_buf);
+       ctx->curl_buf = NULL;
+       ctx->curl_buf_len = 0;
+}
+
+
+xml_node_t * soap_send_receive(struct http_ctx *ctx, xml_node_t *node)
+{
+       char *str;
+       xml_node_t *envelope, *ret, *resp, *n;
+       CURLcode res;
+       long http = 0;
+
+       ctx->last_err = NULL;
+
+       wpa_printf(MSG_DEBUG, "SOAP: Sending message");
+       envelope = soap_build_envelope(ctx->xml, node);
+       str = xml_node_to_str(ctx->xml, envelope);
+       xml_node_free(ctx->xml, envelope);
+       wpa_printf(MSG_MSGDUMP, "SOAP[%s]", str);
+
+       curl_easy_setopt(ctx->curl, CURLOPT_POSTFIELDS, str);
+       free_curl_buf(ctx);
+
+       res = curl_easy_perform(ctx->curl);
+       if (res != CURLE_OK) {
+               if (!ctx->last_err)
+                       ctx->last_err = curl_easy_strerror(res);
+               wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s",
+                          ctx->last_err);
+               os_free(str);
+               free_curl_buf(ctx);
+               return NULL;
+       }
+       os_free(str);
+
+       curl_easy_getinfo(ctx->curl, CURLINFO_RESPONSE_CODE, &http);
+       wpa_printf(MSG_DEBUG, "SOAP: Server response code %ld", http);
+       if (http != 200) {
+               ctx->last_err = "HTTP download failed";
+               wpa_printf(MSG_INFO, "HTTP download failed - code %ld", http);
+               free_curl_buf(ctx);
+               return NULL;
+       }
+
+       if (ctx->curl_buf == NULL)
+               return NULL;
+
+       wpa_printf(MSG_MSGDUMP, "Server response:\n%s", ctx->curl_buf);
+       resp = xml_node_from_buf(ctx->xml, ctx->curl_buf);
+       free_curl_buf(ctx);
+       if (resp == NULL) {
+               wpa_printf(MSG_INFO, "Could not parse SOAP response");
+               ctx->last_err = "Could not parse SOAP response";
+               return NULL;
+       }
+
+       ret = soap_get_body(ctx->xml, resp);
+       if (ret == NULL) {
+               wpa_printf(MSG_INFO, "Could not get SOAP body");
+               ctx->last_err = "Could not get SOAP body";
+               return NULL;
+       }
+
+       wpa_printf(MSG_DEBUG, "SOAP body localname: '%s'",
+                  xml_node_get_localname(ctx->xml, ret));
+       n = xml_node_copy(ctx->xml, ret);
+       xml_node_free(ctx->xml, resp);
+
+       return n;
+}
+
+
+struct http_ctx * http_init_ctx(void *upper_ctx, struct xml_node_ctx *xml_ctx)
+{
+       struct http_ctx *ctx;
+
+       ctx = os_zalloc(sizeof(*ctx));
+       if (ctx == NULL)
+               return NULL;
+       ctx->ctx = upper_ctx;
+       ctx->xml = xml_ctx;
+       ctx->ocsp = OPTIONAL_OCSP;
+
+       curl_global_init(CURL_GLOBAL_ALL);
+
+       return ctx;
+}
+
+
+void http_ocsp_set(struct http_ctx *ctx, int val)
+{
+       if (val == 0)
+               ctx->ocsp = NO_OCSP;
+       else if (val == 1)
+               ctx->ocsp = OPTIONAL_OCSP;
+       if (val == 2)
+               ctx->ocsp = MANDATORY_OCSP;
+}
+
+
+void http_deinit_ctx(struct http_ctx *ctx)
+{
+       clear_curl(ctx);
+       os_free(ctx->curl_buf);
+       curl_global_cleanup();
+
+       os_free(ctx->svc_address);
+       os_free(ctx->svc_ca_fname);
+       str_clear_free(ctx->svc_username);
+       str_clear_free(ctx->svc_password);
+       os_free(ctx->svc_client_cert);
+       os_free(ctx->svc_client_key);
+
+       os_free(ctx);
+}
+
+
+int http_download_file(struct http_ctx *ctx, const char *url,
+                      const char *fname, const char *ca_fname)
+{
+       CURL *curl;
+       FILE *f;
+       CURLcode res;
+       long http = 0;
+
+       ctx->last_err = NULL;
+
+       wpa_printf(MSG_DEBUG, "curl: Download file from %s to %s (ca=%s)",
+                  url, fname, ca_fname);
+       curl = curl_easy_init();
+       if (curl == NULL)
+               return -1;
+
+       f = fopen(fname, "wb");
+       if (f == NULL) {
+               curl_easy_cleanup(curl);
+               return -1;
+       }
+
+       curl_easy_setopt(curl, CURLOPT_URL, url);
+       if (ca_fname) {
+               curl_easy_setopt(curl, CURLOPT_CAINFO, ca_fname);
+               curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1L);
+               curl_easy_setopt(curl, CURLOPT_CERTINFO, 1L);
+       } else {
+               curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
+       }
+       curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, curl_cb_debug);
+       curl_easy_setopt(curl, CURLOPT_DEBUGDATA, ctx);
+       curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
+       curl_easy_setopt(curl, CURLOPT_WRITEDATA, f);
+       curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
+
+       res = curl_easy_perform(curl);
+       if (res != CURLE_OK) {
+               if (!ctx->last_err)
+                       ctx->last_err = curl_easy_strerror(res);
+               wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s",
+                          ctx->last_err);
+               curl_easy_cleanup(curl);
+               fclose(f);
+               return -1;
+       }
+
+       curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http);
+       wpa_printf(MSG_DEBUG, "curl: Server response code %ld", http);
+       if (http != 200) {
+               ctx->last_err = "HTTP download failed";
+               wpa_printf(MSG_INFO, "HTTP download failed - code %ld", http);
+               curl_easy_cleanup(curl);
+               fclose(f);
+               return -1;
+       }
+
+       curl_easy_cleanup(curl);
+       fclose(f);
+
+       return 0;
+}
+
+
+char * http_post(struct http_ctx *ctx, const char *url, const char *data,
+                const char *content_type, const char *ext_hdr,
+                const char *ca_fname,
+                const char *username, const char *password,
+                const char *client_cert, const char *client_key,
+                size_t *resp_len)
+{
+       long http = 0;
+       CURLcode res;
+       char *ret;
+       CURL *curl;
+       struct curl_slist *curl_hdr = NULL;
+
+       ctx->last_err = NULL;
+       wpa_printf(MSG_DEBUG, "curl: HTTP POST to %s", url);
+       curl = setup_curl_post(ctx, url, ca_fname, username, password,
+                              client_cert, client_key);
+       if (curl == NULL)
+               return NULL;
+
+       if (content_type) {
+               char ct[200];
+               snprintf(ct, sizeof(ct), "Content-Type: %s", content_type);
+               curl_hdr = curl_slist_append(curl_hdr, ct);
+       }
+       if (ext_hdr)
+               curl_hdr = curl_slist_append(curl_hdr, ext_hdr);
+       curl_easy_setopt(curl, CURLOPT_HTTPHEADER, curl_hdr);
+
+       curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data);
+       free_curl_buf(ctx);
+
+       res = curl_easy_perform(curl);
+       if (res != CURLE_OK) {
+               if (!ctx->last_err)
+                       ctx->last_err = curl_easy_strerror(res);
+               wpa_printf(MSG_ERROR, "curl_easy_perform() failed: %s",
+                          ctx->last_err);
+               free_curl_buf(ctx);
+               return NULL;
+       }
+
+       curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http);
+       wpa_printf(MSG_DEBUG, "curl: Server response code %ld", http);
+       if (http != 200) {
+               ctx->last_err = "HTTP POST failed";
+               wpa_printf(MSG_INFO, "HTTP POST failed - code %ld", http);
+               free_curl_buf(ctx);
+               return NULL;
+       }
+
+       if (ctx->curl_buf == NULL)
+               return NULL;
+
+       ret = ctx->curl_buf;
+       if (resp_len)
+               *resp_len = ctx->curl_buf_len;
+       ctx->curl_buf = NULL;
+       ctx->curl_buf_len = 0;
+
+       wpa_printf(MSG_MSGDUMP, "Server response:\n%s", ret);
+
+       return ret;
+}
+
+
+void http_set_cert_cb(struct http_ctx *ctx,
+                     int (*cb)(void *ctx, struct http_cert *cert),
+                     void *cb_ctx)
+{
+       ctx->cert_cb = cb;
+       ctx->cert_cb_ctx = cb_ctx;
+}
+
+
+const char * http_get_err(struct http_ctx *ctx)
+{
+       return ctx->last_err;
+}
index 3647c76..92a3590 100644 (file)
@@ -33,30 +33,6 @@ const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf,
 }
 
 
-int hostapd_ip_diff(struct hostapd_ip_addr *a, struct hostapd_ip_addr *b)
-{
-       if (a == NULL && b == NULL)
-               return 0;
-       if (a == NULL || b == NULL)
-               return 1;
-
-       switch (a->af) {
-       case AF_INET:
-               if (a->u.v4.s_addr != b->u.v4.s_addr)
-                       return 1;
-               break;
-#ifdef CONFIG_IPV6
-       case AF_INET6:
-               if (os_memcmp(&a->u.v6, &b->u.v6, sizeof(a->u.v6)) != 0)
-                       return 1;
-               break;
-#endif /* CONFIG_IPV6 */
-       }
-
-       return 0;
-}
-
-
 int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr)
 {
 #ifndef CONFIG_NATIVE_WINDOWS
index 79ac20c..0670411 100644 (file)
@@ -22,7 +22,6 @@ struct hostapd_ip_addr {
 
 const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf,
                            size_t buflen);
-int hostapd_ip_diff(struct hostapd_ip_addr *a, struct hostapd_ip_addr *b);
 int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr);
 
 #endif /* IP_ADDR_H */
index 6881130..ee2f485 100644 (file)
@@ -17,6 +17,8 @@ struct dl_list {
        struct dl_list *prev;
 };
 
+#define DL_LIST_HEAD_INIT(l) { &(l), &(l) }
+
 static inline void dl_list_init(struct dl_list *list)
 {
        list->next = list;
index ad20834..77250d6 100644 (file)
@@ -23,6 +23,11 @@ struct os_time {
        os_time_t usec;
 };
 
+struct os_reltime {
+       os_time_t sec;
+       os_time_t usec;
+};
+
 /**
  * os_get_time - Get current time (sec, usec)
  * @t: Pointer to buffer for the time
@@ -30,21 +35,84 @@ struct os_time {
  */
 int os_get_time(struct os_time *t);
 
+/**
+ * os_get_reltime - Get relative time (sec, usec)
+ * @t: Pointer to buffer for the time
+ * Returns: 0 on success, -1 on failure
+ */
+int os_get_reltime(struct os_reltime *t);
+
+
+/* Helpers for handling struct os_time */
+
+static inline int os_time_before(struct os_time *a, struct os_time *b)
+{
+       return (a->sec < b->sec) ||
+              (a->sec == b->sec && a->usec < b->usec);
+}
+
+
+static inline void os_time_sub(struct os_time *a, struct os_time *b,
+                              struct os_time *res)
+{
+       res->sec = a->sec - b->sec;
+       res->usec = a->usec - b->usec;
+       if (res->usec < 0) {
+               res->sec--;
+               res->usec += 1000000;
+       }
+}
+
+
+/* Helpers for handling struct os_reltime */
+
+static inline int os_reltime_before(struct os_reltime *a,
+                                   struct os_reltime *b)
+{
+       return (a->sec < b->sec) ||
+              (a->sec == b->sec && a->usec < b->usec);
+}
+
+
+static inline void os_reltime_sub(struct os_reltime *a, struct os_reltime *b,
+                                 struct os_reltime *res)
+{
+       res->sec = a->sec - b->sec;
+       res->usec = a->usec - b->usec;
+       if (res->usec < 0) {
+               res->sec--;
+               res->usec += 1000000;
+       }
+}
+
+
+static inline void os_reltime_age(struct os_reltime *start,
+                                 struct os_reltime *age)
+{
+       struct os_reltime now;
+
+       os_get_reltime(&now);
+       os_reltime_sub(&now, start, age);
+}
+
+
+static inline int os_reltime_expired(struct os_reltime *now,
+                                    struct os_reltime *ts,
+                                    os_time_t timeout_secs)
+{
+       struct os_reltime age;
+
+       os_reltime_sub(now, ts, &age);
+       return (age.sec > timeout_secs) ||
+              (age.sec == timeout_secs && age.usec > 0);
+}
 
-/* Helper macros for handling struct os_time */
 
-#define os_time_before(a, b) \
-       ((a)->sec < (b)->sec || \
-        ((a)->sec == (b)->sec && (a)->usec < (b)->usec))
+static inline int os_reltime_initialized(struct os_reltime *t)
+{
+       return t->sec != 0 || t->usec != 0;
+}
 
-#define os_time_sub(a, b, res) do { \
-       (res)->sec = (a)->sec - (b)->sec; \
-       (res)->usec = (a)->usec - (b)->usec; \
-       if ((res)->usec < 0) { \
-               (res)->sec--; \
-               (res)->usec += 1000000; \
-       } \
-} while (0)
 
 /**
  * os_mktime - Convert broken-down time into seconds since 1970-01-01
@@ -172,6 +240,13 @@ int os_unsetenv(const char *name);
 char * os_readfile(const char *name, size_t *len);
 
 /**
+ * os_file_exists - Check whether the specified file exists
+ * @fname: Path and name of the file
+ * Returns: 1 if the file exists or 0 if not
+ */
+int os_file_exists(const char *fname);
+
+/**
  * os_zalloc - Allocate and zero memory
  * @size: Number of bytes to allocate
  * Returns: Pointer to allocated and zeroed memory or %NULL on failure
@@ -361,15 +436,6 @@ int os_strcmp(const char *s1, const char *s2);
 int os_strncmp(const char *s1, const char *s2, size_t n);
 
 /**
- * os_strncpy - Copy a string
- * @dest: Destination
- * @src: Source
- * @n: Maximum number of characters to copy
- * Returns: dest
- */
-char * os_strncpy(char *dest, const char *src, size_t n);
-
-/**
  * os_strstr - Locate a substring
  * @haystack: String (haystack) to search from
  * @needle: Needle to search from haystack
@@ -465,9 +531,6 @@ char * os_strdup(const char *s);
 #ifndef os_strncmp
 #define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n))
 #endif
-#ifndef os_strncpy
-#define os_strncpy(d, s, n) strncpy((d), (s), (n))
-#endif
 #ifndef os_strrchr
 #define os_strrchr(s, c) strrchr((s), (c))
 #endif
@@ -486,6 +549,12 @@ char * os_strdup(const char *s);
 #endif /* OS_NO_C_LIB_DEFINES */
 
 
+static inline int os_snprintf_error(size_t size, int res)
+{
+       return res < 0 || (unsigned int) res >= size;
+}
+
+
 static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size)
 {
        if (size && nmemb > (~(size_t) 0) / size)
@@ -493,6 +562,21 @@ static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size)
        return os_realloc(ptr, nmemb * size);
 }
 
+/**
+ * os_remove_in_array - Remove a member from an array by index
+ * @ptr: Pointer to the array
+ * @nmemb: Current member count of the array
+ * @size: The size per member of the array
+ * @idx: Index of the member to be removed
+ */
+static inline void os_remove_in_array(void *ptr, size_t nmemb, size_t size,
+                                     size_t idx)
+{
+       if (idx < nmemb - 1)
+               os_memmove(((unsigned char *) ptr) + idx * size,
+                          ((unsigned char *) ptr) + (idx + 1) * size,
+                          (nmemb - idx - 1) * size);
+}
 
 /**
  * os_strlcpy - Copy a string with size bound and NUL-termination
@@ -506,6 +590,32 @@ static inline void * os_realloc_array(void *ptr, size_t nmemb, size_t size)
  */
 size_t os_strlcpy(char *dest, const char *src, size_t siz);
 
+/**
+ * os_memcmp_const - Constant time memory comparison
+ * @a: First buffer to compare
+ * @b: Second buffer to compare
+ * @len: Number of octets to compare
+ * Returns: 0 if buffers are equal, non-zero if not
+ *
+ * This function is meant for comparing passwords or hash values where
+ * difference in execution time could provide external observer information
+ * about the location of the difference in the memory buffers. The return value
+ * does not behave like os_memcmp(), i.e., os_memcmp_const() cannot be used to
+ * sort items into a defined order. Unlike os_memcmp(), execution time of
+ * os_memcmp_const() does not depend on the contents of the compared memory
+ * buffers, but only on the total compared length.
+ */
+int os_memcmp_const(const void *a, const void *b, size_t len);
+
+/**
+ * os_exec - Execute an external program
+ * @program: Path to the program
+ * @arg: Command line argument string
+ * @wait_completion: Whether to wait until the program execution completes
+ * Returns: 0 on success, -1 on error
+ */
+int os_exec(const char *program, const char *arg, int wait_completion);
+
 
 #ifdef OS_REJECT_C_LIB_FUNCTIONS
 #define malloc OS_DO_NOT_USE_malloc
index e4b7fdb..77733ad 100644 (file)
  */
 
 #include "includes.h"
+#include <time.h>
+#include <sys/wait.h>
 
 #undef OS_REJECT_C_LIB_FUNCTIONS
-#include "os.h"
+#include "common.h"
 
 void os_sleep(os_time_t sec, os_time_t usec)
 {
@@ -41,6 +43,17 @@ int os_get_time(struct os_time *t)
 }
 
 
+int os_get_reltime(struct os_reltime *t)
+{
+       int res;
+       struct timeval tv;
+       res = gettimeofday(&tv, NULL);
+       t->sec = tv.tv_sec;
+       t->usec = tv.tv_usec;
+       return res;
+}
+
+
 int os_mktime(int year, int month, int day, int hour, int min, int sec,
              os_time_t *t)
 {
@@ -85,7 +98,7 @@ int os_gmtime(os_time_t t, struct os_tm *tm)
 int os_daemonize(const char *pid_file)
 {
        if (daemon(0, 0)) {
-               perror("daemon");
+               wpa_printf(MSG_ERROR, "daemon: %s", strerror(errno));
                return -1;
        }
 
@@ -156,8 +169,8 @@ char * os_rel2abs_path(const char *rel_path)
                }
        }
 
-       cwd_len = strlen(cwd);
-       rel_len = strlen(rel_path);
+       cwd_len = os_strlen(cwd);
+       rel_len = os_strlen(rel_path);
        ret_len = cwd_len + 1 + rel_len + 1;
        ret = os_malloc(ret_len);
        if (ret) {
@@ -452,6 +465,20 @@ size_t os_strlcpy(char *dest, const char *src, size_t siz)
 }
 
 
+int os_memcmp_const(const void *a, const void *b, size_t len)
+{
+       const u8 *aa = a;
+       const u8 *bb = b;
+       size_t i;
+       u8 res;
+
+       for (res = 0, i = 0; i < len; i++)
+               res |= aa[i] ^ bb[i];
+
+       return res;
+}
+
+
 char * os_strstr(const char *haystack, const char *needle)
 {
        size_t len = os_strlen(needle);
@@ -481,3 +508,57 @@ int os_snprintf(char *str, size_t size, const char *format, ...)
                str[size - 1] = '\0';
        return ret;
 }
+
+
+int os_exec(const char *program, const char *arg, int wait_completion)
+{
+       pid_t pid;
+       int pid_status;
+
+       pid = fork();
+       if (pid < 0) {
+               wpa_printf(MSG_ERROR, "fork: %s", strerror(errno));
+               return -1;
+       }
+
+       if (pid == 0) {
+               /* run the external command in the child process */
+               const int MAX_ARG = 30;
+               char *_program, *_arg, *pos;
+               char *argv[MAX_ARG + 1];
+               int i;
+
+               _program = os_strdup(program);
+               _arg = os_strdup(arg);
+
+               argv[0] = _program;
+
+               i = 1;
+               pos = _arg;
+               while (i < MAX_ARG && pos && *pos) {
+                       while (*pos == ' ')
+                               pos++;
+                       if (*pos == '\0')
+                               break;
+                       argv[i++] = pos;
+                       pos = os_strchr(pos, ' ');
+                       if (pos)
+                               *pos++ = '\0';
+               }
+               argv[i] = NULL;
+
+               execv(program, argv);
+               wpa_printf(MSG_ERROR, "execv: %s", strerror(errno));
+               os_free(_program);
+               os_free(_arg);
+               exit(0);
+               return -1;
+       }
+
+       if (wait_completion) {
+               /* wait for the child process to complete in the parent */
+               waitpid(pid, &pid_status, 0);
+       }
+
+       return 0;
+}
index cabf73b..83fe025 100644 (file)
@@ -26,6 +26,12 @@ int os_get_time(struct os_time *t)
 }
 
 
+int os_get_reltime(struct os_reltime *t)
+{
+       return -1;
+}
+
+
 int os_mktime(int year, int month, int day, int hour, int min, int sec,
              os_time_t *t)
 {
@@ -212,6 +218,11 @@ size_t os_strlcpy(char *dest, const char *src, size_t size)
 }
 
 
+int os_memcmp_const(const void *a, const void *b, size_t len)
+{
+       return 0;
+}
+
 char * os_strstr(const char *haystack, const char *needle)
 {
        return NULL;
@@ -223,3 +234,9 @@ int os_snprintf(char *str, size_t size, const char *format, ...)
        return 0;
 }
 #endif /* OS_NO_C_LIB_DEFINES */
+
+
+int os_exec(const char *program, const char *arg, int wait_completion)
+{
+       return -1;
+}
index 40ad9d4..e0c1125 100644 (file)
@@ -9,23 +9,24 @@
 #include "includes.h"
 
 #include <time.h>
+#include <sys/wait.h>
 
 #ifdef ANDROID
-#include <linux/capability.h>
-#include <linux/prctl.h>
+#include <sys/capability.h>
+#include <sys/prctl.h>
 #include <private/android_filesystem_config.h>
 #endif /* ANDROID */
 
 #include "os.h"
+#include "common.h"
 
 #ifdef WPA_TRACE
 
-#include "common.h"
 #include "wpa_debug.h"
 #include "trace.h"
 #include "list.h"
 
-static struct dl_list alloc_list;
+static struct dl_list alloc_list = DL_LIST_HEAD_INIT(alloc_list);
 
 #define ALLOC_MAGIC 0xa84ef1b2
 #define FREED_MAGIC 0x67fd487a
@@ -60,6 +61,43 @@ int os_get_time(struct os_time *t)
 }
 
 
+int os_get_reltime(struct os_reltime *t)
+{
+#if defined(CLOCK_BOOTTIME)
+       static clockid_t clock_id = CLOCK_BOOTTIME;
+#elif defined(CLOCK_MONOTONIC)
+       static clockid_t clock_id = CLOCK_MONOTONIC;
+#else
+       static clockid_t clock_id = CLOCK_REALTIME;
+#endif
+       struct timespec ts;
+       int res;
+
+       while (1) {
+               res = clock_gettime(clock_id, &ts);
+               if (res == 0) {
+                       t->sec = ts.tv_sec;
+                       t->usec = ts.tv_nsec / 1000;
+                       return 0;
+               }
+               switch (clock_id) {
+#ifdef CLOCK_BOOTTIME
+               case CLOCK_BOOTTIME:
+                       clock_id = CLOCK_MONOTONIC;
+                       break;
+#endif
+#ifdef CLOCK_MONOTONIC
+               case CLOCK_MONOTONIC:
+                       clock_id = CLOCK_REALTIME;
+                       break;
+#endif
+               case CLOCK_REALTIME:
+                       return -1;
+               }
+       }
+}
+
+
 int os_mktime(int year, int month, int day, int hour, int min, int sec,
              os_time_t *t)
 {
@@ -268,7 +306,7 @@ int os_program_init(void)
        struct __user_cap_header_struct header;
        struct __user_cap_data_struct cap;
 
-       setgroups(sizeof(groups)/sizeof(groups[0]), groups);
+       setgroups(ARRAY_SIZE(groups), groups);
 
        prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
 
@@ -283,9 +321,6 @@ int os_program_init(void)
        capset(&header, &cap);
 #endif /* ANDROID */
 
-#ifdef WPA_TRACE
-       dl_list_init(&alloc_list);
-#endif /* WPA_TRACE */
        return 0;
 }
 
@@ -370,6 +405,16 @@ char * os_readfile(const char *name, size_t *len)
 }
 
 
+int os_file_exists(const char *fname)
+{
+       FILE *f = fopen(fname, "rb");
+       if (f == NULL)
+               return 0;
+       fclose(f);
+       return 1;
+}
+
+
 #ifndef WPA_TRACE
 void * os_zalloc(size_t size)
 {
@@ -403,11 +448,121 @@ size_t os_strlcpy(char *dest, const char *src, size_t siz)
 }
 
 
+int os_memcmp_const(const void *a, const void *b, size_t len)
+{
+       const u8 *aa = a;
+       const u8 *bb = b;
+       size_t i;
+       u8 res;
+
+       for (res = 0, i = 0; i < len; i++)
+               res |= aa[i] ^ bb[i];
+
+       return res;
+}
+
+
 #ifdef WPA_TRACE
 
+#if defined(WPA_TRACE_BFD) && defined(CONFIG_TESTING_OPTIONS)
+char wpa_trace_fail_func[256] = { 0 };
+unsigned int wpa_trace_fail_after;
+
+static int testing_fail_alloc(void)
+{
+       const char *func[WPA_TRACE_LEN];
+       size_t i, res, len;
+       char *pos, *next;
+       int match;
+
+       if (!wpa_trace_fail_after)
+               return 0;
+
+       res = wpa_trace_calling_func(func, WPA_TRACE_LEN);
+       i = 0;
+       if (i < res && os_strcmp(func[i], __func__) == 0)
+               i++;
+       if (i < res && os_strcmp(func[i], "os_malloc") == 0)
+               i++;
+       if (i < res && os_strcmp(func[i], "os_zalloc") == 0)
+               i++;
+       if (i < res && os_strcmp(func[i], "os_calloc") == 0)
+               i++;
+       if (i < res && os_strcmp(func[i], "os_realloc") == 0)
+               i++;
+       if (i < res && os_strcmp(func[i], "os_realloc_array") == 0)
+               i++;
+       if (i < res && os_strcmp(func[i], "os_strdup") == 0)
+               i++;
+
+       pos = wpa_trace_fail_func;
+
+       match = 0;
+       while (i < res) {
+               int allow_skip = 1;
+               int maybe = 0;
+
+               if (*pos == '=') {
+                       allow_skip = 0;
+                       pos++;
+               } else if (*pos == '?') {
+                       maybe = 1;
+                       pos++;
+               }
+               next = os_strchr(pos, ';');
+               if (next)
+                       len = next - pos;
+               else
+                       len = os_strlen(pos);
+               if (os_memcmp(pos, func[i], len) != 0) {
+                       if (maybe && next) {
+                               pos = next + 1;
+                               continue;
+                       }
+                       if (allow_skip) {
+                               i++;
+                               continue;
+                       }
+                       return 0;
+               }
+               if (!next) {
+                       match = 1;
+                       break;
+               }
+               pos = next + 1;
+               i++;
+       }
+       if (!match)
+               return 0;
+
+       wpa_trace_fail_after--;
+       if (wpa_trace_fail_after == 0) {
+               wpa_printf(MSG_INFO, "TESTING: fail allocation at %s",
+                          wpa_trace_fail_func);
+               for (i = 0; i < res; i++)
+                       wpa_printf(MSG_INFO, "backtrace[%d] = %s",
+                                  (int) i, func[i]);
+               return 1;
+       }
+
+       return 0;
+}
+
+#else
+
+static inline int testing_fail_alloc(void)
+{
+       return 0;
+}
+#endif
+
 void * os_malloc(size_t size)
 {
        struct os_alloc_trace *a;
+
+       if (testing_fail_alloc())
+               return NULL;
+
        a = malloc(sizeof(*a) + size);
        if (a == NULL)
                return NULL;
@@ -493,3 +648,57 @@ char * os_strdup(const char *s)
 }
 
 #endif /* WPA_TRACE */
+
+
+int os_exec(const char *program, const char *arg, int wait_completion)
+{
+       pid_t pid;
+       int pid_status;
+
+       pid = fork();
+       if (pid < 0) {
+               perror("fork");
+               return -1;
+       }
+
+       if (pid == 0) {
+               /* run the external command in the child process */
+               const int MAX_ARG = 30;
+               char *_program, *_arg, *pos;
+               char *argv[MAX_ARG + 1];
+               int i;
+
+               _program = os_strdup(program);
+               _arg = os_strdup(arg);
+
+               argv[0] = _program;
+
+               i = 1;
+               pos = _arg;
+               while (i < MAX_ARG && pos && *pos) {
+                       while (*pos == ' ')
+                               pos++;
+                       if (*pos == '\0')
+                               break;
+                       argv[i++] = pos;
+                       pos = os_strchr(pos, ' ');
+                       if (pos)
+                               *pos++ = '\0';
+               }
+               argv[i] = NULL;
+
+               execv(program, argv);
+               perror("execv");
+               os_free(_program);
+               os_free(_arg);
+               exit(0);
+               return -1;
+       }
+
+       if (wait_completion) {
+               /* wait for the child process to complete in the parent */
+               waitpid(pid, &pid_status, 0);
+       }
+
+       return 0;
+}
index 163cebe..296ea13 100644 (file)
@@ -12,6 +12,7 @@
 #include <wincrypt.h>
 
 #include "os.h"
+#include "common.h"
 
 void os_sleep(os_time_t sec, os_time_t usec)
 {
@@ -47,6 +48,17 @@ int os_get_time(struct os_time *t)
 }
 
 
+int os_get_reltime(struct os_reltime *t)
+{
+       /* consider using performance counters or so instead */
+       struct os_time now;
+       int res = os_get_time(&now);
+       t->sec = now.sec;
+       t->usec = now.usec;
+       return res;
+}
+
+
 int os_mktime(int year, int month, int day, int hour, int min, int sec,
              os_time_t *t)
 {
@@ -233,3 +245,23 @@ size_t os_strlcpy(char *dest, const char *src, size_t siz)
 
        return s - src - 1;
 }
+
+
+int os_memcmp_const(const void *a, const void *b, size_t len)
+{
+       const u8 *aa = a;
+       const u8 *bb = b;
+       size_t i;
+       u8 res;
+
+       for (res = 0, i = 0; i < len; i++)
+               res |= aa[i] ^ bb[i];
+
+       return res;
+}
+
+
+int os_exec(const char *program, const char *arg, int wait_completion)
+{
+       return -1;
+}
index 08510d0..6f5ea93 100644 (file)
@@ -281,77 +281,82 @@ static int scard_parse_fsp_templ(unsigned char *buf, size_t buf_len,
        wpa_hexdump(MSG_DEBUG, "SCARD: file header FSP template",
                    pos, end - pos);
 
-       while (pos + 1 < end) {
+       while (end - pos >= 2) {
+               unsigned char type, len;
+
+               type = pos[0];
+               len = pos[1];
                wpa_printf(MSG_MSGDUMP, "SCARD: file header TLV 0x%02x len=%d",
-                          pos[0], pos[1]);
-               if (pos + 2 + pos[1] > end)
+                          type, len);
+               pos += 2;
+
+               if (len > (unsigned int) (end - pos))
                        break;
 
-               switch (pos[0]) {
+               switch (type) {
                case USIM_TLV_FILE_DESC:
                        wpa_hexdump(MSG_MSGDUMP, "SCARD: File Descriptor TLV",
-                                   pos + 2, pos[1]);
+                                   pos, len);
                        break;
                case USIM_TLV_FILE_ID:
                        wpa_hexdump(MSG_MSGDUMP, "SCARD: File Identifier TLV",
-                                   pos + 2, pos[1]);
+                                   pos, len);
                        break;
                case USIM_TLV_DF_NAME:
                        wpa_hexdump(MSG_MSGDUMP, "SCARD: DF name (AID) TLV",
-                                   pos + 2, pos[1]);
+                                   pos, len);
                        break;
                case USIM_TLV_PROPR_INFO:
                        wpa_hexdump(MSG_MSGDUMP, "SCARD: Proprietary "
-                                   "information TLV", pos + 2, pos[1]);
+                                   "information TLV", pos, len);
                        break;
                case USIM_TLV_LIFE_CYCLE_STATUS:
                        wpa_hexdump(MSG_MSGDUMP, "SCARD: Life Cycle Status "
-                                   "Integer TLV", pos + 2, pos[1]);
+                                   "Integer TLV", pos, len);
                        break;
                case USIM_TLV_FILE_SIZE:
                        wpa_hexdump(MSG_MSGDUMP, "SCARD: File size TLV",
-                                   pos + 2, pos[1]);
-                       if ((pos[1] == 1 || pos[1] == 2) && file_len) {
-                               if (pos[1] == 1)
-                                       *file_len = (int) pos[2];
+                                   pos, len);
+                       if ((len == 1 || len == 2) && file_len) {
+                               if (len == 1)
+                                       *file_len = (int) pos[0];
                                else
-                                       *file_len = ((int) pos[2] << 8) |
-                                               (int) pos[3];
+                                       *file_len = WPA_GET_BE16(pos);
                                wpa_printf(MSG_DEBUG, "SCARD: file_size=%d",
                                           *file_len);
                        }
                        break;
                case USIM_TLV_TOTAL_FILE_SIZE:
                        wpa_hexdump(MSG_MSGDUMP, "SCARD: Total file size TLV",
-                                   pos + 2, pos[1]);
+                                   pos, len);
                        break;
                case USIM_TLV_PIN_STATUS_TEMPLATE:
                        wpa_hexdump(MSG_MSGDUMP, "SCARD: PIN Status Template "
-                                   "DO TLV", pos + 2, pos[1]);
-                       if (pos[1] >= 2 && pos[2] == USIM_PS_DO_TAG &&
-                           pos[3] >= 1 && ps_do) {
+                                   "DO TLV", pos, len);
+                       if (len >= 2 && pos[0] == USIM_PS_DO_TAG &&
+                           pos[1] >= 1 && ps_do) {
                                wpa_printf(MSG_DEBUG, "SCARD: PS_DO=0x%02x",
-                                          pos[4]);
-                               *ps_do = (int) pos[4];
+                                          pos[2]);
+                               *ps_do = (int) pos[2];
                        }
                        break;
                case USIM_TLV_SHORT_FILE_ID:
                        wpa_hexdump(MSG_MSGDUMP, "SCARD: Short File "
-                                   "Identifier (SFI) TLV", pos + 2, pos[1]);
+                                   "Identifier (SFI) TLV", pos, len);
                        break;
                case USIM_TLV_SECURITY_ATTR_8B:
                case USIM_TLV_SECURITY_ATTR_8C:
                case USIM_TLV_SECURITY_ATTR_AB:
                        wpa_hexdump(MSG_MSGDUMP, "SCARD: Security attribute "
-                                   "TLV", pos + 2, pos[1]);
+                                   "TLV", pos, len);
                        break;
                default:
                        wpa_hexdump(MSG_MSGDUMP, "SCARD: Unrecognized TLV",
-                                   pos, 2 + pos[1]);
+                                   pos, len);
                        break;
                }
 
-               pos += 2 + pos[1];
+               pos += len;
 
                if (pos == end)
                        return 0;
@@ -397,10 +402,12 @@ static int scard_get_aid(struct scard_data *scard, unsigned char *aid,
                unsigned char rid[5];
                unsigned char appl_code[2]; /* 0x1002 for 3G USIM */
        } *efdir;
-       unsigned char buf[127];
+       unsigned char buf[127], *aid_pos;
        size_t blen;
+       unsigned int aid_len = 0;
 
        efdir = (struct efdir *) buf;
+       aid_pos = &buf[4];
        blen = sizeof(buf);
        if (scard_select_file(scard, SCARD_FILE_EF_DIR, buf, &blen)) {
                wpa_printf(MSG_DEBUG, "SCARD: Failed to read EF_DIR");
@@ -449,14 +456,15 @@ static int scard_get_aid(struct scard_data *scard, unsigned char *aid,
                        continue;
                }
 
-               if (efdir->aid_len < 1 || efdir->aid_len > 16) {
-                       wpa_printf(MSG_DEBUG, "SCARD: Invalid AID length %d",
-                                  efdir->aid_len);
+               aid_len = efdir->aid_len;
+               if (aid_len < 1 || aid_len > 16) {
+                       wpa_printf(MSG_DEBUG, "SCARD: Invalid AID length %u",
+                                  aid_len);
                        continue;
                }
 
                wpa_hexdump(MSG_DEBUG, "SCARD: AID from EF_DIR record",
-                           efdir->rid, efdir->aid_len);
+                           aid_pos, aid_len);
 
                if (efdir->appl_code[0] == 0x10 &&
                    efdir->appl_code[1] == 0x02) {
@@ -472,30 +480,28 @@ static int scard_get_aid(struct scard_data *scard, unsigned char *aid,
                return -1;
        }
 
-       if (efdir->aid_len > maxlen) {
+       if (aid_len > maxlen) {
                wpa_printf(MSG_DEBUG, "SCARD: Too long AID");
                return -1;
        }
 
-       os_memcpy(aid, efdir->rid, efdir->aid_len);
+       os_memcpy(aid, aid_pos, aid_len);
 
-       return efdir->aid_len;
+       return aid_len;
 }
 
 
 /**
  * scard_init - Initialize SIM/USIM connection using PC/SC
- * @sim_type: Allowed SIM types (SIM, USIM, or both)
  * @reader: Reader name prefix to search for
  * Returns: Pointer to private data structure, or %NULL on failure
  *
  * This function is used to initialize SIM/USIM connection. PC/SC is used to
- * open connection to the SIM/USIM card and the card is verified to support the
- * selected sim_type. In addition, local flag is set if a PIN is needed to
- * access some of the card functions. Once the connection is not needed
- * anymore, scard_deinit() can be used to close it.
+ * open connection to the SIM/USIM card. In addition, local flag is set if a
+ * PIN is needed to access some of the card functions. Once the connection is
+ * not needed anymore, scard_deinit() can be used to close it.
  */
-struct scard_data * scard_init(scard_sim_type sim_type, const char *reader)
+struct scard_data * scard_init(const char *reader)
 {
        long ret;
        unsigned long len, pos;
@@ -612,20 +618,14 @@ struct scard_data * scard_init(scard_sim_type sim_type, const char *reader)
 
        blen = sizeof(buf);
 
-       scard->sim_type = SCARD_GSM_SIM;
-       if (sim_type == SCARD_USIM_ONLY || sim_type == SCARD_TRY_BOTH) {
-               wpa_printf(MSG_DEBUG, "SCARD: verifying USIM support");
-               if (_scard_select_file(scard, SCARD_FILE_MF, buf, &blen,
-                                      SCARD_USIM, NULL, 0)) {
-                       wpa_printf(MSG_DEBUG, "SCARD: USIM is not supported");
-                       if (sim_type == SCARD_USIM_ONLY)
-                               goto failed;
-                       wpa_printf(MSG_DEBUG, "SCARD: Trying to use GSM SIM");
-                       scard->sim_type = SCARD_GSM_SIM;
-               } else {
-                       wpa_printf(MSG_DEBUG, "SCARD: USIM is supported");
-                       scard->sim_type = SCARD_USIM;
-               }
+       wpa_printf(MSG_DEBUG, "SCARD: verifying USIM support");
+       if (_scard_select_file(scard, SCARD_FILE_MF, buf, &blen,
+                              SCARD_USIM, NULL, 0)) {
+               wpa_printf(MSG_DEBUG, "SCARD: USIM is not supported. Trying to use GSM SIM");
+               scard->sim_type = SCARD_GSM_SIM;
+       } else {
+               wpa_printf(MSG_DEBUG, "SCARD: USIM is supported");
+               scard->sim_type = SCARD_USIM;
        }
 
        if (scard->sim_type == SCARD_GSM_SIM) {
@@ -1104,7 +1104,7 @@ int scard_get_imsi(struct scard_data *scard, char *imsi, size_t *len)
        }
 
        if (scard->sim_type == SCARD_GSM_SIM) {
-               blen = (buf[2] << 8) | buf[3];
+               blen = WPA_GET_BE16(&buf[2]);
        } else {
                int file_size;
                if (scard_parse_fsp_templ(buf, blen, NULL, &file_size))
@@ -1178,7 +1178,7 @@ int scard_get_mnc_len(struct scard_data *scard)
        }
 
        if (scard->sim_type == SCARD_GSM_SIM) {
-               file_size = (buf[2] << 8) | buf[3];
+               file_size = WPA_GET_BE16(&buf[2]);
        } else {
                if (scard_parse_fsp_templ(buf, blen, NULL, &file_size))
                        return -3;
@@ -1245,6 +1245,7 @@ int scard_gsm_auth(struct scard_data *scard, const unsigned char *_rand,
                cmd[4] = 17;
                cmd[5] = 16;
                os_memcpy(cmd + 6, _rand, 16);
+               get_resp[0] = USIM_CLA;
        }
        len = sizeof(resp);
        ret = scard_transmit(scard, cmd, cmdlen, resp, &len);
@@ -1413,6 +1414,12 @@ int scard_umts_auth(struct scard_data *scard, const unsigned char *_rand,
                pos += IK_LEN;
                wpa_hexdump(MSG_DEBUG, "SCARD: IK", ik, IK_LEN);
 
+               if (end > pos) {
+                       wpa_hexdump(MSG_DEBUG,
+                                   "SCARD: Ignore extra data in end",
+                                   pos, end - pos);
+               }
+
                return 0;
        }
 
index b4ebc99..eacd2a2 100644 (file)
@@ -9,15 +9,8 @@
 #ifndef PCSC_FUNCS_H
 #define PCSC_FUNCS_H
 
-typedef enum {
-       SCARD_GSM_SIM_ONLY,
-       SCARD_USIM_ONLY,
-       SCARD_TRY_BOTH
-} scard_sim_type;
-
-
 #ifdef PCSC_FUNCS
-struct scard_data * scard_init(scard_sim_type sim_type, const char *reader);
+struct scard_data * scard_init(const char *reader);
 void scard_deinit(struct scard_data *scard);
 
 int scard_set_pin(struct scard_data *scard, const char *pin);
@@ -34,7 +27,7 @@ int scard_supports_umts(struct scard_data *scard);
 
 #else /* PCSC_FUNCS */
 
-#define scard_init(s, r) NULL
+#define scard_init(r) NULL
 #define scard_deinit(s) do { } while (0)
 #define scard_set_pin(s, p) -1
 #define scard_get_imsi(s, i, l) -1
diff --git a/src/utils/platform.h b/src/utils/platform.h
new file mode 100644 (file)
index 0000000..46cfe78
--- /dev/null
@@ -0,0 +1,21 @@
+#ifndef PLATFORM_H
+#define PLATFORM_H
+
+#include "includes.h"
+#include "common.h"
+
+#define le16_to_cpu            le_to_host16
+#define le32_to_cpu            le_to_host32
+
+#define get_unaligned(p)                                       \
+({                                                             \
+       struct packed_dummy_struct {                            \
+               typeof(*(p)) __val;                             \
+       } __attribute__((packed)) *__ptr = (void *) (p);        \
+                                                               \
+       __ptr->__val;                                           \
+})
+#define get_unaligned_le16(p)  le16_to_cpu(get_unaligned((uint16_t *)(p)))
+#define get_unaligned_le32(p)  le32_to_cpu(get_unaligned((uint32_t *)(p)))
+
+#endif /* PLATFORM_H */
index 804473f..f8f815a 100644 (file)
@@ -2,6 +2,7 @@
  * Radiotap parser
  *
  * Copyright 2007              Andy Green <andy@warmcat.com>
+ * Copyright 2009              Johannes Berg <johannes@sipsolutions.net>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
  * Alternatively, this software may be distributed under the terms of BSD
  * license.
  *
- * See README and COPYING for more details.
- *
- *
- * Modified for userspace by Johannes Berg <johannes@sipsolutions.net>
- * I only modified some things on top to ease syncing should bugs be found.
+ * See COPYING for more details.
  */
-
-#include "includes.h"
-
-#include "common.h"
 #include "radiotap_iter.h"
-
-#define le16_to_cpu            le_to_host16
-#define le32_to_cpu            le_to_host32
-#define __le32                 uint32_t
-#define ulong                  unsigned long
-#define unlikely(cond)         (cond)
-#define get_unaligned(p)                                       \
-({                                                             \
-       struct packed_dummy_struct {                            \
-               typeof(*(p)) __val;                             \
-       } __attribute__((packed)) *__ptr = (void *) (p);        \
-                                                               \
-       __ptr->__val;                                           \
-})
+#include "platform.h"
 
 /* function prototypes and related defs are in radiotap_iter.h */
 
+static const struct radiotap_align_size rtap_namespace_sizes[] = {
+       [IEEE80211_RADIOTAP_TSFT] = { .align = 8, .size = 8, },
+       [IEEE80211_RADIOTAP_FLAGS] = { .align = 1, .size = 1, },
+       [IEEE80211_RADIOTAP_RATE] = { .align = 1, .size = 1, },
+       [IEEE80211_RADIOTAP_CHANNEL] = { .align = 2, .size = 4, },
+       [IEEE80211_RADIOTAP_FHSS] = { .align = 2, .size = 2, },
+       [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = { .align = 1, .size = 1, },
+       [IEEE80211_RADIOTAP_DBM_ANTNOISE] = { .align = 1, .size = 1, },
+       [IEEE80211_RADIOTAP_LOCK_QUALITY] = { .align = 2, .size = 2, },
+       [IEEE80211_RADIOTAP_TX_ATTENUATION] = { .align = 2, .size = 2, },
+       [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = { .align = 2, .size = 2, },
+       [IEEE80211_RADIOTAP_DBM_TX_POWER] = { .align = 1, .size = 1, },
+       [IEEE80211_RADIOTAP_ANTENNA] = { .align = 1, .size = 1, },
+       [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = { .align = 1, .size = 1, },
+       [IEEE80211_RADIOTAP_DB_ANTNOISE] = { .align = 1, .size = 1, },
+       [IEEE80211_RADIOTAP_RX_FLAGS] = { .align = 2, .size = 2, },
+       [IEEE80211_RADIOTAP_TX_FLAGS] = { .align = 2, .size = 2, },
+       [IEEE80211_RADIOTAP_RTS_RETRIES] = { .align = 1, .size = 1, },
+       [IEEE80211_RADIOTAP_DATA_RETRIES] = { .align = 1, .size = 1, },
+       [IEEE80211_RADIOTAP_MCS] = { .align = 1, .size = 3, },
+       [IEEE80211_RADIOTAP_AMPDU_STATUS] = { .align = 4, .size = 8, },
+       /*
+        * add more here as they are defined in radiotap.h
+        */
+};
+
+static const struct ieee80211_radiotap_namespace radiotap_ns = {
+       .n_bits = sizeof(rtap_namespace_sizes) / sizeof(rtap_namespace_sizes[0]),
+       .align_size = rtap_namespace_sizes,
+};
+
 /**
  * ieee80211_radiotap_iterator_init - radiotap parser iterator initialization
  * @iterator: radiotap_iterator to initialize
  * get_unaligned((type *)iterator.this_arg) to dereference
  * iterator.this_arg for type "type" safely on all arches.
  *
- * Example code:
- * See Documentation/networking/radiotap-headers.txt
+ * Example code: parse.c
  */
 
 int ieee80211_radiotap_iterator_init(
-    struct ieee80211_radiotap_iterator *iterator,
-    struct ieee80211_radiotap_header *radiotap_header,
-    int max_length)
+       struct ieee80211_radiotap_iterator *iterator,
+       struct ieee80211_radiotap_header *radiotap_header,
+       int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns)
 {
+       /* must at least have the radiotap header */
+       if (max_length < (int)sizeof(struct ieee80211_radiotap_header))
+               return -EINVAL;
+
        /* Linux only supports version 0 radiotap format */
        if (radiotap_header->it_version)
                return -EINVAL;
 
        /* sanity check for allowed length and radiotap length field */
-       if (max_length < le16_to_cpu(get_unaligned(&radiotap_header->it_len)))
+       if (max_length < get_unaligned_le16(&radiotap_header->it_len))
                return -EINVAL;
 
-       iterator->rtheader = radiotap_header;
-       iterator->max_length = le16_to_cpu(get_unaligned(
-                                               &radiotap_header->it_len));
-       iterator->arg_index = 0;
-       iterator->bitmap_shifter = le32_to_cpu(get_unaligned(
-                                               &radiotap_header->it_present));
-       iterator->arg = (u8 *)radiotap_header + sizeof(*radiotap_header);
-       iterator->this_arg = NULL;
+       iterator->_rtheader = radiotap_header;
+       iterator->_max_length = get_unaligned_le16(&radiotap_header->it_len);
+       iterator->_arg_index = 0;
+       iterator->_bitmap_shifter = get_unaligned_le32(&radiotap_header->it_present);
+       iterator->_arg = (uint8_t *)radiotap_header + sizeof(*radiotap_header);
+       iterator->_next_ns_data = NULL;
+       iterator->_reset_on_ext = 0;
+       iterator->_next_bitmap = &radiotap_header->it_present;
+       iterator->_next_bitmap++;
+       iterator->_vns = vns;
+       iterator->current_namespace = &radiotap_ns;
+       iterator->is_radiotap_ns = 1;
+#ifdef RADIOTAP_SUPPORT_OVERRIDES
+       iterator->n_overrides = 0;
+       iterator->overrides = NULL;
+#endif
 
        /* find payload start allowing for extended bitmap(s) */
 
-       if (unlikely(iterator->bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT))) {
-               while (le32_to_cpu(get_unaligned((__le32 *)iterator->arg)) &
-                                  (1<<IEEE80211_RADIOTAP_EXT)) {
-                       iterator->arg += sizeof(u32);
+       if (iterator->_bitmap_shifter & (1<<IEEE80211_RADIOTAP_EXT)) {
+               if ((unsigned long)iterator->_arg -
+                   (unsigned long)iterator->_rtheader + sizeof(uint32_t) >
+                   (unsigned long)iterator->_max_length)
+                       return -EINVAL;
+               while (get_unaligned_le32(iterator->_arg) &
+                                       (1 << IEEE80211_RADIOTAP_EXT)) {
+                       iterator->_arg += sizeof(uint32_t);
 
                        /*
                         * check for insanity where the present bitmaps
@@ -112,12 +138,14 @@ int ieee80211_radiotap_iterator_init(
                         * stated radiotap header length
                         */
 
-                       if (((ulong)iterator->arg - (ulong)iterator->rtheader)
-                           > (ulong)iterator->max_length)
+                       if ((unsigned long)iterator->_arg -
+                           (unsigned long)iterator->_rtheader +
+                           sizeof(uint32_t) >
+                           (unsigned long)iterator->_max_length)
                                return -EINVAL;
                }
 
-               iterator->arg += sizeof(u32);
+               iterator->_arg += sizeof(uint32_t);
 
                /*
                 * no need to check again for blowing past stated radiotap
@@ -126,11 +154,59 @@ int ieee80211_radiotap_iterator_init(
                 */
        }
 
+       iterator->this_arg = iterator->_arg;
+       iterator->this_arg_index = 0;
+       iterator->this_arg_size = 0;
+
        /* we are all initialized happily */
 
        return 0;
 }
 
+static void find_ns(struct ieee80211_radiotap_iterator *iterator,
+                   uint32_t oui, uint8_t subns)
+{
+       int i;
+
+       iterator->current_namespace = NULL;
+
+       if (!iterator->_vns)
+               return;
+
+       for (i = 0; i < iterator->_vns->n_ns; i++) {
+               if (iterator->_vns->ns[i].oui != oui)
+                       continue;
+               if (iterator->_vns->ns[i].subns != subns)
+                       continue;
+
+               iterator->current_namespace = &iterator->_vns->ns[i];
+               break;
+       }
+}
+
+#ifdef RADIOTAP_SUPPORT_OVERRIDES
+static int find_override(struct ieee80211_radiotap_iterator *iterator,
+                        int *align, int *size)
+{
+       int i;
+
+       if (!iterator->overrides)
+               return 0;
+
+       for (i = 0; i < iterator->n_overrides; i++) {
+               if (iterator->_arg_index == iterator->overrides[i].field) {
+                       *align = iterator->overrides[i].align;
+                       *size = iterator->overrides[i].size;
+                       if (!*align) /* erroneous override */
+                               return 0;
+                       return 1;
+               }
+       }
+
+       return 0;
+}
+#endif
+
 
 /**
  * ieee80211_radiotap_iterator_next - return next radiotap parser iterator arg
@@ -156,99 +232,106 @@ int ieee80211_radiotap_iterator_init(
  */
 
 int ieee80211_radiotap_iterator_next(
-    struct ieee80211_radiotap_iterator *iterator)
+       struct ieee80211_radiotap_iterator *iterator)
 {
-
-       /*
-        * small length lookup table for all radiotap types we heard of
-        * starting from b0 in the bitmap, so we can walk the payload
-        * area of the radiotap header
-        *
-        * There is a requirement to pad args, so that args
-        * of a given length must begin at a boundary of that length
-        * -- but note that compound args are allowed (eg, 2 x u16
-        * for IEEE80211_RADIOTAP_CHANNEL) so total arg length is not
-        * a reliable indicator of alignment requirement.
-        *
-        * upper nybble: content alignment for arg
-        * lower nybble: content length for arg
-        */
-
-       static const u8 rt_sizes[] = {
-               [IEEE80211_RADIOTAP_TSFT] = 0x88,
-               [IEEE80211_RADIOTAP_FLAGS] = 0x11,
-               [IEEE80211_RADIOTAP_RATE] = 0x11,
-               [IEEE80211_RADIOTAP_CHANNEL] = 0x24,
-               [IEEE80211_RADIOTAP_FHSS] = 0x22,
-               [IEEE80211_RADIOTAP_DBM_ANTSIGNAL] = 0x11,
-               [IEEE80211_RADIOTAP_DBM_ANTNOISE] = 0x11,
-               [IEEE80211_RADIOTAP_LOCK_QUALITY] = 0x22,
-               [IEEE80211_RADIOTAP_TX_ATTENUATION] = 0x22,
-               [IEEE80211_RADIOTAP_DB_TX_ATTENUATION] = 0x22,
-               [IEEE80211_RADIOTAP_DBM_TX_POWER] = 0x11,
-               [IEEE80211_RADIOTAP_ANTENNA] = 0x11,
-               [IEEE80211_RADIOTAP_DB_ANTSIGNAL] = 0x11,
-               [IEEE80211_RADIOTAP_DB_ANTNOISE] = 0x11,
-               [IEEE80211_RADIOTAP_RX_FLAGS] = 0x22,
-               [IEEE80211_RADIOTAP_TX_FLAGS] = 0x22,
-               [IEEE80211_RADIOTAP_RTS_RETRIES] = 0x11,
-               [IEEE80211_RADIOTAP_DATA_RETRIES] = 0x11,
-               /*
-                * add more here as they are defined in
-                * include/net/ieee80211_radiotap.h
-                */
-       };
-
-       /*
-        * for every radiotap entry we can at
-        * least skip (by knowing the length)...
-        */
-
-       while (iterator->arg_index < (int) sizeof(rt_sizes)) {
+       while (1) {
                int hit = 0;
-               int pad;
+               int pad, align, size, subns;
+               uint32_t oui;
+
+               /* if no more EXT bits, that's it */
+               if ((iterator->_arg_index % 32) == IEEE80211_RADIOTAP_EXT &&
+                   !(iterator->_bitmap_shifter & 1))
+                       return -ENOENT;
 
-               if (!(iterator->bitmap_shifter & 1))
+               if (!(iterator->_bitmap_shifter & 1))
                        goto next_entry; /* arg not present */
 
+               /* get alignment/size of data */
+               switch (iterator->_arg_index % 32) {
+               case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE:
+               case IEEE80211_RADIOTAP_EXT:
+                       align = 1;
+                       size = 0;
+                       break;
+               case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
+                       align = 2;
+                       size = 6;
+                       break;
+               default:
+#ifdef RADIOTAP_SUPPORT_OVERRIDES
+                       if (find_override(iterator, &align, &size)) {
+                               /* all set */
+                       } else
+#endif
+                       if (!iterator->current_namespace ||
+                           iterator->_arg_index >= iterator->current_namespace->n_bits) {
+                               if (iterator->current_namespace == &radiotap_ns)
+                                       return -ENOENT;
+                               align = 0;
+                       } else {
+                               align = iterator->current_namespace->align_size[iterator->_arg_index].align;
+                               size = iterator->current_namespace->align_size[iterator->_arg_index].size;
+                       }
+                       if (!align) {
+                               /* skip all subsequent data */
+                               iterator->_arg = iterator->_next_ns_data;
+                               /* give up on this namespace */
+                               iterator->current_namespace = NULL;
+                               goto next_entry;
+                       }
+                       break;
+               }
+
                /*
                 * arg is present, account for alignment padding
-                *  8-bit args can be at any alignment
-                * 16-bit args must start on 16-bit boundary
-                * 32-bit args must start on 32-bit boundary
-                * 64-bit args must start on 64-bit boundary
-                *
-                * note that total arg size can differ from alignment of
-                * elements inside arg, so we use upper nybble of length
-                * table to base alignment on
                 *
-                * also note: these alignments are ** relative to the
-                * start of the radiotap header **.  There is no guarantee
+                * Note that these alignments are relative to the start
+                * of the radiotap header.  There is no guarantee
                 * that the radiotap header itself is aligned on any
                 * kind of boundary.
                 *
-                * the above is why get_unaligned() is used to dereference
-                * multibyte elements from the radiotap area
+                * The above is why get_unaligned() is used to dereference
+                * multibyte elements from the radiotap area.
                 */
 
-               pad = (((ulong)iterator->arg) -
-                       ((ulong)iterator->rtheader)) &
-                       ((rt_sizes[iterator->arg_index] >> 4) - 1);
+               pad = ((unsigned long)iterator->_arg -
+                      (unsigned long)iterator->_rtheader) & (align - 1);
 
                if (pad)
-                       iterator->arg +=
-                               (rt_sizes[iterator->arg_index] >> 4) - pad;
+                       iterator->_arg += align - pad;
+
+               if (iterator->_arg_index % 32 == IEEE80211_RADIOTAP_VENDOR_NAMESPACE) {
+                       int vnslen;
+
+                       if ((unsigned long)iterator->_arg + size -
+                           (unsigned long)iterator->_rtheader >
+                           (unsigned long)iterator->_max_length)
+                               return -EINVAL;
+
+                       oui = (*iterator->_arg << 16) |
+                               (*(iterator->_arg + 1) << 8) |
+                               *(iterator->_arg + 2);
+                       subns = *(iterator->_arg + 3);
+
+                       find_ns(iterator, oui, subns);
+
+                       vnslen = get_unaligned_le16(iterator->_arg + 4);
+                       iterator->_next_ns_data = iterator->_arg + size + vnslen;
+                       if (!iterator->current_namespace)
+                               size += vnslen;
+               }
 
                /*
                 * this is what we will return to user, but we need to
                 * move on first so next call has something fresh to test
                 */
-               iterator->this_arg_index = iterator->arg_index;
-               iterator->this_arg = iterator->arg;
-               hit = 1;
+               iterator->this_arg_index = iterator->_arg_index;
+               iterator->this_arg = iterator->_arg;
+               iterator->this_arg_size = size;
 
                /* internally move on the size of this arg */
-               iterator->arg += rt_sizes[iterator->arg_index] & 0x0f;
+               iterator->_arg += size;
 
                /*
                 * check for insanity where we are given a bitmap that
@@ -257,31 +340,57 @@ int ieee80211_radiotap_iterator_next(
                 * max_length on the last arg, never exceeding it.
                 */
 
-               if (((ulong)iterator->arg - (ulong)iterator->rtheader) >
-                   (ulong) iterator->max_length)
+               if ((unsigned long)iterator->_arg -
+                   (unsigned long)iterator->_rtheader >
+                   (unsigned long)iterator->_max_length)
                        return -EINVAL;
 
-       next_entry:
-               iterator->arg_index++;
-               if (unlikely((iterator->arg_index & 31) == 0)) {
-                       /* completed current u32 bitmap */
-                       if (iterator->bitmap_shifter & 1) {
-                               /* b31 was set, there is more */
-                               /* move to next u32 bitmap */
-                               iterator->bitmap_shifter = le32_to_cpu(
-                                       get_unaligned(iterator->next_bitmap));
-                               iterator->next_bitmap++;
-                       } else
-                               /* no more bitmaps: end */
-                               iterator->arg_index = sizeof(rt_sizes);
-               } else /* just try the next bit */
-                       iterator->bitmap_shifter >>= 1;
+               /* these special ones are valid in each bitmap word */
+               switch (iterator->_arg_index % 32) {
+               case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
+                       iterator->_reset_on_ext = 1;
+
+                       iterator->is_radiotap_ns = 0;
+                       /*
+                        * If parser didn't register this vendor
+                        * namespace with us, allow it to show it
+                        * as 'raw. Do do that, set argument index
+                        * to vendor namespace.
+                        */
+                       iterator->this_arg_index =
+                               IEEE80211_RADIOTAP_VENDOR_NAMESPACE;
+                       if (!iterator->current_namespace)
+                               hit = 1;
+                       goto next_entry;
+               case IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE:
+                       iterator->_reset_on_ext = 1;
+                       iterator->current_namespace = &radiotap_ns;
+                       iterator->is_radiotap_ns = 1;
+                       goto next_entry;
+               case IEEE80211_RADIOTAP_EXT:
+                       /*
+                        * bit 31 was set, there is more
+                        * -- move to next u32 bitmap
+                        */
+                       iterator->_bitmap_shifter =
+                               get_unaligned_le32(iterator->_next_bitmap);
+                       iterator->_next_bitmap++;
+                       if (iterator->_reset_on_ext)
+                               iterator->_arg_index = 0;
+                       else
+                               iterator->_arg_index++;
+                       iterator->_reset_on_ext = 0;
+                       break;
+               default:
+                       /* we've got a hit! */
+                       hit = 1;
+ next_entry:
+                       iterator->_bitmap_shifter >>= 1;
+                       iterator->_arg_index++;
+               }
 
                /* if we found a valid arg earlier, return it now */
                if (hit)
                        return 0;
        }
-
-       /* we don't know how to handle any more args, we're done */
-       return -ENOENT;
 }
index 137288f..0572e7c 100644 (file)
@@ -1,6 +1,3 @@
-/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.5 2005/01/22 20:12:05 sam Exp $ */
-/* $NetBSD: ieee80211_radiotap.h,v 1.11 2005/06/22 06:16:02 dyoung Exp $ */
-
 /*-
  * Copyright (c) 2003, 2004 David Young.  All rights reserved.
  *
@@ -178,6 +175,14 @@ struct ieee80211_radiotap_header {
  *
  *     Number of unicast retries a transmitted frame used.
  *
+ * IEEE80211_RADIOTAP_MCS      u8, u8, u8              unitless
+ *
+ *     Contains a bitmap of known fields/flags, the flags, and
+ *     the MCS index.
+ *
+ * IEEE80211_RADIOTAP_AMPDU_STATUS     u32, u16, u8, u8        unitlesss
+ *
+ *     Contains the AMPDU information for the subframe.
  */
 enum ieee80211_radiotap_type {
        IEEE80211_RADIOTAP_TSFT = 0,
@@ -198,6 +203,13 @@ enum ieee80211_radiotap_type {
        IEEE80211_RADIOTAP_TX_FLAGS = 15,
        IEEE80211_RADIOTAP_RTS_RETRIES = 16,
        IEEE80211_RADIOTAP_DATA_RETRIES = 17,
+
+       IEEE80211_RADIOTAP_MCS = 19,
+       IEEE80211_RADIOTAP_AMPDU_STATUS = 20,
+
+       /* valid in every it_present bitmap, even vendor namespaces */
+       IEEE80211_RADIOTAP_RADIOTAP_NAMESPACE = 29,
+       IEEE80211_RADIOTAP_VENDOR_NAMESPACE = 30,
        IEEE80211_RADIOTAP_EXT = 31
 };
 
@@ -230,8 +242,10 @@ enum ieee80211_radiotap_type {
                                                 * 802.11 header and payload
                                                 * (to 32-bit boundary)
                                                 */
+#define IEEE80211_RADIOTAP_F_BADFCS    0x40    /* frame failed FCS check */
+
 /* For IEEE80211_RADIOTAP_RX_FLAGS */
-#define IEEE80211_RADIOTAP_F_RX_BADFCS 0x0001  /* frame failed crc check */
+#define IEEE80211_RADIOTAP_F_RX_BADPLCP        0x0002 /* bad PLCP */
 
 /* For IEEE80211_RADIOTAP_TX_FLAGS */
 #define IEEE80211_RADIOTAP_F_TX_FAIL   0x0001  /* failed due to excessive
@@ -240,4 +254,38 @@ enum ieee80211_radiotap_type {
 #define IEEE80211_RADIOTAP_F_TX_RTS    0x0004  /* used rts/cts handshake */
 #define IEEE80211_RADIOTAP_F_TX_NOACK  0x0008  /* don't expect an ACK */
 
+/* For IEEE80211_RADIOTAP_AMPDU_STATUS */
+#define IEEE80211_RADIOTAP_AMPDU_REPORT_ZEROLEN                0x0001
+#define IEEE80211_RADIOTAP_AMPDU_IS_ZEROLEN            0x0002
+#define IEEE80211_RADIOTAP_AMPDU_LAST_KNOWN            0x0004
+#define IEEE80211_RADIOTAP_AMPDU_IS_LAST               0x0008
+#define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_ERR         0x0010
+#define IEEE80211_RADIOTAP_AMPDU_DELIM_CRC_KNOWN       0x0020
+
+/* For IEEE80211_RADIOTAP_MCS */
+#define IEEE80211_RADIOTAP_MCS_HAVE_BW         0x01
+#define IEEE80211_RADIOTAP_MCS_HAVE_MCS                0x02
+#define IEEE80211_RADIOTAP_MCS_HAVE_GI         0x04
+#define IEEE80211_RADIOTAP_MCS_HAVE_FMT                0x08
+#define IEEE80211_RADIOTAP_MCS_HAVE_FEC                0x10
+#define IEEE80211_RADIOTAP_MCS_HAVE_STBC       0x20
+#define IEEE80211_RADIOTAP_MCS_HAVE_NESS       0x40
+#define IEEE80211_RADIOTAP_MCS_NESS_BIT1       0x80
+
+
+#define IEEE80211_RADIOTAP_MCS_BW_MASK         0x03
+#define                IEEE80211_RADIOTAP_MCS_BW_20    0
+#define                IEEE80211_RADIOTAP_MCS_BW_40    1
+#define                IEEE80211_RADIOTAP_MCS_BW_20L   2
+#define                IEEE80211_RADIOTAP_MCS_BW_20U   3
+#define IEEE80211_RADIOTAP_MCS_SGI             0x04
+#define IEEE80211_RADIOTAP_MCS_FMT_GF          0x08
+#define IEEE80211_RADIOTAP_MCS_FEC_LDPC                0x10
+#define IEEE80211_RADIOTAP_MCS_STBC_MASK       0x60
+#define IEEE80211_RADIOTAP_MCS_STBC_SHIFT      5
+#define                IEEE80211_RADIOTAP_MCS_STBC_1   1
+#define                IEEE80211_RADIOTAP_MCS_STBC_2   2
+#define                IEEE80211_RADIOTAP_MCS_STBC_3   3
+#define IEEE80211_RADIOTAP_MCS_NESS_BIT0       0x80
+
 #endif                         /* IEEE80211_RADIOTAP_H */
index 2e0e872..b768c85 100644 (file)
@@ -1,56 +1,96 @@
-/*
- * Radiotap parser
- *
- * Copyright 2007              Andy Green <andy@warmcat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * Alternatively, this software may be distributed under the terms of BSD
- * license.
- *
- * See README and COPYING for more details.
- */
-
 #ifndef __RADIOTAP_ITER_H
 #define __RADIOTAP_ITER_H
 
+#include <stdint.h>
 #include "radiotap.h"
 
 /* Radiotap header iteration
  *   implemented in radiotap.c
  */
+
+struct radiotap_override {
+       uint8_t field;
+       uint8_t align:4, size:4;
+};
+
+struct radiotap_align_size {
+       uint8_t align:4, size:4;
+};
+
+struct ieee80211_radiotap_namespace {
+       const struct radiotap_align_size *align_size;
+       int n_bits;
+       uint32_t oui;
+       uint8_t subns;
+};
+
+struct ieee80211_radiotap_vendor_namespaces {
+       const struct ieee80211_radiotap_namespace *ns;
+       int n_ns;
+};
+
 /**
  * struct ieee80211_radiotap_iterator - tracks walk thru present radiotap args
- * @rtheader: pointer to the radiotap header we are walking through
- * @max_length: length of radiotap header in cpu byte ordering
- * @this_arg_index: IEEE80211_RADIOTAP_... index of current arg
- * @this_arg: pointer to current radiotap arg
- * @arg_index: internal next argument index
- * @arg: internal next argument pointer
- * @next_bitmap: internal pointer to next present u32
- * @bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present
+ * @this_arg_index: index of current arg, valid after each successful call
+ *     to ieee80211_radiotap_iterator_next()
+ * @this_arg: pointer to current radiotap arg; it is valid after each
+ *     call to ieee80211_radiotap_iterator_next() but also after
+ *     ieee80211_radiotap_iterator_init() where it will point to
+ *     the beginning of the actual data portion
+ * @this_arg_size: length of the current arg, for convenience
+ * @current_namespace: pointer to the current namespace definition
+ *     (or internally %NULL if the current namespace is unknown)
+ * @is_radiotap_ns: indicates whether the current namespace is the default
+ *     radiotap namespace or not
+ *
+ * @overrides: override standard radiotap fields
+ * @n_overrides: number of overrides
+ *
+ * @_rtheader: pointer to the radiotap header we are walking through
+ * @_max_length: length of radiotap header in cpu byte ordering
+ * @_arg_index: next argument index
+ * @_arg: next argument pointer
+ * @_next_bitmap: internal pointer to next present u32
+ * @_bitmap_shifter: internal shifter for curr u32 bitmap, b0 set == arg present
+ * @_vns: vendor namespace definitions
+ * @_next_ns_data: beginning of the next namespace's data
+ * @_reset_on_ext: internal; reset the arg index to 0 when going to the
+ *     next bitmap word
+ *
+ * Describes the radiotap parser state. Fields prefixed with an underscore
+ * must not be used by users of the parser, only by the parser internally.
  */
 
 struct ieee80211_radiotap_iterator {
-       struct ieee80211_radiotap_header *rtheader;
-       int max_length;
-       int this_arg_index;
+       struct ieee80211_radiotap_header *_rtheader;
+       const struct ieee80211_radiotap_vendor_namespaces *_vns;
+       const struct ieee80211_radiotap_namespace *current_namespace;
+
+       unsigned char *_arg, *_next_ns_data;
+       uint32_t *_next_bitmap;
+
        unsigned char *this_arg;
+#ifdef RADIOTAP_SUPPORT_OVERRIDES
+       const struct radiotap_override *overrides;
+       int n_overrides;
+#endif
+       int this_arg_index;
+       int this_arg_size;
+
+       int is_radiotap_ns;
 
-       int arg_index;
-       unsigned char *arg;
-       uint32_t *next_bitmap;
-       uint32_t bitmap_shifter;
+       int _max_length;
+       int _arg_index;
+       uint32_t _bitmap_shifter;
+       int _reset_on_ext;
 };
 
 extern int ieee80211_radiotap_iterator_init(
-   struct ieee80211_radiotap_iterator *iterator,
-   struct ieee80211_radiotap_header *radiotap_header,
-   int max_length);
+       struct ieee80211_radiotap_iterator *iterator,
+       struct ieee80211_radiotap_header *radiotap_header,
+       int max_length, const struct ieee80211_radiotap_vendor_namespaces *vns);
 
 extern int ieee80211_radiotap_iterator_next(
-   struct ieee80211_radiotap_iterator *iterator);
+       struct ieee80211_radiotap_iterator *iterator);
 
 #endif /* __RADIOTAP_ITER_H */
index 6795d41..8484d27 100644 (file)
@@ -18,11 +18,9 @@ static struct dl_list active_references =
 
 #ifdef WPA_TRACE_BFD
 #include <bfd.h>
-#ifdef __linux__
-#include <demangle.h>
-#else /* __linux__ */
-#include <libiberty/demangle.h>
-#endif /* __linux__ */
+
+#define DMGL_PARAMS      (1 << 0)
+#define DMGL_ANSI        (1 << 1)
 
 static char *prg_fname = NULL;
 static bfd *cached_abfd = NULL;
@@ -35,7 +33,7 @@ static void get_prg_fname(void)
        os_snprintf(exe, sizeof(exe) - 1, "/proc/%u/exe", getpid());
        len = readlink(exe, fname, sizeof(fname) - 1);
        if (len < 0 || len >= (int) sizeof(fname)) {
-               perror("readlink");
+               wpa_printf(MSG_ERROR, "readlink: %s", strerror(errno));
                return;
        }
        fname[len] = '\0';
@@ -162,7 +160,7 @@ static void wpa_trace_bfd_addr(void *pc)
        if (abfd == NULL)
                return;
 
-       data.pc = (bfd_vma) pc;
+       data.pc = (bfd_hostptr_t) pc;
        data.found = FALSE;
        bfd_map_over_sections(abfd, find_addr_sect, &data);
 
@@ -187,6 +185,7 @@ static void wpa_trace_bfd_addr(void *pc)
                wpa_printf(MSG_INFO, "     %s() %s:%u",
                           name, filename, data.line);
                free(aname);
+               aname = NULL;
 
                data.found = bfd_find_inliner_info(abfd, &data.filename,
                                                   &data.function, &data.line);
@@ -202,7 +201,7 @@ static const char * wpa_trace_bfd_addr2func(void *pc)
        if (abfd == NULL)
                return NULL;
 
-       data.pc = (bfd_vma) pc;
+       data.pc = (bfd_hostptr_t) pc;
        data.found = FALSE;
        bfd_map_over_sections(abfd, find_addr_sect, &data);
 
@@ -244,6 +243,53 @@ void wpa_trace_dump_funcname(const char *title, void *pc)
        wpa_trace_bfd_addr(pc);
 }
 
+
+size_t wpa_trace_calling_func(const char *buf[], size_t len)
+{
+       bfd *abfd;
+       void *btrace_res[WPA_TRACE_LEN];
+       int i, btrace_num;
+       size_t pos = 0;
+
+       if (len == 0)
+               return 0;
+       if (len > WPA_TRACE_LEN)
+               len = WPA_TRACE_LEN;
+
+       wpa_trace_bfd_init();
+       abfd = cached_abfd;
+       if (!abfd)
+               return 0;
+
+       btrace_num = backtrace(btrace_res, len);
+       if (btrace_num < 1)
+               return 0;
+
+       for (i = 0; i < btrace_num; i++) {
+               struct bfd_data data;
+
+               data.pc = (bfd_hostptr_t) btrace_res[i];
+               data.found = FALSE;
+               bfd_map_over_sections(abfd, find_addr_sect, &data);
+
+               while (data.found) {
+                       if (data.function &&
+                           (pos > 0 ||
+                            os_strcmp(data.function, __func__) != 0)) {
+                               buf[pos++] = data.function;
+                               if (pos == len)
+                                       return pos;
+                       }
+
+                       data.found = bfd_find_inliner_info(abfd, &data.filename,
+                                                          &data.function,
+                                                          &data.line);
+               }
+       }
+
+       return pos;
+}
+
 #else /* WPA_TRACE_BFD */
 
 #define wpa_trace_bfd_init() do { } while (0)
index 38f43fb..43ed86c 100644 (file)
@@ -40,6 +40,7 @@ void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr);
                        dl_list_del(&(ptr)->wpa_trace_ref_##name.list); \
        } while (0)
 void wpa_trace_check_ref(const void *addr);
+size_t wpa_trace_calling_func(const char *buf[], size_t len);
 
 #else /* WPA_TRACE */
 
diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c
new file mode 100644 (file)
index 0000000..4b97dad
--- /dev/null
@@ -0,0 +1,423 @@
+/*
+ * utils module 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.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/bitfield.h"
+#include "utils/ext_password.h"
+#include "utils/trace.h"
+#include "utils/base64.h"
+
+
+struct printf_test_data {
+       u8 *data;
+       size_t len;
+       char *encoded;
+};
+
+static const struct printf_test_data printf_tests[] = {
+       { (u8 *) "abcde", 5, "abcde" },
+       { (u8 *) "a\0b\nc\ed\re\tf\"\\", 13, "a\\0b\\nc\\ed\\re\\tf\\\"\\\\" },
+       { (u8 *) "\x00\x31\x00\x32\x00\x39", 6, "\\x001\\0002\\09" },
+       { (u8 *) "\n\n\n", 3, "\n\12\x0a" },
+       { (u8 *) "\303\245\303\244\303\266\303\205\303\204\303\226", 12,
+         "\\xc3\\xa5\xc3\\xa4\\xc3\\xb6\\xc3\\x85\\xc3\\x84\\xc3\\x96" },
+       { (u8 *) "\303\245\303\244\303\266\303\205\303\204\303\226", 12,
+         "\\303\\245\\303\\244\\303\\266\\303\\205\\303\\204\\303\\226" },
+       { (u8 *) "\xe5\xe4\xf6\xc5\xc4\xd6", 6,
+         "\\xe5\\xe4\\xf6\\xc5\\xc4\\xd6" },
+       { NULL, 0, NULL }
+};
+
+
+static int printf_encode_decode_tests(void)
+{
+       int i;
+       size_t binlen;
+       char buf[100];
+       u8 bin[100];
+       int errors = 0;
+
+       wpa_printf(MSG_INFO, "printf encode/decode tests");
+
+       for (i = 0; printf_tests[i].data; i++) {
+               const struct printf_test_data *test = &printf_tests[i];
+               printf_encode(buf, sizeof(buf), test->data, test->len);
+               wpa_printf(MSG_INFO, "%d: -> \"%s\"", i, buf);
+
+               binlen = printf_decode(bin, sizeof(bin), buf);
+               if (binlen != test->len ||
+                   os_memcmp(bin, test->data, binlen) != 0) {
+                       wpa_hexdump(MSG_ERROR, "Error in decoding#1",
+                                   bin, binlen);
+                       errors++;
+               }
+
+               binlen = printf_decode(bin, sizeof(bin), test->encoded);
+               if (binlen != test->len ||
+                   os_memcmp(bin, test->data, binlen) != 0) {
+                       wpa_hexdump(MSG_ERROR, "Error in decoding#2",
+                                   bin, binlen);
+                       errors++;
+               }
+       }
+
+       buf[5] = 'A';
+       printf_encode(buf, 5, (const u8 *) "abcde", 5);
+       if (buf[5] != 'A') {
+               wpa_printf(MSG_ERROR, "Error in bounds checking#1");
+               errors++;
+       }
+
+       for (i = 5; i < 10; i++) {
+               buf[i] = 'A';
+               printf_encode(buf, i, (const u8 *) "\xdd\xdd\xdd\xdd\xdd", 5);
+               if (buf[i] != 'A') {
+                       wpa_printf(MSG_ERROR, "Error in bounds checking#2(%d)",
+                                  i);
+                       errors++;
+               }
+       }
+
+       if (printf_decode(bin, 3, "abcde") != 2)
+               errors++;
+
+       if (printf_decode(bin, 3, "\\xa") != 1 || bin[0] != 10)
+               errors++;
+
+       if (printf_decode(bin, 3, "\\a") != 1 || bin[0] != 'a')
+               errors++;
+
+       if (errors) {
+               wpa_printf(MSG_ERROR, "%d printf test(s) failed", errors);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int bitfield_tests(void)
+{
+       struct bitfield *bf;
+       int i;
+       int errors = 0;
+
+       wpa_printf(MSG_INFO, "bitfield tests");
+
+       bf = bitfield_alloc(123);
+       if (bf == NULL)
+               return -1;
+
+       for (i = 0; i < 123; i++) {
+               if (bitfield_is_set(bf, i) || bitfield_is_set(bf, i + 1))
+                       errors++;
+               if (i > 0 && bitfield_is_set(bf, i - 1))
+                       errors++;
+               bitfield_set(bf, i);
+               if (!bitfield_is_set(bf, i))
+                       errors++;
+               bitfield_clear(bf, i);
+               if (bitfield_is_set(bf, i))
+                       errors++;
+       }
+
+       for (i = 123; i < 200; i++) {
+               if (bitfield_is_set(bf, i) || bitfield_is_set(bf, i + 1))
+                       errors++;
+               if (i > 0 && bitfield_is_set(bf, i - 1))
+                       errors++;
+               bitfield_set(bf, i);
+               if (bitfield_is_set(bf, i))
+                       errors++;
+               bitfield_clear(bf, i);
+               if (bitfield_is_set(bf, i))
+                       errors++;
+       }
+
+       for (i = 0; i < 123; i++) {
+               if (bitfield_is_set(bf, i) || bitfield_is_set(bf, i + 1))
+                       errors++;
+               bitfield_set(bf, i);
+               if (!bitfield_is_set(bf, i))
+                       errors++;
+       }
+
+       for (i = 0; i < 123; i++) {
+               if (!bitfield_is_set(bf, i))
+                       errors++;
+               bitfield_clear(bf, i);
+               if (bitfield_is_set(bf, i))
+                       errors++;
+       }
+
+       for (i = 0; i < 123; i++) {
+               if (bitfield_get_first_zero(bf) != i)
+                       errors++;
+               bitfield_set(bf, i);
+       }
+       if (bitfield_get_first_zero(bf) != -1)
+               errors++;
+       for (i = 0; i < 123; i++) {
+               if (!bitfield_is_set(bf, i))
+                       errors++;
+               bitfield_clear(bf, i);
+               if (bitfield_get_first_zero(bf) != i)
+                       errors++;
+               bitfield_set(bf, i);
+       }
+       if (bitfield_get_first_zero(bf) != -1)
+               errors++;
+
+       bitfield_free(bf);
+
+       bf = bitfield_alloc(8);
+       if (bf == NULL)
+               return -1;
+       if (bitfield_get_first_zero(bf) != 0)
+               errors++;
+       for (i = 0; i < 8; i++)
+               bitfield_set(bf, i);
+       if (bitfield_get_first_zero(bf) != -1)
+               errors++;
+       bitfield_free(bf);
+
+       if (errors) {
+               wpa_printf(MSG_ERROR, "%d bitfield test(s) failed", errors);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int int_array_tests(void)
+{
+       int test1[] = { 1, 2, 3, 4, 5, 6, 0 };
+       int test2[] = { 1, -1, 0 };
+       int test3[] = { 1, 1, 1, -1, 2, 3, 4, 1, 2, 0 };
+       int test3_res[] = { -1, 1, 2, 3, 4, 0 };
+       int errors = 0;
+       int len;
+
+       wpa_printf(MSG_INFO, "int_array tests");
+
+       if (int_array_len(test1) != 6 ||
+           int_array_len(test2) != 2)
+               errors++;
+
+       int_array_sort_unique(test3);
+       len = int_array_len(test3_res);
+       if (int_array_len(test3) != len)
+               errors++;
+       else if (os_memcmp(test3, test3_res, len * sizeof(int)) != 0)
+               errors++;
+
+       if (errors) {
+               wpa_printf(MSG_ERROR, "%d int_array test(s) failed", errors);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int ext_password_tests(void)
+{
+       struct ext_password_data *data;
+       int ret = 0;
+       struct wpabuf *pw;
+
+       wpa_printf(MSG_INFO, "ext_password tests");
+
+       data = ext_password_init("unknown", "foo");
+       if (data != NULL)
+               return -1;
+
+       data = ext_password_init("test", NULL);
+       if (data == NULL)
+               return -1;
+       pw = ext_password_get(data, "foo");
+       if (pw != NULL)
+               ret = -1;
+       ext_password_free(pw);
+
+       ext_password_deinit(data);
+
+       pw = ext_password_get(NULL, "foo");
+       if (pw != NULL)
+               ret = -1;
+       ext_password_free(pw);
+
+       return ret;
+}
+
+
+static int trace_tests(void)
+{
+       wpa_printf(MSG_INFO, "trace tests");
+
+       wpa_trace_show("test backtrace");
+       wpa_trace_dump_funcname("test funcname", trace_tests);
+
+       return 0;
+}
+
+
+static int base64_tests(void)
+{
+       int errors = 0;
+       unsigned char *res;
+       size_t res_len;
+
+       wpa_printf(MSG_INFO, "base64 tests");
+
+       res = base64_encode((const unsigned char *) "", ~0, &res_len);
+       if (res) {
+               errors++;
+               os_free(res);
+       }
+
+       res = base64_encode((const unsigned char *) "=", 1, &res_len);
+       if (!res || res_len != 5 || res[0] != 'P' || res[1] != 'Q' ||
+           res[2] != '=' || res[3] != '=' || res[4] != '\n')
+               errors++;
+       os_free(res);
+
+       res = base64_encode((const unsigned char *) "=", 1, NULL);
+       if (!res || res[0] != 'P' || res[1] != 'Q' ||
+           res[2] != '=' || res[3] != '=' || res[4] != '\n')
+               errors++;
+       os_free(res);
+
+       res = base64_decode((const unsigned char *) "", 0, &res_len);
+       if (res) {
+               errors++;
+               os_free(res);
+       }
+
+       res = base64_decode((const unsigned char *) "a", 1, &res_len);
+       if (res) {
+               errors++;
+               os_free(res);
+       }
+
+       res = base64_decode((const unsigned char *) "====", 4, &res_len);
+       if (res) {
+               errors++;
+               os_free(res);
+       }
+
+       res = base64_decode((const unsigned char *) "PQ==", 4, &res_len);
+       if (!res || res_len != 1 || res[0] != '=')
+               errors++;
+       os_free(res);
+
+       res = base64_decode((const unsigned char *) "P.Q-=!=*", 8, &res_len);
+       if (!res || res_len != 1 || res[0] != '=')
+               errors++;
+       os_free(res);
+
+       if (errors) {
+               wpa_printf(MSG_ERROR, "%d base64 test(s) failed", errors);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+static int common_tests(void)
+{
+       char buf[3];
+       u8 addr[ETH_ALEN] = { 1, 2, 3, 4, 5, 6 };
+       u8 bin[3];
+       int errors = 0;
+       struct wpa_freq_range_list ranges;
+
+       wpa_printf(MSG_INFO, "common tests");
+
+       if (hwaddr_mask_txt(buf, 3, addr, addr) != -1)
+               errors++;
+
+       if (wpa_scnprintf(buf, 0, "hello") != 0 ||
+           wpa_scnprintf(buf, 3, "hello") != 2)
+               errors++;
+
+       if (wpa_snprintf_hex(buf, 0, addr, ETH_ALEN) != 0 ||
+           wpa_snprintf_hex(buf, 3, addr, ETH_ALEN) != 2)
+               errors++;
+
+       if (merge_byte_arrays(bin, 3, addr, ETH_ALEN, NULL, 0) != 3 ||
+           merge_byte_arrays(bin, 3, NULL, 0, addr, ETH_ALEN) != 3)
+               errors++;
+
+       if (dup_binstr(NULL, 0) != NULL)
+               errors++;
+
+       if (freq_range_list_includes(NULL, 0) != 0)
+               errors++;
+
+       os_memset(&ranges, 0, sizeof(ranges));
+       if (freq_range_list_parse(&ranges, "") != 0 ||
+           freq_range_list_includes(&ranges, 0) != 0 ||
+           freq_range_list_str(&ranges) != NULL)
+               errors++;
+
+       if (utf8_unescape(NULL, 0, buf, sizeof(buf)) != 0 ||
+           utf8_unescape("a", 1, NULL, 0) != 0 ||
+           utf8_unescape("a\\", 2, buf, sizeof(buf)) != 0 ||
+           utf8_unescape("abcde", 5, buf, sizeof(buf)) != 0 ||
+           utf8_unescape("abc", 3, buf, 3) != 3)
+               errors++;
+
+       if (utf8_unescape("a", 0, buf, sizeof(buf)) != 1 || buf[0] != 'a')
+               errors++;
+
+       if (utf8_unescape("\\b", 2, buf, sizeof(buf)) != 1 || buf[0] != 'b')
+               errors++;
+
+       if (utf8_escape(NULL, 0, buf, sizeof(buf)) != 0 ||
+           utf8_escape("a", 1, NULL, 0) != 0 ||
+           utf8_escape("abcde", 5, buf, sizeof(buf)) != 0 ||
+           utf8_escape("a\\bcde", 6, buf, sizeof(buf)) != 0 ||
+           utf8_escape("ab\\cde", 6, buf, sizeof(buf)) != 0 ||
+           utf8_escape("abc\\de", 6, buf, sizeof(buf)) != 0 ||
+           utf8_escape("abc", 3, buf, 3) != 3)
+               errors++;
+
+       if (utf8_escape("a", 0, buf, sizeof(buf)) != 1 || buf[0] != 'a')
+               errors++;
+
+       if (errors) {
+               wpa_printf(MSG_ERROR, "%d common test(s) failed", errors);
+               return -1;
+       }
+
+       return 0;
+}
+
+
+int utils_module_tests(void)
+{
+       int ret = 0;
+
+       wpa_printf(MSG_INFO, "utils module tests");
+
+       if (printf_encode_decode_tests() < 0 ||
+           ext_password_tests() < 0 ||
+           trace_tests() < 0 ||
+           bitfield_tests() < 0 ||
+           base64_tests() < 0 ||
+           common_tests() < 0 ||
+           int_array_tests() < 0)
+               ret = -1;
+
+       return ret;
+}
index 2aa4bcb..0f224f9 100644 (file)
@@ -55,7 +55,7 @@ int uuid_bin2str(const u8 *bin, char *str, size_t max_len)
                          bin[4], bin[5], bin[6], bin[7],
                          bin[8], bin[9], bin[10], bin[11],
                          bin[12], bin[13], bin[14], bin[15]);
-       if (len < 0 || (size_t) len >= max_len)
+       if (os_snprintf_error(max_len, len))
                return -1;
        return 0;
 }
index 38ea8aa..0d11905 100644 (file)
@@ -375,19 +375,19 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf,
 #endif /* CONFIG_ANDROID_LOG */
 }
 
-void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len)
+void wpa_hexdump(int level, const char *title, const void *buf, size_t len)
 {
        _wpa_hexdump(level, title, buf, len, 1);
 }
 
 
-void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len)
+void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len)
 {
        _wpa_hexdump(level, title, buf, len, wpa_debug_show_keys);
 }
 
 
-static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf,
+static void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
                               size_t len, int show)
 {
        size_t i, llen;
@@ -407,7 +407,7 @@ static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf,
                        /* can do ascii processing in userspace */
                        for (i = 0; i < len; i++)
                                fprintf(wpa_debug_tracing_file,
-                                       " %02x", buf[i]);
+                                       " %02x", pos[i]);
                }
                fflush(wpa_debug_tracing_file);
        }
@@ -495,13 +495,14 @@ static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf,
 }
 
 
-void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len)
+void wpa_hexdump_ascii(int level, const char *title, const void *buf,
+                      size_t len)
 {
        _wpa_hexdump_ascii(level, title, buf, len, 1);
 }
 
 
-void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf,
+void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
                           size_t len)
 {
        _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys);
@@ -554,6 +555,8 @@ int wpa_debug_open_file(const char *path)
 #ifndef _WIN32
        setvbuf(out_file, NULL, _IOLBF, 0);
 #endif /* _WIN32 */
+#else /* CONFIG_DEBUG_FILE */
+       (void)path;
 #endif /* CONFIG_DEBUG_FILE */
        return 0;
 }
@@ -571,6 +574,14 @@ void wpa_debug_close_file(void)
 #endif /* CONFIG_DEBUG_FILE */
 }
 
+
+void wpa_debug_setup_stdout(void)
+{
+#ifndef _WIN32
+       setvbuf(stdout, NULL, _IOLBF, 0);
+#endif /* _WIN32 */
+}
+
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 
 
@@ -595,10 +606,14 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...)
 {
        va_list ap;
        char *buf;
-       const int buflen = 2048;
+       int buflen;
        int len;
        char prefix[130];
 
+       va_start(ap, fmt);
+       buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+       va_end(ap);
+
        buf = os_malloc(buflen);
        if (buf == NULL) {
                wpa_printf(MSG_ERROR, "wpa_msg: Failed to allocate message "
@@ -612,7 +627,7 @@ void wpa_msg(void *ctx, int level, const char *fmt, ...)
                if (ifname) {
                        int res = os_snprintf(prefix, sizeof(prefix), "%s: ",
                                              ifname);
-                       if (res < 0 || res >= (int) sizeof(prefix))
+                       if (os_snprintf_error(sizeof(prefix), res))
                                prefix[0] = '\0';
                }
        }
@@ -629,12 +644,16 @@ void wpa_msg_ctrl(void *ctx, int level, const char *fmt, ...)
 {
        va_list ap;
        char *buf;
-       const int buflen = 2048;
+       int buflen;
        int len;
 
        if (!wpa_msg_cb)
                return;
 
+       va_start(ap, fmt);
+       buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+       va_end(ap);
+
        buf = os_malloc(buflen);
        if (buf == NULL) {
                wpa_printf(MSG_ERROR, "wpa_msg_ctrl: Failed to allocate "
@@ -653,9 +672,13 @@ void wpa_msg_global(void *ctx, int level, const char *fmt, ...)
 {
        va_list ap;
        char *buf;
-       const int buflen = 2048;
+       int buflen;
        int len;
 
+       va_start(ap, fmt);
+       buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+       va_end(ap);
+
        buf = os_malloc(buflen);
        if (buf == NULL) {
                wpa_printf(MSG_ERROR, "wpa_msg_global: Failed to allocate "
@@ -672,13 +695,45 @@ void wpa_msg_global(void *ctx, int level, const char *fmt, ...)
 }
 
 
+void wpa_msg_global_ctrl(void *ctx, int level, const char *fmt, ...)
+{
+       va_list ap;
+       char *buf;
+       int buflen;
+       int len;
+
+       if (!wpa_msg_cb)
+               return;
+
+       va_start(ap, fmt);
+       buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+       va_end(ap);
+
+       buf = os_malloc(buflen);
+       if (buf == NULL) {
+               wpa_printf(MSG_ERROR,
+                          "wpa_msg_global_ctrl: Failed to allocate message buffer");
+               return;
+       }
+       va_start(ap, fmt);
+       len = vsnprintf(buf, buflen, fmt, ap);
+       va_end(ap);
+       wpa_msg_cb(ctx, level, 1, buf, len);
+       os_free(buf);
+}
+
+
 void wpa_msg_no_global(void *ctx, int level, const char *fmt, ...)
 {
        va_list ap;
        char *buf;
-       const int buflen = 2048;
+       int buflen;
        int len;
 
+       va_start(ap, fmt);
+       buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+       va_end(ap);
+
        buf = os_malloc(buflen);
        if (buf == NULL) {
                wpa_printf(MSG_ERROR, "wpa_msg_no_global: Failed to allocate "
@@ -711,9 +766,13 @@ void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level,
 {
        va_list ap;
        char *buf;
-       const int buflen = 2048;
+       int buflen;
        int len;
 
+       va_start(ap, fmt);
+       buflen = vsnprintf(NULL, 0, fmt, ap) + 1;
+       va_end(ap);
+
        buf = os_malloc(buflen);
        if (buf == NULL) {
                wpa_printf(MSG_ERROR, "hostapd_logger: Failed to allocate "
index 2ed1bd8..400bea9 100644 (file)
 
 #include "wpabuf.h"
 
+extern int wpa_debug_level;
+extern int wpa_debug_show_keys;
+extern int wpa_debug_timestamp;
+
 /* Debugging function - conditional printf and hex dump. Driver wrappers can
  * use these for debugging purposes. */
 
@@ -30,6 +34,7 @@ enum {
 #define wpa_hexdump_ascii_key(l,t,b,le) do { } while (0)
 #define wpa_debug_open_file(p) do { } while (0)
 #define wpa_debug_close_file() do { } while (0)
+#define wpa_debug_setup_stdout() do { } while (0)
 #define wpa_dbg(args...) do { } while (0)
 
 static inline int wpa_debug_reopen_file(void)
@@ -42,6 +47,7 @@ static inline int wpa_debug_reopen_file(void)
 int wpa_debug_open_file(const char *path);
 int wpa_debug_reopen_file(void);
 void wpa_debug_close_file(void);
+void wpa_debug_setup_stdout(void);
 
 /**
  * wpa_debug_printf_timestamp - Print timestamp for debug output
@@ -77,7 +83,7 @@ PRINTF_FORMAT(2, 3);
  * output may be directed to stdout, stderr, and/or syslog based on
  * configuration. The contents of buf is printed out has hex dump.
  */
-void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len);
+void wpa_hexdump(int level, const char *title, const void *buf, size_t len);
 
 static inline void wpa_hexdump_buf(int level, const char *title,
                                   const struct wpabuf *buf)
@@ -99,7 +105,7 @@ static inline void wpa_hexdump_buf(int level, const char *title,
  * like wpa_hexdump(), but by default, does not include secret keys (passwords,
  * etc.) in debug output.
  */
-void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len);
+void wpa_hexdump_key(int level, const char *title, const void *buf, size_t len);
 
 static inline void wpa_hexdump_buf_key(int level, const char *title,
                                       const struct wpabuf *buf)
@@ -121,7 +127,7 @@ static inline void wpa_hexdump_buf_key(int level, const char *title,
  * the hex numbers and ASCII characters (for printable range) are shown. 16
  * bytes per line will be shown.
  */
-void wpa_hexdump_ascii(int level, const char *title, const u8 *buf,
+void wpa_hexdump_ascii(int level, const char *title, const void *buf,
                       size_t len);
 
 /**
@@ -138,7 +144,7 @@ void wpa_hexdump_ascii(int level, const char *title, const u8 *buf,
  * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by
  * default, does not include secret keys (passwords, etc.) in debug output.
  */
-void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf,
+void wpa_hexdump_ascii_key(int level, const char *title, const void *buf,
                           size_t len);
 
 /*
@@ -156,6 +162,7 @@ void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf,
 #define wpa_msg(args...) do { } while (0)
 #define wpa_msg_ctrl(args...) do { } while (0)
 #define wpa_msg_global(args...) do { } while (0)
+#define wpa_msg_global_ctrl(args...) do { } while (0)
 #define wpa_msg_no_global(args...) do { } while (0)
 #define wpa_msg_register_cb(f) do { } while (0)
 #define wpa_msg_register_ifname_cb(f) do { } while (0)
@@ -208,6 +215,21 @@ void wpa_msg_global(void *ctx, int level, const char *fmt, ...)
 PRINTF_FORMAT(3, 4);
 
 /**
+ * wpa_msg_global_ctrl - Conditional global printf for ctrl_iface monitors
+ * @ctx: Pointer to context data; this is the ctx variable registered
+ *     with struct wpa_driver_ops::init()
+ * @level: priority level (MSG_*) of the message
+ * @fmt: printf format string, followed by optional arguments
+ *
+ * This function is used to print conditional debugging and error messages.
+ * This function is like wpa_msg_global(), but it sends the output only to the
+ * attached global ctrl_iface monitors. In other words, it can be used for
+ * frequent events that do not need to be sent to syslog.
+ */
+void wpa_msg_global_ctrl(void *ctx, int level, const char *fmt, ...)
+PRINTF_FORMAT(3, 4);
+
+/**
  * wpa_msg_no_global - Conditional printf for ctrl_iface monitors
  * @ctx: Pointer to context data; this is the ctx variable registered
  *     with struct wpa_driver_ops::init()
index b257b36..7aafa0a 100644 (file)
@@ -205,6 +205,15 @@ void wpabuf_free(struct wpabuf *buf)
 }
 
 
+void wpabuf_clear_free(struct wpabuf *buf)
+{
+       if (buf) {
+               os_memset(wpabuf_mhead(buf), 0, wpabuf_len(buf));
+               wpabuf_free(buf);
+       }
+}
+
+
 void * wpabuf_put(struct wpabuf *buf, size_t len)
 {
        void *tmp = wpabuf_mhead_u8(buf) + wpabuf_len(buf);
index dbce925..c3ef1ba 100644 (file)
@@ -32,6 +32,7 @@ struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len);
 struct wpabuf * wpabuf_alloc_copy(const void *data, size_t len);
 struct wpabuf * wpabuf_dup(const struct wpabuf *src);
 void wpabuf_free(struct wpabuf *buf);
+void wpabuf_clear_free(struct wpabuf *buf);
 void * wpabuf_put(struct wpabuf *buf, size_t len);
 struct wpabuf * wpabuf_concat(struct wpabuf *a, struct wpabuf *b);
 struct wpabuf * wpabuf_zeropad(struct wpabuf *buf, size_t len);
diff --git a/src/utils/xml-utils.c b/src/utils/xml-utils.c
new file mode 100644 (file)
index 0000000..4916d29
--- /dev/null
@@ -0,0 +1,471 @@
+/*
+ * Generic XML helper functions
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "xml-utils.h"
+
+
+static xml_node_t * get_node_uri_iter(struct xml_node_ctx *ctx,
+                                     xml_node_t *root, char *uri)
+{
+       char *end;
+       xml_node_t *node;
+       const char *name;
+
+       end = strchr(uri, '/');
+       if (end)
+               *end++ = '\0';
+
+       node = root;
+       xml_node_for_each_sibling(ctx, node) {
+               xml_node_for_each_check(ctx, node);
+               name = xml_node_get_localname(ctx, node);
+               if (strcasecmp(name, uri) == 0)
+                       break;
+       }
+
+       if (node == NULL)
+               return NULL;
+
+       if (end) {
+               return get_node_uri_iter(ctx, xml_node_first_child(ctx, node),
+                                        end);
+       }
+
+       return node;
+}
+
+
+xml_node_t * get_node_uri(struct xml_node_ctx *ctx, xml_node_t *root,
+                         const char *uri)
+{
+       char *search;
+       xml_node_t *node;
+
+       search = os_strdup(uri);
+       if (search == NULL)
+               return NULL;
+
+       node = get_node_uri_iter(ctx, root, search);
+
+       os_free(search);
+       return node;
+}
+
+
+static xml_node_t * get_node_iter(struct xml_node_ctx *ctx,
+                                 xml_node_t *root, const char *path)
+{
+       char *end;
+       xml_node_t *node;
+       const char *name;
+
+       end = os_strchr(path, '/');
+       if (end)
+               *end++ = '\0';
+
+       xml_node_for_each_child(ctx, node, root) {
+               xml_node_for_each_check(ctx, node);
+               name = xml_node_get_localname(ctx, node);
+               if (os_strcasecmp(name, path) == 0)
+                       break;
+       }
+
+       if (node == NULL)
+               return NULL;
+       if (end)
+               return get_node_iter(ctx, node, end);
+       return node;
+}
+
+
+xml_node_t * get_node(struct xml_node_ctx *ctx, xml_node_t *root,
+                     const char *path)
+{
+       char *search;
+       xml_node_t *node;
+
+       search = os_strdup(path);
+       if (search == NULL)
+               return NULL;
+
+       node = get_node_iter(ctx, root, search);
+
+       os_free(search);
+       return node;
+}
+
+
+xml_node_t * get_child_node(struct xml_node_ctx *ctx, xml_node_t *root,
+                           const char *path)
+{
+       xml_node_t *node;
+       xml_node_t *match;
+
+       xml_node_for_each_child(ctx, node, root) {
+               xml_node_for_each_check(ctx, node);
+               match = get_node(ctx, node, path);
+               if (match)
+                       return match;
+       }
+
+       return NULL;
+}
+
+
+xml_node_t * node_from_file(struct xml_node_ctx *ctx, const char *name)
+{
+       xml_node_t *node;
+       char *buf, *buf2, *start;
+       size_t len;
+
+       buf = os_readfile(name, &len);
+       if (buf == NULL)
+               return NULL;
+       buf2 = os_realloc(buf, len + 1);
+       if (buf2 == NULL) {
+               os_free(buf);
+               return NULL;
+       }
+       buf = buf2;
+       buf[len] = '\0';
+
+       start = os_strstr(buf, "<!DOCTYPE ");
+       if (start) {
+               char *pos = start + 1;
+               int count = 1;
+               while (*pos) {
+                       if (*pos == '<')
+                               count++;
+                       else if (*pos == '>') {
+                               count--;
+                               if (count == 0) {
+                                       pos++;
+                                       break;
+                               }
+                       }
+                       pos++;
+               }
+               if (count == 0) {
+                       /* Remove DOCTYPE to allow the file to be parsed */
+                       os_memset(start, ' ', pos - start);
+               }
+       }
+
+       node = xml_node_from_buf(ctx, buf);
+       os_free(buf);
+
+       return node;
+}
+
+
+int node_to_file(struct xml_node_ctx *ctx, const char *fname, xml_node_t *node)
+{
+       FILE *f;
+       char *str;
+
+       str = xml_node_to_str(ctx, node);
+       if (str == NULL)
+               return -1;
+
+       f = fopen(fname, "w");
+       if (!f) {
+               os_free(str);
+               return -1;
+       }
+
+       fprintf(f, "%s\n", str);
+       os_free(str);
+       fclose(f);
+
+       return 0;
+}
+
+
+static char * get_val(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+       char *val, *pos;
+
+       val = xml_node_get_text(ctx, node);
+       if (val == NULL)
+               return NULL;
+       pos = val;
+       while (*pos) {
+               if (*pos != ' ' && *pos != '\t' && *pos != '\r' && *pos != '\n')
+                       return val;
+               pos++;
+       }
+
+       return NULL;
+}
+
+
+static char * add_path(const char *prev, const char *leaf)
+{
+       size_t len;
+       char *new_uri;
+
+       if (prev == NULL)
+               return NULL;
+
+       len = os_strlen(prev) + 1 + os_strlen(leaf) + 1;
+       new_uri = os_malloc(len);
+       if (new_uri)
+               os_snprintf(new_uri, len, "%s/%s", prev, leaf);
+
+       return new_uri;
+}
+
+
+static void node_to_tnds(struct xml_node_ctx *ctx, xml_node_t *out,
+                        xml_node_t *in, const char *uri)
+{
+       xml_node_t *node;
+       xml_node_t *tnds;
+       const char *name;
+       char *val;
+       char *new_uri;
+
+       xml_node_for_each_child(ctx, node, in) {
+               xml_node_for_each_check(ctx, node);
+               name = xml_node_get_localname(ctx, node);
+
+               tnds = xml_node_create(ctx, out, NULL, "Node");
+               if (tnds == NULL)
+                       return;
+               xml_node_create_text(ctx, tnds, NULL, "NodeName", name);
+
+               if (uri)
+                       xml_node_create_text(ctx, tnds, NULL, "Path", uri);
+
+               val = get_val(ctx, node);
+               if (val) {
+                       xml_node_create_text(ctx, tnds, NULL, "Value", val);
+                       xml_node_get_text_free(ctx, val);
+               }
+
+               new_uri = add_path(uri, name);
+               node_to_tnds(ctx, new_uri ? out : tnds, node, new_uri);
+               os_free(new_uri);
+       }
+}
+
+
+static int add_ddfname(struct xml_node_ctx *ctx, xml_node_t *parent,
+                      const char *urn)
+{
+       xml_node_t *node;
+
+       node = xml_node_create(ctx, parent, NULL, "RTProperties");
+       if (node == NULL)
+               return -1;
+       node = xml_node_create(ctx, node, NULL, "Type");
+       if (node == NULL)
+               return -1;
+       xml_node_create_text(ctx, node, NULL, "DDFName", urn);
+       return 0;
+}
+
+
+xml_node_t * mo_to_tnds(struct xml_node_ctx *ctx, xml_node_t *mo,
+                       int use_path, const char *urn, const char *ns_uri)
+{
+       xml_node_t *root;
+       xml_node_t *node;
+       const char *name;
+
+       root = xml_node_create_root(ctx, ns_uri, NULL, NULL, "MgmtTree");
+       if (root == NULL)
+               return NULL;
+
+       xml_node_create_text(ctx, root, NULL, "VerDTD", "1.2");
+
+       name = xml_node_get_localname(ctx, mo);
+
+       node = xml_node_create(ctx, root, NULL, "Node");
+       if (node == NULL)
+               goto fail;
+       xml_node_create_text(ctx, node, NULL, "NodeName", name);
+       if (urn)
+               add_ddfname(ctx, node, urn);
+
+       node_to_tnds(ctx, use_path ? root : node, mo, use_path ? name : NULL);
+
+       return root;
+
+fail:
+       xml_node_free(ctx, root);
+       return NULL;
+}
+
+
+static xml_node_t * get_first_child_node(struct xml_node_ctx *ctx,
+                                        xml_node_t *node,
+                                        const char *name)
+{
+       const char *lname;
+       xml_node_t *child;
+
+       xml_node_for_each_child(ctx, child, node) {
+               xml_node_for_each_check(ctx, child);
+               lname = xml_node_get_localname(ctx, child);
+               if (os_strcasecmp(lname, name) == 0)
+                       return child;
+       }
+
+       return NULL;
+}
+
+
+static char * get_node_text(struct xml_node_ctx *ctx, xml_node_t *node,
+                           const char *node_name)
+{
+       node = get_first_child_node(ctx, node, node_name);
+       if (node == NULL)
+               return NULL;
+       return xml_node_get_text(ctx, node);
+}
+
+
+static xml_node_t * add_mo_node(struct xml_node_ctx *ctx, xml_node_t *root,
+                               xml_node_t *node, const char *uri)
+{
+       char *nodename, *value, *path;
+       xml_node_t *parent;
+
+       nodename = get_node_text(ctx, node, "NodeName");
+       if (nodename == NULL)
+               return NULL;
+       value = get_node_text(ctx, node, "Value");
+
+       if (root == NULL) {
+               root = xml_node_create_root(ctx, NULL, NULL, NULL,
+                                           nodename);
+               if (root && value)
+                       xml_node_set_text(ctx, root, value);
+       } else {
+               if (uri == NULL) {
+                       xml_node_get_text_free(ctx, nodename);
+                       xml_node_get_text_free(ctx, value);
+                       return NULL;
+               }
+               path = get_node_text(ctx, node, "Path");
+               if (path)
+                       uri = path;
+               parent = get_node_uri(ctx, root, uri);
+               xml_node_get_text_free(ctx, path);
+               if (parent == NULL) {
+                       printf("Could not find URI '%s'\n", uri);
+                       xml_node_get_text_free(ctx, nodename);
+                       xml_node_get_text_free(ctx, value);
+                       return NULL;
+               }
+               if (value)
+                       xml_node_create_text(ctx, parent, NULL, nodename,
+                                            value);
+               else
+                       xml_node_create(ctx, parent, NULL, nodename);
+       }
+
+       xml_node_get_text_free(ctx, nodename);
+       xml_node_get_text_free(ctx, value);
+
+       return root;
+}
+
+
+static xml_node_t * tnds_to_mo_iter(struct xml_node_ctx *ctx, xml_node_t *root,
+                                   xml_node_t *node, const char *uri)
+{
+       xml_node_t *child;
+       const char *name;
+       char *nodename;
+
+       xml_node_for_each_sibling(ctx, node) {
+               xml_node_for_each_check(ctx, node);
+
+               nodename = get_node_text(ctx, node, "NodeName");
+               if (nodename == NULL)
+                       return NULL;
+
+               name = xml_node_get_localname(ctx, node);
+               if (strcmp(name, "Node") == 0) {
+                       if (root && !uri) {
+                               printf("Invalid TNDS tree structure - "
+                                      "multiple top level nodes\n");
+                               xml_node_get_text_free(ctx, nodename);
+                               return NULL;
+                       }
+                       root = add_mo_node(ctx, root, node, uri);
+               }
+
+               child = get_first_child_node(ctx, node, "Node");
+               if (child) {
+                       if (uri == NULL)
+                               tnds_to_mo_iter(ctx, root, child, nodename);
+                       else {
+                               char *new_uri;
+                               new_uri = add_path(uri, nodename);
+                               tnds_to_mo_iter(ctx, root, child, new_uri);
+                               os_free(new_uri);
+                       }
+               }
+               xml_node_get_text_free(ctx, nodename);
+       }
+
+       return root;
+}
+
+
+xml_node_t * tnds_to_mo(struct xml_node_ctx *ctx, xml_node_t *tnds)
+{
+       const char *name;
+       xml_node_t *node;
+
+       name = xml_node_get_localname(ctx, tnds);
+       if (name == NULL || os_strcmp(name, "MgmtTree") != 0)
+               return NULL;
+
+       node = get_first_child_node(ctx, tnds, "Node");
+       if (!node)
+               return NULL;
+       return tnds_to_mo_iter(ctx, NULL, node, NULL);
+}
+
+
+xml_node_t * soap_build_envelope(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+       xml_node_t *envelope, *body;
+       xml_namespace_t *ns;
+
+       envelope = xml_node_create_root(
+               ctx, "http://www.w3.org/2003/05/soap-envelope", "soap12", &ns,
+               "Envelope");
+       if (envelope == NULL)
+               return NULL;
+       body = xml_node_create(ctx, envelope, ns, "Body");
+       xml_node_add_child(ctx, body, node);
+       return envelope;
+}
+
+
+xml_node_t * soap_get_body(struct xml_node_ctx *ctx, xml_node_t *soap)
+{
+       xml_node_t *body, *child;
+
+       body = get_node_uri(ctx, soap, "Envelope/Body");
+       if (body == NULL)
+               return NULL;
+       xml_node_for_each_child(ctx, child, body) {
+               xml_node_for_each_check(ctx, child);
+               return child;
+       }
+       return NULL;
+}
diff --git a/src/utils/xml-utils.h b/src/utils/xml-utils.h
new file mode 100644 (file)
index 0000000..fb6208c
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Generic XML helper functions
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef XML_UTILS_H
+#define XML_UTILS_H
+
+struct xml_node_ctx;
+typedef struct xml_node xml_node_t;
+typedef struct xml_namespace_foo xml_namespace_t;
+
+/* XML library wrappers */
+
+int xml_validate(struct xml_node_ctx *ctx, xml_node_t *node,
+                const char *xml_schema_fname, char **ret_err);
+int xml_validate_dtd(struct xml_node_ctx *ctx, xml_node_t *node,
+                    const char *dtd_fname, char **ret_err);
+void xml_node_free(struct xml_node_ctx *ctx, xml_node_t *node);
+xml_node_t * xml_node_get_parent(struct xml_node_ctx *ctx, xml_node_t *node);
+xml_node_t * xml_node_from_buf(struct xml_node_ctx *ctx, const char *buf);
+const char * xml_node_get_localname(struct xml_node_ctx *ctx,
+                                   xml_node_t *node);
+char * xml_node_to_str(struct xml_node_ctx *ctx, xml_node_t *node);
+void xml_node_detach(struct xml_node_ctx *ctx, xml_node_t *node);
+void xml_node_add_child(struct xml_node_ctx *ctx, xml_node_t *parent,
+                       xml_node_t *child);
+xml_node_t * xml_node_create_root(struct xml_node_ctx *ctx, const char *ns_uri,
+                                 const char *ns_prefix,
+                                 xml_namespace_t **ret_ns, const char *name);
+xml_node_t * xml_node_create(struct xml_node_ctx *ctx, xml_node_t *parent,
+                            xml_namespace_t *ns, const char *name);
+xml_node_t * xml_node_create_text(struct xml_node_ctx *ctx,
+                                 xml_node_t *parent, xml_namespace_t *ns,
+                                 const char *name, const char *value);
+xml_node_t * xml_node_create_text_ns(struct xml_node_ctx *ctx,
+                                    xml_node_t *parent, const char *ns_uri,
+                                    const char *name, const char *value);
+void xml_node_set_text(struct xml_node_ctx *ctx, xml_node_t *node,
+                      const char *value);
+int xml_node_add_attr(struct xml_node_ctx *ctx, xml_node_t *node,
+                     xml_namespace_t *ns, const char *name, const char *value);
+char * xml_node_get_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
+                              char *name);
+char * xml_node_get_attr_value_ns(struct xml_node_ctx *ctx, xml_node_t *node,
+                                 const char *ns_uri, char *name);
+void xml_node_get_attr_value_free(struct xml_node_ctx *ctx, char *val);
+xml_node_t * xml_node_first_child(struct xml_node_ctx *ctx,
+                                 xml_node_t *parent);
+xml_node_t * xml_node_next_sibling(struct xml_node_ctx *ctx,
+                                  xml_node_t *node);
+int xml_node_is_element(struct xml_node_ctx *ctx, xml_node_t *node);
+char * xml_node_get_text(struct xml_node_ctx *ctx, xml_node_t *node);
+void xml_node_get_text_free(struct xml_node_ctx *ctx, char *val);
+char * xml_node_get_base64_text(struct xml_node_ctx *ctx, xml_node_t *node,
+                               int *ret_len);
+xml_node_t * xml_node_copy(struct xml_node_ctx *ctx, xml_node_t *node);
+
+#define xml_node_for_each_child(ctx, child, parent) \
+for (child = xml_node_first_child(ctx, parent); \
+     child; \
+     child = xml_node_next_sibling(ctx, child))
+
+#define xml_node_for_each_sibling(ctx, node) \
+for (; \
+     node; \
+     node = xml_node_next_sibling(ctx, node))
+
+#define xml_node_for_each_check(ctx, child) \
+if (!xml_node_is_element(ctx, child)) \
+       continue
+
+
+struct xml_node_ctx * xml_node_init_ctx(void *upper_ctx,
+                                       const void *env);
+void xml_node_deinit_ctx(struct xml_node_ctx *ctx);
+
+
+xml_node_t * get_node_uri(struct xml_node_ctx *ctx, xml_node_t *root,
+                         const char *uri);
+xml_node_t * get_node(struct xml_node_ctx *ctx, xml_node_t *root,
+                     const char *path);
+xml_node_t * get_child_node(struct xml_node_ctx *ctx, xml_node_t *root,
+                           const char *path);
+xml_node_t * node_from_file(struct xml_node_ctx *ctx, const char *name);
+int node_to_file(struct xml_node_ctx *ctx, const char *fname, xml_node_t *node);
+xml_node_t * mo_to_tnds(struct xml_node_ctx *ctx, xml_node_t *mo,
+                       int use_path, const char *urn, const char *ns_uri);
+xml_node_t * tnds_to_mo(struct xml_node_ctx *ctx, xml_node_t *tnds);
+
+xml_node_t * soap_build_envelope(struct xml_node_ctx *ctx, xml_node_t *node);
+xml_node_t * soap_get_body(struct xml_node_ctx *ctx, xml_node_t *soap);
+
+#endif /* XML_UTILS_H */
diff --git a/src/utils/xml_libxml2.c b/src/utils/xml_libxml2.c
new file mode 100644 (file)
index 0000000..c928394
--- /dev/null
@@ -0,0 +1,457 @@
+/*
+ * XML wrapper for libxml2
+ * Copyright (c) 2012-2013, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+#define LIBXML_VALID_ENABLED
+#include <libxml/tree.h>
+#include <libxml/xmlschemastypes.h>
+
+#include "common.h"
+#include "base64.h"
+#include "xml-utils.h"
+
+
+struct xml_node_ctx {
+       void *ctx;
+};
+
+
+struct str_buf {
+       char *buf;
+       size_t len;
+};
+
+#define MAX_STR 1000
+
+static void add_str(void *ctx_ptr, const char *fmt, ...)
+{
+       struct str_buf *str = ctx_ptr;
+       va_list ap;
+       char *n;
+       int len;
+
+       n = os_realloc(str->buf, str->len + MAX_STR + 2);
+       if (n == NULL)
+               return;
+       str->buf = n;
+
+       va_start(ap, fmt);
+       len = vsnprintf(str->buf + str->len, MAX_STR, fmt, ap);
+       va_end(ap);
+       if (len >= MAX_STR)
+               len = MAX_STR - 1;
+       str->len += len;
+       str->buf[str->len] = '\0';
+}
+
+
+int xml_validate(struct xml_node_ctx *ctx, xml_node_t *node,
+                const char *xml_schema_fname, char **ret_err)
+{
+       xmlDocPtr doc;
+       xmlNodePtr n;
+       xmlSchemaParserCtxtPtr pctx;
+       xmlSchemaValidCtxtPtr vctx;
+       xmlSchemaPtr schema;
+       int ret;
+       struct str_buf errors;
+
+       if (ret_err)
+               *ret_err = NULL;
+
+       doc = xmlNewDoc((xmlChar *) "1.0");
+       if (doc == NULL)
+               return -1;
+       n = xmlDocCopyNode((xmlNodePtr) node, doc, 1);
+       if (n == NULL) {
+               xmlFreeDoc(doc);
+               return -1;
+       }
+       xmlDocSetRootElement(doc, n);
+
+       os_memset(&errors, 0, sizeof(errors));
+
+       pctx = xmlSchemaNewParserCtxt(xml_schema_fname);
+       xmlSchemaSetParserErrors(pctx, (xmlSchemaValidityErrorFunc) add_str,
+                                (xmlSchemaValidityWarningFunc) add_str,
+                                &errors);
+       schema = xmlSchemaParse(pctx);
+       xmlSchemaFreeParserCtxt(pctx);
+
+       vctx = xmlSchemaNewValidCtxt(schema);
+       xmlSchemaSetValidErrors(vctx, (xmlSchemaValidityErrorFunc) add_str,
+                               (xmlSchemaValidityWarningFunc) add_str,
+                               &errors);
+
+       ret = xmlSchemaValidateDoc(vctx, doc);
+       xmlSchemaFreeValidCtxt(vctx);
+       xmlFreeDoc(doc);
+       xmlSchemaFree(schema);
+
+       if (ret == 0) {
+               os_free(errors.buf);
+               return 0;
+       } else if (ret > 0) {
+               if (ret_err)
+                       *ret_err = errors.buf;
+               else
+                       os_free(errors.buf);
+               return -1;
+       } else {
+               if (ret_err)
+                       *ret_err = errors.buf;
+               else
+                       os_free(errors.buf);
+               return -1;
+       }
+}
+
+
+int xml_validate_dtd(struct xml_node_ctx *ctx, xml_node_t *node,
+                    const char *dtd_fname, char **ret_err)
+{
+       xmlDocPtr doc;
+       xmlNodePtr n;
+       xmlValidCtxt vctx;
+       xmlDtdPtr dtd;
+       int ret;
+       struct str_buf errors;
+
+       if (ret_err)
+               *ret_err = NULL;
+
+       doc = xmlNewDoc((xmlChar *) "1.0");
+       if (doc == NULL)
+               return -1;
+       n = xmlDocCopyNode((xmlNodePtr) node, doc, 1);
+       if (n == NULL) {
+               xmlFreeDoc(doc);
+               return -1;
+       }
+       xmlDocSetRootElement(doc, n);
+
+       os_memset(&errors, 0, sizeof(errors));
+
+       dtd = xmlParseDTD(NULL, (const xmlChar *) dtd_fname);
+       if (dtd == NULL) {
+               xmlFreeDoc(doc);
+               return -1;
+       }
+
+       os_memset(&vctx, 0, sizeof(vctx));
+       vctx.userData = &errors;
+       vctx.error = add_str;
+       vctx.warning = add_str;
+       ret = xmlValidateDtd(&vctx, doc, dtd);
+       xmlFreeDoc(doc);
+       xmlFreeDtd(dtd);
+
+       if (ret == 1) {
+               os_free(errors.buf);
+               return 0;
+       } else {
+               if (ret_err)
+                       *ret_err = errors.buf;
+               else
+                       os_free(errors.buf);
+               return -1;
+       }
+}
+
+
+void xml_node_free(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+       xmlFreeNode((xmlNodePtr) node);
+}
+
+
+xml_node_t * xml_node_get_parent(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+       return (xml_node_t *) ((xmlNodePtr) node)->parent;
+}
+
+
+xml_node_t * xml_node_from_buf(struct xml_node_ctx *ctx, const char *buf)
+{
+       xmlDocPtr doc;
+       xmlNodePtr node;
+
+       doc = xmlParseMemory(buf, strlen(buf));
+       if (doc == NULL)
+               return NULL;
+       node = xmlDocGetRootElement(doc);
+       node = xmlCopyNode(node, 1);
+       xmlFreeDoc(doc);
+
+       return (xml_node_t *) node;
+}
+
+
+const char * xml_node_get_localname(struct xml_node_ctx *ctx,
+                                   xml_node_t *node)
+{
+       return (const char *) ((xmlNodePtr) node)->name;
+}
+
+
+char * xml_node_to_str(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+       xmlChar *buf;
+       int bufsiz;
+       char *ret, *pos;
+       xmlNodePtr n = (xmlNodePtr) node;
+       xmlDocPtr doc;
+
+       doc = xmlNewDoc((xmlChar *) "1.0");
+       n = xmlDocCopyNode(n, doc, 1);
+       xmlDocSetRootElement(doc, n);
+       xmlDocDumpFormatMemory(doc, &buf, &bufsiz, 0);
+       xmlFreeDoc(doc);
+       pos = (char *) buf;
+       if (strncmp(pos, "<?xml", 5) == 0) {
+               pos = strchr(pos, '>');
+               if (pos)
+                       pos++;
+               while (pos && (*pos == '\r' || *pos == '\n'))
+                       pos++;
+       }
+       if (pos)
+               ret = os_strdup(pos);
+       else
+               ret = NULL;
+       xmlFree(buf);
+
+       if (ret) {
+               pos = ret;
+               if (pos[0]) {
+                       while (pos[1])
+                               pos++;
+               }
+               while (pos >= ret && *pos == '\n')
+                       *pos-- = '\0';
+       }
+
+       return ret;
+}
+
+
+void xml_node_detach(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+       xmlUnlinkNode((xmlNodePtr) node);
+}
+
+
+void xml_node_add_child(struct xml_node_ctx *ctx, xml_node_t *parent,
+                       xml_node_t *child)
+{
+       xmlAddChild((xmlNodePtr) parent, (xmlNodePtr) child);
+}
+
+
+xml_node_t * xml_node_create_root(struct xml_node_ctx *ctx, const char *ns_uri,
+                                 const char *ns_prefix,
+                                 xml_namespace_t **ret_ns, const char *name)
+{
+       xmlNodePtr node;
+       xmlNsPtr ns = NULL;
+
+       node = xmlNewNode(NULL, (const xmlChar *) name);
+       if (node == NULL)
+               return NULL;
+       if (ns_uri) {
+               ns = xmlNewNs(node, (const xmlChar *) ns_uri,
+                             (const xmlChar *) ns_prefix);
+               xmlSetNs(node, ns);
+       }
+
+       if (ret_ns)
+               *ret_ns = (xml_namespace_t *) ns;
+
+       return (xml_node_t *) node;
+}
+
+
+xml_node_t * xml_node_create(struct xml_node_ctx *ctx, xml_node_t *parent,
+                            xml_namespace_t *ns, const char *name)
+{
+       xmlNodePtr node;
+       node = xmlNewChild((xmlNodePtr) parent, (xmlNsPtr) ns,
+                          (const xmlChar *) name, NULL);
+       return (xml_node_t *) node;
+}
+
+
+xml_node_t * xml_node_create_text(struct xml_node_ctx *ctx,
+                                 xml_node_t *parent, xml_namespace_t *ns,
+                                 const char *name, const char *value)
+{
+       xmlNodePtr node;
+       node = xmlNewTextChild((xmlNodePtr) parent, (xmlNsPtr) ns,
+                              (const xmlChar *) name, (const xmlChar *) value);
+       return (xml_node_t *) node;
+}
+
+
+xml_node_t * xml_node_create_text_ns(struct xml_node_ctx *ctx,
+                                    xml_node_t *parent, const char *ns_uri,
+                                    const char *name, const char *value)
+{
+       xmlNodePtr node;
+       xmlNsPtr ns;
+
+       node = xmlNewTextChild((xmlNodePtr) parent, NULL,
+                              (const xmlChar *) name, (const xmlChar *) value);
+       ns = xmlNewNs(node, (const xmlChar *) ns_uri, NULL);
+       xmlSetNs(node, ns);
+       return (xml_node_t *) node;
+}
+
+
+void xml_node_set_text(struct xml_node_ctx *ctx, xml_node_t *node,
+                      const char *value)
+{
+       /* TODO: escape XML special chars in value */
+       xmlNodeSetContent((xmlNodePtr) node, (xmlChar *) value);
+}
+
+
+int xml_node_add_attr(struct xml_node_ctx *ctx, xml_node_t *node,
+                     xml_namespace_t *ns, const char *name, const char *value)
+{
+       xmlAttrPtr attr;
+
+       if (ns) {
+               attr = xmlNewNsProp((xmlNodePtr) node, (xmlNsPtr) ns,
+                                   (const xmlChar *) name,
+                                   (const xmlChar *) value);
+       } else {
+               attr = xmlNewProp((xmlNodePtr) node, (const xmlChar *) name,
+                                 (const xmlChar *) value);
+       }
+
+       return attr ? 0 : -1;
+}
+
+
+char * xml_node_get_attr_value(struct xml_node_ctx *ctx, xml_node_t *node,
+                              char *name)
+{
+       return (char *) xmlGetNoNsProp((xmlNodePtr) node,
+                                      (const xmlChar *) name);
+}
+
+
+char * xml_node_get_attr_value_ns(struct xml_node_ctx *ctx, xml_node_t *node,
+                                 const char *ns_uri, char *name)
+{
+       return (char *) xmlGetNsProp((xmlNodePtr) node, (const xmlChar *) name,
+                                    (const xmlChar *) ns_uri);
+}
+
+
+void xml_node_get_attr_value_free(struct xml_node_ctx *ctx, char *val)
+{
+       if (val)
+               xmlFree((xmlChar *) val);
+}
+
+
+xml_node_t * xml_node_first_child(struct xml_node_ctx *ctx,
+                                 xml_node_t *parent)
+{
+       return (xml_node_t *) ((xmlNodePtr) parent)->children;
+}
+
+
+xml_node_t * xml_node_next_sibling(struct xml_node_ctx *ctx,
+                                  xml_node_t *node)
+{
+       return (xml_node_t *) ((xmlNodePtr) node)->next;
+}
+
+
+int xml_node_is_element(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+       return ((xmlNodePtr) node)->type == XML_ELEMENT_NODE;
+}
+
+
+char * xml_node_get_text(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+       if (xmlChildElementCount((xmlNodePtr) node) > 0)
+               return NULL;
+       return (char *) xmlNodeGetContent((xmlNodePtr) node);
+}
+
+
+void xml_node_get_text_free(struct xml_node_ctx *ctx, char *val)
+{
+       if (val)
+               xmlFree((xmlChar *) val);
+}
+
+
+char * xml_node_get_base64_text(struct xml_node_ctx *ctx, xml_node_t *node,
+                               int *ret_len)
+{
+       char *txt;
+       unsigned char *ret;
+       size_t len;
+
+       txt = xml_node_get_text(ctx, node);
+       if (txt == NULL)
+               return NULL;
+
+       ret = base64_decode((unsigned char *) txt, strlen(txt), &len);
+       if (ret_len)
+               *ret_len = len;
+       xml_node_get_text_free(ctx, txt);
+       if (ret == NULL)
+               return NULL;
+       txt = os_malloc(len + 1);
+       if (txt == NULL) {
+               os_free(ret);
+               return NULL;
+       }
+       os_memcpy(txt, ret, len);
+       txt[len] = '\0';
+       return txt;
+}
+
+
+xml_node_t * xml_node_copy(struct xml_node_ctx *ctx, xml_node_t *node)
+{
+       if (node == NULL)
+               return NULL;
+       return (xml_node_t *) xmlCopyNode((xmlNodePtr) node, 1);
+}
+
+
+struct xml_node_ctx * xml_node_init_ctx(void *upper_ctx,
+                                       const void *env)
+{
+       struct xml_node_ctx *xctx;
+
+       xctx = os_zalloc(sizeof(*xctx));
+       if (xctx == NULL)
+               return NULL;
+       xctx->ctx = upper_ctx;
+
+       LIBXML_TEST_VERSION
+
+       return xctx;
+}
+
+
+void xml_node_deinit_ctx(struct xml_node_ctx *ctx)
+{
+       xmlSchemaCleanupTypes();
+       xmlCleanupParser();
+       xmlMemoryDump();
+       os_free(ctx);
+}
index 9c41962..adfd3df 100644 (file)
@@ -2,7 +2,7 @@ all:
        @echo Nothing to be made.
 
 clean:
-       rm -f *~ *.o *.d
+       rm -f *~ *.o *.d *.gcno *.gcda *.gcov
 
 install:
        @echo Nothing to be made.
index 6ca3214..ac088c4 100644 (file)
@@ -232,6 +232,7 @@ struct http_server * http_server_init(struct in_addr *addr, int port,
 {
        struct sockaddr_in sin;
        struct http_server *srv;
+       int on = 1;
 
        srv = os_zalloc(sizeof(*srv));
        if (srv == NULL)
@@ -242,6 +243,15 @@ struct http_server * http_server_init(struct in_addr *addr, int port,
        srv->fd = socket(AF_INET, SOCK_STREAM, 0);
        if (srv->fd < 0)
                goto fail;
+
+       if (setsockopt(srv->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0)
+       {
+               wpa_printf(MSG_DEBUG,
+                          "HTTP: setsockopt(SO_REUSEADDR) failed: %s",
+                          strerror(errno));
+               /* try to continue anyway */
+       }
+
        if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0)
                goto fail;
        if (port < 0)
index ad4f4a1..2f08f37 100644 (file)
@@ -67,8 +67,6 @@ struct httpread {
        int timeout_seconds;            /* 0 or total duration timeout period */
 
        /* dynamically used information follows */
-       int sd_registered;      /* nonzero if we need to unregister socket */
-       int to_registered;      /* nonzero if we need to unregister timeout */
 
        int got_hdr;            /* nonzero when header is finalized */
        char hdr[HTTPREAD_HEADER_MAX_SIZE+1];   /* headers stored here */
@@ -129,19 +127,6 @@ static int word_eq(char *s1, char *s2)
 }
 
 
-/* convert hex to binary
- * Requires that c have been previously tested true with isxdigit().
- */
-static int hex_value(int c)
-{
-       if (isdigit(c))
-               return c - '0';
-       if (islower(c))
-               return 10 + c - 'a';
-       return 10 + c - 'A';
-}
-
-
 static void httpread_timeout_handler(void *eloop_data, void *user_ctx);
 
 /* httpread_destroy -- if h is non-NULL, clean up
@@ -156,12 +141,8 @@ void httpread_destroy(struct httpread *h)
        if (!h)
                return;
 
-       if (h->to_registered)
-               eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
-       h->to_registered = 0;
-       if (h->sd_registered)
-               eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
-       h->sd_registered = 0;
+       eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
+       eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
        os_free(h->body);
        os_free(h->uri);
        os_memset(h, 0, sizeof(*h));  /* aid debugging */
@@ -176,7 +157,6 @@ static void httpread_timeout_handler(void *eloop_data, void *user_ctx)
 {
        struct httpread *h = user_ctx;
        wpa_printf(MSG_DEBUG, "httpread timeout (%p)", h);
-       h->to_registered = 0;   /* is self-cancelling */
        (*h->cb)(h, h->cookie, HTTPREAD_EVENT_TIMEOUT);
 }
 
@@ -295,8 +275,7 @@ static int httpread_hdr_analyze(struct httpread *h)
                        int c = *rawuri;
                        if (c == '%' &&
                            isxdigit(rawuri[1]) && isxdigit(rawuri[2])) {
-                               *uri++ = (hex_value(rawuri[1]) << 4) |
-                                       hex_value(rawuri[2]);
+                               *uri++ = hex2byte(rawuri + 1);
                                rawuri += 3;
                        } else {
                                *uri++ = c;
@@ -434,8 +413,8 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
                 */
                if (httpread_debug >= 10)
                        wpa_printf(MSG_DEBUG, "httpread ok eof(%p)", h);
-                       h->got_body = 1;
-                       goto got_file;
+               h->got_body = 1;
+               goto got_file;
        }
        rbp = readbuf;
 
@@ -638,7 +617,6 @@ static void httpread_read_handler(int sd, void *eloop_ctx, void *sock_ctx)
                 * We do NOT support trailers except to skip them --
                 * this is supported (generally) by the http spec.
                 */
-               bbp = h->body + h->body_nbytes;
                for (;;) {
                        int c;
                        if (nread <= 0)
@@ -703,15 +681,11 @@ got_file:
         * and just in case somehow we don't get destroyed right away,
         * unregister now.
         */
-       if (h->sd_registered)
-               eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
-       h->sd_registered = 0;
+       eloop_unregister_sock(h->sd, EVENT_TYPE_READ);
        /* The application can destroy us whenever they feel like...
         * cancel timeout.
         */
-       if (h->to_registered)
-               eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
-       h->to_registered = 0;
+       eloop_cancel_timeout(httpread_timeout_handler, NULL, h);
        (*h->cb)(h, h->cookie, HTTPREAD_EVENT_FILE_READY);
 }
 
@@ -749,21 +723,17 @@ struct httpread * httpread_create(
        h->max_bytes = max_bytes;
        h->timeout_seconds = timeout_seconds;
 
-       if (timeout_seconds > 0) {
-               if (eloop_register_timeout(timeout_seconds, 0,
-                                          httpread_timeout_handler,
-                                          NULL, h)) {
-                       /* No way to recover (from malloc failure) */
-                       goto fail;
-               }
-               h->to_registered = 1;
+       if (timeout_seconds > 0 &&
+           eloop_register_timeout(timeout_seconds, 0,
+                                  httpread_timeout_handler, NULL, h)) {
+               /* No way to recover (from malloc failure) */
+               goto fail;
        }
        if (eloop_register_sock(sd, EVENT_TYPE_READ, httpread_read_handler,
                                NULL, h)) {
                /* No way to recover (from malloc failure) */
                goto fail;
        }
-       h->sd_registered = 1;
        return h;
 
 fail:
index 96685d2..d45dfc8 100644 (file)
@@ -30,6 +30,7 @@ struct ndef_record {
 };
 
 static char wifi_handover_type[] = "application/vnd.wfa.wsc";
+static char p2p_handover_type[] = "application/vnd.wfa.p2p";
 
 static int ndef_parse_record(const u8 *data, u32 size,
                             struct ndef_record *record)
@@ -147,7 +148,8 @@ static struct wpabuf * ndef_build_record(u8 flags, void *type,
 
 static int wifi_filter(struct ndef_record *record)
 {
-       if (record->type_length != os_strlen(wifi_handover_type))
+       if (record->type == NULL ||
+           record->type_length != os_strlen(wifi_handover_type))
                return 0;
        if (os_memcmp(record->type, wifi_handover_type,
                      os_strlen(wifi_handover_type)) != 0)
@@ -170,85 +172,27 @@ struct wpabuf * ndef_build_wifi(const struct wpabuf *buf)
 }
 
 
-struct wpabuf * ndef_build_wifi_hc(int begin)
+static int p2p_filter(struct ndef_record *record)
 {
-       struct wpabuf *hc, *carrier;
-
-       carrier = wpabuf_alloc(2 + os_strlen(wifi_handover_type));
-       if (carrier == NULL)
-               return NULL;
-       wpabuf_put_u8(carrier, 0x02); /* Carrier Type Format */
-       wpabuf_put_u8(carrier, os_strlen(wifi_handover_type));
-       wpabuf_put_str(carrier, wifi_handover_type);
-
-       hc = ndef_build_record((begin ? FLAG_MESSAGE_BEGIN : 0) |
-                              FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "Hc", 2,
-                              "0", 1, carrier);
-       wpabuf_free(carrier);
-
-       return hc;
+       if (record->type == NULL ||
+           record->type_length != os_strlen(p2p_handover_type))
+               return 0;
+       if (os_memcmp(record->type, p2p_handover_type,
+                     os_strlen(p2p_handover_type)) != 0)
+               return 0;
+       return 1;
 }
 
 
-struct wpabuf * ndef_build_wifi_hr(void)
+struct wpabuf * ndef_parse_p2p(const struct wpabuf *buf)
 {
-       struct wpabuf *rn, *cr, *ac_payload, *ac, *hr_payload, *hr;
-       struct wpabuf *hc;
-
-       rn = wpabuf_alloc(2);
-       if (rn == NULL)
-               return NULL;
-       wpabuf_put_be16(rn, os_random() & 0xffff);
-
-       cr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "cr", 2,
-                              NULL, 0, rn);
-       wpabuf_free(rn);
-
-       if (cr == NULL)
-               return NULL;
-
-       ac_payload = wpabuf_alloc(4);
-       if (ac_payload == NULL) {
-               wpabuf_free(cr);
-               return NULL;
-       }
-       wpabuf_put_u8(ac_payload, 0x01); /* Carrier Flags: CRS=1 "active" */
-       wpabuf_put_u8(ac_payload, 0x01); /* Carrier Data Reference Length */
-       wpabuf_put_u8(ac_payload, '0'); /* Carrier Data Reference: "0" */
-       wpabuf_put_u8(ac_payload, 0); /* Aux Data Reference Count */
-
-       ac = ndef_build_record(FLAG_MESSAGE_END | FLAG_TNF_NFC_FORUM, "ac", 2,
-                              NULL, 0, ac_payload);
-       wpabuf_free(ac_payload);
-       if (ac == NULL) {
-               wpabuf_free(cr);
-               return NULL;
-       }
-
-       hr_payload = wpabuf_alloc(1 + wpabuf_len(cr) + wpabuf_len(ac));
-       if (hr_payload == NULL) {
-               wpabuf_free(cr);
-               wpabuf_free(ac);
-               return NULL;
-       }
-
-       wpabuf_put_u8(hr_payload, 0x12); /* Connection Handover Version 1.2 */
-       wpabuf_put_buf(hr_payload, cr);
-       wpabuf_put_buf(hr_payload, ac);
-       wpabuf_free(cr);
-       wpabuf_free(ac);
+       return ndef_parse_records(buf, p2p_filter);
+}
 
-       hr = ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_TNF_NFC_FORUM, "Hr", 2,
-                              NULL, 0, hr_payload);
-       wpabuf_free(hr_payload);
-       if (hr == NULL)
-               return NULL;
 
-       hc = ndef_build_wifi_hc(0);
-       if (hc == NULL) {
-               wpabuf_free(hr);
-               return NULL;
-       }
-
-       return wpabuf_concat(hr, hc);
+struct wpabuf * ndef_build_p2p(const struct wpabuf *buf)
+{
+       return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END |
+                                FLAG_TNF_RFC2046, p2p_handover_type,
+                                os_strlen(p2p_handover_type), NULL, 0, buf);
 }
index ff4b20d..2c68be8 100644 (file)
@@ -18,6 +18,7 @@
 #ifdef CONFIG_WPS_TESTING
 int wps_version_number = 0x20;
 int wps_testing_dummy_cred = 0;
+int wps_corrupt_pkhash = 0;
 #endif /* CONFIG_WPS_TESTING */
 
 
@@ -58,6 +59,10 @@ struct wps_data * wps_init(const struct wps_config *cfg)
        }
 
 #ifdef CONFIG_WPS_NFC
+       if (cfg->pin == NULL &&
+           cfg->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)
+               data->dev_pw_id = cfg->dev_pw_id;
+
        if (cfg->wps->ap && !cfg->registrar && cfg->wps->ap_nfc_dev_pw_id) {
                /* Keep AP PIN as alternative Device Password */
                data->alt_dev_pw_id = data->dev_pw_id;
@@ -84,7 +89,7 @@ struct wps_data * wps_init(const struct wps_config *cfg)
        if (cfg->pbc) {
                /* Use special PIN '00000000' for PBC */
                data->dev_pw_id = DEV_PW_PUSHBUTTON;
-               os_free(data->dev_password);
+               bin_clear_free(data->dev_password, data->dev_password_len);
                data->dev_password = (u8 *) os_strdup("00000000");
                if (data->dev_password == NULL) {
                        os_free(data);
@@ -117,7 +122,8 @@ struct wps_data * wps_init(const struct wps_config *cfg)
                data->new_ap_settings =
                        os_malloc(sizeof(*data->new_ap_settings));
                if (data->new_ap_settings == NULL) {
-                       os_free(data->dev_password);
+                       bin_clear_free(data->dev_password,
+                                      data->dev_password_len);
                        os_free(data);
                        return NULL;
                }
@@ -133,6 +139,12 @@ struct wps_data * wps_init(const struct wps_config *cfg)
        data->use_psk_key = cfg->use_psk_key;
        data->pbc_in_m1 = cfg->pbc_in_m1;
 
+       if (cfg->peer_pubkey_hash) {
+               os_memcpy(data->peer_pubkey_hash, cfg->peer_pubkey_hash,
+                         WPS_OOB_PUBKEY_HASH_LEN);
+               data->peer_pubkey_hash_set = 1;
+       }
+
        return data;
 }
 
@@ -162,13 +174,12 @@ void wps_deinit(struct wps_data *data)
        wpabuf_free(data->dh_pubkey_e);
        wpabuf_free(data->dh_pubkey_r);
        wpabuf_free(data->last_msg);
-       os_free(data->dev_password);
-       os_free(data->alt_dev_password);
-       os_free(data->new_psk);
+       bin_clear_free(data->dev_password, data->dev_password_len);
+       bin_clear_free(data->alt_dev_password, data->alt_dev_password_len);
+       bin_clear_free(data->new_psk, data->new_psk_len);
        wps_device_data_free(&data->peer_dev);
-       os_free(data->new_ap_settings);
+       bin_clear_free(data->new_ap_settings, sizeof(*data->new_ap_settings));
        dh5_free(data->dh_ctx);
-       os_free(data->nfc_pw_token);
        os_free(data);
 }
 
@@ -497,17 +508,15 @@ struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev,
            wps_build_config_methods(ie, dev->config_methods) ||
            wps_build_uuid_e(ie, uuid) ||
            wps_build_primary_dev_type(dev, ie) ||
-           wps_build_rf_bands(dev, ie) ||
+           wps_build_rf_bands(dev, ie, 0) ||
            wps_build_assoc_state(NULL, ie) ||
            wps_build_config_error(ie, WPS_CFG_NO_ERROR) ||
            wps_build_dev_password_id(ie, pw_id) ||
-#ifdef CONFIG_WPS2
            wps_build_manufacturer(dev, ie) ||
            wps_build_model_name(dev, ie) ||
            wps_build_model_number(dev, ie) ||
            wps_build_dev_name(dev, ie) ||
            wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0) ||
-#endif /* CONFIG_WPS2 */
            wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types)
            ||
            wps_build_secondary_dev_type(dev, ie)
@@ -516,13 +525,6 @@ struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev,
                return NULL;
        }
 
-#ifndef CONFIG_WPS2
-       if (dev->p2p && wps_build_dev_name(dev, ie)) {
-               wpabuf_free(ie);
-               return NULL;
-       }
-#endif /* CONFIG_WPS2 */
-
        return wps_ie_encapsulate(ie);
 }
 
@@ -558,7 +560,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end)
                                          "wps_state=configured\n");
                else
                        ret = 0;
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
@@ -566,7 +568,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end)
        if (attr.ap_setup_locked && *attr.ap_setup_locked) {
                ret = os_snprintf(pos, end - pos,
                                  "wps_ap_setup_locked=1\n");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
@@ -574,7 +576,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end)
        if (attr.selected_registrar && *attr.selected_registrar) {
                ret = os_snprintf(pos, end - pos,
                                  "wps_selected_registrar=1\n");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
@@ -583,7 +585,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end)
                ret = os_snprintf(pos, end - pos,
                                  "wps_device_password_id=%u\n",
                                  WPA_GET_BE16(attr.dev_password_id));
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
@@ -593,7 +595,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end)
                                  "wps_selected_registrar_config_methods="
                                  "0x%04x\n",
                                  WPA_GET_BE16(attr.sel_reg_config_methods));
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
@@ -605,7 +607,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end)
                                  wps_dev_type_bin2str(attr.primary_dev_type,
                                                       devtype,
                                                       sizeof(devtype)));
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
@@ -624,7 +626,7 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end)
                str[i] = '\0';
                ret = os_snprintf(pos, end - pos, "wps_device_name=%s\n", str);
                os_free(str);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
@@ -633,10 +635,27 @@ int wps_attr_text(struct wpabuf *data, char *buf, char *end)
                ret = os_snprintf(pos, end - pos,
                                  "wps_config_methods=0x%04x\n",
                                  WPA_GET_BE16(attr.config_methods));
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
 
        return pos - buf;
 }
+
+
+const char * wps_ei_str(enum wps_error_indication ei)
+{
+       switch (ei) {
+       case WPS_EI_NO_ERROR:
+               return "No Error";
+       case WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED:
+               return "TKIP Only Prohibited";
+       case WPS_EI_SECURITY_WEP_PROHIBITED:
+               return "WEP Prohibited";
+       case WPS_EI_AUTH_FAILURE:
+               return "Authentication Failure";
+       default:
+               return "Unknown";
+       }
+}
index cb03dbd..0a7f65d 100644 (file)
@@ -42,7 +42,6 @@ struct wps_parse_attr;
  * @cred_attr: Unparsed Credential attribute data (used only in cred_cb());
  *     this may be %NULL, if not used
  * @cred_attr_len: Length of cred_attr in octets
- * @ap_channel: AP channel
  */
 struct wps_credential {
        u8 ssid[32];
@@ -55,7 +54,6 @@ struct wps_credential {
        u8 mac_addr[ETH_ALEN];
        const u8 *cred_attr;
        size_t cred_attr_len;
-       u16 ap_channel;
 };
 
 #define WPS_DEV_TYPE_LEN 8
@@ -183,6 +181,11 @@ struct wps_config {
         * PBC with the AP.
         */
        int pbc_in_m1;
+
+       /**
+        * peer_pubkey_hash - Peer public key hash or %NULL if not known
+        */
+       const u8 *peer_pubkey_hash;
 };
 
 struct wps_data * wps_init(const struct wps_config *cfg);
@@ -246,14 +249,15 @@ struct wps_registrar_config {
         * new_psk_cb - Callback for new PSK
         * @ctx: Higher layer context data (cb_ctx)
         * @mac_addr: MAC address of the Enrollee
+        * @p2p_dev_addr: P2P Device Address of the Enrollee or all zeros if not
         * @psk: The new PSK
         * @psk_len: The length of psk in octets
         * Returns: 0 on success, -1 on failure
         *
         * This callback is called when a new per-device PSK is provisioned.
         */
-       int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk,
-                         size_t psk_len);
+       int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr,
+                         const u8 *psk, size_t psk_len);
 
        /**
         * set_ie_cb - Callback for WPS IE changes
@@ -382,6 +386,14 @@ struct wps_registrar_config {
         * dualband - Whether this is a concurrent dualband AP
         */
        int dualband;
+
+       /**
+        * force_per_enrollee_psk - Force per-Enrollee random PSK
+        *
+        * This forces per-Enrollee random PSK to be generated even if a default
+        * PSK is set for a network.
+        */
+       int force_per_enrollee_psk;
 };
 
 
@@ -420,6 +432,16 @@ enum wps_event {
        WPS_EV_PBC_TIMEOUT,
 
        /**
+        * WPS_EV_PBC_ACTIVE - PBC mode was activated
+        */
+       WPS_EV_PBC_ACTIVE,
+
+       /**
+        * WPS_EV_PBC_DISABLE - PBC mode was disabled
+        */
+       WPS_EV_PBC_DISABLE,
+
+       /**
         * WPS_EV_ER_AP_ADD - ER: AP added
         */
        WPS_EV_ER_AP_ADD,
@@ -487,11 +509,17 @@ union wps_event_data {
                int msg;
                u16 config_error;
                u16 error_indication;
+               u8 peer_macaddr[ETH_ALEN];
        } fail;
 
+       struct wps_event_success {
+               u8 peer_macaddr[ETH_ALEN];
+       } success;
+
        struct wps_event_pwd_auth_fail {
                int enrollee;
                int part;
+               u8 peer_macaddr[ETH_ALEN];
        } pwd_auth_fail;
 
        struct wps_event_er_ap {
@@ -640,6 +668,16 @@ struct wps_context {
        u16 auth_types;
 
        /**
+        * encr_types - Current AP encryption type (WPS_ENCR_*)
+        */
+       u16 ap_encr_type;
+
+       /**
+        * ap_auth_type - Current AP authentication types (WPS_AUTH_*)
+        */
+       u16 ap_auth_type;
+
+       /**
         * network_key - The current Network Key (PSK) or %NULL to generate new
         *
         * If %NULL, Registrar will generate per-device PSK. In addition, AP
@@ -730,6 +768,13 @@ struct wps_context {
                         union wps_event_data *data);
 
        /**
+        * rf_band_cb - Fetch currently used RF band
+        * @ctx: Higher layer context data (cb_ctx)
+        * Return: Current used RF band or 0 if not known
+        */
+       int (*rf_band_cb)(void *ctx);
+
+       /**
         * cb_ctx: Higher layer context data for callbacks
         */
        void *cb_ctx;
@@ -769,10 +814,12 @@ int wps_registrar_config_ap(struct wps_registrar *reg,
                            struct wps_credential *cred);
 int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
                                   const u8 *pubkey_hash, u16 pw_id,
-                                  const u8 *dev_pw, size_t dev_pw_len);
+                                  const u8 *dev_pw, size_t dev_pw_len,
+                                  int pk_hash_provided_oob);
 int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
                                         const u8 *oob_dev_pw,
                                         size_t oob_dev_pw_len);
+void wps_registrar_flush(struct wps_registrar *reg);
 
 int wps_build_credential_wrap(struct wpabuf *msg,
                              const struct wps_credential *cred);
@@ -783,9 +830,11 @@ unsigned int wps_generate_pin(void);
 int wps_pin_str_valid(const char *pin);
 void wps_free_pending_msgs(struct upnp_pending_message *msgs);
 
-struct wpabuf * wps_get_oob_cred(struct wps_context *wps);
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band,
+                                int channel);
 int wps_oob_use_cred(struct wps_context *wps, struct wps_parse_attr *attr);
 int wps_attr_text(struct wpabuf *data, char *buf, char *end);
+const char * wps_ei_str(enum wps_error_indication ei);
 
 struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname,
                            const char *filter);
@@ -806,6 +855,9 @@ struct wpabuf * wps_er_config_token_from_cred(struct wps_context *wps,
                                              struct wps_credential *cred);
 struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid,
                                        const u8 *addr);
+struct wpabuf * wps_er_nfc_handover_sel(struct wps_er *er,
+                                       struct wps_context *wps, const u8 *uuid,
+                                       const u8 *addr, struct wpabuf *pubkey);
 
 int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN]);
 char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
@@ -817,15 +869,27 @@ struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id,
                                       const struct wpabuf *dev_pw);
 struct wpabuf * wps_nfc_token_build(int ndef, int id, struct wpabuf *pubkey,
                                    struct wpabuf *dev_pw);
+int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey);
 struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
                                  struct wpabuf **privkey,
                                  struct wpabuf **dev_pw);
+struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx,
+                                          struct wpabuf *nfc_dh_pubkey);
+struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx,
+                                          struct wpabuf *nfc_dh_pubkey,
+                                          const u8 *bssid, int freq);
+struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx,
+                                              struct wpabuf *nfc_dh_pubkey);
+struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx,
+                                              int nfc_dev_pw_id,
+                                              struct wpabuf *nfc_dh_pubkey,
+                                              struct wpabuf *nfc_dev_pw);
 
 /* ndef.c */
 struct wpabuf * ndef_parse_wifi(const struct wpabuf *buf);
 struct wpabuf * ndef_build_wifi(const struct wpabuf *buf);
-struct wpabuf * ndef_build_wifi_hc(int begin);
-struct wpabuf * ndef_build_wifi_hr(void);
+struct wpabuf * ndef_parse_p2p(const struct wpabuf *buf);
+struct wpabuf * ndef_build_p2p(const struct wpabuf *buf);
 
 #ifdef CONFIG_WPS_STRICT
 int wps_validate_beacon(const struct wpabuf *wps_ie);
index ac9bb1e..b689357 100644 (file)
@@ -38,8 +38,15 @@ int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg)
                wps->wps->dh_ctx = NULL;
                pubkey = wpabuf_dup(wps->wps->dh_pubkey);
 #ifdef CONFIG_WPS_NFC
-       } else if (wps->dev_pw_id >= 0x10 && wps->wps->ap &&
-                  wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id) {
+       } else if ((wps->dev_pw_id >= 0x10 ||
+                   wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) &&
+                  (wps->wps->ap ||
+                   (wps->wps->ap_nfc_dh_pubkey &&
+                    wps->wps->ap_nfc_dev_pw_id ==
+                    DEV_PW_NFC_CONNECTION_HANDOVER &&
+                    wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)) &&
+                  (wps->dev_pw_id == wps->wps->ap_nfc_dev_pw_id ||
+                   wps->wps->ap_nfc_dh_pubkey)) {
                wpa_printf(MSG_DEBUG, "WPS: Using NFC password token DH keys");
                if (wps->wps->ap_nfc_dh_privkey == NULL) {
                        wpa_printf(MSG_DEBUG,
@@ -118,6 +125,8 @@ int wps_build_config_methods(struct wpabuf *msg, u16 methods)
 
 int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid)
 {
+       if (wpabuf_tailroom(msg) < 4 + WPS_UUID_LEN)
+               return -1;
        wpa_printf(MSG_DEBUG, "WPS:  * UUID-E");
        wpabuf_put_be16(msg, ATTR_UUID_E);
        wpabuf_put_be16(msg, WPS_UUID_LEN);
@@ -183,6 +192,8 @@ int wps_build_version(struct wpabuf *msg)
         * backwards compatibility reasons. The real version negotiation is
         * done with Version2.
         */
+       if (wpabuf_tailroom(msg) < 5)
+               return -1;
        wpa_printf(MSG_DEBUG, "WPS:  * Version (hardcoded 0x10)");
        wpabuf_put_be16(msg, ATTR_VERSION);
        wpabuf_put_be16(msg, 1);
@@ -194,9 +205,17 @@ int wps_build_version(struct wpabuf *msg)
 int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
                      const u8 *auth_macs, size_t auth_macs_count)
 {
-#ifdef CONFIG_WPS2
        u8 *len;
 
+#ifdef CONFIG_WPS_TESTING
+       if (WPS_VERSION == 0x10)
+               return 0;
+#endif /* CONFIG_WPS_TESTING */
+
+       if (wpabuf_tailroom(msg) <
+           7 + 3 + (req_to_enroll ? 3 : 0) +
+           (auth_macs ? 2 + auth_macs_count * ETH_ALEN : 0))
+               return -1;
        wpabuf_put_be16(msg, ATTR_VENDOR_EXT);
        len = wpabuf_put(msg, 2); /* to be filled */
        wpabuf_put_be24(msg, WPS_VENDOR_ID_WFA);
@@ -226,10 +245,11 @@ int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
        }
 
        WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2);
-#endif /* CONFIG_WPS2 */
 
 #ifdef CONFIG_WPS_TESTING
        if (WPS_VERSION > 0x20) {
+               if (wpabuf_tailroom(msg) < 5)
+                       return -1;
                wpa_printf(MSG_DEBUG, "WPS:  * Extensibility Testing - extra "
                           "attribute");
                wpabuf_put_be16(msg, ATTR_EXTENSIBILITY_TEST);
@@ -274,9 +294,10 @@ int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg)
 int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg)
 {
        u16 auth_types = WPS_AUTH_TYPES;
-#ifdef CONFIG_WPS2
+       /* WPA/WPA2-Enterprise enrollment not supported through WPS */
+       auth_types &= ~WPS_AUTH_WPA;
+       auth_types &= ~WPS_AUTH_WPA2;
        auth_types &= ~WPS_AUTH_SHARED;
-#endif /* CONFIG_WPS2 */
        wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type Flags");
        wpabuf_put_be16(msg, ATTR_AUTH_TYPE_FLAGS);
        wpabuf_put_be16(msg, 2);
@@ -288,9 +309,7 @@ int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg)
 int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg)
 {
        u16 encr_types = WPS_ENCR_TYPES;
-#ifdef CONFIG_WPS2
        encr_types &= ~WPS_ENCR_WEP;
-#endif /* CONFIG_WPS2 */
        wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type Flags");
        wpabuf_put_be16(msg, ATTR_ENCR_TYPE_FLAGS);
        wpabuf_put_be16(msg, 2);
@@ -372,15 +391,31 @@ int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id,
        const u8 *addr[1];
        u8 pubkey_hash[WPS_HASH_LEN];
 
+       wpa_printf(MSG_DEBUG, "WPS:  * OOB Device Password (dev_pw_id=%u)",
+                  dev_pw_id);
        addr[0] = wpabuf_head(pubkey);
        hash_len = wpabuf_len(pubkey);
        sha256_vector(1, addr, &hash_len, pubkey_hash);
+#ifdef CONFIG_WPS_TESTING
+       if (wps_corrupt_pkhash) {
+               wpa_hexdump(MSG_DEBUG, "WPS: Real Public Key Hash",
+                           pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+               wpa_printf(MSG_INFO, "WPS: Testing - corrupt public key hash");
+               pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN - 2]++;
+       }
+#endif /* CONFIG_WPS_TESTING */
 
        wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD);
        wpabuf_put_be16(msg, WPS_OOB_PUBKEY_HASH_LEN + 2 + dev_pw_len);
+       wpa_hexdump(MSG_DEBUG, "WPS: Public Key Hash",
+                   pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
        wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
        wpabuf_put_be16(msg, dev_pw_id);
-       wpabuf_put_data(msg, dev_pw, dev_pw_len);
+       if (dev_pw) {
+               wpa_hexdump_key(MSG_DEBUG, "WPS: OOB Device Password",
+                               dev_pw, dev_pw_len);
+               wpabuf_put_data(msg, dev_pw, dev_pw_len);
+       }
 
        return 0;
 }
@@ -428,3 +463,23 @@ int wps_build_mac_addr(struct wpabuf *msg, const u8 *addr)
        wpabuf_put_data(msg, addr, ETH_ALEN);
        return 0;
 }
+
+
+int wps_build_rf_bands_attr(struct wpabuf *msg, u8 rf_bands)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * RF Bands (%x)", rf_bands);
+       wpabuf_put_be16(msg, ATTR_RF_BANDS);
+       wpabuf_put_be16(msg, 1);
+       wpabuf_put_u8(msg, rf_bands);
+       return 0;
+}
+
+
+int wps_build_ap_channel(struct wpabuf *msg, u16 ap_channel)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * AP Channel (%u)", ap_channel);
+       wpabuf_put_be16(msg, ATTR_AP_CHANNEL);
+       wpabuf_put_be16(msg, 2);
+       wpabuf_put_be16(msg, ap_channel);
+       return 0;
+}
index 3999b1b..40bc1ad 100644 (file)
@@ -59,6 +59,14 @@ static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr,
                }
                attr->settings_delay_time = pos;
                break;
+       case WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS:
+               if (len != 2) {
+                       wpa_printf(MSG_DEBUG, "WPS: Invalid Registrar Configuration Methods length %u",
+                                  len);
+                       return -1;
+               }
+               attr->registrar_configuration_methods = pos;
+               break;
        default:
                wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor "
                           "Extension subelement %u", id);
@@ -75,7 +83,7 @@ static int wps_parse_vendor_ext_wfa(struct wps_parse_attr *attr, const u8 *pos,
        const u8 *end = pos + len;
        u8 id, elen;
 
-       while (pos + 2 < end) {
+       while (pos + 2 <= end) {
                id = *pos++;
                elen = *pos++;
                if (pos + elen > end)
@@ -263,10 +271,13 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
                attr->dev_password_id = pos;
                break;
        case ATTR_OOB_DEVICE_PASSWORD:
-               if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
-                   WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
+               if (len < WPS_OOB_PUBKEY_HASH_LEN + 2 ||
                    len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
-                   WPS_OOB_DEVICE_PASSWORD_LEN) {
+                   WPS_OOB_DEVICE_PASSWORD_LEN ||
+                   (len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
+                    WPS_OOB_DEVICE_PASSWORD_MIN_LEN &&
+                    WPA_GET_BE16(pos + WPS_OOB_PUBKEY_HASH_LEN) !=
+                    DEV_PW_NFC_CONNECTION_HANDOVER)) {
                        wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device "
                                   "Password length %u", len);
                        return -1;
@@ -410,22 +421,6 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
                }
                attr->mac_addr = pos;
                break;
-       case ATTR_KEY_PROVIDED_AUTO:
-               if (len != 1) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid Key Provided "
-                                  "Automatically length %u", len);
-                       return -1;
-               }
-               attr->key_prov_auto = pos;
-               break;
-       case ATTR_802_1X_ENABLED:
-               if (len != 1) {
-                       wpa_printf(MSG_DEBUG, "WPS: Invalid 802.1X Enabled "
-                                  "length %u", len);
-                       return -1;
-               }
-               attr->dot1x_enabled = pos;
-               break;
        case ATTR_SELECTED_REGISTRAR:
                if (len != 1) {
                        wpa_printf(MSG_DEBUG, "WPS: Invalid Selected Registrar"
@@ -497,14 +492,6 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type,
                attr->network_key = pos;
                attr->network_key_len = len;
                break;
-       case ATTR_EAP_TYPE:
-               attr->eap_type = pos;
-               attr->eap_type_len = len;
-               break;
-       case ATTR_EAP_IDENTITY:
-               attr->eap_identity = pos;
-               attr->eap_identity_len = len;
-               break;
        case ATTR_AP_SETUP_LOCKED:
                if (len != 1) {
                        wpa_printf(MSG_DEBUG, "WPS: Invalid AP Setup Locked "
index 88e51a4..82c4739 100644 (file)
@@ -47,8 +47,6 @@ struct wps_parse_attr {
        const u8 *network_idx; /* 1 octet */
        const u8 *network_key_idx; /* 1 octet */
        const u8 *mac_addr; /* ETH_ALEN (6) octets */
-       const u8 *key_prov_auto; /* 1 octet (Bool) */
-       const u8 *dot1x_enabled; /* 1 octet (Bool) */
        const u8 *selected_registrar; /* 1 octet (Bool) */
        const u8 *request_type; /* 1 octet */
        const u8 *response_type; /* 1 octet */
@@ -57,6 +55,7 @@ struct wps_parse_attr {
        const u8 *network_key_shareable; /* 1 octet (Bool) */
        const u8 *request_to_enroll; /* 1 octet (Bool) */
        const u8 *ap_channel; /* 2 octets */
+       const u8 *registrar_configuration_methods; /* 2 octets */
 
        /* variable length fields */
        const u8 *manufacturer;
@@ -77,10 +76,6 @@ struct wps_parse_attr {
        size_t ssid_len;
        const u8 *network_key; /* <= 64 octets */
        size_t network_key_len;
-       const u8 *eap_type; /* <= 8 octets */
-       size_t eap_type_len;
-       const u8 *eap_identity; /* <= 64 octets */
-       size_t eap_identity_len;
        const u8 *authorized_macs; /* <= 30 octets */
        size_t authorized_macs_len;
        const u8 *sec_dev_type_list; /* <= 128 octets */
index b81f106..eadb22f 100644 (file)
@@ -41,7 +41,7 @@ int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
        len[1] = wpabuf_len(msg) - 4 - WPS_AUTHENTICATOR_LEN;
        hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 2, addr, len, hash);
 
-       if (os_memcmp(hash, authenticator, WPS_AUTHENTICATOR_LEN) != 0) {
+       if (os_memcmp_const(hash, authenticator, WPS_AUTHENTICATOR_LEN) != 0) {
                wpa_printf(MSG_DEBUG, "WPS: Incorrect Authenticator");
                return -1;
        }
@@ -71,7 +71,7 @@ int wps_process_key_wrap_auth(struct wps_data *wps, struct wpabuf *msg,
        }
 
        hmac_sha256(wps->authkey, WPS_AUTHKEY_LEN, head, len, hash);
-       if (os_memcmp(hash, key_wrap_auth, WPS_KWA_LEN) != 0) {
+       if (os_memcmp_const(hash, key_wrap_auth, WPS_KWA_LEN) != 0) {
                wpa_printf(MSG_DEBUG, "WPS: Invalid KWA");
                return -1;
        }
@@ -207,70 +207,6 @@ static int wps_process_cred_mac_addr(struct wps_credential *cred,
 }
 
 
-static int wps_process_cred_eap_type(struct wps_credential *cred,
-                                    const u8 *eap_type, size_t eap_type_len)
-{
-       if (eap_type == NULL)
-               return 0; /* optional attribute */
-
-       wpa_hexdump(MSG_DEBUG, "WPS: EAP Type", eap_type, eap_type_len);
-
-       return 0;
-}
-
-
-static int wps_process_cred_eap_identity(struct wps_credential *cred,
-                                        const u8 *identity,
-                                        size_t identity_len)
-{
-       if (identity == NULL)
-               return 0; /* optional attribute */
-
-       wpa_hexdump_ascii(MSG_DEBUG, "WPS: EAP Identity",
-                         identity, identity_len);
-
-       return 0;
-}
-
-
-static int wps_process_cred_key_prov_auto(struct wps_credential *cred,
-                                         const u8 *key_prov_auto)
-{
-       if (key_prov_auto == NULL)
-               return 0; /* optional attribute */
-
-       wpa_printf(MSG_DEBUG, "WPS: Key Provided Automatically: %d",
-                  *key_prov_auto);
-
-       return 0;
-}
-
-
-static int wps_process_cred_802_1x_enabled(struct wps_credential *cred,
-                                          const u8 *dot1x_enabled)
-{
-       if (dot1x_enabled == NULL)
-               return 0; /* optional attribute */
-
-       wpa_printf(MSG_DEBUG, "WPS: 802.1X Enabled: %d", *dot1x_enabled);
-
-       return 0;
-}
-
-
-static int wps_process_cred_ap_channel(struct wps_credential *cred,
-                                      const u8 *ap_channel)
-{
-       if (ap_channel == NULL)
-               return 0; /* optional attribute */
-
-       cred->ap_channel = WPA_GET_BE16(ap_channel);
-       wpa_printf(MSG_DEBUG, "WPS: AP Channel: %u", cred->ap_channel);
-
-       return 0;
-}
-
-
 static int wps_workaround_cred_key(struct wps_credential *cred)
 {
        if (cred->auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK) &&
@@ -310,14 +246,7 @@ int wps_process_cred(struct wps_parse_attr *attr,
            wps_process_cred_network_key_idx(cred, attr->network_key_idx) ||
            wps_process_cred_network_key(cred, attr->network_key,
                                         attr->network_key_len) ||
-           wps_process_cred_mac_addr(cred, attr->mac_addr) ||
-           wps_process_cred_eap_type(cred, attr->eap_type,
-                                     attr->eap_type_len) ||
-           wps_process_cred_eap_identity(cred, attr->eap_identity,
-                                         attr->eap_identity_len) ||
-           wps_process_cred_key_prov_auto(cred, attr->key_prov_auto) ||
-           wps_process_cred_802_1x_enabled(cred, attr->dot1x_enabled) ||
-           wps_process_cred_ap_channel(cred, attr->ap_channel))
+           wps_process_cred_mac_addr(cred, attr->mac_addr))
                return -1;
 
        return wps_workaround_cred_key(cred);
index 4e4da5e..c1ede6a 100644 (file)
@@ -9,6 +9,8 @@
 #include "includes.h"
 
 #include "common.h"
+#include "common/defs.h"
+#include "common/ieee802_11_common.h"
 #include "crypto/aes_wrap.h"
 #include "crypto/crypto.h"
 #include "crypto/dh_group5.h"
@@ -16,6 +18,7 @@
 #include "crypto/sha256.h"
 #include "crypto/random.h"
 #include "wps_i.h"
+#include "wps_dev_attr.h"
 
 
 void wps_kdf(const u8 *key, const u8 *label_prefix, size_t label_prefix_len,
@@ -266,7 +269,7 @@ int wps_pin_str_valid(const char *pin)
 
 
 void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
-                   u16 config_error, u16 error_indication)
+                   u16 config_error, u16 error_indication, const u8 *mac_addr)
 {
        union wps_event_data data;
 
@@ -277,20 +280,26 @@ void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
        data.fail.msg = msg;
        data.fail.config_error = config_error;
        data.fail.error_indication = error_indication;
+       os_memcpy(data.fail.peer_macaddr, mac_addr, ETH_ALEN);
        wps->event_cb(wps->cb_ctx, WPS_EV_FAIL, &data);
 }
 
 
-void wps_success_event(struct wps_context *wps)
+void wps_success_event(struct wps_context *wps, const u8 *mac_addr)
 {
+       union wps_event_data data;
+
        if (wps->event_cb == NULL)
                return;
 
-       wps->event_cb(wps->cb_ctx, WPS_EV_SUCCESS, NULL);
+       os_memset(&data, 0, sizeof(data));
+       os_memcpy(data.success.peer_macaddr, mac_addr, ETH_ALEN);
+       wps->event_cb(wps->cb_ctx, WPS_EV_SUCCESS, &data);
 }
 
 
-void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part)
+void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part,
+                            const u8 *mac_addr)
 {
        union wps_event_data data;
 
@@ -300,6 +309,7 @@ void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part)
        os_memset(&data, 0, sizeof(data));
        data.pwd_auth_fail.enrollee = enrollee;
        data.pwd_auth_fail.part = part;
+       os_memcpy(data.pwd_auth_fail.peer_macaddr, mac_addr, ETH_ALEN);
        wps->event_cb(wps->cb_ctx, WPS_EV_PWD_AUTH_FAIL, &data);
 }
 
@@ -322,9 +332,28 @@ void wps_pbc_timeout_event(struct wps_context *wps)
 }
 
 
+void wps_pbc_active_event(struct wps_context *wps)
+{
+       if (wps->event_cb == NULL)
+               return;
+
+       wps->event_cb(wps->cb_ctx, WPS_EV_PBC_ACTIVE, NULL);
+}
+
+
+void wps_pbc_disable_event(struct wps_context *wps)
+{
+       if (wps->event_cb == NULL)
+               return;
+
+       wps->event_cb(wps->cb_ctx, WPS_EV_PBC_DISABLE, NULL);
+}
+
+
 #ifdef CONFIG_WPS_OOB
 
-struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
+struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band,
+                                int channel)
 {
        struct wps_data data;
        struct wpabuf *plain;
@@ -340,8 +369,10 @@ struct wpabuf * wps_get_oob_cred(struct wps_context *wps)
        data.wps = wps;
        data.auth_type = wps->auth_types;
        data.encr_type = wps->encr_types;
-       if (wps_build_version(plain) ||
-           wps_build_cred(&data, plain) ||
+       if (wps_build_cred(&data, plain) ||
+           (rf_band && wps_build_rf_bands_attr(plain, rf_band)) ||
+           (channel && wps_build_ap_channel(plain, channel)) ||
+           wps_build_mac_addr(plain, wps->dev.mac_addr) ||
            wps_build_wfa_ext(plain, 0, NULL, 0)) {
                os_free(data.new_psk);
                wpabuf_free(plain);
@@ -387,8 +418,7 @@ struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id,
        if (data == NULL)
                return NULL;
 
-       if (wps_build_version(data) ||
-           wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
+       if (wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
                                 wpabuf_head(dev_pw), wpabuf_len(dev_pw)) ||
            wps_build_wfa_ext(data, 0, NULL, 0)) {
                wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password "
@@ -459,7 +489,7 @@ char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf,
        ret = os_snprintf(buf, buf_len, "%u-%08X-%u",
                          WPA_GET_BE16(dev_type), WPA_GET_BE32(&dev_type[2]),
                          WPA_GET_BE16(&dev_type[6]));
-       if (ret < 0 || (unsigned int) ret >= buf_len)
+       if (os_snprintf_error(buf_len, ret))
                return NULL;
 
        return buf;
@@ -501,12 +531,13 @@ u16 wps_config_methods_str2bin(const char *str)
        if (str == NULL) {
                /* Default to enabling methods based on build configuration */
                methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD;
-#ifdef CONFIG_WPS2
                methods |= WPS_CONFIG_VIRT_DISPLAY;
-#endif /* CONFIG_WPS2 */
 #ifdef CONFIG_WPS_NFC
                methods |= WPS_CONFIG_NFC_INTERFACE;
 #endif /* CONFIG_WPS_NFC */
+#ifdef CONFIG_P2P
+               methods |= WPS_CONFIG_P2PS;
+#endif /* CONFIG_P2P */
        } else {
                if (os_strstr(str, "ethernet"))
                        methods |= WPS_CONFIG_ETHERNET;
@@ -524,7 +555,6 @@ u16 wps_config_methods_str2bin(const char *str)
                        methods |= WPS_CONFIG_PUSHBUTTON;
                if (os_strstr(str, "keypad"))
                        methods |= WPS_CONFIG_KEYPAD;
-#ifdef CONFIG_WPS2
                if (os_strstr(str, "virtual_display"))
                        methods |= WPS_CONFIG_VIRT_DISPLAY;
                if (os_strstr(str, "physical_display"))
@@ -533,7 +563,8 @@ u16 wps_config_methods_str2bin(const char *str)
                        methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
                if (os_strstr(str, "physical_push_button"))
                        methods |= WPS_CONFIG_PHY_PUSHBUTTON;
-#endif /* CONFIG_WPS2 */
+               if (os_strstr(str, "p2ps"))
+                       methods |= WPS_CONFIG_P2PS;
        }
 
        return methods;
@@ -611,12 +642,36 @@ struct wpabuf * wps_nfc_token_build(int ndef, int id, struct wpabuf *pubkey,
 }
 
 
+int wps_nfc_gen_dh(struct wpabuf **pubkey, struct wpabuf **privkey)
+{
+       struct wpabuf *priv = NULL, *pub = NULL;
+       void *dh_ctx;
+
+       dh_ctx = dh5_init(&priv, &pub);
+       if (dh_ctx == NULL)
+               return -1;
+       pub = wpabuf_zeropad(pub, 192);
+       if (pub == NULL) {
+               wpabuf_free(priv);
+               return -1;
+       }
+       wpa_hexdump_buf(MSG_DEBUG, "WPS: Generated new DH pubkey", pub);
+       dh5_free(dh_ctx);
+
+       wpabuf_free(*pubkey);
+       *pubkey = pub;
+       wpabuf_free(*privkey);
+       *privkey = priv;
+
+       return 0;
+}
+
+
 struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
                                  struct wpabuf **privkey,
                                  struct wpabuf **dev_pw)
 {
-       struct wpabuf *priv = NULL, *pub = NULL, *pw;
-       void *dh_ctx;
+       struct wpabuf *pw;
        u16 val;
 
        pw = wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN);
@@ -630,22 +685,223 @@ struct wpabuf * wps_nfc_token_gen(int ndef, int *id, struct wpabuf **pubkey,
                return NULL;
        }
 
-       dh_ctx = dh5_init(&priv, &pub);
-       if (dh_ctx == NULL) {
+       if (wps_nfc_gen_dh(pubkey, privkey) < 0) {
                wpabuf_free(pw);
                return NULL;
        }
-       dh5_free(dh_ctx);
 
        *id = 0x10 + val % 0xfff0;
-       wpabuf_free(*pubkey);
-       *pubkey = pub;
-       wpabuf_free(*privkey);
-       *privkey = priv;
        wpabuf_free(*dev_pw);
        *dev_pw = pw;
 
        return wps_nfc_token_build(ndef, *id, *pubkey, *dev_pw);
 }
 
+
+struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx,
+                                          struct wpabuf *nfc_dh_pubkey)
+{
+       struct wpabuf *msg;
+       void *len;
+
+       if (ctx == NULL)
+               return NULL;
+
+       wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+                  "handover request");
+
+       if (nfc_dh_pubkey == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password "
+                          "configured");
+               return NULL;
+       }
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL)
+               return msg;
+       len = wpabuf_put(msg, 2);
+
+       if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
+                                nfc_dh_pubkey, NULL, 0) ||
+           wps_build_uuid_e(msg, ctx->uuid) ||
+           wps_build_wfa_ext(msg, 0, NULL, 0)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       WPA_PUT_BE16(len, wpabuf_len(msg) - 2);
+
+       return msg;
+}
+
+
+static int wps_build_ssid(struct wpabuf *msg, struct wps_context *wps)
+{
+       wpa_printf(MSG_DEBUG, "WPS:  * SSID");
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID in Connection Handover Select",
+                         wps->ssid, wps->ssid_len);
+       wpabuf_put_be16(msg, ATTR_SSID);
+       wpabuf_put_be16(msg, wps->ssid_len);
+       wpabuf_put_data(msg, wps->ssid, wps->ssid_len);
+       return 0;
+}
+
+
+static int wps_build_ap_freq(struct wpabuf *msg, int freq)
+{
+       enum hostapd_hw_mode mode;
+       u8 channel, rf_band;
+       u16 ap_channel;
+
+       if (freq <= 0)
+               return 0;
+
+       mode = ieee80211_freq_to_chan(freq, &channel);
+       if (mode == NUM_HOSTAPD_MODES)
+               return 0; /* Unknown channel */
+
+       if (mode == HOSTAPD_MODE_IEEE80211G || mode == HOSTAPD_MODE_IEEE80211B)
+               rf_band = WPS_RF_24GHZ;
+       else if (mode == HOSTAPD_MODE_IEEE80211A)
+               rf_band = WPS_RF_50GHZ;
+       else
+               return 0; /* Unknown band */
+       ap_channel = channel;
+
+       if (wps_build_rf_bands_attr(msg, rf_band) ||
+           wps_build_ap_channel(msg, ap_channel))
+               return -1;
+
+       return 0;
+}
+
+
+struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx,
+                                          struct wpabuf *nfc_dh_pubkey,
+                                          const u8 *bssid, int freq)
+{
+       struct wpabuf *msg;
+       void *len;
+
+       if (ctx == NULL)
+               return NULL;
+
+       wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+                  "handover select");
+
+       if (nfc_dh_pubkey == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password "
+                          "configured");
+               return NULL;
+       }
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL)
+               return msg;
+       len = wpabuf_put(msg, 2);
+
+       if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
+                                nfc_dh_pubkey, NULL, 0) ||
+           wps_build_ssid(msg, ctx) ||
+           wps_build_ap_freq(msg, freq) ||
+           (bssid && wps_build_mac_addr(msg, bssid)) ||
+           wps_build_wfa_ext(msg, 0, NULL, 0)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       WPA_PUT_BE16(len, wpabuf_len(msg) - 2);
+
+       return msg;
+}
+
+
+struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx,
+                                              struct wpabuf *nfc_dh_pubkey)
+{
+       struct wpabuf *msg;
+
+       if (ctx == NULL)
+               return NULL;
+
+       wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+                  "handover request (P2P)");
+
+       if (nfc_dh_pubkey == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No NFC DH Public Key configured");
+               return NULL;
+       }
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL)
+               return msg;
+
+       if (wps_build_manufacturer(&ctx->dev, msg) ||
+           wps_build_model_name(&ctx->dev, msg) ||
+           wps_build_model_number(&ctx->dev, msg) ||
+           wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
+                                nfc_dh_pubkey, NULL, 0) ||
+           wps_build_rf_bands(&ctx->dev, msg, 0) ||
+           wps_build_serial_number(&ctx->dev, msg) ||
+           wps_build_uuid_e(msg, ctx->uuid) ||
+           wps_build_wfa_ext(msg, 0, NULL, 0)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       return msg;
+}
+
+
+struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx,
+                                              int nfc_dev_pw_id,
+                                              struct wpabuf *nfc_dh_pubkey,
+                                              struct wpabuf *nfc_dev_pw)
+{
+       struct wpabuf *msg;
+       const u8 *dev_pw;
+       size_t dev_pw_len;
+
+       if (ctx == NULL)
+               return NULL;
+
+       wpa_printf(MSG_DEBUG, "WPS: Building attributes for NFC connection "
+                  "handover select (P2P)");
+
+       if (nfc_dh_pubkey == NULL ||
+           (nfc_dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER &&
+            nfc_dev_pw == NULL)) {
+               wpa_printf(MSG_DEBUG, "WPS: No NFC OOB Device Password "
+                          "configured");
+               return NULL;
+       }
+
+       msg = wpabuf_alloc(1000);
+       if (msg == NULL)
+               return msg;
+
+       if (nfc_dev_pw) {
+               dev_pw = wpabuf_head(nfc_dev_pw);
+               dev_pw_len = wpabuf_len(nfc_dev_pw);
+       } else {
+               dev_pw = NULL;
+               dev_pw_len = 0;
+       }
+
+       if (wps_build_manufacturer(&ctx->dev, msg) ||
+           wps_build_model_name(&ctx->dev, msg) ||
+           wps_build_model_number(&ctx->dev, msg) ||
+           wps_build_oob_dev_pw(msg, nfc_dev_pw_id, nfc_dh_pubkey,
+                                dev_pw, dev_pw_len) ||
+           wps_build_rf_bands(&ctx->dev, msg, 0) ||
+           wps_build_serial_number(&ctx->dev, msg) ||
+           wps_build_uuid_e(msg, ctx->uuid) ||
+           wps_build_wfa_ext(msg, 0, NULL, 0)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+       return msg;
+}
+
 #endif /* CONFIG_WPS_NFC */
index 2f42603..25cd14a 100644 (file)
 
 extern int wps_version_number;
 extern int wps_testing_dummy_cred;
+extern int wps_corrupt_pkhash;
 #define WPS_VERSION wps_version_number
 
 #else /* CONFIG_WPS_TESTING */
 
-#ifdef CONFIG_WPS2
 #define WPS_VERSION 0x20
-#else /* CONFIG_WPS2 */
-#define WPS_VERSION 0x10
-#endif /* CONFIG_WPS2 */
 
 #endif /* CONFIG_WPS_TESTING */
 
@@ -145,7 +142,8 @@ enum {
        WFA_ELEM_AUTHORIZEDMACS = 0x01,
        WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02,
        WFA_ELEM_REQUEST_TO_ENROLL = 0x03,
-       WFA_ELEM_SETTINGS_DELAY_TIME = 0x04
+       WFA_ELEM_SETTINGS_DELAY_TIME = 0x04,
+       WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS = 0x05
 };
 
 /* Device Password ID */
@@ -155,7 +153,9 @@ enum wps_dev_password_id {
        DEV_PW_MACHINE_SPECIFIED = 0x0002,
        DEV_PW_REKEY = 0x0003,
        DEV_PW_PUSHBUTTON = 0x0004,
-       DEV_PW_REGISTRAR_SPECIFIED = 0x0005
+       DEV_PW_REGISTRAR_SPECIFIED = 0x0005,
+       DEV_PW_NFC_CONNECTION_HANDOVER = 0x0007,
+       DEV_PW_P2PS_DEFAULT = 0x0008
 };
 
 /* Message Type */
@@ -180,7 +180,7 @@ enum wps_msg_type {
 /* Authentication Type Flags */
 #define WPS_AUTH_OPEN 0x0001
 #define WPS_AUTH_WPAPSK 0x0002
-#define WPS_AUTH_SHARED 0x0004
+#define WPS_AUTH_SHARED 0x0004 /* deprecated */
 #define WPS_AUTH_WPA 0x0008
 #define WPS_AUTH_WPA2 0x0010
 #define WPS_AUTH_WPA2PSK 0x0020
@@ -189,7 +189,7 @@ enum wps_msg_type {
 
 /* Encryption Type Flags */
 #define WPS_ENCR_NONE 0x0001
-#define WPS_ENCR_WEP 0x0002
+#define WPS_ENCR_WEP 0x0002 /* deprecated */
 #define WPS_ENCR_TKIP 0x0004
 #define WPS_ENCR_AES 0x0008
 #define WPS_ENCR_TYPES (WPS_ENCR_NONE | WPS_ENCR_WEP | WPS_ENCR_TKIP | \
@@ -215,7 +215,9 @@ enum wps_config_error {
        WPS_CFG_SETUP_LOCKED = 15,
        WPS_CFG_MSG_TIMEOUT = 16,
        WPS_CFG_REG_SESS_TIMEOUT = 17,
-       WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18
+       WPS_CFG_DEV_PASSWORD_AUTH_FAILURE = 18,
+       WPS_CFG_60G_CHAN_NOT_SUPPORTED = 19,
+       WPS_CFG_PUBLIC_KEY_HASH_MISMATCH = 20
 };
 
 /* Vendor specific Error Indication for WPS event messages */
@@ -223,6 +225,7 @@ enum wps_error_indication {
        WPS_EI_NO_ERROR,
        WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED,
        WPS_EI_SECURITY_WEP_PROHIBITED,
+       WPS_EI_AUTH_FAILURE,
        NUM_WPS_EI_VALUES
 };
 
@@ -240,12 +243,11 @@ enum wps_error_indication {
 #define WPS_CONFIG_NFC_INTERFACE 0x0040
 #define WPS_CONFIG_PUSHBUTTON 0x0080
 #define WPS_CONFIG_KEYPAD 0x0100
-#ifdef CONFIG_WPS2
 #define WPS_CONFIG_VIRT_PUSHBUTTON 0x0280
 #define WPS_CONFIG_PHY_PUSHBUTTON 0x0480
+#define WPS_CONFIG_P2PS 0x1000
 #define WPS_CONFIG_VIRT_DISPLAY 0x2008
 #define WPS_CONFIG_PHY_DISPLAY 0x4008
-#endif /* CONFIG_WPS2 */
 
 /* Connection Type Flags */
 #define WPS_CONN_ESS 0x01
@@ -279,30 +281,71 @@ enum wps_dev_categ {
        WPS_DEV_DISPLAY = 7,
        WPS_DEV_MULTIMEDIA = 8,
        WPS_DEV_GAMING = 9,
-       WPS_DEV_PHONE = 10
+       WPS_DEV_PHONE = 10,
+       WPS_DEV_AUDIO = 11,
 };
 
 enum wps_dev_subcateg {
        WPS_DEV_COMPUTER_PC = 1,
        WPS_DEV_COMPUTER_SERVER = 2,
        WPS_DEV_COMPUTER_MEDIA_CENTER = 3,
+       WPS_DEV_COMPUTER_ULTRA_MOBILE = 4,
+       WPS_DEV_COMPUTER_NOTEBOOK = 5,
+       WPS_DEV_COMPUTER_DESKTOP = 6,
+       WPS_DEV_COMPUTER_MID = 7,
+       WPS_DEV_COMPUTER_NETBOOK = 8,
+       WPS_DEV_COMPUTER_TABLET = 9,
+       WPS_DEV_INPUT_KEYBOARD = 1,
+       WPS_DEV_INPUT_MOUSE = 2,
+       WPS_DEV_INPUT_JOYSTICK = 3,
+       WPS_DEV_INPUT_TRACKBALL = 4,
+       WPS_DEV_INPUT_GAMING = 5,
+       WPS_DEV_INPUT_REMOTE = 6,
+       WPS_DEV_INPUT_TOUCHSCREEN = 7,
+       WPS_DEV_INPUT_BIOMETRIC_READER = 8,
+       WPS_DEV_INPUT_BARCODE_READER = 9,
        WPS_DEV_PRINTER_PRINTER = 1,
        WPS_DEV_PRINTER_SCANNER = 2,
+       WPS_DEV_PRINTER_FAX = 3,
+       WPS_DEV_PRINTER_COPIER = 4,
+       WPS_DEV_PRINTER_ALL_IN_ONE = 5,
        WPS_DEV_CAMERA_DIGITAL_STILL_CAMERA = 1,
+       WPS_DEV_CAMERA_VIDEO = 2,
+       WPS_DEV_CAMERA_WEB = 3,
+       WPS_DEV_CAMERA_SECURITY = 4,
        WPS_DEV_STORAGE_NAS = 1,
        WPS_DEV_NETWORK_INFRA_AP = 1,
        WPS_DEV_NETWORK_INFRA_ROUTER = 2,
        WPS_DEV_NETWORK_INFRA_SWITCH = 3,
+       WPS_DEV_NETWORK_INFRA_GATEWAY = 4,
+       WPS_DEV_NETWORK_INFRA_BRIDGE = 5,
        WPS_DEV_DISPLAY_TV = 1,
        WPS_DEV_DISPLAY_PICTURE_FRAME = 2,
        WPS_DEV_DISPLAY_PROJECTOR = 3,
+       WPS_DEV_DISPLAY_MONITOR = 4,
        WPS_DEV_MULTIMEDIA_DAR = 1,
        WPS_DEV_MULTIMEDIA_PVR = 2,
        WPS_DEV_MULTIMEDIA_MCX = 3,
+       WPS_DEV_MULTIMEDIA_SET_TOP_BOX = 4,
+       WPS_DEV_MULTIMEDIA_MEDIA_SERVER = 5,
+       WPS_DEV_MULTIMEDIA_PORTABLE_VIDEO_PLAYER = 6,
        WPS_DEV_GAMING_XBOX = 1,
        WPS_DEV_GAMING_XBOX360 = 2,
        WPS_DEV_GAMING_PLAYSTATION = 3,
-       WPS_DEV_PHONE_WINDOWS_MOBILE = 1
+       WPS_DEV_GAMING_GAME_CONSOLE = 4,
+       WPS_DEV_GAMING_PORTABLE_DEVICE = 5,
+       WPS_DEV_PHONE_WINDOWS_MOBILE = 1,
+       WPS_DEV_PHONE_SINGLE_MODE = 2,
+       WPS_DEV_PHONE_DUAL_MODE = 3,
+       WPS_DEV_PHONE_SP_SINGLE_MODE = 4,
+       WPS_DEV_PHONE_SP_DUAL_MODE = 5,
+       WPS_DEV_AUDIO_TUNER_RECV = 1,
+       WPS_DEV_AUDIO_SPEAKERS = 2,
+       WPS_DEV_AUDIO_PMP = 3,
+       WPS_DEV_AUDIO_HEADSET = 4,
+       WPS_DEV_AUDIO_HEADPHONES = 5,
+       WPS_DEV_AUDIO_MICROPHONE = 6,
+       WPS_DEV_AUDIO_HOME_THEATRE = 7,
 };
 
 
index 7a7c099..0d01211 100644 (file)
@@ -85,8 +85,7 @@ int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg)
 }
 
 
-static int wps_build_serial_number(struct wps_device_data *dev,
-                                  struct wpabuf *msg)
+int wps_build_serial_number(struct wps_device_data *dev, struct wpabuf *msg)
 {
        size_t len;
        wpa_printf(MSG_DEBUG, "WPS:  * Serial Number");
@@ -217,13 +216,10 @@ int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg)
 }
 
 
-int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg)
+int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg,
+                      u8 rf_band)
 {
-       wpa_printf(MSG_DEBUG, "WPS:  * RF Bands (%x)", dev->rf_bands);
-       wpabuf_put_be16(msg, ATTR_RF_BANDS);
-       wpabuf_put_be16(msg, 1);
-       wpabuf_put_u8(msg, dev->rf_bands);
-       return 0;
+       return wps_build_rf_bands_attr(msg, rf_band ? rf_band : dev->rf_bands);
 }
 
 
@@ -408,25 +404,6 @@ int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands)
 }
 
 
-void wps_device_data_dup(struct wps_device_data *dst,
-                        const struct wps_device_data *src)
-{
-       if (src->device_name)
-               dst->device_name = os_strdup(src->device_name);
-       if (src->manufacturer)
-               dst->manufacturer = os_strdup(src->manufacturer);
-       if (src->model_name)
-               dst->model_name = os_strdup(src->model_name);
-       if (src->model_number)
-               dst->model_number = os_strdup(src->model_number);
-       if (src->serial_number)
-               dst->serial_number = os_strdup(src->serial_number);
-       os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN);
-       dst->os_version = src->os_version;
-       dst->rf_bands = src->rf_bands;
-}
-
-
 void wps_device_data_free(struct wps_device_data *dev)
 {
        os_free(dev->device_name);
index 200c9c4..c9034ad 100644 (file)
@@ -14,11 +14,13 @@ struct wps_parse_attr;
 int wps_build_manufacturer(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_model_name(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_model_number(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_serial_number(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_device_attrs(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_os_version(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_vendor_ext_m1(struct wps_device_data *dev, struct wpabuf *msg);
-int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg);
+int wps_build_rf_bands(struct wps_device_data *dev, struct wpabuf *msg,
+                      u8 rf_band);
 int wps_build_primary_dev_type(struct wps_device_data *dev,
                               struct wpabuf *msg);
 int wps_build_secondary_dev_type(struct wps_device_data *dev,
@@ -28,8 +30,6 @@ int wps_process_device_attrs(struct wps_device_data *dev,
                             struct wps_parse_attr *attr);
 int wps_process_os_version(struct wps_device_data *dev, const u8 *ver);
 int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands);
-void wps_device_data_dup(struct wps_device_data *dst,
-                        const struct wps_device_data *src);
 void wps_device_data_free(struct wps_device_data *dev);
 int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg);
 int wps_build_req_dev_type(struct wps_device_data *dev, struct wpabuf *msg,
index 27c554f..89957b1 100644 (file)
@@ -130,10 +130,8 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps)
                 * workaround.
                 */
                config_methods &= ~WPS_CONFIG_PUSHBUTTON;
-#ifdef CONFIG_WPS2
                config_methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
                                    WPS_CONFIG_PHY_PUSHBUTTON);
-#endif /* CONFIG_WPS2 */
        }
 
        if (wps_build_version(msg) ||
@@ -148,7 +146,8 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps)
            wps_build_config_methods(msg, config_methods) ||
            wps_build_wps_state(wps, msg) ||
            wps_build_device_attrs(&wps->wps->dev, msg) ||
-           wps_build_rf_bands(&wps->wps->dev, msg) ||
+           wps_build_rf_bands(&wps->wps->dev, msg,
+                              wps->wps->rf_band_cb(wps->wps->cb_ctx)) ||
            wps_build_assoc_state(wps, msg) ||
            wps_build_dev_password_id(msg, wps->dev_pw_id) ||
            wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
@@ -176,6 +175,12 @@ static struct wpabuf * wps_build_m3(struct wps_data *wps)
        }
        wps_derive_psk(wps, wps->dev_password, wps->dev_password_len);
 
+       if (wps->wps->ap && random_pool_ready() != 1) {
+               wpa_printf(MSG_INFO,
+                          "WPS: Not enough entropy in random pool to proceed - do not allow AP PIN to be used");
+               return NULL;
+       }
+
        msg = wpabuf_alloc(1000);
        if (msg == NULL)
                return NULL;
@@ -242,17 +247,19 @@ static int wps_build_cred_ssid(struct wps_data *wps, struct wpabuf *msg)
 
 static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
 {
-       u16 auth_type = wps->wps->auth_types;
+       u16 auth_type = wps->wps->ap_auth_type;
 
-       /* Select the best authentication type */
+       /*
+        * Work around issues with Windows 7 WPS implementation not liking
+        * multiple Authentication Type bits in M7 AP Settings attribute by
+        * showing only the most secure option from current configuration.
+        */
        if (auth_type & WPS_AUTH_WPA2PSK)
                auth_type = WPS_AUTH_WPA2PSK;
        else if (auth_type & WPS_AUTH_WPAPSK)
                auth_type = WPS_AUTH_WPAPSK;
        else if (auth_type & WPS_AUTH_OPEN)
                auth_type = WPS_AUTH_OPEN;
-       else if (auth_type & WPS_AUTH_SHARED)
-               auth_type = WPS_AUTH_SHARED;
 
        wpa_printf(MSG_DEBUG, "WPS:  * Authentication Type (0x%x)", auth_type);
        wpabuf_put_be16(msg, ATTR_AUTH_TYPE);
@@ -264,19 +271,18 @@ static int wps_build_cred_auth_type(struct wps_data *wps, struct wpabuf *msg)
 
 static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
 {
-       u16 encr_type = wps->wps->encr_types;
+       u16 encr_type = wps->wps->ap_encr_type;
 
-       /* Select the best encryption type */
-       if (wps->wps->auth_types & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
+       /*
+        * Work around issues with Windows 7 WPS implementation not liking
+        * multiple Encryption Type bits in M7 AP Settings attribute by
+        * showing only the most secure option from current configuration.
+        */
+       if (wps->wps->ap_auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
                if (encr_type & WPS_ENCR_AES)
                        encr_type = WPS_ENCR_AES;
                else if (encr_type & WPS_ENCR_TKIP)
                        encr_type = WPS_ENCR_TKIP;
-       } else {
-               if (encr_type & WPS_ENCR_WEP)
-                       encr_type = WPS_ENCR_WEP;
-               else if (encr_type & WPS_ENCR_NONE)
-                       encr_type = WPS_ENCR_NONE;
        }
 
        wpa_printf(MSG_DEBUG, "WPS:  * Encryption Type (0x%x)", encr_type);
@@ -289,7 +295,35 @@ static int wps_build_cred_encr_type(struct wps_data *wps, struct wpabuf *msg)
 
 static int wps_build_cred_network_key(struct wps_data *wps, struct wpabuf *msg)
 {
-       wpa_printf(MSG_DEBUG, "WPS:  * Network Key");
+       if ((wps->wps->ap_auth_type & (WPS_AUTH_WPAPSK | WPS_AUTH_WPA2PSK)) &&
+           wps->wps->network_key_len == 0) {
+               char hex[65];
+               u8 psk[32];
+               /* Generate a random per-device PSK */
+               if (random_pool_ready() != 1 ||
+                   random_get_bytes(psk, sizeof(psk)) < 0) {
+                       wpa_printf(MSG_INFO,
+                                  "WPS: Could not generate random PSK");
+                       return -1;
+               }
+               wpa_hexdump_key(MSG_DEBUG, "WPS: Generated per-device PSK",
+                               psk, sizeof(psk));
+               wpa_printf(MSG_DEBUG, "WPS:  * Network Key (len=%u)",
+                          (unsigned int) wps->new_psk_len * 2);
+               wpa_snprintf_hex(hex, sizeof(hex), psk, sizeof(psk));
+               wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
+               wpabuf_put_be16(msg, sizeof(psk) * 2);
+               wpabuf_put_data(msg, hex, sizeof(psk) * 2);
+               if (wps->wps->registrar) {
+                       wps_cb_new_psk(wps->wps->registrar,
+                                      wps->peer_dev.mac_addr,
+                                      wps->p2p_dev_addr, psk, sizeof(psk));
+               }
+               return 0;
+       }
+
+       wpa_printf(MSG_DEBUG, "WPS:  * Network Key (len=%u)",
+                  (unsigned int) wps->wps->network_key_len);
        wpabuf_put_be16(msg, ATTR_NETWORK_KEY);
        wpabuf_put_be16(msg, wps->wps->network_key_len);
        wpabuf_put_data(msg, wps->wps->network_key, wps->wps->network_key_len);
@@ -309,6 +343,9 @@ static int wps_build_cred_mac_addr(struct wps_data *wps, struct wpabuf *msg)
 
 static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *plain)
 {
+       const u8 *start, *end;
+       int ret;
+
        if (wps->wps->ap_settings) {
                wpa_printf(MSG_DEBUG, "WPS:  * AP Settings (pre-configured)");
                wpabuf_put_data(plain, wps->wps->ap_settings,
@@ -316,11 +353,19 @@ static int wps_build_ap_settings(struct wps_data *wps, struct wpabuf *plain)
                return 0;
        }
 
-       return wps_build_cred_ssid(wps, plain) ||
+       wpa_printf(MSG_DEBUG, "WPS:  * AP Settings based on current configuration");
+       start = wpabuf_put(plain, 0);
+       ret = wps_build_cred_ssid(wps, plain) ||
                wps_build_cred_mac_addr(wps, plain) ||
                wps_build_cred_auth_type(wps, plain) ||
                wps_build_cred_encr_type(wps, plain) ||
                wps_build_cred_network_key(wps, plain);
+       end = wpabuf_put(plain, 0);
+
+       wpa_hexdump_key(MSG_DEBUG, "WPS: Plaintext AP Settings",
+                       start, end - start);
+
+       return ret;
 }
 
 
@@ -392,7 +437,7 @@ static struct wpabuf * wps_build_wsc_done(struct wps_data *wps)
        if (wps->wps->ap)
                wps->state = RECV_ACK;
        else {
-               wps_success_event(wps->wps);
+               wps_success_event(wps->wps, wps->peer_dev.mac_addr);
                wps->state = WPS_FINISHED;
        }
        return msg;
@@ -513,6 +558,24 @@ static int wps_process_pubkey(struct wps_data *wps, const u8 *pk,
                return -1;
        }
 
+       if (wps->peer_pubkey_hash_set) {
+               u8 hash[WPS_HASH_LEN];
+               sha256_vector(1, &pk, &pk_len, hash);
+               if (os_memcmp_const(hash, wps->peer_pubkey_hash,
+                                   WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+                       wpa_printf(MSG_ERROR, "WPS: Public Key hash mismatch");
+                       wpa_hexdump(MSG_DEBUG, "WPS: Received public key",
+                                   pk, pk_len);
+                       wpa_hexdump(MSG_DEBUG, "WPS: Calculated public key "
+                                   "hash", hash, WPS_OOB_PUBKEY_HASH_LEN);
+                       wpa_hexdump(MSG_DEBUG, "WPS: Expected public key hash",
+                                   wps->peer_pubkey_hash,
+                                   WPS_OOB_PUBKEY_HASH_LEN);
+                       wps->config_error = WPS_CFG_PUBLIC_KEY_HASH_MISMATCH;
+                       return -1;
+               }
+       }
+
        wpabuf_free(wps->dh_pubkey_r);
        wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len);
        if (wps->dh_pubkey_r == NULL)
@@ -578,11 +641,11 @@ static int wps_process_r_snonce1(struct wps_data *wps, const u8 *r_snonce1)
        len[3] = wpabuf_len(wps->dh_pubkey_r);
        hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
 
-       if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
+       if (os_memcmp_const(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
                wpa_printf(MSG_DEBUG, "WPS: R-Hash1 derived from R-S1 does "
                           "not match with the pre-committed value");
                wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
-               wps_pwd_auth_fail_event(wps->wps, 1, 1);
+               wps_pwd_auth_fail_event(wps->wps, 1, 1, wps->peer_dev.mac_addr);
                return -1;
        }
 
@@ -618,11 +681,11 @@ static int wps_process_r_snonce2(struct wps_data *wps, const u8 *r_snonce2)
        len[3] = wpabuf_len(wps->dh_pubkey_r);
        hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
 
-       if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
+       if (os_memcmp_const(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
                wpa_printf(MSG_DEBUG, "WPS: R-Hash2 derived from R-S2 does "
                           "not match with the pre-committed value");
                wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
-               wps_pwd_auth_fail_event(wps->wps, 1, 2);
+               wps_pwd_auth_fail_event(wps->wps, 1, 2, wps->peer_dev.mac_addr);
                return -1;
        }
 
@@ -669,7 +732,6 @@ static int wps_process_cred_e(struct wps_data *wps, const u8 *cred,
 #endif /* CONFIG_WPS_STRICT */
        }
 
-#ifdef CONFIG_WPS2
        if (!(wps->cred.encr_type &
              (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES))) {
                if (wps->cred.encr_type & WPS_ENCR_WEP) {
@@ -683,7 +745,6 @@ static int wps_process_cred_e(struct wps_data *wps, const u8 *cred,
                           "invalid encr_type 0x%x", wps->cred.encr_type);
                return -1;
        }
-#endif /* CONFIG_WPS2 */
 
        if (wps->wps->cred_cb) {
                wps->cred.cred_attr = cred - 4;
@@ -770,7 +831,6 @@ static int wps_process_ap_settings_e(struct wps_data *wps,
 #endif /* CONFIG_WPS_STRICT */
        }
 
-#ifdef CONFIG_WPS2
        if (!(cred.encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP | WPS_ENCR_AES)))
        {
                if (cred.encr_type & WPS_ENCR_WEP) {
@@ -784,7 +844,6 @@ static int wps_process_ap_settings_e(struct wps_data *wps,
                           "invalid encr_type 0x%x", cred.encr_type);
                return -1;
        }
-#endif /* CONFIG_WPS2 */
 
 #ifdef CONFIG_WPS_STRICT
        if (wps2) {
@@ -801,7 +860,6 @@ static int wps_process_ap_settings_e(struct wps_data *wps,
        }
 #endif /* CONFIG_WPS_STRICT */
 
-#ifdef CONFIG_WPS2
        if ((cred.encr_type & (WPS_ENCR_TKIP | WPS_ENCR_AES)) == WPS_ENCR_TKIP)
        {
                wpa_printf(MSG_DEBUG, "WPS: Upgrade encr_type TKIP -> "
@@ -815,7 +873,6 @@ static int wps_process_ap_settings_e(struct wps_data *wps,
                           "WPAPSK+WPA2PSK");
                cred.auth_type |= WPS_AUTH_WPA2PSK;
        }
-#endif /* CONFIG_WPS2 */
 
        if (wps->wps->cred_cb) {
                cred.cred_attr = wpabuf_head(attrs);
@@ -863,9 +920,15 @@ static int wps_process_dev_pw_id(struct wps_data *wps, const u8 *dev_pw_id)
        wpa_printf(MSG_DEBUG, "WPS: Registrar trying to change Device Password "
                   "ID from %u to %u", wps->dev_pw_id, id);
 
+       if (wps->dev_pw_id == DEV_PW_PUSHBUTTON && id == DEV_PW_DEFAULT) {
+               wpa_printf(MSG_DEBUG,
+                          "WPS: Workaround - ignore PBC-to-PIN change");
+               return 0;
+       }
+
        if (wps->alt_dev_password && wps->alt_dev_pw_id == id) {
                wpa_printf(MSG_DEBUG, "WPS: Found a matching Device Password");
-               os_free(wps->dev_password);
+               bin_clear_free(wps->dev_password, wps->dev_password_len);
                wps->dev_pw_id = wps->alt_dev_pw_id;
                wps->dev_password = wps->alt_dev_password;
                wps->dev_password_len = wps->alt_dev_password_len;
@@ -922,6 +985,38 @@ static enum wps_process_res wps_process_m2(struct wps_data *wps,
                return WPS_CONTINUE;
        }
 
+#ifdef CONFIG_WPS_NFC
+       if (wps->peer_pubkey_hash_set) {
+               struct wpabuf *decrypted;
+               struct wps_parse_attr eattr;
+
+               decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings,
+                                                     attr->encr_settings_len);
+               if (decrypted == NULL) {
+                       wpa_printf(MSG_DEBUG, "WPS: Failed to decrypt "
+                                  "Encrypted Settings attribute");
+                       wps->state = SEND_WSC_NACK;
+                       return WPS_CONTINUE;
+               }
+
+               wpa_printf(MSG_DEBUG, "WPS: Processing decrypted Encrypted "
+                          "Settings attribute");
+               if (wps_parse_msg(decrypted, &eattr) < 0 ||
+                   wps_process_key_wrap_auth(wps, decrypted,
+                                             eattr.key_wrap_auth) ||
+                   wps_process_creds(wps, eattr.cred, eattr.cred_len,
+                                     eattr.num_cred, attr->version2 != NULL)) {
+                       wpabuf_free(decrypted);
+                       wps->state = SEND_WSC_NACK;
+                       return WPS_CONTINUE;
+               }
+               wpabuf_free(decrypted);
+
+               wps->state = WPS_MSG_DONE;
+               return WPS_CONTINUE;
+       }
+#endif /* CONFIG_WPS_NFC */
+
        wps->state = SEND_M3;
        return WPS_CONTINUE;
 }
@@ -1204,7 +1299,8 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
                ret = wps_process_m4(wps, msg, &attr);
                if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
                        wps_fail_event(wps->wps, WPS_M4, wps->config_error,
-                                      wps->error_indication);
+                                      wps->error_indication,
+                                      wps->peer_dev.mac_addr);
                break;
        case WPS_M6:
                if (wps_validate_m6(msg) < 0)
@@ -1212,7 +1308,8 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
                ret = wps_process_m6(wps, msg, &attr);
                if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
                        wps_fail_event(wps->wps, WPS_M6, wps->config_error,
-                                      wps->error_indication);
+                                      wps->error_indication,
+                                      wps->peer_dev.mac_addr);
                break;
        case WPS_M8:
                if (wps_validate_m8(msg) < 0)
@@ -1220,7 +1317,8 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
                ret = wps_process_m8(wps, msg, &attr);
                if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
                        wps_fail_event(wps->wps, WPS_M8, wps->config_error,
-                                      wps->error_indication);
+                                      wps->error_indication,
+                                      wps->peer_dev.mac_addr);
                break;
        default:
                wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
@@ -1283,7 +1381,7 @@ static enum wps_process_res wps_process_wsc_ack(struct wps_data *wps,
        if (wps->state == RECV_ACK && wps->wps->ap) {
                wpa_printf(MSG_DEBUG, "WPS: External Registrar registration "
                           "completed successfully");
-               wps_success_event(wps->wps);
+               wps_success_event(wps->wps, wps->peer_dev.mac_addr);
                wps->state = WPS_FINISHED;
                return WPS_DONE;
        }
@@ -1348,15 +1446,15 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
        switch (wps->state) {
        case RECV_M4:
                wps_fail_event(wps->wps, WPS_M3, config_error,
-                              wps->error_indication);
+                              wps->error_indication, wps->peer_dev.mac_addr);
                break;
        case RECV_M6:
                wps_fail_event(wps->wps, WPS_M5, config_error,
-                              wps->error_indication);
+                              wps->error_indication, wps->peer_dev.mac_addr);
                break;
        case RECV_M8:
                wps_fail_event(wps->wps, WPS_M7, config_error,
-                              wps->error_indication);
+                              wps->error_indication, wps->peer_dev.mac_addr);
                break;
        default:
                break;
index 5694997..078ff72 100644 (file)
@@ -185,10 +185,8 @@ static void wps_er_ap_unsubscribed(struct wps_er *er, struct wps_er_ap *ap)
        dl_list_del(&ap->list);
        wps_er_ap_free(ap);
 
-       if (er->deinitializing && dl_list_empty(&er->ap_unsubscribing)) {
-               eloop_cancel_timeout(wps_er_deinit_finish, er, NULL);
+       if (er->deinitializing && dl_list_empty(&er->ap_unsubscribing))
                wps_er_deinit_finish(er, NULL);
-       }
 }
 
 
@@ -581,12 +579,15 @@ static void wps_er_parse_device_description(struct wps_er_ap *ap,
        wpa_printf(MSG_DEBUG, "WPS ER: serialNumber='%s'", ap->serial_number);
 
        ap->udn = xml_get_first_item(data, "UDN");
-       wpa_printf(MSG_DEBUG, "WPS ER: UDN='%s'", ap->udn);
-       pos = os_strstr(ap->udn, "uuid:");
-       if (pos) {
-               pos += 5;
-               if (uuid_str2bin(pos, ap->uuid) < 0)
-                       wpa_printf(MSG_DEBUG, "WPS ER: Invalid UUID in UDN");
+       if (ap->udn) {
+               wpa_printf(MSG_DEBUG, "WPS ER: UDN='%s'", ap->udn);
+               pos = os_strstr(ap->udn, "uuid:");
+               if (pos) {
+                       pos += 5;
+                       if (uuid_str2bin(pos, ap->uuid) < 0)
+                               wpa_printf(MSG_DEBUG,
+                                          "WPS ER: Invalid UUID in UDN");
+               }
        }
 
        ap->upc = xml_get_first_item(data, "UPC");
@@ -1347,9 +1348,19 @@ static void wps_er_deinit_finish(void *eloop_data, void *user_ctx)
        struct wps_er *er = eloop_data;
        void (*deinit_done_cb)(void *ctx);
        void *deinit_done_ctx;
+       struct wps_er_ap *ap, *tmp;
 
        wpa_printf(MSG_DEBUG, "WPS ER: Finishing deinit");
 
+       dl_list_for_each_safe(ap, tmp, &er->ap_unsubscribing, struct wps_er_ap,
+                             list) {
+               wpa_printf(MSG_DEBUG, "WPS ER: AP entry for %s (%s) still in ap_unsubscribing list - free it",
+                          inet_ntoa(ap->addr), ap->location);
+               dl_list_del(&ap->list);
+               wps_er_ap_free(ap);
+       }
+
+       eloop_cancel_timeout(wps_er_deinit_finish, er, NULL);
        deinit_done_cb = er->deinit_done_cb;
        deinit_done_ctx = er->deinit_done_ctx;
        os_free(er->ip_addr_text);
@@ -1482,11 +1493,9 @@ static int wps_er_build_sel_reg_config_methods(struct wpabuf *msg,
 
 static int wps_er_build_uuid_r(struct wpabuf *msg, const u8 *uuid_r)
 {
-#ifdef CONFIG_WPS2
        wpabuf_put_be16(msg, ATTR_UUID_R);
        wpabuf_put_be16(msg, WPS_UUID_LEN);
        wpabuf_put_data(msg, uuid_r, WPS_UUID_LEN);
-#endif /* CONFIG_WPS2 */
        return 0;
 }
 
@@ -1498,9 +1507,7 @@ void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
        struct wps_er_ap *ap;
        struct wps_registrar *reg = er->wps->registrar;
        const u8 *auth_macs;
-#ifdef CONFIG_WPS2
        u8 bcast[ETH_ALEN];
-#endif /* CONFIG_WPS2 */
        size_t count;
        union wps_event_data data;
 
@@ -1514,13 +1521,11 @@ void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
                return;
 
        auth_macs = wps_authorized_macs(reg, &count);
-#ifdef CONFIG_WPS2
        if (count == 0) {
                os_memset(bcast, 0xff, ETH_ALEN);
                auth_macs = bcast;
                count = 1;
        }
-#endif /* CONFIG_WPS2 */
 
        if (wps_build_version(msg) ||
            wps_er_build_selected_registrar(msg, sel_reg) ||
@@ -2032,8 +2037,7 @@ struct wpabuf * wps_er_config_token_from_cred(struct wps_context *wps,
        os_memset(&data, 0, sizeof(data));
        data.wps = wps;
        data.use_cred = cred;
-       if (wps_build_version(ret) ||
-           wps_build_cred(&data, ret) ||
+       if (wps_build_cred(&data, ret) ||
            wps_build_wfa_ext(ret, 0, NULL, 0)) {
                wpabuf_free(ret);
                return NULL;
@@ -2063,4 +2067,29 @@ struct wpabuf * wps_er_nfc_config_token(struct wps_er *er, const u8 *uuid,
        return wps_er_config_token_from_cred(er->wps, ap->ap_settings);
 }
 
+
+struct wpabuf * wps_er_nfc_handover_sel(struct wps_er *er,
+                                       struct wps_context *wps, const u8 *uuid,
+                                       const u8 *addr, struct wpabuf *pubkey)
+{
+       struct wps_er_ap *ap;
+
+       if (er == NULL)
+               return NULL;
+
+       ap = wps_er_ap_get(er, NULL, uuid, addr);
+       if (ap == NULL)
+               return NULL;
+       if (ap->ap_settings == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS ER: No settings known for the "
+                          "selected AP");
+               return NULL;
+       }
+
+       os_memcpy(wps->ssid, ap->ap_settings->ssid, ap->ap_settings->ssid_len);
+       wps->ssid_len = ap->ap_settings->ssid_len;
+
+       return wps_build_nfc_handover_sel(wps, pubkey, addr, 0);
+}
+
 #endif /* CONFIG_WPS_NFC */
index 413379b..f7154f8 100644 (file)
@@ -75,6 +75,9 @@ struct wps_data {
        size_t alt_dev_password_len;
        u16 alt_dev_pw_id;
 
+       u8 peer_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+       int peer_pubkey_hash_set;
+
        /**
         * request_type - Request Type attribute from (Re)AssocReq
         */
@@ -134,11 +137,14 @@ void wps_derive_psk(struct wps_data *wps, const u8 *dev_passwd,
 struct wpabuf * wps_decrypt_encr_settings(struct wps_data *wps, const u8 *encr,
                                          size_t encr_len);
 void wps_fail_event(struct wps_context *wps, enum wps_msg_type msg,
-                   u16 config_error, u16 error_indication);
-void wps_success_event(struct wps_context *wps);
-void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part);
+                   u16 config_error, u16 error_indication, const u8 *mac_addr);
+void wps_success_event(struct wps_context *wps, const u8 *mac_addr);
+void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part,
+                            const u8 *mac_addr);
 void wps_pbc_overlap_event(struct wps_context *wps);
 void wps_pbc_timeout_event(struct wps_context *wps);
+void wps_pbc_active_event(struct wps_context *wps);
+void wps_pbc_disable_event(struct wps_context *wps);
 
 struct wpabuf * wps_build_wsc_ack(struct wps_data *wps);
 struct wpabuf * wps_build_wsc_nack(struct wps_data *wps);
@@ -170,6 +176,8 @@ int wps_build_oob_dev_pw(struct wpabuf *msg, u16 dev_pw_id,
                         size_t dev_pw_len);
 struct wpabuf * wps_ie_encapsulate(struct wpabuf *data);
 int wps_build_mac_addr(struct wpabuf *msg, const u8 *addr);
+int wps_build_rf_bands_attr(struct wpabuf *msg, u8 rf_bands);
+int wps_build_ap_channel(struct wpabuf *msg, u16 ap_channel);
 
 /* wps_attr_process.c */
 int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator,
@@ -204,5 +212,7 @@ int wps_registrar_pbc_overlap(struct wps_registrar *reg,
                              const u8 *addr, const u8 *uuid_e);
 void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg,
                                       struct wps_nfc_pw_token *token);
+int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr,
+                  const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len);
 
 #endif /* WPS_I_H */
diff --git a/src/wps/wps_module_tests.c b/src/wps/wps_module_tests.c
new file mode 100644 (file)
index 0000000..6800e86
--- /dev/null
@@ -0,0 +1,337 @@
+/*
+ * WPS 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.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "wps_attr_parse.h"
+
+struct wps_attr_parse_test {
+       const char *data;
+       int result;
+       int extra;
+};
+
+struct wps_attr_parse_test wps_attr_parse_test_cases[] = {
+       /* Empty message */
+       { "", 0, 0 },
+       /* Truncated attribute header */
+       { "10", -1, 0 },
+       { "1010", -1, 0 },
+       { "101000", -1, 0 },
+       /* Attribute overflow */
+       { "10100001", -1, 0 },
+#ifdef CONFIG_WPS_STRICT
+       { "10270000001057000101", -1, 0 },
+       { "1027000010570001010000000000", -1, 0 },
+#else /* CONFIG_WPS_STRICT */
+       /* Network Key workaround */
+       { "10270000001057000101", 0, 1 },
+       { "10230000001057000101", -1, 0 },
+       { "10270000101057000101", -1, 0 },
+       /* Mac OS X 10.6 padding workaround */
+       { "1027000010570001010000000000", 0, 1 },
+       { "1027000010570001010000000000000001000000", -1, 0 },
+#endif /* CONFIG_WPS_STRICT */
+       /* Version */
+       { "104a000110", 0, 0 },
+       { "104a0000", -1, 0 },
+       /* Message Type */
+       { "1022000101", 0, 0 },
+       { "10220000", -1, 0 },
+       /* Enrollee Nonce */
+       { "101a001000112233445566778899aabbccddeeff", 0, 0 },
+       { "101a00111122334455667788990011223344556677", -1, 0 },
+       /* Registrar Nonce */
+       { "1039001000112233445566778899aabbccddeeff", 0, 0 },
+       { "103900111122334455667788990011223344556677", -1, 0 },
+       /* UUID-E */
+       { "1047001000112233445566778899aabbccddeeff", 0, 0 },
+       { "10470000", -1, 0 },
+       { "104700111122334455667788990011223344556677", -1, 0 },
+       /* UUID-R */
+       { "1048001000112233445566778899aabbccddeeff", 0, 0 },
+       { "10480000", -1, 0 },
+       { "104800111122334455667788990011223344556677", -1, 0 },
+       /* Auth Type Flags */
+       { "100400021122", 0, 0 },
+       { "10040001ff", -1, 0 },
+       /* Encr Type Flags */
+       { "101000021122", 0, 0 },
+       { "10100001ff", -1, 0 },
+       /* Connection Type Flags */
+       { "100d0001ff", 0, 0 },
+       { "100d0002ffff", -1, 0 },
+       /* Config Methods */
+       { "10080002ffff", 0, 0 },
+       { "10080001ff", -1, 0 },
+       /* Selected Registrar Config Methods */
+       { "10530002ffff", 0, 0 },
+       { "10530001ff", -1, 0 },
+       /* Primary Device Type */
+       { "105400081122334455667788", 0, 0 },
+       { "105400111122334455667788990011223344556677", -1, 0 },
+       /* RF Bands */
+       { "103c0001ff", 0, 0 },
+       { "103c0002ffff", -1, 0 },
+       /* Association State */
+       { "10020002ffff", 0, 0 },
+       { "10020001ff", -1, 0 },
+       /* Config Error */
+       { "100900020001", 0, 0 },
+       { "10090001ff", -1, 0 },
+       /* Device Password ID */
+       { "101200020004", 0, 0 },
+       { "10120001ff", -1, 0 },
+       /* OOB Device Password */
+       { "102c001611223344556677889900112233445566778899000007", 0, 0 },
+       { "102c0036112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344", 0, 0 },
+       { "102c0001ff", -1, 0 },
+       { "102c003711223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455", -1, 0 },
+       { "102c002511223344556677889900112233445566778899001122334455667788990011223344556677", -1, 0 },
+       /* OS Version */
+       { "102d000411223344", 0, 0 },
+       { "102d00111122334455667788990011223344556677", -1, 0 },
+       /* WPS State */
+       { "1044000101", 0, 0 },
+       { "10440002ffff", -1, 0 },
+       /* Authenticator */
+       { "100500081122334455667788", 0, 0 },
+       { "10050000", -1, 0 },
+       { "100500111122334455667788990011223344556677", -1, 0 },
+       /* R-Hash1 */
+       { "103d00201122334455667788990011223344556677889900112233445566778899001122", 0, 0 },
+       { "103d0000", -1, 0 },
+       { "103d0021112233445566778899001122334455667788990011223344556677889900112233", -1, 0 },
+       /* R-Hash2 */
+       { "103e00201122334455667788990011223344556677889900112233445566778899001122", 0, 0 },
+       { "103e0000", -1, 0 },
+       { "103e0021112233445566778899001122334455667788990011223344556677889900112233", -1, 0 },
+       /* E-Hash1 */
+       { "101400201122334455667788990011223344556677889900112233445566778899001122", 0, 0 },
+       { "10140000", -1, 0 },
+       { "10140021112233445566778899001122334455667788990011223344556677889900112233", -1, 0 },
+       /* E-Hash2 */
+       { "101500201122334455667788990011223344556677889900112233445566778899001122", 0, 0 },
+       { "10150000", -1, 0 },
+       { "10150021112233445566778899001122334455667788990011223344556677889900112233", -1, 0 },
+       /* R-SNonce1 */
+       { "103f001011223344556677889900112233445566", 0, 0 },
+       { "103f0000", -1, 0 },
+       { "103f00111122334455667788990011223344556677", -1, 0 },
+       /* R-SNonce2 */
+       { "1040001011223344556677889900112233445566", 0, 0 },
+       { "10400000", -1, 0 },
+       { "104000111122334455667788990011223344556677", -1, 0 },
+       /* E-SNonce1 */
+       { "1016001011223344556677889900112233445566", 0, 0 },
+       { "10160000", -1, 0 },
+       { "101600111122334455667788990011223344556677", -1, 0 },
+       /* E-SNonce2 */
+       { "1017001011223344556677889900112233445566", 0, 0 },
+       { "10170000", -1, 0 },
+       { "101700111122334455667788990011223344556677", -1, 0 },
+       /* Key Wrap Authenticator */
+       { "101e00081122334455667788", 0, 0 },
+       { "101e0000", -1, 0 },
+       { "101e0009112233445566778899", -1, 0 },
+       /* Authentication Type */
+       { "100300020001", 0, 0 },
+       { "10030001ff", -1, 0 },
+       /* Encryption Type */
+       { "100f00020001", 0, 0 },
+       { "100f0001ff", -1, 0 },
+       /* Network Index */
+       { "1026000101", 0, 0 },
+       { "10260002ffff", -1, 0 },
+       /* Network Key Index */
+       { "1028000101", 0, 3 },
+       { "10280002ffff", -1, 0 },
+       /* MAC Address */
+       { "10200006112233445566", 0, 0 },
+       { "10200000", -1, 0 },
+       { "1020000711223344556677", -1, 0 },
+       /* Selected Registrar */
+       { "1041000101", 0, 0 },
+       { "10410002ffff", -1, 0 },
+       /* Request Type */
+       { "103a000101", 0, 0 },
+       { "103a0002ffff", -1, 0 },
+       /* Response Type */
+       { "103b000101", 0, 0 },
+       { "103b0002ffff", -1, 0 },
+       /* Manufacturer */
+       { "10210000", 0, 0 },
+       /* Model Name */
+       { "10230000", 0, 0 },
+       /* Model Number */
+       { "10240000", 0, 0 },
+       /* Serial Number */
+       { "10420000", 0, 0 },
+       /* Device Name */
+       { "10110000", 0, 0 },
+       /* Public Key */
+       { "10320000", 0, 0 },
+       /* Enc Settings */
+       { "10180000", 0, 0 },
+       /* SSID */
+       { "10450000", 0, 0 },
+       /* AP Setup Locked */
+       { "1057000101", 0, 0 },
+       { "10570002ffff", -1, 0 },
+       /* Requested Device Type */
+       { "106a00081122334455667788", 0, 0 },
+       { "106a0000", -1, 0 },
+       { "106a0009112233445566778899", -1, 0 },
+       /* More than maximum Requested Device Type attributes */
+       { "106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788106a00081122334455667788", 0, 4 },
+       /* Secondary Device Type List */
+       { "105500081122334455667788", 0, 0 },
+       { "1055000711223344556677", -1, 0 },
+       { "1055008811223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900112233445566", -1, 0 },
+       /* AP Channel */
+       { "100100020001", 0, 0 },
+       { "1001000101", -1, 0 },
+       /* Skip invalid Vendor Extension */
+       { "10490000", 0, 0 },
+       { "1049000100", 0, 0 },
+       { "104900020000", 0, 0 },
+       /* Too long unknown vendor extension */
+       { "10490401"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "112233445566778899001122334455667788990011223344556677889900"
+         "1122334455", -1, 0 },
+       /* Maximum unknown vendor extensions */
+       { "10490003111111104900032222221049000333333310490003444444104900035555551049000366666610490003777777104900038888881049000399999910490003AAAAAA", 0, 5 },
+       /* More than maximum unknown vendor extensions */
+       { "10490003111111104900032222221049000333333310490003444444104900035555551049000366666610490003777777104900038888881049000399999910490003AAAAAA10490003BBBBBB", -1, 0 },
+       /* WFA vendor extensions */
+       { "1049000300372a", 0, 0 },
+       { "1049000400372a00", 0, 0 },
+       { "1049000500372a0001", 0, 0 },
+       { "1049001600372a0001ff0100020101030101040101ff00fe0101", 0, 6 },
+       /* Invalid Version2 length */
+       { "1049000500372a0000", -1, 0 },
+       /* Invalid Network Key Shareable length */
+       { "1049000500372a0200", -1, 0 },
+       /* Invalid Requedt To Enroll length */
+       { "1049000500372a0300", -1, 0 },
+       /* Invalid Settings Delay Time length */
+       { "1049000500372a0400", -1, 0 },
+       /* More than maximum Credential attributes */
+       { "100e0000100e0000100e0000100e0000100e0000100e0000100e0000100e0000100e0000100e0000100e0000100e0000", 0, 2 },
+};
+
+
+static int wps_attr_parse_tests(void)
+{
+       struct wps_parse_attr attr;
+       unsigned int i;
+       int ret = 0;
+
+       wpa_printf(MSG_INFO, "WPS attribute parsing tests");
+
+       for (i = 0; i < ARRAY_SIZE(wps_attr_parse_test_cases); i++) {
+               struct wpabuf *buf;
+               size_t len;
+               struct wps_attr_parse_test *test =
+                       &wps_attr_parse_test_cases[i];
+
+               len = os_strlen(test->data) / 2;
+               buf = wpabuf_alloc(len);
+               if (buf == NULL)
+                       return -1;
+               if (hexstr2bin(test->data, wpabuf_put(buf, len), len) < 0) {
+                       wpabuf_free(buf);
+                       return -1;
+               }
+               if (wps_parse_msg(buf, &attr) != test->result) {
+                       wpa_printf(MSG_ERROR, "WPS attribute parsing test %u failed: %s",
+                                  i, test->data);
+                       ret = -1;
+               }
+               switch (test->extra) {
+               case 1:
+                       if (!attr.network_key || !attr.ap_setup_locked)
+                               ret = -1;
+                       break;
+               case 2:
+                       if (attr.num_cred != MAX_CRED_COUNT)
+                               ret = -1;
+                       break;
+               case 3:
+                       if (!attr.network_key_idx)
+                               ret = -1;
+                       break;
+               case 4:
+                       if (attr.num_req_dev_type != MAX_REQ_DEV_TYPE_COUNT)
+                               ret = -1;
+                       break;
+               case 5:
+                       if (attr.num_vendor_ext != MAX_WPS_PARSE_VENDOR_EXT)
+                               ret = -1;
+                       break;
+               case 6:
+                       if (!attr.version2 ||
+                           !attr.authorized_macs ||
+                           !attr.network_key_shareable ||
+                           !attr.request_to_enroll ||
+                           !attr.settings_delay_time)
+                               ret = -1;
+                       break;
+               }
+               wpabuf_free(buf);
+       }
+
+       return ret;
+}
+
+
+int wps_module_tests(void)
+{
+       int ret = 0;
+
+       wpa_printf(MSG_INFO, "WPS module tests");
+
+       if (wps_attr_parse_tests() < 0)
+               ret = -1;
+
+       return ret;
+}
index 4812893..48b7e12 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Wi-Fi Protected Setup - Registrar
- * Copyright (c) 2008-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 struct wps_nfc_pw_token {
        struct dl_list list;
        u8 pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+       unsigned int peer_pk_hash_known:1;
        u16 pw_id;
        u8 dev_pw[WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1];
        size_t dev_pw_len;
+       int pk_hash_provided_oob; /* whether own PK hash was provided OOB */
 };
 
 
 static void wps_remove_nfc_pw_token(struct wps_nfc_pw_token *token)
 {
        dl_list_del(&token->list);
-       os_free(token);
+       bin_clear_free(token, sizeof(*token));
 }
 
 
@@ -82,14 +84,14 @@ struct wps_uuid_pin {
 #define PIN_LOCKED BIT(0)
 #define PIN_EXPIRES BIT(1)
        int flags;
-       struct os_time expiration;
+       struct os_reltime expiration;
        u8 enrollee_addr[ETH_ALEN];
 };
 
 
 static void wps_free_pin(struct wps_uuid_pin *pin)
 {
-       os_free(pin->pin);
+       bin_clear_free(pin->pin, pin->pin_len);
        os_free(pin);
 }
 
@@ -113,7 +115,7 @@ struct wps_pbc_session {
        struct wps_pbc_session *next;
        u8 addr[ETH_ALEN];
        u8 uuid_e[WPS_UUID_LEN];
-       struct os_time timestamp;
+       struct os_reltime timestamp;
 };
 
 
@@ -142,8 +144,8 @@ struct wps_registrar {
        int pbc;
        int selected_registrar;
 
-       int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk,
-                         size_t psk_len);
+       int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr,
+                         const u8 *psk, size_t psk_len);
        int (*set_ie_cb)(void *ctx, struct wpabuf *beacon_ie,
                         struct wpabuf *probe_resp_ie);
        void (*pin_needed_cb)(void *ctx, const u8 *uuid_e,
@@ -171,6 +173,7 @@ struct wps_registrar {
        int sel_reg_config_methods_override;
        int static_wep_only;
        int dualband;
+       int force_per_enrollee_psk;
 
        struct wps_registrar_device *devices;
 
@@ -182,7 +185,9 @@ struct wps_registrar {
        u8 p2p_dev_addr[ETH_ALEN];
 
        u8 pbc_ignore_uuid[WPS_UUID_LEN];
-       struct os_time pbc_ignore_start;
+#ifdef WPS_WORKAROUNDS
+       struct os_reltime pbc_ignore_start;
+#endif /* WPS_WORKAROUNDS */
 };
 
 
@@ -310,9 +315,9 @@ static void wps_registrar_add_pbc_session(struct wps_registrar *reg,
                                          const u8 *addr, const u8 *uuid_e)
 {
        struct wps_pbc_session *pbc, *prev = NULL;
-       struct os_time now;
+       struct os_reltime now;
 
-       os_get_time(&now);
+       os_get_reltime(&now);
 
        pbc = reg->pbc_sessions;
        while (pbc) {
@@ -346,7 +351,8 @@ static void wps_registrar_add_pbc_session(struct wps_registrar *reg,
        pbc = pbc->next;
 
        while (pbc) {
-               if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) {
+               if (os_reltime_expired(&now, &pbc->timestamp,
+                                      WPS_PBC_WALK_TIME)) {
                        prev->next = NULL;
                        wps_free_pbc_sessions(pbc);
                        break;
@@ -394,9 +400,9 @@ int wps_registrar_pbc_overlap(struct wps_registrar *reg,
        int count = 0;
        struct wps_pbc_session *pbc;
        struct wps_pbc_session *first = NULL;
-       struct os_time now;
+       struct os_reltime now;
 
-       os_get_time(&now);
+       os_get_reltime(&now);
 
        wpa_printf(MSG_DEBUG, "WPS: Checking active PBC sessions for overlap");
 
@@ -412,9 +418,9 @@ int wps_registrar_pbc_overlap(struct wps_registrar *reg,
                           MAC2STR(pbc->addr));
                wpa_hexdump(MSG_DEBUG, "WPS: UUID-E",
                            pbc->uuid_e, WPS_UUID_LEN);
-               if (now.sec > pbc->timestamp.sec + WPS_PBC_WALK_TIME) {
-                       wpa_printf(MSG_DEBUG, "WPS: PBC walk time has "
-                                  "expired");
+               if (os_reltime_expired(&now, &pbc->timestamp,
+                                      WPS_PBC_WALK_TIME)) {
+                       wpa_printf(MSG_DEBUG, "WPS: PBC walk time has expired");
                        break;
                }
                if (first &&
@@ -532,7 +538,6 @@ static int wps_build_sel_pbc_reg_uuid_e(struct wps_registrar *reg,
 static void wps_set_pushbutton(u16 *methods, u16 conf_methods)
 {
        *methods |= WPS_CONFIG_PUSHBUTTON;
-#ifdef CONFIG_WPS2
        if ((conf_methods & WPS_CONFIG_VIRT_PUSHBUTTON) ==
            WPS_CONFIG_VIRT_PUSHBUTTON)
                *methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
@@ -550,7 +555,6 @@ static void wps_set_pushbutton(u16 *methods, u16 conf_methods)
                 */
                *methods |= WPS_CONFIG_PHY_PUSHBUTTON;
        }
-#endif /* CONFIG_WPS2 */
 }
 
 
@@ -562,10 +566,8 @@ static int wps_build_sel_reg_config_methods(struct wps_registrar *reg,
                return 0;
        methods = reg->wps->config_methods;
        methods &= ~WPS_CONFIG_PUSHBUTTON;
-#ifdef CONFIG_WPS2
        methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
                     WPS_CONFIG_PHY_PUSHBUTTON);
-#endif /* CONFIG_WPS2 */
        if (reg->pbc)
                wps_set_pushbutton(&methods, reg->wps->config_methods);
        if (reg->sel_reg_config_methods_override >= 0)
@@ -588,10 +590,8 @@ static int wps_build_probe_config_methods(struct wps_registrar *reg,
         * external Registrars.
         */
        methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
-#ifdef CONFIG_WPS2
        methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
                     WPS_CONFIG_PHY_PUSHBUTTON);
-#endif /* CONFIG_WPS2 */
        wpa_printf(MSG_DEBUG, "WPS:  * Config Methods (%x)", methods);
        wpabuf_put_be16(msg, ATTR_CONFIG_METHODS);
        wpabuf_put_be16(msg, 2);
@@ -611,13 +611,11 @@ const u8 * wps_authorized_macs(struct wps_registrar *reg, size_t *count)
 {
        *count = 0;
 
-#ifdef CONFIG_WPS2
        while (*count < WPS_MAX_AUTHORIZED_MACS) {
                if (is_zero_ether_addr(reg->authorized_macs_union[*count]))
                        break;
                (*count)++;
        }
-#endif /* CONFIG_WPS2 */
 
        return (const u8 *) reg->authorized_macs_union;
 }
@@ -667,6 +665,7 @@ wps_registrar_init(struct wps_context *wps,
        reg->sel_reg_config_methods_override = -1;
        reg->static_wep_only = cfg->static_wep_only;
        reg->dualband = cfg->dualband;
+       reg->force_per_enrollee_psk = cfg->force_per_enrollee_psk;
 
        if (wps_set_ie(reg)) {
                wps_registrar_deinit(reg);
@@ -677,6 +676,22 @@ wps_registrar_init(struct wps_context *wps,
 }
 
 
+void wps_registrar_flush(struct wps_registrar *reg)
+{
+       if (reg == NULL)
+               return;
+       wps_free_pins(&reg->pins);
+       wps_free_nfc_pw_tokens(&reg->nfc_pw_tokens, 0);
+       wps_free_pbc_sessions(reg->pbc_sessions);
+       reg->pbc_sessions = NULL;
+       wps_free_devices(reg->devices);
+       reg->devices = NULL;
+#ifdef WPS_WORKAROUNDS
+       reg->pbc_ignore_start.sec = 0;
+#endif /* WPS_WORKAROUNDS */
+}
+
+
 /**
  * wps_registrar_deinit - Deinitialize WPS Registrar data
  * @reg: Registrar data from wps_registrar_init()
@@ -687,11 +702,8 @@ void wps_registrar_deinit(struct wps_registrar *reg)
                return;
        eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
        eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
-       wps_free_pins(&reg->pins);
-       wps_free_nfc_pw_tokens(&reg->nfc_pw_tokens, 0);
-       wps_free_pbc_sessions(reg->pbc_sessions);
+       wps_registrar_flush(reg);
        wpabuf_free(reg->extra_cred);
-       wps_free_devices(reg->devices);
        os_free(reg);
 }
 
@@ -746,7 +758,7 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *addr,
 
        if (timeout) {
                p->flags |= PIN_EXPIRES;
-               os_get_time(&p->expiration);
+               os_get_reltime(&p->expiration);
                p->expiration.sec += timeout;
        }
 
@@ -795,13 +807,13 @@ static void wps_registrar_remove_pin(struct wps_registrar *reg,
 static void wps_registrar_expire_pins(struct wps_registrar *reg)
 {
        struct wps_uuid_pin *pin, *prev;
-       struct os_time now;
+       struct os_reltime now;
 
-       os_get_time(&now);
+       os_get_reltime(&now);
        dl_list_for_each_safe(pin, prev, &reg->pins, struct wps_uuid_pin, list)
        {
                if ((pin->flags & PIN_EXPIRES) &&
-                   os_time_before(&pin->expiration, &now)) {
+                   os_reltime_before(&pin->expiration, &now)) {
                        wpa_hexdump(MSG_DEBUG, "WPS: Expired PIN for UUID",
                                    pin->uuid, WPS_UUID_LEN);
                        wps_registrar_remove_pin(reg, pin);
@@ -827,7 +839,7 @@ static int wps_registrar_invalidate_wildcard_pin(struct wps_registrar *reg,
        {
                if (dev_pw && pin->pin &&
                    (dev_pw_len != pin->pin_len ||
-                    os_memcmp(dev_pw, pin->pin, dev_pw_len) != 0))
+                    os_memcmp_const(dev_pw, pin->pin, dev_pw_len) != 0))
                        continue; /* different PIN */
                if (pin->wildcard_uuid) {
                        wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID",
@@ -1001,6 +1013,7 @@ int wps_registrar_button_pushed(struct wps_registrar *reg,
                                         (u8 *) "\xff\xff\xff\xff\xff\xff");
        wps_registrar_selected_registrar_changed(reg, 0);
 
+       wps_pbc_active_event(reg->wps);
        eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
        eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
        eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout,
@@ -1014,6 +1027,7 @@ static void wps_registrar_pbc_completed(struct wps_registrar *reg)
        wpa_printf(MSG_DEBUG, "WPS: PBC completed - stopping PBC mode");
        eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL);
        wps_registrar_stop_pbc(reg);
+       wps_pbc_disable_event(reg->wps);
 }
 
 
@@ -1033,7 +1047,9 @@ void wps_registrar_complete(struct wps_registrar *registrar, const u8 *uuid_e,
                wps_registrar_remove_pbc_session(registrar,
                                                 uuid_e, NULL);
                wps_registrar_pbc_completed(registrar);
-               os_get_time(&registrar->pbc_ignore_start);
+#ifdef WPS_WORKAROUNDS
+               os_get_reltime(&registrar->pbc_ignore_start);
+#endif /* WPS_WORKAROUNDS */
                os_memcpy(registrar->pbc_ignore_uuid, uuid_e, WPS_UUID_LEN);
        } else {
                wps_registrar_pin_completed(registrar);
@@ -1136,9 +1152,9 @@ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
 #ifdef WPS_WORKAROUNDS
        if (reg->pbc_ignore_start.sec &&
            os_memcmp(attr.uuid_e, reg->pbc_ignore_uuid, WPS_UUID_LEN) == 0) {
-               struct os_time now, dur;
-               os_get_time(&now);
-               os_time_sub(&now, &reg->pbc_ignore_start, &dur);
+               struct os_reltime now, dur;
+               os_get_reltime(&now);
+               os_reltime_sub(&now, &reg->pbc_ignore_start, &dur);
                if (dur.sec >= 0 && dur.sec < 5) {
                        wpa_printf(MSG_DEBUG, "WPS: Ignore PBC activation "
                                   "based on Probe Request from the Enrollee "
@@ -1159,13 +1175,14 @@ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr,
 }
 
 
-static int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr,
-                         const u8 *psk, size_t psk_len)
+int wps_cb_new_psk(struct wps_registrar *reg, const u8 *mac_addr,
+                  const u8 *p2p_dev_addr, const u8 *psk, size_t psk_len)
 {
        if (reg->new_psk_cb == NULL)
                return 0;
 
-       return reg->new_psk_cb(reg->cb_ctx, mac_addr, psk, psk_len);
+       return reg->new_psk_cb(reg->cb_ctx, mac_addr, p2p_dev_addr, psk,
+                              psk_len);
 }
 
 
@@ -1205,10 +1222,8 @@ static void wps_cb_set_sel_reg(struct wps_registrar *reg)
 
        if (reg->selected_registrar) {
                methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
-#ifdef CONFIG_WPS2
                methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
                             WPS_CONFIG_PHY_PUSHBUTTON);
-#endif /* CONFIG_WPS2 */
                if (reg->pbc)
                        wps_set_pushbutton(&methods, reg->wps->config_methods);
        }
@@ -1263,7 +1278,7 @@ static int wps_set_ie(struct wps_registrar *reg)
            wps_build_sel_reg_dev_password_id(reg, beacon) ||
            wps_build_sel_reg_config_methods(reg, beacon) ||
            wps_build_sel_pbc_reg_uuid_e(reg, beacon) ||
-           (reg->dualband && wps_build_rf_bands(&reg->wps->dev, beacon)) ||
+           (reg->dualband && wps_build_rf_bands(&reg->wps->dev, beacon, 0)) ||
            wps_build_wfa_ext(beacon, 0, auth_macs, count) ||
            wps_build_vendor_ext(&reg->wps->dev, beacon)) {
                wpabuf_free(beacon);
@@ -1293,7 +1308,7 @@ static int wps_set_ie(struct wps_registrar *reg)
            wps_build_uuid_e(probe, reg->wps->uuid) ||
            wps_build_device_attrs(&reg->wps->dev, probe) ||
            wps_build_probe_config_methods(reg, probe) ||
-           (reg->dualband && wps_build_rf_bands(&reg->wps->dev, probe)) ||
+           (reg->dualband && wps_build_rf_bands(&reg->wps->dev, probe, 0)) ||
            wps_build_wfa_ext(probe, 0, auth_macs, count) ||
            wps_build_vendor_ext(&reg->wps->dev, probe)) {
                wpabuf_free(beacon);
@@ -1341,7 +1356,7 @@ static int wps_get_dev_password(struct wps_data *wps)
        const u8 *pin;
        size_t pin_len = 0;
 
-       os_free(wps->dev_password);
+       bin_clear_free(wps->dev_password, wps->dev_password_len);
        wps->dev_password = NULL;
 
        if (wps->pbc) {
@@ -1350,10 +1365,23 @@ static int wps_get_dev_password(struct wps_data *wps)
                pin_len = 8;
 #ifdef CONFIG_WPS_NFC
        } else if (wps->nfc_pw_token) {
+               if (wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER)
+               {
+                       wpa_printf(MSG_DEBUG, "WPS: Using NFC connection "
+                                  "handover and abbreviated WPS handshake "
+                                  "without Device Password");
+                       return 0;
+               }
                wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from NFC "
                           "Password Token");
                pin = wps->nfc_pw_token->dev_pw;
                pin_len = wps->nfc_pw_token->dev_pw_len;
+       } else if (wps->dev_pw_id >= 0x10 &&
+                  wps->wps->ap_nfc_dev_pw_id == wps->dev_pw_id &&
+                  wps->wps->ap_nfc_dev_pw) {
+               wpa_printf(MSG_DEBUG, "WPS: Use OOB Device Password from own NFC Password Token");
+               pin = wpabuf_head(wps->wps->ap_nfc_dev_pw);
+               pin_len = wpabuf_len(wps->wps->ap_nfc_dev_pw);
 #endif /* CONFIG_WPS_NFC */
        } else {
                pin = wps_registrar_get_pin(wps->wps->registrar, wps->uuid_e,
@@ -1369,7 +1397,8 @@ static int wps_get_dev_password(struct wps_data *wps)
        }
        if (pin == NULL) {
                wpa_printf(MSG_DEBUG, "WPS: No Device Password available for "
-                          "the Enrollee");
+                          "the Enrollee (context %p registrar %p)",
+                          wps->wps, wps->wps->registrar);
                wps_cb_pin_needed(wps->wps->registrar, wps->uuid_e,
                                  &wps->peer_dev);
                return -1;
@@ -1583,8 +1612,6 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
                wps->auth_type = WPS_AUTH_WPAPSK;
        else if (wps->auth_type & WPS_AUTH_OPEN)
                wps->auth_type = WPS_AUTH_OPEN;
-       else if (wps->auth_type & WPS_AUTH_SHARED)
-               wps->auth_type = WPS_AUTH_SHARED;
        else {
                wpa_printf(MSG_DEBUG, "WPS: Unsupported auth_type 0x%x",
                           wps->auth_type);
@@ -1604,10 +1631,12 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
                        return -1;
                }
        } else {
-               if (wps->encr_type & WPS_ENCR_WEP)
-                       wps->encr_type = WPS_ENCR_WEP;
-               else if (wps->encr_type & WPS_ENCR_NONE)
+               if (wps->encr_type & WPS_ENCR_NONE)
                        wps->encr_type = WPS_ENCR_NONE;
+#ifdef CONFIG_TESTING_OPTIONS
+               else if (wps->encr_type & WPS_ENCR_WEP)
+                       wps->encr_type = WPS_ENCR_WEP;
+#endif /* CONFIG_TESTING_OPTIONS */
                else {
                        wpa_printf(MSG_DEBUG, "WPS: No suitable encryption "
                                   "type for non-WPA/WPA2 mode");
@@ -1624,8 +1653,12 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
            !wps->wps->registrar->disable_auto_conf) {
                u8 r[16];
                /* Generate a random passphrase */
-               if (random_get_bytes(r, sizeof(r)) < 0)
+               if (random_pool_ready() != 1 ||
+                   random_get_bytes(r, sizeof(r)) < 0) {
+                       wpa_printf(MSG_INFO,
+                                  "WPS: Could not generate random PSK");
                        return -1;
+               }
                os_free(wps->new_psk);
                wps->new_psk = base64_encode(r, sizeof(r), &wps->new_psk_len);
                if (wps->new_psk == NULL)
@@ -1638,13 +1671,15 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
                                      wps->new_psk, wps->new_psk_len);
                os_memcpy(wps->cred.key, wps->new_psk, wps->new_psk_len);
                wps->cred.key_len = wps->new_psk_len;
-       } else if (wps->use_psk_key && wps->wps->psk_set) {
+       } else if (!wps->wps->registrar->force_per_enrollee_psk &&
+                  wps->use_psk_key && wps->wps->psk_set) {
                char hex[65];
                wpa_printf(MSG_DEBUG, "WPS: Use PSK format for Network Key");
                wpa_snprintf_hex(hex, sizeof(hex), wps->wps->psk, 32);
                os_memcpy(wps->cred.key, hex, 32 * 2);
                wps->cred.key_len = 32 * 2;
-       } else if (wps->wps->network_key) {
+       } else if (!wps->wps->registrar->force_per_enrollee_psk &&
+                  wps->wps->network_key) {
                os_memcpy(wps->cred.key, wps->wps->network_key,
                          wps->wps->network_key_len);
                wps->cred.key_len = wps->wps->network_key_len;
@@ -1656,7 +1691,10 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
                wps->new_psk = os_malloc(wps->new_psk_len);
                if (wps->new_psk == NULL)
                        return -1;
-               if (random_get_bytes(wps->new_psk, wps->new_psk_len) < 0) {
+               if (random_pool_ready() != 1 ||
+                   random_get_bytes(wps->new_psk, wps->new_psk_len) < 0) {
+                       wpa_printf(MSG_INFO,
+                                  "WPS: Could not generate random PSK");
                        os_free(wps->new_psk);
                        wps->new_psk = NULL;
                        return -1;
@@ -1764,6 +1802,7 @@ static struct wpabuf * wps_build_ap_cred(struct wps_data *wps)
 static struct wpabuf * wps_build_m2(struct wps_data *wps)
 {
        struct wpabuf *msg;
+       int config_in_m2 = 0;
 
        if (random_get_bytes(wps->nonce_r, WPS_NONCE_LEN) < 0)
                return NULL;
@@ -1788,19 +1827,47 @@ static struct wpabuf * wps_build_m2(struct wps_data *wps)
            wps_build_conn_type_flags(wps, msg) ||
            wps_build_config_methods_r(wps->wps->registrar, msg) ||
            wps_build_device_attrs(&wps->wps->dev, msg) ||
-           wps_build_rf_bands(&wps->wps->dev, msg) ||
+           wps_build_rf_bands(&wps->wps->dev, msg,
+                              wps->wps->rf_band_cb(wps->wps->cb_ctx)) ||
            wps_build_assoc_state(wps, msg) ||
            wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
            wps_build_dev_password_id(msg, wps->dev_pw_id) ||
            wps_build_os_version(&wps->wps->dev, msg) ||
-           wps_build_wfa_ext(msg, 0, NULL, 0) ||
-           wps_build_authenticator(wps, msg)) {
+           wps_build_wfa_ext(msg, 0, NULL, 0)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
+
+#ifdef CONFIG_WPS_NFC
+       if (wps->nfc_pw_token && wps->nfc_pw_token->pk_hash_provided_oob &&
+           wps->nfc_pw_token->pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
+               /*
+                * Use abbreviated handshake since public key hash allowed
+                * Enrollee to validate our public key similarly to how Enrollee
+                * public key was validated. There is no need to validate Device
+                * Password in this case.
+                */
+               struct wpabuf *plain = wpabuf_alloc(500);
+               if (plain == NULL ||
+                   wps_build_cred(wps, plain) ||
+                   wps_build_key_wrap_auth(wps, plain) ||
+                   wps_build_encr_settings(wps, msg, plain)) {
+                       wpabuf_free(msg);
+                       wpabuf_free(plain);
+                       return NULL;
+               }
+               wpabuf_free(plain);
+               config_in_m2 = 1;
+       }
+#endif /* CONFIG_WPS_NFC */
+
+       if (wps_build_authenticator(wps, msg)) {
                wpabuf_free(msg);
                return NULL;
        }
 
        wps->int_reg = 1;
-       wps->state = RECV_M3;
+       wps->state = config_in_m2 ? RECV_DONE : RECV_M3;
        return msg;
 }
 
@@ -1829,7 +1896,8 @@ static struct wpabuf * wps_build_m2d(struct wps_data *wps)
            wps_build_conn_type_flags(wps, msg) ||
            wps_build_config_methods_r(wps->wps->registrar, msg) ||
            wps_build_device_attrs(&wps->wps->dev, msg) ||
-           wps_build_rf_bands(&wps->wps->dev, msg) ||
+           wps_build_rf_bands(&wps->wps->dev, msg,
+                              wps->wps->rf_band_cb(wps->wps->cb_ctx)) ||
            wps_build_assoc_state(wps, msg) ||
            wps_build_config_error(msg, err) ||
            wps_build_os_version(&wps->wps->dev, msg) ||
@@ -2163,11 +2231,11 @@ static int wps_process_e_snonce1(struct wps_data *wps, const u8 *e_snonce1)
        len[3] = wpabuf_len(wps->dh_pubkey_r);
        hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
 
-       if (os_memcmp(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
+       if (os_memcmp_const(wps->peer_hash1, hash, WPS_HASH_LEN) != 0) {
                wpa_printf(MSG_DEBUG, "WPS: E-Hash1 derived from E-S1 does "
                           "not match with the pre-committed value");
                wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
-               wps_pwd_auth_fail_event(wps->wps, 0, 1);
+               wps_pwd_auth_fail_event(wps->wps, 0, 1, wps->mac_addr_e);
                return -1;
        }
 
@@ -2203,12 +2271,12 @@ static int wps_process_e_snonce2(struct wps_data *wps, const u8 *e_snonce2)
        len[3] = wpabuf_len(wps->dh_pubkey_r);
        hmac_sha256_vector(wps->authkey, WPS_AUTHKEY_LEN, 4, addr, len, hash);
 
-       if (os_memcmp(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
+       if (os_memcmp_const(wps->peer_hash2, hash, WPS_HASH_LEN) != 0) {
                wpa_printf(MSG_DEBUG, "WPS: E-Hash2 derived from E-S2 does "
                           "not match with the pre-committed value");
                wps_registrar_invalidate_pin(wps->wps->registrar, wps->uuid_e);
                wps->config_error = WPS_CFG_DEV_PASSWORD_AUTH_FAILURE;
-               wps_pwd_auth_fail_event(wps->wps, 0, 2);
+               wps_pwd_auth_fail_event(wps->wps, 0, 2, wps->mac_addr_e);
                return -1;
        }
 
@@ -2510,9 +2578,13 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps,
 
        if (wps->dev_pw_id < 0x10 &&
            wps->dev_pw_id != DEV_PW_DEFAULT &&
+           wps->dev_pw_id != DEV_PW_P2PS_DEFAULT &&
            wps->dev_pw_id != DEV_PW_USER_SPECIFIED &&
            wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED &&
            wps->dev_pw_id != DEV_PW_REGISTRAR_SPECIFIED &&
+#ifdef CONFIG_WPS_NFC
+           wps->dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER &&
+#endif /* CONFIG_WPS_NFC */
            (wps->dev_pw_id != DEV_PW_PUSHBUTTON ||
             !wps->wps->registrar->pbc)) {
                wpa_printf(MSG_DEBUG, "WPS: Unsupported Device Password ID %d",
@@ -2522,14 +2594,17 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps,
        }
 
 #ifdef CONFIG_WPS_NFC
-       if (wps->dev_pw_id >= 0x10) {
+       if (wps->dev_pw_id >= 0x10 ||
+           wps->dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
                struct wps_nfc_pw_token *token;
                const u8 *addr[1];
                u8 hash[WPS_HASH_LEN];
 
+               wpa_printf(MSG_DEBUG, "WPS: Searching for NFC token match for id=%d (ctx %p registrar %p)",
+                          wps->dev_pw_id, wps->wps, wps->wps->registrar);
                token = wps_get_nfc_pw_token(
                        &wps->wps->registrar->nfc_pw_tokens, wps->dev_pw_id);
-               if (token) {
+               if (token && token->peer_pk_hash_known) {
                        wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
                                   "Password Token");
                        dl_list_del(&token->list);
@@ -2537,12 +2612,24 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps,
 
                        addr[0] = attr->public_key;
                        sha256_vector(1, addr, &attr->public_key_len, hash);
-                       if (os_memcmp(hash, wps->nfc_pw_token->pubkey_hash,
-                                     WPS_OOB_PUBKEY_HASH_LEN) != 0) {
+                       if (os_memcmp_const(hash,
+                                           wps->nfc_pw_token->pubkey_hash,
+                                           WPS_OOB_PUBKEY_HASH_LEN) != 0) {
                                wpa_printf(MSG_ERROR, "WPS: Public Key hash "
                                           "mismatch");
-                               return WPS_FAILURE;
+                               wps->state = SEND_M2D;
+                               wps->config_error =
+                                       WPS_CFG_PUBLIC_KEY_HASH_MISMATCH;
+                               return WPS_CONTINUE;
                        }
+               } else if (token) {
+                       wpa_printf(MSG_DEBUG, "WPS: Found matching NFC "
+                                  "Password Token (no peer PK hash)");
+                       wps->nfc_pw_token = token;
+               } else if (wps->dev_pw_id >= 0x10 &&
+                          wps->wps->ap_nfc_dev_pw_id == wps->dev_pw_id &&
+                          wps->wps->ap_nfc_dev_pw) {
+                       wpa_printf(MSG_DEBUG, "WPS: Found match with own NFC Password Token");
                }
        }
 #endif /* CONFIG_WPS_NFC */
@@ -2560,7 +2647,7 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps,
                        wps_pbc_overlap_event(wps->wps);
                        wps_fail_event(wps->wps, WPS_M1,
                                       WPS_CFG_MULTIPLE_PBC_DETECTED,
-                                      WPS_EI_NO_ERROR);
+                                      WPS_EI_NO_ERROR, wps->mac_addr_e);
                        wps->wps->registrar->force_pbc_overlap = 1;
                        return WPS_CONTINUE;
                }
@@ -2890,7 +2977,7 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
                ret = wps_process_m3(wps, msg, &attr);
                if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
                        wps_fail_event(wps->wps, WPS_M3, wps->config_error,
-                                      wps->error_indication);
+                                      wps->error_indication, wps->mac_addr_e);
                break;
        case WPS_M5:
                if (wps_validate_m5(msg) < 0)
@@ -2898,7 +2985,7 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
                ret = wps_process_m5(wps, msg, &attr);
                if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
                        wps_fail_event(wps->wps, WPS_M5, wps->config_error,
-                                      wps->error_indication);
+                                      wps->error_indication, wps->mac_addr_e);
                break;
        case WPS_M7:
                if (wps_validate_m7(msg) < 0)
@@ -2906,7 +2993,7 @@ static enum wps_process_res wps_process_wsc_msg(struct wps_data *wps,
                ret = wps_process_m7(wps, msg, &attr);
                if (ret == WPS_FAILURE || wps->state == SEND_WSC_NACK)
                        wps_fail_event(wps->wps, WPS_M7, wps->config_error,
-                                      wps->error_indication);
+                                      wps->error_indication, wps->mac_addr_e);
                break;
        default:
                wpa_printf(MSG_DEBUG, "WPS: Unsupported Message Type %d",
@@ -3052,19 +3139,19 @@ static enum wps_process_res wps_process_wsc_nack(struct wps_data *wps,
        switch (old_state) {
        case RECV_M3:
                wps_fail_event(wps->wps, WPS_M2, config_error,
-                              wps->error_indication);
+                              wps->error_indication, wps->mac_addr_e);
                break;
        case RECV_M5:
                wps_fail_event(wps->wps, WPS_M4, config_error,
-                              wps->error_indication);
+                              wps->error_indication, wps->mac_addr_e);
                break;
        case RECV_M7:
                wps_fail_event(wps->wps, WPS_M6, config_error,
-                              wps->error_indication);
+                              wps->error_indication, wps->mac_addr_e);
                break;
        case RECV_DONE:
                wps_fail_event(wps->wps, WPS_M8, config_error,
-                              wps->error_indication);
+                              wps->error_indication, wps->mac_addr_e);
                break;
        default:
                break;
@@ -3160,7 +3247,8 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps,
 
        if (wps->new_psk) {
                if (wps_cb_new_psk(wps->wps->registrar, wps->mac_addr_e,
-                                  wps->new_psk, wps->new_psk_len)) {
+                                  wps->p2p_dev_addr, wps->new_psk,
+                                  wps->new_psk_len)) {
                        wpa_printf(MSG_DEBUG, "WPS: Failed to configure the "
                                   "new PSK");
                }
@@ -3176,7 +3264,9 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps,
                                                 wps->uuid_e,
                                                 wps->p2p_dev_addr);
                wps_registrar_pbc_completed(wps->wps->registrar);
-               os_get_time(&wps->wps->registrar->pbc_ignore_start);
+#ifdef WPS_WORKAROUNDS
+               os_get_reltime(&wps->wps->registrar->pbc_ignore_start);
+#endif /* WPS_WORKAROUNDS */
                os_memcpy(wps->wps->registrar->pbc_ignore_uuid, wps->uuid_e,
                          WPS_UUID_LEN);
        } else {
@@ -3185,7 +3275,7 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps,
        /* TODO: maintain AuthorizedMACs somewhere separately for each ER and
         * merge them into APs own list.. */
 
-       wps_success_event(wps->wps);
+       wps_success_event(wps->wps, wps->mac_addr_e);
 
        return WPS_DONE;
 }
@@ -3254,7 +3344,7 @@ enum wps_process_res wps_registrar_process_msg(struct wps_data *wps,
                        wps->state = SEND_WSC_NACK;
                        wps_fail_event(wps->wps, WPS_WSC_DONE,
                                       wps->config_error,
-                                      wps->error_indication);
+                                      wps->error_indication, wps->mac_addr_e);
                }
                return ret;
        default:
@@ -3368,10 +3458,8 @@ void wps_registrar_selected_registrar_changed(struct wps_registrar *reg,
                u16 methods;
 
                methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON;
-#ifdef CONFIG_WPS2
                methods &= ~(WPS_CONFIG_VIRT_PUSHBUTTON |
                             WPS_CONFIG_PHY_PUSHBUTTON);
-#endif /* CONFIG_WPS2 */
                if (reg->pbc) {
                        reg->sel_reg_dev_password_id_override =
                                DEV_PW_PUSHBUTTON;
@@ -3421,7 +3509,7 @@ int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr,
                          d->dev.model_name ? d->dev.model_name : "",
                          d->dev.model_number ? d->dev.model_number : "",
                          d->dev.serial_number ? d->dev.serial_number : "");
-       if (ret < 0 || (size_t) ret >= buflen - len)
+       if (os_snprintf_error(buflen - len, ret))
                return len;
        len += ret;
 
@@ -3432,8 +3520,7 @@ int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr,
 int wps_registrar_config_ap(struct wps_registrar *reg,
                            struct wps_credential *cred)
 {
-#ifdef CONFIG_WPS2
-       printf("encr_type=0x%x\n", cred->encr_type);
+       wpa_printf(MSG_DEBUG, "WPS: encr_type=0x%x", cred->encr_type);
        if (!(cred->encr_type & (WPS_ENCR_NONE | WPS_ENCR_TKIP |
                                 WPS_ENCR_AES))) {
                if (cred->encr_type & WPS_ENCR_WEP) {
@@ -3460,7 +3547,6 @@ int wps_registrar_config_ap(struct wps_registrar *reg,
                           "WPAPSK+WPA2PSK");
                cred->auth_type |= WPS_AUTH_WPA2PSK;
        }
-#endif /* CONFIG_WPS2 */
 
        if (reg->wps->cred_cb)
                return reg->wps->cred_cb(reg->wps->cb_ctx, cred);
@@ -3473,25 +3559,39 @@ int wps_registrar_config_ap(struct wps_registrar *reg,
 
 int wps_registrar_add_nfc_pw_token(struct wps_registrar *reg,
                                   const u8 *pubkey_hash, u16 pw_id,
-                                  const u8 *dev_pw, size_t dev_pw_len)
+                                  const u8 *dev_pw, size_t dev_pw_len,
+                                  int pk_hash_provided_oob)
 {
        struct wps_nfc_pw_token *token;
 
        if (dev_pw_len > WPS_OOB_DEVICE_PASSWORD_LEN)
                return -1;
 
+       if (pw_id == DEV_PW_NFC_CONNECTION_HANDOVER &&
+           (pubkey_hash == NULL || !pk_hash_provided_oob)) {
+               wpa_printf(MSG_DEBUG, "WPS: Unexpected NFC Password Token "
+                          "addition - missing public key hash");
+               return -1;
+       }
+
        wps_free_nfc_pw_tokens(&reg->nfc_pw_tokens, pw_id);
 
        token = os_zalloc(sizeof(*token));
        if (token == NULL)
                return -1;
 
-       os_memcpy(token->pubkey_hash, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN);
+       token->peer_pk_hash_known = pubkey_hash != NULL;
+       if (pubkey_hash)
+               os_memcpy(token->pubkey_hash, pubkey_hash,
+                         WPS_OOB_PUBKEY_HASH_LEN);
        token->pw_id = pw_id;
-       wpa_snprintf_hex_uppercase((char *) token->dev_pw,
-                                  sizeof(token->dev_pw),
-                                  dev_pw, dev_pw_len);
-       token->dev_pw_len = dev_pw_len * 2;
+       token->pk_hash_provided_oob = pk_hash_provided_oob;
+       if (dev_pw) {
+               wpa_snprintf_hex_uppercase((char *) token->dev_pw,
+                                          sizeof(token->dev_pw),
+                                          dev_pw, dev_pw_len);
+               token->dev_pw_len = dev_pw_len * 2;
+       }
 
        dl_list_add(&reg->nfc_pw_tokens, &token->list);
 
@@ -3520,8 +3620,7 @@ int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
        u16 id;
        size_t dev_pw_len;
 
-       if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 +
-           WPS_OOB_DEVICE_PASSWORD_MIN_LEN ||
+       if (oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2 ||
            oob_dev_pw_len > WPS_OOB_PUBKEY_HASH_LEN + 2 +
            WPS_OOB_DEVICE_PASSWORD_LEN)
                return -1;
@@ -3540,7 +3639,7 @@ int wps_registrar_add_nfc_password_token(struct wps_registrar *reg,
        wpa_hexdump_key(MSG_DEBUG, "WPS: Device Password", dev_pw, dev_pw_len);
 
        return wps_registrar_add_nfc_pw_token(reg, hash, id, dev_pw,
-                                             dev_pw_len);
+                                             dev_pw_len, 0);
 }
 
 
@@ -3550,6 +3649,14 @@ void wps_registrar_remove_nfc_pw_token(struct wps_registrar *reg,
        wps_registrar_remove_authorized_mac(reg,
                                            (u8 *) "\xff\xff\xff\xff\xff\xff");
        wps_registrar_selected_registrar_changed(reg, 0);
+
+       /*
+        * Free the NFC password token if it was used only for a single protocol
+        * run. The static handover case uses the same password token multiple
+        * times, so do not free that case here.
+        */
+       if (token->peer_pk_hash_known)
+               os_free(token);
 }
 
 #endif /* CONFIG_WPS_NFC */
index af63e4d..933d734 100644 (file)
@@ -227,6 +227,8 @@ void format_date(struct wpabuf *buf)
 
        t = time(NULL);
        date = gmtime(&t);
+       if (date == NULL)
+               return;
        wpabuf_printf(buf, "%s, %02d %s %d %02d:%02d:%02d GMT",
                      &weekday_str[date->tm_wday * 4], date->tm_mday,
                      &month_str[date->tm_mon * 4], date->tm_year + 1900,
@@ -249,13 +251,16 @@ void format_date(struct wpabuf *buf)
  * use for constructing UUIDs for subscriptions. Presumably any method from
  * rfc4122 is good enough; I've chosen random number method.
  */
-static void uuid_make(u8 uuid[UUID_LEN])
+static int uuid_make(u8 uuid[UUID_LEN])
 {
-       os_get_random(uuid, UUID_LEN);
+       if (os_get_random(uuid, UUID_LEN) < 0)
+               return -1;
 
        /* Replace certain bits as specified in rfc4122 or X.667 */
        uuid[6] &= 0x0f; uuid[6] |= (4 << 4);   /* version 4 == random gen */
        uuid[8] &= 0x3f; uuid[8] |= 0x80;
+
+       return 0;
 }
 
 
@@ -432,23 +437,6 @@ static void subscr_addr_list_create(struct subscription *s,
 }
 
 
-int send_wpabuf(int fd, struct wpabuf *buf)
-{
-       wpa_printf(MSG_DEBUG, "WPS UPnP: Send %lu byte message",
-                  (unsigned long) wpabuf_len(buf));
-       errno = 0;
-       if (write(fd, wpabuf_head(buf), wpabuf_len(buf)) !=
-           (int) wpabuf_len(buf)) {
-               wpa_printf(MSG_ERROR, "WPS UPnP: Failed to send buffer: "
-                          "errno=%d (%s)",
-                          errno, strerror(errno));
-               return -1;
-       }
-
-       return 0;
-}
-
-
 static void wpabuf_put_property(struct wpabuf *buf, const char *name,
                                const char *value)
 {
@@ -480,14 +468,14 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm)
                "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
                "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n";
        const char *format_tail = "</e:propertyset>\n";
-       struct os_time now;
+       struct os_reltime now;
 
        if (dl_list_empty(&sm->subscriptions)) {
                /* optimize */
                return;
        }
 
-       if (os_get_time(&now) == 0) {
+       if (os_get_reltime(&now) == 0) {
                if (now.sec != sm->last_event_sec) {
                        sm->last_event_sec = now.sec;
                        sm->num_events_in_sec = 1;
@@ -611,7 +599,10 @@ static struct wpabuf * build_fake_wsc_ack(void)
        wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
        wpabuf_put_be16(msg, WPS_NONCE_LEN);
        wpabuf_put(msg, WPS_NONCE_LEN);
-       wps_build_wfa_ext(msg, 0, NULL, 0);
+       if (wps_build_wfa_ext(msg, 0, NULL, 0)) {
+               wpabuf_free(msg);
+               return NULL;
+       }
        return msg;
 }
 
@@ -712,10 +703,12 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm,
        if (dl_list_len(&sm->subscriptions) >= MAX_SUBSCRIPTIONS) {
                s = dl_list_first(&sm->subscriptions, struct subscription,
                                  list);
-               wpa_printf(MSG_INFO, "WPS UPnP: Too many subscriptions, "
-                          "trashing oldest");
-               dl_list_del(&s->list);
-               subscription_destroy(s);
+               if (s) {
+                       wpa_printf(MSG_INFO,
+                                  "WPS UPnP: Too many subscriptions, trashing oldest");
+                       dl_list_del(&s->list);
+                       subscription_destroy(s);
+               }
        }
 
        s = os_zalloc(sizeof(*s));
@@ -726,7 +719,10 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm,
 
        s->sm = sm;
        s->timeout_time = expire;
-       uuid_make(s->uuid);
+       if (uuid_make(s->uuid) < 0) {
+               subscription_destroy(s);
+               return NULL;
+       }
        subscr_addr_list_create(s, callback_urls);
        if (dl_list_empty(&s->addr_list)) {
                wpa_printf(MSG_DEBUG, "WPS UPnP: No valid callback URLs in "
@@ -982,6 +978,7 @@ static void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
 
        wpa_printf(MSG_DEBUG, "WPS UPnP: Stop device");
        web_listener_stop(sm);
+       ssdp_listener_stop(sm);
        upnp_wps_free_msearchreply(&sm->msearch_replies);
        upnp_wps_free_subscriptions(&sm->subscriptions, NULL);
 
@@ -995,7 +992,6 @@ static void upnp_wps_device_stop(struct upnp_wps_device_sm *sm)
        if (sm->multicast_sd >= 0)
                close(sm->multicast_sd);
        sm->multicast_sd = -1;
-       ssdp_listener_stop(sm);
 
        sm->started = 0;
 }
index 4f1dd8f..2949f14 100644 (file)
@@ -61,11 +61,9 @@ int upnp_er_set_selected_registrar(struct wps_registrar *reg,
                        os_memcpy(s->authorized_macs, attr.authorized_macs,
                                  count * ETH_ALEN);
                } else if (!attr.version2) {
-#ifdef CONFIG_WPS2
                        wpa_printf(MSG_DEBUG, "WPS: Add broadcast "
                                   "AuthorizedMACs for WPS 1.0 ER");
                        os_memset(s->authorized_macs, 0xff, ETH_ALEN);
-#endif /* CONFIG_WPS2 */
                }
                eloop_register_timeout(WPS_PBC_WALK_TIME, 0,
                                       upnp_er_set_selected_timeout, s, reg);
index 5c39f7e..f289fe6 100644 (file)
@@ -158,7 +158,6 @@ void subscription_destroy(struct subscription *s);
 struct subscription * subscription_find(struct upnp_wps_device_sm *sm,
                                        const u8 uuid[UUID_LEN]);
 void subscr_addr_delete(struct subscr_addr *a);
-int send_wpabuf(int fd, struct wpabuf *buf);
 int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text,
                   u8 mac[ETH_ALEN]);
 
index 416961c..26a740d 100644 (file)
@@ -134,6 +134,8 @@ next_advertisement(struct upnp_wps_device_sm *sm,
        *islast = 0;
        iface = dl_list_first(&sm->interfaces,
                              struct upnp_wps_device_interface, list);
+       if (!iface)
+               return NULL;
        uuid_bin2str(iface->wps->uuid, uuid_string, sizeof(uuid_string));
        msg = wpabuf_alloc(800); /* more than big enough */
        if (msg == NULL)
@@ -315,7 +317,8 @@ static void advertisement_state_machine_handler(void *eloop_data,
                         * (see notes above)
                         */
                        next_timeout_msec = 0;
-                       os_get_random((void *) &r, sizeof(r));
+                       if (os_get_random((void *) &r, sizeof(r)) < 0)
+                               r = 32768;
                        next_timeout_sec = UPNP_CACHE_SEC / 4 +
                                (((UPNP_CACHE_SEC / 4) * r) >> 16);
                        sm->advertise_count++;
@@ -587,6 +590,8 @@ static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm,
                                        &sm->interfaces,
                                        struct upnp_wps_device_interface,
                                        list);
+                               if (!iface)
+                                       continue;
                                data += os_strlen("uuid:");
                                uuid_bin2str(iface->wps->uuid, uuid_string,
                                             sizeof(uuid_string));
index 11386d8..b1cf571 100644 (file)
@@ -179,15 +179,12 @@ static const char *wps_device_xml_postfix =
 /* format_wps_device_xml -- produce content of "file" wps_device.xml
  * (UPNP_WPS_DEVICE_XML_FILE)
  */
-static void format_wps_device_xml(struct upnp_wps_device_sm *sm,
+static void format_wps_device_xml(struct upnp_wps_device_interface *iface,
+                                 struct upnp_wps_device_sm *sm,
                                  struct wpabuf *buf)
 {
        const char *s;
        char uuid_string[80];
-       struct upnp_wps_device_interface *iface;
-
-       iface = dl_list_first(&sm->interfaces,
-                             struct upnp_wps_device_interface, list);
 
        wpabuf_put_str(buf, wps_device_xml_prefix);
 
@@ -319,13 +316,15 @@ static void web_connection_parse_get(struct upnp_wps_device_sm *sm,
 
        iface = dl_list_first(&sm->interfaces,
                              struct upnp_wps_device_interface, list);
+       if (iface == NULL) {
+               http_request_deinit(hreq);
+               return;
+       }
 
        /*
         * It is not required that filenames be case insensitive but it is
         * allowed and cannot hurt here.
         */
-       if (filename == NULL)
-               filename = "(null)"; /* just in case */
        if (os_strcasecmp(filename, UPNP_WPS_DEVICE_XML_FILE) == 0) {
                wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET for device XML");
                req = GET_DEVICE_XML_FILE;
@@ -393,7 +392,7 @@ static void web_connection_parse_get(struct upnp_wps_device_sm *sm,
 
        switch (req) {
        case GET_DEVICE_XML_FILE:
-               format_wps_device_xml(sm, buf);
+               format_wps_device_xml(iface, sm, buf);
                break;
        case GET_SCPD_XML_FILE:
                wpabuf_put_str(buf, wps_scpd_xml);
@@ -421,13 +420,14 @@ web_process_get_device_info(struct upnp_wps_device_sm *sm,
 
        iface = dl_list_first(&sm->interfaces,
                              struct upnp_wps_device_interface, list);
-       peer = &iface->peer;
 
        wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo");
 
-       if (iface->ctx->ap_pin == NULL)
+       if (!iface || iface->ctx->ap_pin == NULL)
                return HTTP_INTERNAL_SERVER_ERROR;
 
+       peer = &iface->peer;
+
        /*
         * Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS
         * registration over UPnP with the AP acting as an Enrollee. It should
@@ -475,6 +475,8 @@ web_process_put_message(struct upnp_wps_device_sm *sm, char *data,
 
        iface = dl_list_first(&sm->interfaces,
                              struct upnp_wps_device_interface, list);
+       if (!iface)
+               return HTTP_INTERNAL_SERVER_ERROR;
 
        /*
         * PutMessage is used by external UPnP-based Registrar to perform WPS
@@ -948,7 +950,7 @@ static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm,
        wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP SUBSCRIBE for event");
        end = os_strchr(h, '\n');
 
-       for (; end != NULL; h = end + 1) {
+       while (end) {
                /* Option line by option line */
                h = end + 1;
                end = os_strchr(h, '\n');
@@ -1155,7 +1157,7 @@ static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm,
        wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP UNSUBSCRIBE for event");
        end = os_strchr(h, '\n');
 
-       for (; end != NULL; h = end + 1) {
+       while (end) {
                /* Option line by option line */
                h = end + 1;
                end = os_strchr(h, '\n');
@@ -1173,7 +1175,6 @@ static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm,
                        .....
                }
 #endif
-               /* SID is only for renewal */
                match = "SID:";
                match_len = os_strlen(match);
                if (os_strncasecmp(h, match, match_len) == 0) {
@@ -1196,6 +1197,20 @@ static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm,
                        got_uuid = 1;
                        continue;
                }
+
+               match = "NT:";
+               match_len = os_strlen(match);
+               if (os_strncasecmp(h, match, match_len) == 0) {
+                       ret = HTTP_BAD_REQUEST;
+                       goto send_msg;
+               }
+
+               match = "CALLBACK:";
+               match_len = os_strlen(match);
+               if (os_strncasecmp(h, match, match_len) == 0) {
+                       ret = HTTP_BAD_REQUEST;
+                       goto send_msg;
+               }
        }
 
        if (got_uuid) {
@@ -1209,6 +1224,10 @@ static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm,
                                   sa->domain_and_port : "-null-");
                        dl_list_del(&s->list);
                        subscription_destroy(s);
+               } else {
+                       wpa_printf(MSG_INFO, "WPS UPnP: Could not find matching subscription to unsubscribe");
+                       ret = HTTP_PRECONDITION_FAILED;
+                       goto send_msg;
                }
        } else {
                wpa_printf(MSG_INFO, "WPS UPnP: Unsubscribe fails (not "
index e366256..1c6a14b 100644 (file)
@@ -267,7 +267,7 @@ static int wps_validate_config_error(const u8 *config_error, int mandatory)
                return 0;
        }
        val = WPA_GET_BE16(config_error);
-       if (val > 18) {
+       if (val > 20) {
                wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Configuration Error "
                           "attribute value 0x%04x", val);
                return -1;
@@ -290,7 +290,7 @@ static int wps_validate_dev_password_id(const u8 *dev_password_id,
                return 0;
        }
        val = WPA_GET_BE16(dev_password_id);
-       if (val >= 0x0006 && val <= 0x000f) {
+       if (val >= 0x0008 && val <= 0x000f) {
                wpa_printf(MSG_INFO, "WPS-STRICT: Invalid Device Password ID "
                           "attribute value 0x%04x", val);
                return -1;
index 80341a1..782396a 100644 (file)
@@ -1,6 +1,5 @@
-TESTS=test-base64 test-md4 test-md5 test-milenage test-ms_funcs \
-       test-bitfield \
-       test-printf \
+TESTS=test-base64 test-md4 test-milenage \
+       test-rsa-sig-ver \
        test-sha1 \
        test-sha256 test-aes test-asn1 test-x509 test-x509v3 test-list test-rc4
 
@@ -29,6 +28,9 @@ DLIBS = ../src/crypto/libcrypto.a \
 LIBS = $(SLIBS) $(DLIBS)
 LLIBS = -Wl,--start-group $(DLIBS) -Wl,--end-group $(SLIBS)
 
+# glibc < 2.17 needs -lrt for clock_gettime()
+LLIBS += -lrt
+
 ../src/utils/libutils.a:
        $(MAKE) -C ../src/utils
 
@@ -43,43 +45,34 @@ test-aes: test-aes.o $(LIBS)
        $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
 
 test-asn1: test-asn1.o $(LIBS)
-       $(LDO) $(LDFLAGS) -o $@ $^
+       $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
 
 test-base64: test-base64.o $(LIBS)
-       $(LDO) $(LDFLAGS) -o $@ $^
-
-test-bitfield: test-bitfield.o $(LIBS)
-       $(LDO) $(LDFLAGS) -o $@ $^
+       $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
 
 test-https: test-https.o $(LIBS)
        $(LDO) $(LDFLAGS) -o $@ $< $(LLIBS)
 
 test-list: test-list.o $(LIBS)
-       $(LDO) $(LDFLAGS) -o $@ $^
+       $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
 
 test-md4: test-md4.o $(LIBS)
-       $(LDO) $(LDFLAGS) -o $@ $^
-
-test-md5: test-md5.o $(LIBS)
-       $(LDO) $(LDFLAGS) -o $@ $^ $(LIBS)
+       $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
 
 test-milenage: test-milenage.o $(LIBS)
-       $(LDO) $(LDFLAGS) -o $@ $^
-
-test-ms_funcs: test-ms_funcs.o $(LIBS)
-       $(LDO) $(LDFLAGS) -o $@ $^
-
-test-printf: test-printf.o $(LIBS)
-       $(LDO) $(LDFLAGS) -o $@ $^
+       $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
 
 test-rc4: test-rc4.o $(LIBS)
-       $(LDO) $(LDFLAGS) -o $@ $^
+       $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
+
+test-rsa-sig-ver: test-rsa-sig-ver.o $(LIBS)
+       $(LDO) $(LDFLAGS) -o $@ $< $(LLIBS)
 
 test-sha1: test-sha1.o $(LIBS)
-       $(LDO) $(LDFLAGS) -o $@ $^ $(LIBS)
+       $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
 
 test-sha256: test-sha256.o $(LIBS)
-       $(LDO) $(LDFLAGS) -o $@ $^
+       $(LDO) $(LDFLAGS) -o $@ $^ $(LLIBS)
 
 test-x509: test-x509.o $(LIBS)
        $(LDO) $(LDFLAGS) -o $@ $< $(LLIBS)
@@ -90,12 +83,10 @@ test-x509v3: test-x509v3.o $(LIBS)
 
 run-tests: $(TESTS)
        ./test-aes
-       ./test-bitfield
        ./test-list
        ./test-md4
-       ./test-md5
        ./test-milenage
-       ./test-printf
+       ./test-rsa-sig-ver
        ./test-sha1
        ./test-sha256
        @echo
diff --git a/tests/hwsim/auth_serv/as.conf b/tests/hwsim/auth_serv/as.conf
new file mode 100644 (file)
index 0000000..0d89b92
--- /dev/null
@@ -0,0 +1,21 @@
+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
+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
diff --git a/tests/hwsim/auth_serv/as2.conf b/tests/hwsim/auth_serv/as2.conf
new file mode 100644 (file)
index 0000000..d9ee031
--- /dev/null
@@ -0,0 +1,21 @@
+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
+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
diff --git a/tests/hwsim/auth_serv/ca-incorrect.pem b/tests/hwsim/auth_serv/ca-incorrect.pem
new file mode 100644 (file)
index 0000000..2e9a492
--- /dev/null
@@ -0,0 +1,55 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 10855188644662735910 (0x96a5608f1ef9f426)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=FI, CN=TEST - Incorrect Root CA
+        Validity
+            Not Before: Oct 20 16:30:06 2013 GMT
+            Not After : Oct 18 16:30:06 2023 GMT
+        Subject: C=FI, CN=TEST - Incorrect Root CA
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:bc:0c:8e:61:1e:5b:ea:b2:6b:cc:8a:8c:38:85:
+                    6d:79:e0:7a:28:d1:b5:55:65:52:f8:e2:2c:74:c1:
+                    00:15:c6:15:84:56:08:f5:e9:eb:bc:07:8d:b7:97:
+                    b6:73:7f:46:77:86:31:d0:f0:7f:95:d6:4a:7c:35:
+                    07:85:43:41:5e:f4:07:84:e6:52:cb:52:38:ef:fe:
+                    6a:16:84:22:45:2e:c1:a1:16:8d:d2:b3:62:c2:05:
+                    77:43:04:2e:d0:52:ee:db:78:10:79:44:49:92:35:
+                    ee:99:83:aa:a0:1d:e6:3d:c3:c6:a2:8e:b6:4d:7f:
+                    d8:11:a9:a3:bc:68:1d:a2:6f
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                3E:49:CB:A7:6A:A7:08:4F:DA:99:E4:3C:64:A2:AC:96:BE:99:E4:F2
+            X509v3 Authority Key Identifier: 
+                keyid:3E:49:CB:A7:6A:A7:08:4F:DA:99:E4:3C:64:A2:AC:96:BE:99:E4:F2
+
+            X509v3 Basic Constraints: 
+                CA:TRUE
+    Signature Algorithm: sha1WithRSAEncryption
+         31:98:35:4b:d8:d2:8e:55:7a:af:06:f8:ef:6b:24:13:11:12:
+         b0:77:81:b9:ab:50:20:d6:78:99:3f:bc:3d:89:d4:b2:bd:7a:
+         54:03:fc:a7:a4:9f:2b:09:da:75:c9:8d:4c:65:90:c5:df:fc:
+         6b:48:52:f1:0a:aa:57:8a:b1:f5:fe:35:87:87:32:39:b9:ad:
+         80:f0:8e:36:72:63:d5:97:20:e5:b6:06:64:31:5a:66:66:15:
+         85:68:b7:9d:26:8b:46:7f:e8:1b:09:f5:c2:4a:35:7c:49:e2:
+         b2:dc:59:b2:91:8d:85:33:07:09:ca:78:7a:db:b3:e5:58:2c:
+         cc:6a
+-----BEGIN CERTIFICATE-----
+MIICLjCCAZegAwIBAgIJAJalYI8e+fQmMA0GCSqGSIb3DQEBBQUAMDAxCzAJBgNV
+BAYTAkZJMSEwHwYDVQQDDBhURVNUIC0gSW5jb3JyZWN0IFJvb3QgQ0EwHhcNMTMx
+MDIwMTYzMDA2WhcNMjMxMDE4MTYzMDA2WjAwMQswCQYDVQQGEwJGSTEhMB8GA1UE
+AwwYVEVTVCAtIEluY29ycmVjdCBSb290IENBMIGfMA0GCSqGSIb3DQEBAQUAA4GN
+ADCBiQKBgQC8DI5hHlvqsmvMiow4hW154Hoo0bVVZVL44ix0wQAVxhWEVgj16eu8
+B423l7Zzf0Z3hjHQ8H+V1kp8NQeFQ0Fe9AeE5lLLUjjv/moWhCJFLsGhFo3Ss2LC
+BXdDBC7QUu7beBB5REmSNe6Zg6qgHeY9w8aijrZNf9gRqaO8aB2ibwIDAQABo1Aw
+TjAdBgNVHQ4EFgQUPknLp2qnCE/ameQ8ZKKslr6Z5PIwHwYDVR0jBBgwFoAUPknL
+p2qnCE/ameQ8ZKKslr6Z5PIwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOB
+gQAxmDVL2NKOVXqvBvjvayQTERKwd4G5q1Ag1niZP7w9idSyvXpUA/ynpJ8rCdp1
+yY1MZZDF3/xrSFLxCqpXirH1/jWHhzI5ua2A8I42cmPVlyDltgZkMVpmZhWFaLed
+JotGf+gbCfXCSjV8SeKy3FmykY2FMwcJynh627PlWCzMag==
+-----END CERTIFICATE-----
diff --git a/tests/hwsim/auth_serv/ca.der b/tests/hwsim/auth_serv/ca.der
new file mode 100644 (file)
index 0000000..09d5fa0
Binary files /dev/null and b/tests/hwsim/auth_serv/ca.der differ
diff --git a/tests/hwsim/auth_serv/ca.pem b/tests/hwsim/auth_serv/ca.pem
new file mode 100644 (file)
index 0000000..b128893
--- /dev/null
@@ -0,0 +1,55 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 15624081837803162817 (0xd8d3e3a6cbe3ccc1)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=FI, O=w1.fi, CN=Root CA
+        Validity
+            Not Before: Jun 29 16:41:22 2013 GMT
+            Not After : Jun 27 16:41:22 2023 GMT
+        Subject: C=FI, O=w1.fi, CN=Root CA
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:be:1e:86:e4:79:03:c1:d1:94:d5:d4:b3:b1:28:
+                    90:76:fb:b8:a6:cd:6d:1c:d1:48:f4:08:9a:67:ff:
+                    f9:a6:54:b1:19:29:df:29:1b:cd:f1:6f:66:01:e7:
+                    db:79:ce:c0:39:2a:25:13:26:94:0c:2c:7b:5a:2c:
+                    81:0f:94:ee:51:d0:75:e6:46:db:17:46:a7:15:8b:
+                    0e:57:0f:b0:54:76:63:12:ca:86:18:bc:1a:c3:16:
+                    c0:70:09:d6:6b:43:39:b8:98:29:46:ac:cb:6a:ad:
+                    38:88:3b:07:dc:81:cd:3a:f6:1d:f6:2f:ef:1d:d7:
+                    ae:8a:b6:d1:e7:b3:15:02:b9
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Subject Key Identifier: 
+                B8:92:DE:FD:8A:18:B3:30:C3:9F:55:F3:33:5D:B4:C8:29:8A:41:14
+            X509v3 Authority Key Identifier: 
+                keyid:B8:92:DE:FD:8A:18:B3:30:C3:9F:55:F3:33:5D:B4:C8:29:8A:41:14
+
+            X509v3 Basic Constraints: 
+                CA:TRUE
+    Signature Algorithm: sha1WithRSAEncryption
+         1a:cf:77:60:44:43:c4:55:0e:99:e0:89:aa:b9:d3:7b:32:b7:
+         5c:9c:7c:ca:fe:8c:d4:94:c6:5e:f3:83:19:5f:29:59:68:a4:
+         4f:dc:04:2e:b8:71:c0:6d:3b:ae:01:e4:b9:88:99:cc:ce:82:
+         be:6a:28:c2:ac:6a:94:c6:87:90:ed:85:3c:10:71:c5:ff:3c:
+         70:64:e2:41:62:31:ea:86:7b:11:8c:93:ea:c6:f3:f3:4e:f9:
+         d4:f2:81:90:d7:f4:fa:a1:91:6e:d4:dd:15:3e:26:3b:ac:1e:
+         c3:c2:1f:ed:bb:34:bf:cb:b2:67:c6:c6:51:e8:51:22:b4:f3:
+         92:e8
+-----BEGIN CERTIFICATE-----
+MIICLDCCAZWgAwIBAgIJANjT46bL48zBMA0GCSqGSIb3DQEBBQUAMC8xCzAJBgNV
+BAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xMzA2
+MjkxNjQxMjJaFw0yMzA2MjcxNjQxMjJaMC8xCzAJBgNVBAYTAkZJMQ4wDAYDVQQK
+DAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTCBnzANBgkqhkiG9w0BAQEFAAOBjQAw
+gYkCgYEAvh6G5HkDwdGU1dSzsSiQdvu4ps1tHNFI9AiaZ//5plSxGSnfKRvN8W9m
+Aefbec7AOSolEyaUDCx7WiyBD5TuUdB15kbbF0anFYsOVw+wVHZjEsqGGLwawxbA
+cAnWa0M5uJgpRqzLaq04iDsH3IHNOvYd9i/vHdeuirbR57MVArkCAwEAAaNQME4w
+HQYDVR0OBBYEFLiS3v2KGLMww59V8zNdtMgpikEUMB8GA1UdIwQYMBaAFLiS3v2K
+GLMww59V8zNdtMgpikEUMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEA
+Gs93YERDxFUOmeCJqrnTezK3XJx8yv6M1JTGXvODGV8pWWikT9wELrhxwG07rgHk
+uYiZzM6CvmoowqxqlMaHkO2FPBBxxf88cGTiQWIx6oZ7EYyT6sbz80751PKBkNf0
++qGRbtTdFT4mO6wew8If7bs0v8uyZ8bGUehRIrTzkug=
+-----END CERTIFICATE-----
diff --git a/tests/hwsim/auth_serv/dh.conf b/tests/hwsim/auth_serv/dh.conf
new file mode 100644 (file)
index 0000000..7bc8325
--- /dev/null
@@ -0,0 +1,5 @@
+-----BEGIN DH PARAMETERS-----
+MIGHAoGBAP3V8IHq3H2DUlYywsvjYNuS17eCdt0mJo6/os6PHqdhgkMrPxF9u4Gr
+qKXq9e6GqmZYdjta30N3FkXaV924BJ0xOqb2TntiKg4u50/l6hSUneWt6UFBaizd
+XrqjNFIme/5RXMZ7RglXliBpCepAaFLMcKhOS4ulUyYYHSy+oqRjAgEC
+-----END DH PARAMETERS-----
diff --git a/tests/hwsim/auth_serv/eap_user.conf b/tests/hwsim/auth_serv/eap_user.conf
new file mode 100644 (file)
index 0000000..873cf48
--- /dev/null
@@ -0,0 +1,97 @@
+"pwd user"     PWD     "secret password"
+"pwd.user@test123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890.example.com"  PWD     "secret password"
+"gpsk user"    GPSK    "abcdefghijklmnop0123456789abcdef"
+"sake user"    SAKE    0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
+"eke user"     EKE     "hello"
+"ikev2 user"   IKEV2   "ike password"
+"pax.user@example.com" PAX     0123456789abcdef0123456789abcdef
+"psk.user@example.com" PSK     0123456789abcdef0123456789abcdef
+"vendor-test"  VENDOR-TEST     "foo"
+"osen@example.com"     WFA-UNAUTH-TLS
+"unauth-tls"   UNAUTH-TLS
+
+"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-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-ikev2@example.com"        IKEV2   "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
+
+"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-user-session-timeout"    GPSK    "abcdefghijklmnop0123456789abcdef"
+radius_accept_attr=27:d:3
+
+"020000000000" MACACL  "020000000000"
+
+"0232010000000000@ttls"        TTLS,AKA
+"0232010000000000@peap"        PEAP,AKA
+"0232010000000000@fast"        FAST,AKA
+"6555444333222111@both" AKA',AKA
+
+"0"*           AKA
+"1"*           SIM
+"2"*           AKA
+"3"*           SIM
+"4"*           AKA
+"5"*           SIM
+"6"*           AKA'
+"7"*           AKA'
+"8"*           AKA'
+*              TTLS,TLS,PEAP,FAST,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]
+"hs20-test"    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]
+"cert user"    TLS     [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
diff --git a/tests/hwsim/auth_serv/ec-ca-openssl.cnf b/tests/hwsim/auth_serv/ec-ca-openssl.cnf
new file mode 100644 (file)
index 0000000..c803dd3
--- /dev/null
@@ -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   = 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 ]
+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/tests/hwsim/auth_serv/ec-ca.pem b/tests/hwsim/auth_serv/ec-ca.pem
new file mode 100644 (file)
index 0000000..a04b886
--- /dev/null
@@ -0,0 +1,13 @@
+-----BEGIN CERTIFICATE-----
+MIICAjCCAaegAwIBAgIJANry4MnEh6ybMAoGCCqGSM49BAMCMFIxCzAJBgNVBAYT
+AkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIDAeBgNVBAMM
+F1N1aXRlIEIgMTI4LWJpdCBSb290IENBMB4XDTE1MDEyNTExMjk1M1oXDTI1MDEy
+MjExMjk1M1owUjELMAkGA1UEBhMCRkkxETAPBgNVBAcMCEhlbHNpbmtpMQ4wDAYD
+VQQKDAV3MS5maTEgMB4GA1UEAwwXU3VpdGUgQiAxMjgtYml0IFJvb3QgQ0EwWTAT
+BgcqhkjOPQIBBggqhkjOPQMBBwNCAASqUNEASvF83W/PA2xqq/2fhIgZeLdSnnLc
+0yLcjku5WvpLHGy/pLhRsvghtjWjTsgqBqfeW8tq0ywsUdY0ylsNo2YwZDAdBgNV
+HQ4EFgQU/IP6SzTrGV4cfeWF7Mf8IfXodWgwHwYDVR0jBBgwFoAU/IP6SzTrGV4c
+feWF7Mf8IfXodWgwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAQYw
+CgYIKoZIzj0EAwIDSQAwRgIhAIfEWvUO4+28moKfVL8RXbKKexTZk82UCRL2yi01
+c81AAiEAxBGPZU0vnwxjAaCOhRIH+5X9PDkdLSs25S4ua6BicT8=
+-----END CERTIFICATE-----
diff --git a/tests/hwsim/auth_serv/ec-generate.sh b/tests/hwsim/auth_serv/ec-generate.sh
new file mode 100644 (file)
index 0000000..c9fdabc
--- /dev/null
@@ -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/tests/hwsim/auth_serv/ec-server.key b/tests/hwsim/auth_serv/ec-server.key
new file mode 100644 (file)
index 0000000..391e9ed
--- /dev/null
@@ -0,0 +1,8 @@
+-----BEGIN EC PARAMETERS-----
+BggqhkjOPQMBBw==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIN/qNiKLsQDpQWumSiRRF6LM7TP7GTwdS8vG7xP8vKz/oAoGCCqGSM49
+AwEHoUQDQgAEvl8WCLIK1vIZbxQZ7yDyKzzgvoxlhl+VwbuQNuzcWTq6QJqdEXbH
+gFohTPzAXxlSyHi45Uz6yWrR/uq2OldcmQ==
+-----END EC PRIVATE KEY-----
diff --git a/tests/hwsim/auth_serv/ec-server.pem b/tests/hwsim/auth_serv/ec-server.pem
new file mode 100644 (file)
index 0000000..4222b1e
--- /dev/null
@@ -0,0 +1,53 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 9573410140069116734 (0x84db95ccdff13b3e)
+    Signature Algorithm: ecdsa-with-SHA256
+        Issuer: C=FI, L=Helsinki, O=w1.fi, CN=Suite B 128-bit Root CA
+        Validity
+            Not Before: Jan 25 11:29:53 2015 GMT
+            Not After : Jan 25 11:29:53 2016 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:be:5f:16:08:b2:0a:d6:f2:19:6f:14:19:ef:20:
+                    f2:2b:3c:e0:be:8c:65:86:5f:95:c1:bb:90:36:ec:
+                    dc:59:3a:ba:40:9a:9d:11:76:c7:80:5a:21:4c:fc:
+                    c0:5f:19:52:c8:78:b8:e5:4c:fa:c9:6a:d1:fe:ea:
+                    b6:3a:57:5c:99
+                ASN1 OID: prime256v1
+        X509v3 extensions:
+            X509v3 Basic Constraints: critical
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                6E:21:26:96:72:29:39:BF:8B:EF:EB:65:CD:E0:4E:97:6F:1A:2C:E5
+            X509v3 Authority Key Identifier: 
+                keyid:FC:83:FA:4B:34:EB:19:5E:1C:7D:E5:85:EC:C7:FC:21:F5:E8:75:68
+
+            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:44:02:20:47:b1:5e:57:ae:6c:0b:df:78:11:79:5c:b2:60:
+         fd:0c:9c:37:18:19:fe:c1:b6:ca:f6:4f:62:63:13:ff:ff:64:
+         02:20:07:1f:3b:1d:c7:d8:fe:ff:26:0b:68:d0:85:bc:01:15:
+         62:e4:7f:f4:c7:e4:ad:d5:da:40:44:5a:0b:f5:72:9e
+-----BEGIN CERTIFICATE-----
+MIICDzCCAbagAwIBAgIJAITblczf8Ts+MAoGCCqGSM49BAMCMFIxCzAJBgNVBAYT
+AkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIDAeBgNVBAMM
+F1N1aXRlIEIgMTI4LWJpdCBSb290IENBMB4XDTE1MDEyNTExMjk1M1oXDTE2MDEy
+NTExMjk1M1owNDELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMRUwEwYDVQQD
+DAxzZXJ2ZXIudzEuZmkwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS+XxYIsgrW
+8hlvFBnvIPIrPOC+jGWGX5XBu5A27NxZOrpAmp0RdseAWiFM/MBfGVLIeLjlTPrJ
+atH+6rY6V1yZo4GSMIGPMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFG4hJpZyKTm/
+i+/rZc3gTpdvGizlMB8GA1UdIwQYMBaAFPyD+ks06xleHH3lhezH/CH16HVoMBoG
+A1UdEQEB/wQQMA6CDHNlcnZlci53MS5maTAWBgNVHSUBAf8EDDAKBggrBgEFBQcD
+ATALBgNVHQ8EBAMCBaAwCgYIKoZIzj0EAwIDRwAwRAIgR7FeV65sC994EXlcsmD9
+DJw3GBn+wbbK9k9iYxP//2QCIAcfOx3H2P7/Jgto0IW8ARVi5H/0x+St1dpARFoL
+9XKe
+-----END CERTIFICATE-----
diff --git a/tests/hwsim/auth_serv/ec-user.key b/tests/hwsim/auth_serv/ec-user.key
new file mode 100644 (file)
index 0000000..e390c06
--- /dev/null
@@ -0,0 +1,8 @@
+-----BEGIN EC PARAMETERS-----
+BggqhkjOPQMBBw==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEIL52ZfaYm8GAzhot94BCQriTmQEq2+JPkS+HCwUpLuwaoAoGCCqGSM49
+AwEHoUQDQgAEnE2sSN8ZOateUoi3Ao0VewSH+1ceTf+NkiJpoymO6U6q0CSlG2bp
+dZyBk+6UIOD9WiCi2tN+QGbvPnPrlLfBOg==
+-----END EC PRIVATE KEY-----
diff --git a/tests/hwsim/auth_serv/ec-user.pem b/tests/hwsim/auth_serv/ec-user.pem
new file mode 100644 (file)
index 0000000..9a6aba8
--- /dev/null
@@ -0,0 +1,52 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 9573410140069116735 (0x84db95ccdff13b3f)
+    Signature Algorithm: ecdsa-with-SHA256
+        Issuer: C=FI, L=Helsinki, O=w1.fi, CN=Suite B 128-bit Root CA
+        Validity
+            Not Before: Jan 25 11:29:53 2015 GMT
+            Not After : Jan 25 11:29:53 2016 GMT
+        Subject: C=FI, O=w1.fi, CN=user
+        Subject Public Key Info:
+            Public Key Algorithm: id-ecPublicKey
+                Public-Key: (256 bit)
+                pub: 
+                    04:9c:4d:ac:48:df:19:39:ab:5e:52:88:b7:02:8d:
+                    15:7b:04:87:fb:57:1e:4d:ff:8d:92:22:69:a3:29:
+                    8e:e9:4e:aa:d0:24:a5:1b:66:e9:75:9c:81:93:ee:
+                    94:20:e0:fd:5a:20:a2:da:d3:7e:40:66:ef:3e:73:
+                    eb:94:b7:c1:3a
+                ASN1 OID: prime256v1
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                89:28:76:9A:42:DB:B6:F8:36:97:63:8F:7D:0A:EA:0B:FE:66:2B:CD
+            X509v3 Authority Key Identifier: 
+                keyid:FC:83:FA:4B:34:EB:19:5E:1C:7D:E5:85:EC:C7:FC:21:F5:E8:75:68
+
+            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:45:02:20:26:84:14:f6:50:ac:ed:da:88:27:6d:18:d5:b3:
+         2c:c8:59:ea:2a:c3:ae:69:03:79:0d:66:5e:5f:a5:52:27:92:
+         02:21:00:db:8d:fd:58:e5:22:9b:17:32:57:34:e9:2e:30:da:
+         1d:77:4c:15:18:9b:7d:e4:5d:bc:64:cd:21:ff:57:df:16
+-----BEGIN CERTIFICATE-----
+MIIB/TCCAaOgAwIBAgIJAITblczf8Ts/MAoGCCqGSM49BAMCMFIxCzAJBgNVBAYT
+AkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIDAeBgNVBAMM
+F1N1aXRlIEIgMTI4LWJpdCBSb290IENBMB4XDTE1MDEyNTExMjk1M1oXDTE2MDEy
+NTExMjk1M1owLDELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMQ0wCwYDVQQD
+DAR1c2VyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEnE2sSN8ZOateUoi3Ao0V
+ewSH+1ceTf+NkiJpoymO6U6q0CSlG2bpdZyBk+6UIOD9WiCi2tN+QGbvPnPrlLfB
+OqOBhzCBhDAJBgNVHRMEAjAAMB0GA1UdDgQWBBSJKHaaQtu2+DaXY499CuoL/mYr
+zTAfBgNVHSMEGDAWgBT8g/pLNOsZXhx95YXsx/wh9eh1aDAVBgNVHREEDjAMgQp1
+c2VyQHcxLmZpMBMGA1UdJQQMMAoGCCsGAQUFBwMCMAsGA1UdDwQEAwIFoDAKBggq
+hkjOPQQDAgNIADBFAiAmhBT2UKzt2ognbRjVsyzIWeoqw65pA3kNZl5fpVInkgIh
+ANuN/VjlIpsXMlc06S4w2h13TBUYm33kXbxkzSH/V98W
+-----END CERTIFICATE-----
diff --git a/tests/hwsim/auth_serv/ec2-ca.pem b/tests/hwsim/auth_serv/ec2-ca.pem
new file mode 100644 (file)
index 0000000..0054ba8
--- /dev/null
@@ -0,0 +1,15 @@
+-----BEGIN CERTIFICATE-----
+MIICPTCCAcSgAwIBAgIJAL63h7lu0KZpMAoGCCqGSM49BAMDMFIxCzAJBgNVBAYT
+AkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIDAeBgNVBAMM
+F1N1aXRlIEIgMTkyLWJpdCBSb290IENBMB4XDTE1MDEyNTExMzIwM1oXDTI1MDEy
+MjExMzIwM1owUjELMAkGA1UEBhMCRkkxETAPBgNVBAcMCEhlbHNpbmtpMQ4wDAYD
+VQQKDAV3MS5maTEgMB4GA1UEAwwXU3VpdGUgQiAxOTItYml0IFJvb3QgQ0EwdjAQ
+BgcqhkjOPQIBBgUrgQQAIgNiAAQjdOMC9bqcDR9/SaOhxNbmQLQTGZfhtmoxHkJL
+5GG3bwW5hYA2jYHWU84H+mR6om6fg78G+IxjLly2OWiByYUeWDcsYqLGj3UHHaVv
+rIitaRPyg3dExemnmK3zjgXnoaajZjBkMB0GA1UdDgQWBBSuBbynInvH0vn8IKZc
+MbtBTo9svTAfBgNVHSMEGDAWgBSuBbynInvH0vn8IKZcMbtBTo9svTASBgNVHRMB
+Af8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAKBggqhkjOPQQDAwNnADBkAjBZ
+vEbGRDQNvzAY3nfYrsrE2Dd11smT6zv0mIvDeQCktbISGpStRBQjAaFjcCyjDEkC
+MH7ywcqJe+mpWDt5xFJvB52iZ7rX7rO0OX0qmjI38PC0IOo7euJdfcC1gHdSoAW3
+bA==
+-----END CERTIFICATE-----
diff --git a/tests/hwsim/auth_serv/ec2-generate.sh b/tests/hwsim/auth_serv/ec2-generate.sh
new file mode 100644 (file)
index 0000000..5a8d2d2
--- /dev/null
@@ -0,0 +1,53 @@
+#!/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 "---[ Verify ]-----------------------------------------------------------"
+echo
+
+$OPENSSL verify -CAfile ec2-ca.pem ec2-server.pem
+$OPENSSL verify -CAfile ec2-ca.pem ec2-user.pem
diff --git a/tests/hwsim/auth_serv/ec2-server.key b/tests/hwsim/auth_serv/ec2-server.key
new file mode 100644 (file)
index 0000000..9cd76ee
--- /dev/null
@@ -0,0 +1,9 @@
+-----BEGIN EC PARAMETERS-----
+BgUrgQQAIg==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDCjaz/zVDXqNO/XprtliomKOC6QjbBFgsF2YwUAtKB5ukL4miVGNyCu
+jIlq9eUD1x6gBwYFK4EEACKhZANiAARWq1ut1b6ctOFBkEOjULL3VjJFP15g0gk+
+sBTMBogU5WRN7Qod/jfem5k4O7FKYZNAarDFMh2yDMXZvRooiNyL0AH2wk0qzN5u
+n02JOt9Q76TVYflE91C5DTxjgLOgxBw=
+-----END EC PRIVATE KEY-----
diff --git a/tests/hwsim/auth_serv/ec2-server.pem b/tests/hwsim/auth_serv/ec2-server.pem
new file mode 100644 (file)
index 0000000..a7cc37e
--- /dev/null
@@ -0,0 +1,58 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 9347590364512421238 (0x81b94fe92ea08576)
+    Signature Algorithm: ecdsa-with-SHA384
+        Issuer: C=FI, L=Helsinki, O=w1.fi, CN=Suite B 192-bit Root CA
+        Validity
+            Not Before: Jan 25 11:32:03 2015 GMT
+            Not After : Jan 25 11:32:03 2016 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:56:ab:5b:ad:d5:be:9c:b4:e1:41:90:43:a3:50:
+                    b2:f7:56:32:45:3f:5e:60:d2:09:3e:b0:14:cc:06:
+                    88:14:e5:64:4d:ed:0a:1d:fe:37:de:9b:99:38:3b:
+                    b1:4a:61:93:40:6a:b0:c5:32:1d:b2:0c:c5:d9:bd:
+                    1a:28:88:dc:8b:d0:01:f6:c2:4d:2a:cc:de:6e:9f:
+                    4d:89:3a:df:50:ef:a4:d5:61:f9:44:f7:50:b9:0d:
+                    3c:63:80:b3:a0:c4:1c
+                ASN1 OID: secp384r1
+        X509v3 extensions:
+            X509v3 Basic Constraints: critical
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                19:D7:57:D0:3B:91:84:A4:AF:93:03:32:3C:AB:C4:F9:A7:B0:27:19
+            X509v3 Authority Key Identifier: 
+                keyid:AE:05:BC:A7:22:7B:C7:D2:F9:FC:20:A6:5C:31:BB:41:4E:8F:6C:BD
+
+            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:79:68:37:af:eb:46:e8:77:35:13:77:d5:db:eb:
+         f9:75:40:cd:d4:3d:0a:03:ec:67:a0:22:fe:65:f5:d7:ca:53:
+         4a:85:f5:14:4b:41:f9:b9:98:a6:85:8b:ac:e0:c8:6c:02:31:
+         00:83:12:02:be:93:2b:c2:00:74:ec:cb:fc:5a:8c:a6:5e:52:
+         ee:20:76:3d:73:2b:fb:fe:60:4c:52:f3:bc:1e:4c:e8:f9:ea:
+         f6:e2:f6:ca:c6:a8:3b:2d:9a:17:eb:4d:0a
+-----BEGIN CERTIFICATE-----
+MIICTTCCAdOgAwIBAgIJAIG5T+kuoIV2MAoGCCqGSM49BAMDMFIxCzAJBgNVBAYT
+AkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIDAeBgNVBAMM
+F1N1aXRlIEIgMTkyLWJpdCBSb290IENBMB4XDTE1MDEyNTExMzIwM1oXDTE2MDEy
+NTExMzIwM1owNDELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMRUwEwYDVQQD
+DAxzZXJ2ZXIudzEuZmkwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAARWq1ut1b6ctOFB
+kEOjULL3VjJFP15g0gk+sBTMBogU5WRN7Qod/jfem5k4O7FKYZNAarDFMh2yDMXZ
+vRooiNyL0AH2wk0qzN5un02JOt9Q76TVYflE91C5DTxjgLOgxByjgZIwgY8wDAYD
+VR0TAQH/BAIwADAdBgNVHQ4EFgQUGddX0DuRhKSvkwMyPKvE+aewJxkwHwYDVR0j
+BBgwFoAUrgW8pyJ7x9L5/CCmXDG7QU6PbL0wGgYDVR0RAQH/BBAwDoIMc2VydmVy
+LncxLmZpMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMBMAsGA1UdDwQEAwIFoDAKBggq
+hkjOPQQDAwNoADBlAjB5aDev60bodzUTd9Xb6/l1QM3UPQoD7GegIv5l9dfKU0qF
+9RRLQfm5mKaFi6zgyGwCMQCDEgK+kyvCAHTsy/xajKZeUu4gdj1zK/v+YExS87we
+TOj56vbi9srGqDstmhfrTQo=
+-----END CERTIFICATE-----
diff --git a/tests/hwsim/auth_serv/ec2-user.key b/tests/hwsim/auth_serv/ec2-user.key
new file mode 100644 (file)
index 0000000..adfa937
--- /dev/null
@@ -0,0 +1,9 @@
+-----BEGIN EC PARAMETERS-----
+BgUrgQQAIg==
+-----END EC PARAMETERS-----
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDB7tpaHBuZZG+MVYjRVpZvZvZxxFOu/reH2Ms3DiBH5DHW7dLP7T4Gs
+X+yw8bQZwCqgBwYFK4EEACKhZANiAATJYVk5woo/LAFd+znRAoMOClGXtfO2yZlp
+3n6jYUsG48W03XOlYd2/aCJVtGp6SwRVxumfYT4TejEj/Ky44vOlmQ9pasNfMYYN
+kpHcAWJ8sV7mP7LM9YVksksfhon91+E=
+-----END EC PRIVATE KEY-----
diff --git a/tests/hwsim/auth_serv/ec2-user.pem b/tests/hwsim/auth_serv/ec2-user.pem
new file mode 100644 (file)
index 0000000..ef86cd1
--- /dev/null
@@ -0,0 +1,57 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 9347590364512421239 (0x81b94fe92ea08577)
+    Signature Algorithm: ecdsa-with-SHA384
+        Issuer: C=FI, L=Helsinki, O=w1.fi, CN=Suite B 192-bit Root CA
+        Validity
+            Not Before: Jan 25 11:32:03 2015 GMT
+            Not After : Jan 25 11:32:03 2016 GMT
+        Subject: C=FI, O=w1.fi, CN=user
+        Subject Public Key Info:
+            Public Key Algorithm: id-ecPublicKey
+                Public-Key: (384 bit)
+                pub: 
+                    04:c9:61:59:39:c2:8a:3f:2c:01:5d:fb:39:d1:02:
+                    83:0e:0a:51:97:b5:f3:b6:c9:99:69:de:7e:a3:61:
+                    4b:06:e3:c5:b4:dd:73:a5:61:dd:bf:68:22:55:b4:
+                    6a:7a:4b:04:55:c6:e9:9f:61:3e:13:7a:31:23:fc:
+                    ac:b8:e2:f3:a5:99:0f:69:6a:c3:5f:31:86:0d:92:
+                    91:dc:01:62:7c:b1:5e:e6:3f:b2:cc:f5:85:64:b2:
+                    4b:1f:86:89:fd:d7:e1
+                ASN1 OID: secp384r1
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                75:EA:7B:CE:8A:99:D2:E7:77:B4:3B:80:68:59:E9:B6:88:B2:FA:F6
+            X509v3 Authority Key Identifier: 
+                keyid:AE:05:BC:A7:22:7B:C7:D2:F9:FC:20:A6:5C:31:BB:41:4E:8F:6C:BD
+
+            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:65:02:31:00:c2:b7:35:4e:5e:d1:da:7f:35:a0:ac:54:92:
+         18:08:0d:9c:86:e9:4e:cf:3a:09:48:23:eb:4d:56:77:e5:d0:
+         e7:b0:55:b3:0e:91:2d:f8:3e:1c:4e:0d:b7:32:dc:11:1b:02:
+         30:49:c2:6b:63:39:3c:4b:d9:e9:8d:b9:ce:6e:8e:9f:88:43:
+         03:e0:5f:7e:75:44:12:66:f8:c6:ae:8e:f1:da:10:02:36:8c:
+         7b:a2:89:a0:05:3b:c6:39:d6:e1:7a:b7:85
+-----BEGIN CERTIFICATE-----
+MIICOjCCAcCgAwIBAgIJAIG5T+kuoIV3MAoGCCqGSM49BAMDMFIxCzAJBgNVBAYT
+AkZJMREwDwYDVQQHDAhIZWxzaW5raTEOMAwGA1UECgwFdzEuZmkxIDAeBgNVBAMM
+F1N1aXRlIEIgMTkyLWJpdCBSb290IENBMB4XDTE1MDEyNTExMzIwM1oXDTE2MDEy
+NTExMzIwM1owLDELMAkGA1UEBhMCRkkxDjAMBgNVBAoMBXcxLmZpMQ0wCwYDVQQD
+DAR1c2VyMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEyWFZOcKKPywBXfs50QKDDgpR
+l7XztsmZad5+o2FLBuPFtN1zpWHdv2giVbRqeksEVcbpn2E+E3oxI/ysuOLzpZkP
+aWrDXzGGDZKR3AFifLFe5j+yzPWFZLJLH4aJ/dfho4GHMIGEMAkGA1UdEwQCMAAw
+HQYDVR0OBBYEFHXqe86KmdLnd7Q7gGhZ6baIsvr2MB8GA1UdIwQYMBaAFK4FvKci
+e8fS+fwgplwxu0FOj2y9MBUGA1UdEQQOMAyBCnVzZXJAdzEuZmkwEwYDVR0lBAww
+CgYIKwYBBQUHAwIwCwYDVR0PBAQDAgWgMAoGCCqGSM49BAMDA2gAMGUCMQDCtzVO
+XtHafzWgrFSSGAgNnIbpTs86CUgj601Wd+XQ57BVsw6RLfg+HE4NtzLcERsCMEnC
+a2M5PEvZ6Y25zm6On4hDA+BffnVEEmb4xq6O8doQAjaMe6KJoAU7xjnW4Xq3hQ==
+-----END CERTIFICATE-----
diff --git a/tests/hwsim/auth_serv/hlr_auc_gw.gsm b/tests/hwsim/auth_serv/hlr_auc_gw.gsm
new file mode 100644 (file)
index 0000000..b67aeca
--- /dev/null
@@ -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/tests/hwsim/auth_serv/hlr_auc_gw.milenage_db b/tests/hwsim/auth_serv/hlr_auc_gw.milenage_db
new file mode 100644 (file)
index 0000000..ecd06d7
--- /dev/null
@@ -0,0 +1,13 @@
+# 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
+
+# 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/tests/hwsim/auth_serv/index-revoked.txt b/tests/hwsim/auth_serv/index-revoked.txt
new file mode 100644 (file)
index 0000000..95b052e
--- /dev/null
@@ -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      150929211122Z   160111185024Z   D8D3E3A6CBE3CCD0        unknown /C=FI/O=w1.fi/CN=server.w1.fi
+R      150929211300Z   160111185024Z   D8D3E3A6CBE3CCD1        unknown /C=FI/O=w1.fi/CN=Test User
diff --git a/tests/hwsim/auth_serv/index-unknown.txt b/tests/hwsim/auth_serv/index-unknown.txt
new file mode 100644 (file)
index 0000000..97dfbba
--- /dev/null
@@ -0,0 +1 @@
+V      230627164122Z           D8D3E3A6CBE3CCC1        unknown /C=FI/O=w1.fi/CN=Root CA
diff --git a/tests/hwsim/auth_serv/index.txt b/tests/hwsim/auth_serv/index.txt
new file mode 100644 (file)
index 0000000..52c8e0c
--- /dev/null
@@ -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      150929211122Z           D8D3E3A6CBE3CCD0        unknown /C=FI/O=w1.fi/CN=server.w1.fi
+V      150929211300Z           D8D3E3A6CBE3CCD1        unknown /C=FI/O=w1.fi/CN=Test User
diff --git a/tests/hwsim/auth_serv/ocsp-req.der b/tests/hwsim/auth_serv/ocsp-req.der
new file mode 100644 (file)
index 0000000..20999b9
Binary files /dev/null and b/tests/hwsim/auth_serv/ocsp-req.der differ
diff --git a/tests/hwsim/auth_serv/ocsp-responder.key b/tests/hwsim/auth_serv/ocsp-responder.key
new file mode 100644 (file)
index 0000000..fb866fb
--- /dev/null
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALJeLx3nLPZsq7AW
+nvoSL7JMyCN7aAh2OIOX9T8FrF3ZganOdZKhvJbGyADuHtfw2orY58DXQsMlYufH
+YPqogkwbznOaq42z/j22fwH+WWRCdagEGActImQnufGvAbTtv6bqkXjRnDD1YTf/
++Rv4Fl9rdzL51+OdDNXDuUMW8DrDAgMBAAECgYAja1yD3aIqFQ5K21MaaX4bM/AS
+S7Eu7Prv9r72ktPVlxmOdLcYNRHUBwk0VhS94NAk/kmXG6fgRI5NZGQ3ojqtOXLV
+VhlcitYAfJvNpyKmFKgdGZQIxaaQr/F2X8tH5yFdIt+6mDOGptTb/S3ljQwNsg59
+7t/jYzSe5mK/Gbw4MQJBAN3sZqGz6ABygLTuTiXhE9sCXDSGy4d8ZWMaajuD7N6k
+sAGKsaiVozeIvg0JNiCMm02A8M/cWjGedDWFxrnvvF8CQQDNwagUpozfXMboibHI
+BNwpUzyri/5bqJ/dU7/sAOA1AZ9yoO5s2WlNutXkG3mDoQCzseG/pNxU403dU0jQ
+wpwdAkEAk5lbWUkSkNmXCL9GcqMUVaFoOfc8/suZkyRKa3L+48Wc2imop3t+przn
+yjvKKDPcRtvvThA8XKwKll53Ict0+QJBAKj7o09Sed/4EmRosdnUI/zMn8dD8mLU
+2narkbQCBCGEc69w/F/pLtLn30K4TdQNJsZuETmT7GDLTee3vtW0/wECQCtyVgw/
+aZ0QTac8ut1oG072qOA2cFGhEuDELlX8JcNy28ygmzn0KS8uiTsq6YVu8V7WCj4X
+EkAZMm19nY5ZE+A=
+-----END PRIVATE KEY-----
diff --git a/tests/hwsim/auth_serv/ocsp-responder.pem b/tests/hwsim/auth_serv/ocsp-responder.pem
new file mode 100644 (file)
index 0000000..bbde1e8
--- /dev/null
@@ -0,0 +1,54 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 15624081837803162829 (0xd8d3e3a6cbe3cccd)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: C=FI, O=w1.fi, CN=Root CA
+        Validity
+            Not Before: Jan 11 18:50:24 2015 GMT
+            Not After : Jan 11 18:50:24 2016 GMT
+        Subject: C=FI, O=w1.fi, CN=ocsp.w1.fi
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:b2:5e:2f:1d:e7:2c:f6:6c:ab:b0:16:9e:fa:12:
+                    2f:b2:4c:c8:23:7b:68:08:76:38:83:97:f5:3f:05:
+                    ac:5d:d9:81:a9:ce:75:92:a1:bc:96:c6:c8:00:ee:
+                    1e:d7:f0:da:8a:d8:e7:c0:d7:42:c3:25:62:e7:c7:
+                    60:fa:a8:82:4c:1b:ce:73:9a:ab:8d:b3:fe:3d:b6:
+                    7f:01:fe:59:64:42:75:a8:04:18:07:2d:22:64:27:
+                    b9:f1:af:01:b4:ed:bf:a6:ea:91:78:d1:9c:30:f5:
+                    61:37:ff:f9:1b:f8:16:5f:6b:77:32:f9:d7:e3:9d:
+                    0c:d5:c3:b9:43:16:f0:3a:c3
+                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
+         41:42:b6:70:4a:70:1f:ad:d9:25:f7:02:94:bd:91:b7:69:ad:
+         31:59:c6:2a:4e:5e:4a:ed:5d:c1:24:09:98:94:15:42:86:2c:
+         b2:9d:62:7a:e0:ec:60:39:47:93:c9:c7:61:01:b5:2c:00:53:
+         86:6e:66:99:ee:b3:57:5d:fb:83:6b:d3:77:26:0c:c7:2d:16:
+         ea:84:69:59:b7:a8:de:35:61:0b:7a:f3:62:1e:1a:94:91:c4:
+         bd:85:4a:63:10:09:11:88:75:c9:f5:57:84:9a:ef:d1:78:29:
+         5e:76:fc:33:76:84:b2:b5:f6:88:cc:fb:f9:cf:9f:b4:88:29:
+         3c:9d
+-----BEGIN CERTIFICATE-----
+MIICDjCCAXegAwIBAgIJANjT46bL48zNMA0GCSqGSIb3DQEBCwUAMC8xCzAJBgNV
+BAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xNTAx
+MTExODUwMjRaFw0xNjAxMTExODUwMjRaMDIxCzAJBgNVBAYTAkZJMQ4wDAYDVQQK
+DAV3MS5maTETMBEGA1UEAwwKb2NzcC53MS5maTCBnzANBgkqhkiG9w0BAQEFAAOB
+jQAwgYkCgYEAsl4vHecs9myrsBae+hIvskzII3toCHY4g5f1PwWsXdmBqc51kqG8
+lsbIAO4e1/DaitjnwNdCwyVi58dg+qiCTBvOc5qrjbP+PbZ/Af5ZZEJ1qAQYBy0i
+ZCe58a8BtO2/puqReNGcMPVhN//5G/gWX2t3MvnX450M1cO5QxbwOsMCAwEAAaMv
+MC0wCQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwEwYDVR0lBAwwCgYIKwYBBQUHAwkw
+DQYJKoZIhvcNAQELBQADgYEAQUK2cEpwH63ZJfcClL2Rt2mtMVnGKk5eSu1dwSQJ
+mJQVQoYssp1ieuDsYDlHk8nHYQG1LABThm5mme6zV137g2vTdyYMxy0W6oRpWbeo
+3jVhC3rzYh4alJHEvYVKYxAJEYh1yfVXhJrv0XgpXnb8M3aEsrX2iMz7+c+ftIgp
+PJ0=
+-----END CERTIFICATE-----
diff --git a/tests/hwsim/auth_serv/ocsp-server-cache.der b/tests/hwsim/auth_serv/ocsp-server-cache.der
new file mode 100644 (file)
index 0000000..33e6753
Binary files /dev/null and b/tests/hwsim/auth_serv/ocsp-server-cache.der differ
diff --git a/tests/hwsim/auth_serv/ocsp-server-cache.der-invalid b/tests/hwsim/auth_serv/ocsp-server-cache.der-invalid
new file mode 100644 (file)
index 0000000..218bd03
Binary files /dev/null and b/tests/hwsim/auth_serv/ocsp-server-cache.der-invalid differ
diff --git a/tests/hwsim/auth_serv/radius_clients.conf b/tests/hwsim/auth_serv/radius_clients.conf
new file mode 100644 (file)
index 0000000..7e34015
--- /dev/null
@@ -0,0 +1 @@
+0.0.0.0/0      radius
diff --git a/tests/hwsim/auth_serv/radius_clients_ipv6.conf b/tests/hwsim/auth_serv/radius_clients_ipv6.conf
new file mode 100644 (file)
index 0000000..8723efc
--- /dev/null
@@ -0,0 +1 @@
+::1    radius
diff --git a/tests/hwsim/auth_serv/server-eku-client-server.key b/tests/hwsim/auth_serv/server-eku-client-server.key
new file mode 100644 (file)
index 0000000..ce2e5f2
--- /dev/null
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMowHv0TagIoUZoO
+qR5yfudayMsMfoqZgY0FswmwqYbnrkT64Mfu8xi0MWXjBW9mTuPkhYGbR39ftRYr
+sFmRnMVV09PKLIHO8CeoVN4OT9jwEb0LEFY4Jt+pOpUVk6YW7dIetLXAqGGOrhAE
+/eYmykoNkEu5rMmU8rFrl2tgJOq9AgMBAAECgYAdONdBvIyVwz4IBhZrUCEHTxe2
+QRgI8CbJOwmlXOMjnFiTn67dNqvr5h89mpIuh5rfVSf2k3rB7hM+IRJb36/Ik7qg
+GdktPSEIK/ktUcfofVLaLn+ehG7vXhkkB6juBR7jaXDZRBPvFM+TCtirlaZ5sQ0u
+TbSw7m9NcFD2APxgAQJBAPIoCxZCJGpMvh+5ta8EJQVQKhJeMWmDlUQvscKTauWb
+aTz0z+OMBGpZH7DWCTww4+/3fjqZt/TURuPSh0ZcACUCQQDVvyPTO3h3R5fig/zV
+NV8E0/dCYH6kwsFk0AUIRbMHdaN3sEHWszKG9nTNyPyHhDo8i9jguSjkb9MwdgR7
+BJC5AkBB6/bAs3bYXVXwqwyzvWwamy0o3O2UrNaIvnck4h7arMkkZ/zkFCzriqGe
+8VWIRkL3A6ggadJzWwqFYL2kwMzlAkEAhfEdFgUyXCy09PEYwtKLFI9vZlzpf327
+it0ACksDAS2qnhoJZ+0rQH+4eiv0c0dc5wwLf+cHxP5+LOQHsr8NoQJAcsRe+KyX
+G0TLKZg/J5E+zJMH6M19BZ4BC32UIMTJWe1xzp+9XrCWflagRJMJ+DOWtHzu/Opo
+Ty4OiT0uZUxcMw==
+-----END PRIVATE KEY-----
diff --git a/tests/hwsim/auth_serv/server-eku-client-server.pem b/tests/hwsim/auth_serv/server-eku-client-server.pem
new file mode 100644 (file)
index 0000000..4f94455
--- /dev/null
@@ -0,0 +1,62 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 15624081837803162828 (0xd8d3e3a6cbe3cccc)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=FI, O=w1.fi, CN=Root CA
+        Validity
+            Not Before: Feb 28 22:41:44 2014 GMT
+            Not After : Feb 28 22:41:44 2015 GMT
+        Subject: C=FI, O=w1.fi, CN=server6.w1.fi
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:ca:30:1e:fd:13:6a:02:28:51:9a:0e:a9:1e:72:
+                    7e:e7:5a:c8:cb:0c:7e:8a:99:81:8d:05:b3:09:b0:
+                    a9:86:e7:ae:44:fa:e0:c7:ee:f3:18:b4:31:65:e3:
+                    05:6f:66:4e:e3:e4:85:81:9b:47:7f:5f:b5:16:2b:
+                    b0:59:91:9c:c5:55:d3:d3:ca:2c:81:ce:f0:27:a8:
+                    54:de:0e:4f:d8:f0:11:bd:0b:10:56:38:26:df:a9:
+                    3a:95:15:93:a6:16:ed:d2:1e:b4:b5:c0:a8:61:8e:
+                    ae:10:04:fd:e6:26:ca:4a:0d:90:4b:b9:ac:c9:94:
+                    f2:b1:6b:97:6b:60:24:ea:bd
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                C7:C6:EF:F5:61:D2:A0:08:81:6A:6B:44:2C:F5:72:F7:DA:DE:5B:B9
+            X509v3 Authority Key Identifier: 
+                keyid:B8:92:DE:FD:8A:18:B3:30:C3:9F:55:F3:33:5D:B4:C8:29:8A:41:14
+
+            Authority Information Access: 
+                OCSP - URI:http://server.w1.fi:8888/
+
+            X509v3 Extended Key Usage: 
+                TLS Web Client Authentication, TLS Web Server Authentication
+    Signature Algorithm: sha1WithRSAEncryption
+         64:52:09:25:e9:ce:db:1f:fa:81:aa:8a:ed:7e:f7:db:1e:27:
+         de:a7:41:b3:ab:73:e3:bc:b7:24:ed:5f:a6:88:5b:c8:16:1a:
+         f9:60:93:0b:d2:3f:5f:ce:3c:8c:50:53:8e:30:ae:0a:f8:0a:
+         53:74:d7:37:47:55:81:7d:75:c7:a2:e2:ff:82:bd:55:67:3d:
+         dd:e3:ca:d6:ef:33:63:2d:f4:65:4f:a2:8c:d5:f1:ac:af:ce:
+         02:83:91:37:cc:7c:55:7a:81:9c:c9:46:9e:9c:e6:ce:d5:35:
+         6c:f7:2e:08:05:c3:ca:c7:25:8c:e0:ba:4e:4c:fc:d3:a2:5a:
+         57:0e
+-----BEGIN CERTIFICATE-----
+MIIChzCCAfCgAwIBAgIJANjT46bL48zMMA0GCSqGSIb3DQEBBQUAMC8xCzAJBgNV
+BAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xNDAy
+MjgyMjQxNDRaFw0xNTAyMjgyMjQxNDRaMDUxCzAJBgNVBAYTAkZJMQ4wDAYDVQQK
+DAV3MS5maTEWMBQGA1UEAwwNc2VydmVyNi53MS5maTCBnzANBgkqhkiG9w0BAQEF
+AAOBjQAwgYkCgYEAyjAe/RNqAihRmg6pHnJ+51rIywx+ipmBjQWzCbCphueuRPrg
+x+7zGLQxZeMFb2ZO4+SFgZtHf1+1FiuwWZGcxVXT08osgc7wJ6hU3g5P2PARvQsQ
+Vjgm36k6lRWTphbt0h60tcCoYY6uEAT95ibKSg2QS7msyZTysWuXa2Ak6r0CAwEA
+AaOBpDCBoTAJBgNVHRMEAjAAMB0GA1UdDgQWBBTHxu/1YdKgCIFqa0Qs9XL32t5b
+uTAfBgNVHSMEGDAWgBS4kt79ihizMMOfVfMzXbTIKYpBFDA1BggrBgEFBQcBAQQp
+MCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9zZXJ2ZXIudzEuZmk6ODg4OC8wHQYDVR0l
+BBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMA0GCSqGSIb3DQEBBQUAA4GBAGRSCSXp
+ztsf+oGqiu1+99seJ96nQbOrc+O8tyTtX6aIW8gWGvlgkwvSP1/OPIxQU44wrgr4
+ClN01zdHVYF9dcei4v+CvVVnPd3jytbvM2Mt9GVPoozV8ayvzgKDkTfMfFV6gZzJ
+Rp6c5s7VNWz3LggFw8rHJYzguk5M/NOiWlcO
+-----END CERTIFICATE-----
diff --git a/tests/hwsim/auth_serv/server-eku-client.key b/tests/hwsim/auth_serv/server-eku-client.key
new file mode 100644 (file)
index 0000000..f2a99cd
--- /dev/null
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKOZ6eLhF2A7cDQa
+dFxG47i9u6rJ8+77EjCgacN0OIA6uiNSx8Fqz7rdQePSaTWkpmBsMR+FvVZsewlj
+zadRa4RAkHd+l2h7OLXEFTt0NzQounri14RTeHZNFre43wly54cmdCwEysXOKfW0
+ztso60VHQo/tiFqjI0mbe7w54QFTAgMBAAECgYAngwCtvtc6cqCCtPDtaGGPOKOe
+d+/mA9U80UE551POBGD4LwH3gKhy5QUI1MR8JCvalca3akF0IfcFKYl9o3hnsZ73
+3wGzxM8BEf9wEVtVC2CTRVoIupleaEk3j8dgaUs/O54WkmAoHF1avXAMSGOUDxCO
+Ggpn2tei78Csdj78IQJBANF7a7RaJsXh6xMI7hlrVrUsIbBvsBo1wbbGCwNRvgzL
+I1mq1O+Go7Aao0pDK7sOUa86j6ECZ5pzqcdPaF22tJ8CQQDH7kTy6ERBbLFxs/Wd
+YLDEh1GIGyGW10tuJTOl2R1TKSBXRzPAeI+jcC+AC00238p4MO899WOVeLvaERZa
+IuLNAkAtlxXGp4Qett9JQj1HbPPu9A7U7km+OorRM2K8MzMQZ7lmz2YORxgiwHlf
+NSU0TZZ7c1xE51gS5i9CAEcvdg7zAkAKIZfa20xCKHjhcyYaIIE0pErMY9uS4jwP
+S9FPMS5cPXRHF/OWaEWXGaM+kNQL2NFQv+IPuLSgKWsThNQmIyhtAkEAiQq1HdN7
+3l8YhUuJtxg7nrh2s0V4UcSNOZxVf/85AKrTu1IfjdwmXFeoRB/y9Ef4h1bcXgzj
+clIVhie7r0JNLw==
+-----END PRIVATE KEY-----
diff --git a/tests/hwsim/auth_serv/server-eku-client.pem b/tests/hwsim/auth_serv/server-eku-client.pem
new file mode 100644 (file)
index 0000000..a924042
--- /dev/null
@@ -0,0 +1,62 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 15624081837803162827 (0xd8d3e3a6cbe3cccb)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=FI, O=w1.fi, CN=Root CA
+        Validity
+            Not Before: Feb 15 08:30:08 2014 GMT
+            Not After : Feb 15 08:30:08 2015 GMT
+        Subject: C=FI, O=w1.fi, CN=server5.w1.fi
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:a3:99:e9:e2:e1:17:60:3b:70:34:1a:74:5c:46:
+                    e3:b8:bd:bb:aa:c9:f3:ee:fb:12:30:a0:69:c3:74:
+                    38:80:3a:ba:23:52:c7:c1:6a:cf:ba:dd:41:e3:d2:
+                    69:35:a4:a6:60:6c:31:1f:85:bd:56:6c:7b:09:63:
+                    cd:a7:51:6b:84:40:90:77:7e:97:68:7b:38:b5:c4:
+                    15:3b:74:37:34:28:ba:7a:e2:d7:84:53:78:76:4d:
+                    16:b7:b8:df:09:72:e7:87:26:74:2c:04:ca:c5:ce:
+                    29:f5:b4:ce:db:28:eb:45:47:42:8f:ed:88:5a:a3:
+                    23:49:9b:7b:bc:39:e1:01:53
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                33:16:9D:3B:17:15:82:2B:34:6E:38:E8:CC:22:BF:49:A7:5E:2A:2B
+            X509v3 Authority Key Identifier: 
+                keyid:B8:92:DE:FD:8A:18:B3:30:C3:9F:55:F3:33:5D:B4:C8:29:8A:41:14
+
+            Authority Information Access: 
+                OCSP - URI:http://server.w1.fi:8888/
+
+            X509v3 Extended Key Usage: 
+                TLS Web Client Authentication
+    Signature Algorithm: sha1WithRSAEncryption
+         6f:2d:cb:3b:91:50:15:e1:c7:41:15:6c:a4:89:e5:0e:f9:f9:
+         9b:10:36:d8:67:a8:29:e2:6a:6f:89:7b:66:bd:f1:b8:fa:1c:
+         f7:22:8b:85:4e:37:f3:d6:1e:35:df:c7:04:e6:13:20:ca:fa:
+         62:cc:8d:3a:bd:97:10:5c:1b:0b:39:79:ac:13:61:59:79:fd:
+         a1:4b:7d:c9:c5:c4:19:4d:76:5b:cd:6d:1e:f2:aa:ef:67:51:
+         aa:0c:ef:6a:f2:10:71:6f:19:e6:12:ab:3e:65:76:0f:5a:0f:
+         cf:96:30:c3:fc:59:e9:13:af:e1:8a:b0:2c:78:ad:3d:b4:e9:
+         e5:20
+-----BEGIN CERTIFICATE-----
+MIICfTCCAeagAwIBAgIJANjT46bL48zLMA0GCSqGSIb3DQEBBQUAMC8xCzAJBgNV
+BAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xNDAy
+MTUwODMwMDhaFw0xNTAyMTUwODMwMDhaMDUxCzAJBgNVBAYTAkZJMQ4wDAYDVQQK
+DAV3MS5maTEWMBQGA1UEAwwNc2VydmVyNS53MS5maTCBnzANBgkqhkiG9w0BAQEF
+AAOBjQAwgYkCgYEAo5np4uEXYDtwNBp0XEbjuL27qsnz7vsSMKBpw3Q4gDq6I1LH
+wWrPut1B49JpNaSmYGwxH4W9Vmx7CWPNp1FrhECQd36XaHs4tcQVO3Q3NCi6euLX
+hFN4dk0Wt7jfCXLnhyZ0LATKxc4p9bTO2yjrRUdCj+2IWqMjSZt7vDnhAVMCAwEA
+AaOBmjCBlzAJBgNVHRMEAjAAMB0GA1UdDgQWBBQzFp07FxWCKzRuOOjMIr9Jp14q
+KzAfBgNVHSMEGDAWgBS4kt79ihizMMOfVfMzXbTIKYpBFDA1BggrBgEFBQcBAQQp
+MCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9zZXJ2ZXIudzEuZmk6ODg4OC8wEwYDVR0l
+BAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQEFBQADgYEAby3LO5FQFeHHQRVspInl
+Dvn5mxA22GeoKeJqb4l7Zr3xuPoc9yKLhU4389YeNd/HBOYTIMr6YsyNOr2XEFwb
+Czl5rBNhWXn9oUt9ycXEGU12W81tHvKq72dRqgzvavIQcW8Z5hKrPmV2D1oPz5Yw
+w/xZ6ROv4YqwLHitPbTp5SA=
+-----END CERTIFICATE-----
diff --git a/tests/hwsim/auth_serv/server-expired.key b/tests/hwsim/auth_serv/server-expired.key
new file mode 100644 (file)
index 0000000..882d645
--- /dev/null
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANA7a4aeP7QOYEhU
+Tcbci7lrddDkYPChQwuv+cR3aRGEUr6efXG0qoAf6+bAN95J9IVDrk1S8+swc67m
+GAQUj8JjMKQM6/XWy/SvHU/WOkN4FDLe5YilNL6rmqSj3muE43iTHBwpx/xrzGjX
+7sBd1z2RiIFWulQRnk7ogIPgbMrxAgMBAAECgYEArWSNSO+FRD2kVxY8HZeQkbm1
+xVgmkLj3x0elx79XMkrpS+lVs9UpFL+ABAmTe/pBLqcJAUJN8k3KRp066krk2QyQ
+uilRkugON0vBJzLse9HryXilx0aWEVl3xZBKu1E3G4mcCl2LoPaASCZtjQXd/XCd
+zdBR24qe123ofMpIo0ECQQDooUnHsruInBX9bRP11xXs7bI5298ZLCWHFAhGa/Tb
+KvVXkXnzPVYhRi2w0Leqb0lht/4GX9MB06xcHs5TLvltAkEA5SasURCjxXc7svGJ
+yP1s779DxYWoEBvGiRPygtyO40cnkOuupXKLaSkSuNUGag+6UxNzxGSUx9aiadse
+oxOJFQJAL6y2SSXZBxMt8oUDPTO6O5cvGmp0G12Px1IUrBH92VjBdRPMUUw1tZYD
+USRFL7mk6VDiz32d6dbukOaDVErhNQJASwnoAb/WMXLDHO0VtriudLAIbGVBTM0b
+rYXXs1yweeKyJTXYghtJZc1qcRZpPFAcLto+3cAmLG6vzsRPew2JpQJBAN8krD5c
+RYAGuXtslPkH7BWypJXI+K3brZkKBiyXVB/fbwnpXI1KTbzeBSly60JrjuymY9+X
+NKs5A4HSiCtQjSk=
+-----END PRIVATE KEY-----
diff --git a/tests/hwsim/auth_serv/server-expired.pem b/tests/hwsim/auth_serv/server-expired.pem
new file mode 100644 (file)
index 0000000..f279aae
--- /dev/null
@@ -0,0 +1,62 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 15624081837803162826 (0xd8d3e3a6cbe3ccca)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=FI, O=w1.fi, CN=Root CA
+        Validity
+            Not Before: Jan  1 00:00:00 2014 GMT
+            Not After : Jan  2 00:00:00 2014 GMT
+        Subject: C=FI, O=w1.fi, CN=server4.w1.fi
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:d0:3b:6b:86:9e:3f:b4:0e:60:48:54:4d:c6:dc:
+                    8b:b9:6b:75:d0:e4:60:f0:a1:43:0b:af:f9:c4:77:
+                    69:11:84:52:be:9e:7d:71:b4:aa:80:1f:eb:e6:c0:
+                    37:de:49:f4:85:43:ae:4d:52:f3:eb:30:73:ae:e6:
+                    18:04:14:8f:c2:63:30:a4:0c:eb:f5:d6:cb:f4:af:
+                    1d:4f:d6:3a:43:78:14:32:de:e5:88:a5:34:be:ab:
+                    9a:a4:a3:de:6b:84:e3:78:93:1c:1c:29:c7:fc:6b:
+                    cc:68:d7:ee:c0:5d:d7:3d:91:88:81:56:ba:54:11:
+                    9e:4e:e8:80:83:e0:6c:ca:f1
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                75:B0:65:1F:2F:A9:BE:D7:D0:EE:9D:42:8F:8B:13:5F:D0:AD:13:7B
+            X509v3 Authority Key Identifier: 
+                keyid:B8:92:DE:FD:8A:18:B3:30:C3:9F:55:F3:33:5D:B4:C8:29:8A:41:14
+
+            Authority Information Access: 
+                OCSP - URI:http://server.w1.fi:8888/
+
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication
+    Signature Algorithm: sha1WithRSAEncryption
+         12:e7:8a:e1:3d:d9:fd:36:ce:71:66:b3:74:48:c1:f0:38:75:
+         30:56:c7:2c:9c:0d:da:d0:68:19:47:a2:37:38:0d:db:4f:f9:
+         b9:cc:0d:25:b1:35:ed:df:19:8c:4b:bd:f0:08:11:13:4b:e9:
+         a7:d7:50:2e:fa:7a:16:e1:4f:0f:5a:b4:42:34:ff:43:08:5c:
+         3c:04:6a:f8:44:8d:f6:e5:a7:82:38:60:d0:5c:d1:59:f9:02:
+         84:7f:da:ae:6c:e9:55:c8:f5:0e:da:55:70:f3:77:48:30:1f:
+         ab:60:39:a1:77:49:29:e3:51:54:62:72:c7:78:ae:17:14:c5:
+         dd:2c
+-----BEGIN CERTIFICATE-----
+MIICfTCCAeagAwIBAgIJANjT46bL48zKMA0GCSqGSIb3DQEBBQUAMC8xCzAJBgNV
+BAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xNDAx
+MDEwMDAwMDBaFw0xNDAxMDIwMDAwMDBaMDUxCzAJBgNVBAYTAkZJMQ4wDAYDVQQK
+DAV3MS5maTEWMBQGA1UEAwwNc2VydmVyNC53MS5maTCBnzANBgkqhkiG9w0BAQEF
+AAOBjQAwgYkCgYEA0Dtrhp4/tA5gSFRNxtyLuWt10ORg8KFDC6/5xHdpEYRSvp59
+cbSqgB/r5sA33kn0hUOuTVLz6zBzruYYBBSPwmMwpAzr9dbL9K8dT9Y6Q3gUMt7l
+iKU0vquapKPea4TjeJMcHCnH/GvMaNfuwF3XPZGIgVa6VBGeTuiAg+BsyvECAwEA
+AaOBmjCBlzAJBgNVHRMEAjAAMB0GA1UdDgQWBBR1sGUfL6m+19DunUKPixNf0K0T
+ezAfBgNVHSMEGDAWgBS4kt79ihizMMOfVfMzXbTIKYpBFDA1BggrBgEFBQcBAQQp
+MCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9zZXJ2ZXIudzEuZmk6ODg4OC8wEwYDVR0l
+BAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQEFBQADgYEAEueK4T3Z/TbOcWazdEjB
+8Dh1MFbHLJwN2tBoGUeiNzgN20/5ucwNJbE17d8ZjEu98AgRE0vpp9dQLvp6FuFP
+D1q0QjT/QwhcPARq+ESN9uWngjhg0FzRWfkChH/armzpVcj1DtpVcPN3SDAfq2A5
+oXdJKeNRVGJyx3iuFxTF3Sw=
+-----END CERTIFICATE-----
diff --git a/tests/hwsim/auth_serv/server-no-dnsname.key b/tests/hwsim/auth_serv/server-no-dnsname.key
new file mode 100644 (file)
index 0000000..fd0ad39
--- /dev/null
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANv8D6FIh2iGxJ56
++Bgod22jWA/bvmvUQ0PEuhc3m6j/lqJzFBMcrhkPgVQ1EGSU42RlvpsLFtKekph3
+h+KamfwdVwyKDUwhL65n12Nh65FbWC+tZ2Zl5IMHymo2peYg9lyZJ9tj5YbYK3wd
+kESBIiF3CgMFw+tjYbNMMsCHhzpHAgMBAAECgYEAu0p2MDWk+4xKGDfPxBmn3JOG
+ZTIMhJeakTcLzLqOb6rzn+lkPQVdAH8f+AaZp1jP5OlvB2fAjZ9uZhrWeUpxMA3a
+TTEJqvttF1R+PjQ7hxWByPf+cFtPfJnXmJg8DuCBpc4TbPd0MMqtu37K9m41iO7K
+H5Lj6J+wp4lhv1Y4oaECQQDv0bvCgrGpSMLHigsdVcsFyjZr25+9y1J2Gnm1Hm/Z
+dbUtS9cOihYh8qh3YyGAKS5psCVzdeMXGKDN05pOhEGxAkEA6tO8Bhh+YA/oG+pl
+Ps9W9XjWwBCByVI+Hub6/Y9NcWckmBP+41DN1Oi7cKsSyMJ74WD5r+QYqS258tC6
+YDsBdwJBAJ8OEWN+XuqRsW26Joj8P7zFUrbSYO32Dej6wkHXwAMQSGuUYzvnZap6
+UDVub+eaaIf8JbqgM088LFqWvz7YBOECQHBlN7GTN6my812pKxyNEQoc9GypefVq
+L+GKnMeQN3j37UP9DhqvKlWlr1GWED+XFsQhLmFJw6P2BvJ5hTtaArECQHBSy14H
+6K7lnk1UNaz4By9MOJPbHkKUl1FCrwtQ1UhJsur1pUCbud2thz4YXQh3NyJ3X0m0
+G3R+tt7p2kJzdlU=
+-----END PRIVATE KEY-----
diff --git a/tests/hwsim/auth_serv/server-no-dnsname.pem b/tests/hwsim/auth_serv/server-no-dnsname.pem
new file mode 100644 (file)
index 0000000..1c745c5
--- /dev/null
@@ -0,0 +1,62 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 15624081837803162825 (0xd8d3e3a6cbe3ccc9)
+    Signature Algorithm: sha1WithRSAEncryption
+        Issuer: C=FI, O=w1.fi, CN=Root CA
+        Validity
+            Not Before: Feb 15 07:59:30 2014 GMT
+            Not After : Feb 15 07:59:30 2015 GMT
+        Subject: C=FI, O=w1.fi, CN=server3.w1.fi
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:db:fc:0f:a1:48:87:68:86:c4:9e:7a:f8:18:28:
+                    77:6d:a3:58:0f:db:be:6b:d4:43:43:c4:ba:17:37:
+                    9b:a8:ff:96:a2:73:14:13:1c:ae:19:0f:81:54:35:
+                    10:64:94:e3:64:65:be:9b:0b:16:d2:9e:92:98:77:
+                    87:e2:9a:99:fc:1d:57:0c:8a:0d:4c:21:2f:ae:67:
+                    d7:63:61:eb:91:5b:58:2f:ad:67:66:65:e4:83:07:
+                    ca:6a:36:a5:e6:20:f6:5c:99:27:db:63:e5:86:d8:
+                    2b:7c:1d:90:44:81:22:21:77:0a:03:05:c3:eb:63:
+                    61:b3:4c:32:c0:87:87:3a:47
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                8E:9A:4F:4D:46:AD:59:AC:7F:4C:9C:BE:6D:5B:D7:99:63:8D:C7:70
+            X509v3 Authority Key Identifier: 
+                keyid:B8:92:DE:FD:8A:18:B3:30:C3:9F:55:F3:33:5D:B4:C8:29:8A:41:14
+
+            Authority Information Access: 
+                OCSP - URI:http://server.w1.fi:8888/
+
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication
+    Signature Algorithm: sha1WithRSAEncryption
+         64:1e:41:7e:12:b1:d2:2d:fb:da:11:29:77:a4:99:13:6a:ff:
+         57:66:4f:30:fe:64:0e:b2:a1:5a:1a:55:37:4e:e1:1d:87:94:
+         b4:5d:9a:2e:2b:01:97:c6:22:b8:74:4b:58:22:83:db:c6:3e:
+         77:b7:73:5b:3b:83:a0:23:a3:c6:1f:33:6c:cf:b5:d6:36:89:
+         fc:ad:92:49:fd:ee:fb:8e:69:6c:84:18:0d:cc:39:01:21:35:
+         f6:46:77:8c:61:f7:18:1c:f6:da:0e:4d:90:69:ca:bd:e6:8d:
+         9b:e8:e6:b6:93:56:24:2d:da:59:0b:cd:cb:68:96:53:a3:16:
+         1f:ae
+-----BEGIN CERTIFICATE-----
+MIICfTCCAeagAwIBAgIJANjT46bL48zJMA0GCSqGSIb3DQEBBQUAMC8xCzAJBgNV
+BAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xNDAy
+MTUwNzU5MzBaFw0xNTAyMTUwNzU5MzBaMDUxCzAJBgNVBAYTAkZJMQ4wDAYDVQQK
+DAV3MS5maTEWMBQGA1UEAwwNc2VydmVyMy53MS5maTCBnzANBgkqhkiG9w0BAQEF
+AAOBjQAwgYkCgYEA2/wPoUiHaIbEnnr4GCh3baNYD9u+a9RDQ8S6FzebqP+WonMU
+ExyuGQ+BVDUQZJTjZGW+mwsW0p6SmHeH4pqZ/B1XDIoNTCEvrmfXY2HrkVtYL61n
+ZmXkgwfKajal5iD2XJkn22PlhtgrfB2QRIEiIXcKAwXD62Nhs0wywIeHOkcCAwEA
+AaOBmjCBlzAJBgNVHRMEAjAAMB0GA1UdDgQWBBSOmk9NRq1ZrH9MnL5tW9eZY43H
+cDAfBgNVHSMEGDAWgBS4kt79ihizMMOfVfMzXbTIKYpBFDA1BggrBgEFBQcBAQQp
+MCcwJQYIKwYBBQUHMAGGGWh0dHA6Ly9zZXJ2ZXIudzEuZmk6ODg4OC8wEwYDVR0l
+BAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQEFBQADgYEAZB5BfhKx0i372hEpd6SZ
+E2r/V2ZPMP5kDrKhWhpVN07hHYeUtF2aLisBl8YiuHRLWCKD28Y+d7dzWzuDoCOj
+xh8zbM+11jaJ/K2SSf3u+45pbIQYDcw5ASE19kZ3jGH3GBz22g5NkGnKveaNm+jm
+tpNWJC3aWQvNy2iWU6MWH64=
+-----END CERTIFICATE-----
diff --git a/tests/hwsim/auth_serv/server.key b/tests/hwsim/auth_serv/server.key
new file mode 100644 (file)
index 0000000..1416327
--- /dev/null
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALqgd1UiFIVVZZtk
+LK3tm91lMcnaYFDOONY03Oi8G54w5xLjU2zJ7UgDeYFpmM6KuHdHNkXPxuDxex5x
+iVT3AcwiraBCsag1nmCqOphR0P8f7r6NCmP7ojkX8mRh9mUCMnl04Z/RiWVVqcMg
+nr9pVrP3Tz+pVMLajzyv8nVU+n6BAgMBAAECgYBH8LlvcM62QyAC0Y/DkBeINY0G
+wY5lN8mDESei83g196XriwPKqOA15Vj+QOVtoN3Q5PuP17NTXOLX7m5A+WKQVK/O
+Cl0uBCEqS9YvPN6Fp9va5VonhWxGpLdZcrxETTpxjHhVBGS9C8wBday65r2nDfo6
+uWlCebceUBuuSzwybQJBAOAwS7ZY8xY/bCNDzvfnNuPsPQDEQWVx6A9mv9BnepT/
+8bQcvfkUbXyWy5NsPN6yt/tqmjdbUEFAuNJlI23I2wsCQQDVG7poTL8KPa7UZge7
+W79FyyEoL5but1VPTAN6JJNTMpp9k2LBWFjUSmTiTkeccHfbvKxMjUuI7NQwya41
+hSQjAkApSRuYUBcsIK/kaqdhxeW44Zd2Xa4BZZGrzGtEkNnlOKElXympBhcHm6mP
+053+EQGKvl36FcnYynd+33s/y35zAkAZ5ZC1c/4TJIPGU8/EuNV5icGxvHa+85Bu
+XnJduWwdxBx5/hsWG8JPqeqwhYq2PASUs0zM0K7JKN5wP1HoNxG5AkEAlaumCfLv
+vA/b3HVZD/b0nxkl/F7g3nACPVJ48FU2BneB+bU75zqeI3B7xGd9CKamkuutH6or
+fe17ZI8ZeLCLqA==
+-----END PRIVATE KEY-----
diff --git a/tests/hwsim/auth_serv/server.pem b/tests/hwsim/auth_serv/server.pem
new file mode 100644 (file)
index 0000000..fa3e0ae
--- /dev/null
@@ -0,0 +1,64 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 15624081837803162832 (0xd8d3e3a6cbe3ccd0)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: C=FI, O=w1.fi, CN=Root CA
+        Validity
+            Not Before: Sep 29 21:11:22 2014 GMT
+            Not After : Sep 29 21:11:22 2015 GMT
+        Subject: C=FI, O=w1.fi, CN=server.w1.fi
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:ba:a0:77:55:22:14:85:55:65:9b:64:2c:ad:ed:
+                    9b:dd:65:31:c9:da:60:50:ce:38:d6:34:dc:e8:bc:
+                    1b:9e:30:e7:12:e3:53:6c:c9:ed:48:03:79:81:69:
+                    98:ce:8a:b8:77:47:36:45:cf:c6:e0:f1:7b:1e:71:
+                    89:54:f7:01:cc:22:ad:a0:42:b1:a8:35:9e:60:aa:
+                    3a:98:51:d0:ff:1f:ee:be:8d:0a:63:fb:a2:39:17:
+                    f2:64:61:f6:65:02:32:79:74:e1:9f:d1:89:65:55:
+                    a9:c3:20:9e:bf:69:56:b3:f7:4f:3f:a9:54:c2:da:
+                    8f:3c:af:f2:75:54:fa:7e:81
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                31:4F:10:5C:67:9F:BE:4E:88:D6:DC:C5:AB:9E:12:88:86:69:02:4F
+            X509v3 Authority Key Identifier: 
+                keyid:B8:92:DE:FD:8A:18:B3:30:C3:9F:55:F3:33:5D:B4:C8:29:8A:41:14
+
+            Authority Information Access: 
+                OCSP - URI:http://server.w1.fi:8888/
+
+            X509v3 Subject Alternative Name: 
+                DNS:server.w1.fi
+            X509v3 Extended Key Usage: 
+                TLS Web Server Authentication
+    Signature Algorithm: sha256WithRSAEncryption
+         92:b7:19:2f:15:84:00:c6:68:01:ba:96:67:11:df:7d:0c:1e:
+         45:eb:59:e5:64:ad:db:f0:23:ce:22:af:a0:35:a2:6f:99:96:
+         9d:2d:bc:b5:8d:58:36:c7:71:f4:fb:c8:a5:e8:44:45:52:7e:
+         1e:44:dd:99:3b:1c:40:f1:f7:73:ec:f9:b7:fc:06:cc:a9:a5:
+         37:41:d1:20:2b:b5:93:75:26:1b:46:2e:3d:25:a3:5e:e9:7e:
+         73:37:9d:e7:71:6f:bb:21:22:cc:31:3e:a2:3f:18:05:ca:35:
+         d2:98:b8:53:6b:92:ac:73:10:8d:8a:09:a4:e3:46:ad:28:72:
+         ab:51
+-----BEGIN CERTIFICATE-----
+MIIClTCCAf6gAwIBAgIJANjT46bL48zQMA0GCSqGSIb3DQEBCwUAMC8xCzAJBgNV
+BAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xNDA5
+MjkyMTExMjJaFw0xNTA5MjkyMTExMjJaMDQxCzAJBgNVBAYTAkZJMQ4wDAYDVQQK
+DAV3MS5maTEVMBMGA1UEAwwMc2VydmVyLncxLmZpMIGfMA0GCSqGSIb3DQEBAQUA
+A4GNADCBiQKBgQC6oHdVIhSFVWWbZCyt7ZvdZTHJ2mBQzjjWNNzovBueMOcS41Ns
+ye1IA3mBaZjOirh3RzZFz8bg8XsecYlU9wHMIq2gQrGoNZ5gqjqYUdD/H+6+jQpj
++6I5F/JkYfZlAjJ5dOGf0YllVanDIJ6/aVaz908/qVTC2o88r/J1VPp+gQIDAQAB
+o4GzMIGwMAkGA1UdEwQCMAAwHQYDVR0OBBYEFDFPEFxnn75OiNbcxaueEoiGaQJP
+MB8GA1UdIwQYMBaAFLiS3v2KGLMww59V8zNdtMgpikEUMDUGCCsGAQUFBwEBBCkw
+JzAlBggrBgEFBQcwAYYZaHR0cDovL3NlcnZlci53MS5maTo4ODg4LzAXBgNVHREE
+EDAOggxzZXJ2ZXIudzEuZmkwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcN
+AQELBQADgYEAkrcZLxWEAMZoAbqWZxHffQweRetZ5WSt2/AjziKvoDWib5mWnS28
+tY1YNsdx9PvIpehERVJ+HkTdmTscQPH3c+z5t/wGzKmlN0HRICu1k3UmG0YuPSWj
+Xul+czed53FvuyEizDE+oj8YBco10pi4U2uSrHMQjYoJpONGrShyq1E=
+-----END CERTIFICATE-----
diff --git a/tests/hwsim/auth_serv/server.pkcs12 b/tests/hwsim/auth_serv/server.pkcs12
new file mode 100644 (file)
index 0000000..7061fd7
Binary files /dev/null and b/tests/hwsim/auth_serv/server.pkcs12 differ
diff --git a/tests/hwsim/auth_serv/user.key b/tests/hwsim/auth_serv/user.key
new file mode 100644 (file)
index 0000000..b9fd702
--- /dev/null
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICeAIBADANBgkqhkiG9w0BAQEFAASCAmIwggJeAgEAAoGBAKaWLpsijN+UvouJ
+SfZ4dqJgfhSV85b+qxklAzRkdAE+qJ988UdhYEyCkih8K6AOy4e/WevX82EiOxTz
+qzH2WpUfuHq4LDypYVN4m+g+UOzC1kTnQ828Pk7nRv6SnsOYDylYyMuJAXVH6ZVX
+D3bFLwVexx4N8jwSY125VBmvf0BrAgMBAAECgYEAkEoS0kKJ3Hqc1IW0r6xFrX2A
+l1oOpCGvl1bswKuloxJfwczZu+cHHx4VdMWgj8Fg3xKJ03K4FtEsdYhdJyhn6c6G
+YsKF7HHGo2WA61VHxgqRB/CZzALy2JR/3rzElvrVQ5ZVh15DipNpwfwP9bW6P99A
+omPQVnZ3p1HgU5WK68kCQQDXHbFUYX3I9SYlR4JhPy5ov2Q8WHu4p9rWXGBO75uS
+7f3FZCbGULKZEOsiVFbloyUdpvLId7wvb343a1EAOnC9AkEAxj9UqsKMAdlXTDrT
+9NcQmJKWt568gEV4/45fjpTzbdndEOtCMwWBWEv/SyiWgWdwPeBViRGEyPrkLV/S
+teesRwJBAIfN6QuaWKyrh591W6xFFOlwGrm2KrVS0ucNfoeW4SKLOPCK36fHflj/
+w1Hy6MEkk+P6Z7+DR7yyqH4YNBTu0AkCQA7uZioWTQU2oWSUabJfIFjdcYyS4A+p
+K9vTlU7f2RXE+ulzTqEZIQzNbIT0oaFNcR637rlMIHwiqVzhgrVApbECQQDK5QqX
+E6Z2VHTNEnCki9YvkgjPhLxSihQMDSaR0ENkre0OctFUufbwzH7DEhzV6CQ1Uw+9
+Au5AOFzcb1tfGczP
+-----END PRIVATE KEY-----
diff --git a/tests/hwsim/auth_serv/user.pem b/tests/hwsim/auth_serv/user.pem
new file mode 100644 (file)
index 0000000..4bc2e1a
--- /dev/null
@@ -0,0 +1,62 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 15624081837803162833 (0xd8d3e3a6cbe3ccd1)
+    Signature Algorithm: sha256WithRSAEncryption
+        Issuer: C=FI, O=w1.fi, CN=Root CA
+        Validity
+            Not Before: Sep 29 21:13:00 2014 GMT
+            Not After : Sep 29 21:13:00 2015 GMT
+        Subject: C=FI, O=w1.fi, CN=Test User
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:a6:96:2e:9b:22:8c:df:94:be:8b:89:49:f6:78:
+                    76:a2:60:7e:14:95:f3:96:fe:ab:19:25:03:34:64:
+                    74:01:3e:a8:9f:7c:f1:47:61:60:4c:82:92:28:7c:
+                    2b:a0:0e:cb:87:bf:59:eb:d7:f3:61:22:3b:14:f3:
+                    ab:31:f6:5a:95:1f:b8:7a:b8:2c:3c:a9:61:53:78:
+                    9b:e8:3e:50:ec:c2:d6:44:e7:43:cd:bc:3e:4e:e7:
+                    46:fe:92:9e:c3:98:0f:29:58:c8:cb:89:01:75:47:
+                    e9:95:57:0f:76:c5:2f:05:5e:c7:1e:0d:f2:3c:12:
+                    63:5d:b9:54:19:af:7f:40:6b
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Subject Key Identifier: 
+                81:DE:DF:E9:5A:00:1A:CA:67:D6:06:DD:65:B2:4E:C5:9A:04:43:7D
+            X509v3 Authority Key Identifier: 
+                keyid:B8:92:DE:FD:8A:18:B3:30:C3:9F:55:F3:33:5D:B4:C8:29:8A:41:14
+
+            Authority Information Access: 
+                OCSP - URI:http://server.w1.fi:8888/
+
+            X509v3 Extended Key Usage: 
+                TLS Web Client Authentication
+    Signature Algorithm: sha256WithRSAEncryption
+         76:24:5a:f8:de:ef:8b:65:02:67:ab:8f:3a:42:88:22:35:40:
+         48:df:97:91:9b:5a:d4:60:af:61:ef:53:7a:2c:76:04:5f:80:
+         27:79:7e:1f:0a:ed:ab:71:0c:6e:90:7a:69:04:4e:21:cb:31:
+         47:ee:e9:36:87:a9:f4:a1:dd:e9:8a:fd:41:cc:d2:ae:dd:47:
+         66:d1:71:08:b2:e8:5e:e3:36:9e:98:c8:66:51:5b:41:95:02:
+         29:fd:b6:46:d2:40:ec:0e:46:40:92:b1:b7:e2:28:6e:85:17:
+         1e:8d:52:40:c8:20:ca:9b:ab:f0:10:30:8c:0b:5d:91:91:8c:
+         ff:ca
+-----BEGIN CERTIFICATE-----
+MIICeTCCAeKgAwIBAgIJANjT46bL48zRMA0GCSqGSIb3DQEBCwUAMC8xCzAJBgNV
+BAYTAkZJMQ4wDAYDVQQKDAV3MS5maTEQMA4GA1UEAwwHUm9vdCBDQTAeFw0xNDA5
+MjkyMTEzMDBaFw0xNTA5MjkyMTEzMDBaMDExCzAJBgNVBAYTAkZJMQ4wDAYDVQQK
+DAV3MS5maTESMBAGA1UEAwwJVGVzdCBVc2VyMIGfMA0GCSqGSIb3DQEBAQUAA4GN
+ADCBiQKBgQCmli6bIozflL6LiUn2eHaiYH4UlfOW/qsZJQM0ZHQBPqiffPFHYWBM
+gpIofCugDsuHv1nr1/NhIjsU86sx9lqVH7h6uCw8qWFTeJvoPlDswtZE50PNvD5O
+50b+kp7DmA8pWMjLiQF1R+mVVw92xS8FXsceDfI8EmNduVQZr39AawIDAQABo4Ga
+MIGXMAkGA1UdEwQCMAAwHQYDVR0OBBYEFIHe3+laABrKZ9YG3WWyTsWaBEN9MB8G
+A1UdIwQYMBaAFLiS3v2KGLMww59V8zNdtMgpikEUMDUGCCsGAQUFBwEBBCkwJzAl
+BggrBgEFBQcwAYYZaHR0cDovL3NlcnZlci53MS5maTo4ODg4LzATBgNVHSUEDDAK
+BggrBgEFBQcDAjANBgkqhkiG9w0BAQsFAAOBgQB2JFr43u+LZQJnq486QogiNUBI
+35eRm1rUYK9h71N6LHYEX4AneX4fCu2rcQxukHppBE4hyzFH7uk2h6n0od3piv1B
+zNKu3Udm0XEIsuhe4zaemMhmUVtBlQIp/bZG0kDsDkZAkrG34ihuhRcejVJAyCDK
+m6vwEDCMC12RkYz/yg==
+-----END CERTIFICATE-----
diff --git a/tests/hwsim/auth_serv/user.pkcs12 b/tests/hwsim/auth_serv/user.pkcs12
new file mode 100644 (file)
index 0000000..9e0ced0
Binary files /dev/null and b/tests/hwsim/auth_serv/user.pkcs12 differ
diff --git a/tests/hwsim/auth_serv/user.rsa-key b/tests/hwsim/auth_serv/user.rsa-key
new file mode 100644 (file)
index 0000000..4c3cfbf
--- /dev/null
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQCmli6bIozflL6LiUn2eHaiYH4UlfOW/qsZJQM0ZHQBPqiffPFH
+YWBMgpIofCugDsuHv1nr1/NhIjsU86sx9lqVH7h6uCw8qWFTeJvoPlDswtZE50PN
+vD5O50b+kp7DmA8pWMjLiQF1R+mVVw92xS8FXsceDfI8EmNduVQZr39AawIDAQAB
+AoGBAJBKEtJCidx6nNSFtK+sRa19gJdaDqQhr5dW7MCrpaMSX8HM2bvnBx8eFXTF
+oI/BYN8SidNyuBbRLHWIXScoZ+nOhmLChexxxqNlgOtVR8YKkQfwmcwC8tiUf968
+xJb61UOWVYdeQ4qTacH8D/W1uj/fQKJj0FZ2d6dR4FOViuvJAkEA1x2xVGF9yPUm
+JUeCYT8uaL9kPFh7uKfa1lxgTu+bku39xWQmxlCymRDrIlRW5aMlHabyyHe8L29+
+N2tRADpwvQJBAMY/VKrCjAHZV0w60/TXEJiSlreevIBFeP+OX46U823Z3RDrQjMF
+gVhL/0soloFncD3gVYkRhMj65C1f0rXnrEcCQQCHzekLmlisq4efdVusRRTpcBq5
+tiq1UtLnDX6HluEiizjwit+nx35Y/8NR8ujBJJPj+me/g0e8sqh+GDQU7tAJAkAO
+7mYqFk0FNqFklGmyXyBY3XGMkuAPqSvb05VO39kVxPrpc06hGSEMzWyE9KGhTXEe
+t+65TCB8Iqlc4YK1QKWxAkEAyuUKlxOmdlR0zRJwpIvWL5IIz4S8UooUDA0mkdBD
+ZK3tDnLRVLn28Mx+wxIc1egkNVMPvQLuQDhc3G9bXxnMzw==
+-----END RSA PRIVATE KEY-----
index 464f026..83defb0 100644 (file)
@@ -1,7 +1,5 @@
-#!/usr/bin/python
-#
 # Python class for controlling hostapd
-# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+# 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.
@@ -9,12 +7,17 @@
 import os
 import time
 import logging
+import binascii
+import struct
 import wpaspy
 
-logger = logging.getLogger(__name__)
+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):
         self.ctrl = wpaspy.Ctrl(hapd_global)
@@ -24,14 +27,39 @@ class HostapdGlobal:
         if not "OK" in res:
             raise Exception("Could not add hostapd interface " + ifname)
 
+    def add_iface(self, ifname, confname):
+        res = self.ctrl.request("ADD " + ifname + " config=" + confname)
+        if not "OK" in res:
+            raise Exception("Could not add hostapd interface")
+
+    def add_bss(self, phy, confname, ignore_error=False):
+        res = self.ctrl.request("ADD bss_config=" + phy + ":" + confname)
+        if not "OK" in res:
+            if not ignore_error:
+                raise Exception("Could not add hostapd BSS")
+
     def remove(self, ifname):
-        self.ctrl.request("REMOVE " + ifname)
+        self.ctrl.request("REMOVE " + ifname, timeout=30)
+
+    def relog(self):
+        self.ctrl.request("RELOG")
+
+    def flush(self):
+        self.ctrl.request("FLUSH")
 
 
 class Hostapd:
     def __init__(self, ifname):
         self.ifname = ifname
         self.ctrl = wpaspy.Ctrl(os.path.join(hapd_ctrl, ifname))
+        self.mon = wpaspy.Ctrl(os.path.join(hapd_ctrl, ifname))
+        self.mon.attach()
+        self.bssid = None
+
+    def own_addr(self):
+        if self.bssid is None:
+            self.bssid = self.get_status_field('bssid[0]')
+        return self.bssid
 
     def request(self, cmd):
         logger.debug(self.ifname + ": CTRL: " + cmd)
@@ -41,7 +69,6 @@ class Hostapd:
         return "PONG" in self.request("PING")
 
     def set(self, field, value):
-        logger.debug(self.ifname + ": SET " + field + "=" + value)
         if not "OK" in self.request("SET " + field + " " + value):
             raise Exception("Failed to set hostapd parameter " + field)
 
@@ -50,6 +77,8 @@ class Hostapd:
         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()
@@ -86,14 +115,138 @@ class Hostapd:
         self.set("wep_key0", key)
 
     def enable(self):
-        if not "OK" in self.ctrl.request("ENABLE"):
+        if not "OK" in self.request("ENABLE"):
             raise Exception("Failed to enable hostapd interface " + self.ifname)
 
     def disable(self):
-        if not "OK" in self.ctrl.request("ENABLE"):
+        if not "OK" in self.request("DISABLE"):
             raise Exception("Failed to disable hostapd interface " + self.ifname)
 
-def add_ap(ifname, params):
+    def dump_monitor(self):
+        while self.mon.pending():
+            ev = self.mon.recv()
+            logger.debug(self.ifname + ": " + ev)
+
+    def wait_event(self, events, timeout):
+        start = os.times()[4]
+        while True:
+            while self.mon.pending():
+                ev = self.mon.recv()
+                logger.debug(self.ifname + ": " + 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 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)
+        self.request("MGMT_TX " + binascii.hexlify(hdr + msg['payload']))
+
+    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 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 add_ap(ifname, params, wait_enabled=True, no_enable=False):
         logger.info("Starting AP " + ifname)
         hapd_global = HostapdGlobal()
         hapd_global.remove(ifname)
@@ -104,7 +257,8 @@ def add_ap(ifname, params):
         hapd.set_defaults()
         fields = [ "ssid", "wpa_passphrase", "nas_identifier", "wpa_key_mgmt",
                    "wpa",
-                   "wpa_pairwise", "rsn_pairwise", "auth_server_addr" ]
+                   "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])
@@ -116,7 +270,37 @@ def add_ap(ifname, params):
                     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=30)
+            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(phy, ifname, confname, ignore_error=False):
+    logger.info("Starting BSS phy=" + phy + " ifname=" + ifname)
+    hapd_global = HostapdGlobal()
+    hapd_global.add_bss(phy, confname, ignore_error)
+    hapd = Hostapd(ifname)
+    if not hapd.ping():
+        raise Exception("Could not ping hostapd")
+
+def add_iface(ifname, confname):
+    logger.info("Starting interface " + ifname)
+    hapd_global = HostapdGlobal()
+    hapd_global.add_iface(ifname, confname)
+    hapd = Hostapd(ifname)
+    if not hapd.ping():
+        raise Exception("Could not ping hostapd")
+
+def remove_bss(ifname):
+    logger.info("Removing BSS " + ifname)
+    hapd_global = HostapdGlobal()
+    hapd_global.remove(ifname)
 
 def wpa2_params(ssid=None, passphrase=None):
     params = { "wpa": "2",
@@ -148,3 +332,30 @@ def wpa_mixed_params(ssid=None, passphrase=None):
     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
index b48cc41..edf0a42 100644 (file)
-#!/usr/bin/python
-#
 # hwsim testing utilities
-# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+# 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 subprocess
+import time
 import logging
-logger = logging.getLogger(__name__)
-
-def test_connectivity(ifname1, ifname2):
-    if os.path.isfile("../../mac80211_hwsim/tools/hwsim_test"):
-        hwsim_test = "../../mac80211_hwsim/tools/hwsim_test"
-    else:
-        hwsim_test = "hwsim_test"
-    cmd = ["sudo",
-           hwsim_test,
-           ifname1,
-           ifname2]
+logger = logging.getLogger()
+
+from wpasupplicant import WpaSupplicant
+
+def run_connectivity_test(dev1, dev2, tos, dev1group=False, dev2group=False,
+                          ifname1=None, ifname2=None, config=True):
+    addr1 = dev1.own_addr()
+    if not dev1group and isinstance(dev1, WpaSupplicant):
+        addr1 = dev1.get_driver_status_field('addr')
+
+    addr2 = dev2.own_addr()
+    if not dev2group and isinstance(dev2, WpaSupplicant):
+        addr2 = dev2.get_driver_status_field('addr')
+
+    dev1.dump_monitor()
+    dev2.dump_monitor()
+
     try:
-        s = subprocess.check_output(cmd)
-        logger.debug(s)
-    except subprocess.CalledProcessError, e:
-        print "hwsim failed: " + str(e.returncode)
-        print e.output
-        raise
-
-def test_connectivity_p2p(dev1, dev2):
-    ifname1 = dev1.group_ifname if dev1.group_ifname else dev1.ifname
-    ifname2 = dev2.group_ifname if dev2.group_ifname else dev2.ifname
-    test_connectivity(ifname1, ifname2)
-
-def test_connectivity_sta(dev1, dev2):
-    ifname1 = dev1.ifname
-    ifname2 = dev2.ifname
-    test_connectivity(ifname1, ifname2)
+        if config:
+            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")
+
+        cmd = "DATA_TEST_TX {} {} {}".format(addr2, addr1, tos)
+        if dev1group:
+            dev1.group_request(cmd)
+        else:
+            dev1.request(cmd)
+        if dev2group:
+            ev = dev2.wait_group_event(["DATA-TEST-RX"], timeout=5)
+        else:
+            ev = dev2.wait_event(["DATA-TEST-RX"], timeout=5)
+        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")
+
+        cmd = "DATA_TEST_TX ff:ff:ff:ff:ff:ff {} {}".format(addr1, tos)
+        if dev1group:
+            dev1.group_request(cmd)
+        else:
+            dev1.request(cmd)
+        if dev2group:
+            ev = dev2.wait_group_event(["DATA-TEST-RX"], timeout=5)
+        else:
+            ev = dev2.wait_event(["DATA-TEST-RX"], timeout=5)
+        if ev is None:
+            raise Exception("dev1->dev2 broadcast data delivery failed")
+        if "DATA-TEST-RX ff:ff:ff:ff:ff:ff {}".format(addr1) not in ev:
+            raise Exception("Unexpected dev1->dev2 broadcast data result")
+
+        cmd = "DATA_TEST_TX {} {} {}".format(addr1, addr2, tos)
+        if dev2group:
+            dev2.group_request(cmd)
+        else:
+            dev2.request(cmd)
+        if dev1group:
+            ev = dev1.wait_group_event(["DATA-TEST-RX"], timeout=5)
+        else:
+            ev = dev1.wait_event(["DATA-TEST-RX"], timeout=5)
+        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")
+
+        cmd = "DATA_TEST_TX ff:ff:ff:ff:ff:ff {} {}".format(addr2, tos)
+        if dev2group:
+            dev2.group_request(cmd)
+        else:
+            dev2.request(cmd)
+        if dev1group:
+            ev = dev1.wait_group_event(["DATA-TEST-RX"], timeout=5)
+        else:
+            ev = dev1.wait_event(["DATA-TEST-RX"], timeout=5)
+        if ev is None:
+            raise Exception("dev2->dev1 broadcast data delivery failed")
+        if "DATA-TEST-RX ff:ff:ff:ff:ff:ff {}".format(addr2) not in ev:
+            raise Exception("Unexpected dev2->dev1 broadcast data result")
+    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):
+    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)
+            success = True
+            break
+        except Exception, e:
+            last_err = e
+            if i + 1 < max_tries:
+                time.sleep(1)
+    if not success:
+        raise Exception(last_err)
+
+def test_connectivity_iface(dev1, dev2, ifname, dscp=None, tos=None,
+                            max_tries=1):
+    test_connectivity(dev1, dev2, dscp, tos, ifname2=ifname,
+                      max_tries=max_tries)
+
+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)
old mode 100755 (executable)
new mode 100644 (file)
index 3ef4666..9bdb7b1
 
 errors=0
 umask 0002
-./start.sh
-DATE=`ls -1tr logs | tail -1 | cut -f1 -d-`
-./run-tests.py -e logs/$DATE-failed || errors=1
-./stop-wifi.sh
+
+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
+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
+                       ;;
+               *)
+                       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 $VALGRIND $TRACE $NUM_CH; then
+       if ! [ -z "$LOGBASEDIR" ] ; then
+               echo "Could not start test environment" > $LOGDIR/run
+       fi
+       exit 1
+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 [ ! -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
-    tar czf /tmp/hwsim-tests-$DATE-FAILED.tar.gz logs/$DATE*
+    tar czf /tmp/hwsim-tests-$DATE-FAILED$SUFFIX.tar.gz $LOGDIR/
     exit 1
 fi
+
+echo "ALL-PASSED"
old mode 100755 (executable)
new mode 100644 (file)
index 364115a..09a3acd
@@ -1,7 +1,7 @@
-#!/usr/bin/python
+#!/usr/bin/env python2
 #
-# AP tests
-# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+# Test case executor
+# 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.
@@ -10,44 +10,304 @@ import os
 import re
 import sys
 import time
+from datetime import datetime
+import argparse
+import subprocess
+import termios
 
 import logging
+logger = logging.getLogger()
+
+if os.path.exists('../../wpaspy'):
+    sys.path.append('../../wpaspy')
+else:
+    sys.path.append('../../../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):
-    hapd = HostapdGlobal()
+    ok = True
     for d in dev:
-        d.reset()
-    for ap in apdev:
-        hapd.remove(ap['ifname'])
+        try:
+            d.reset()
+        except Exception, e:
+            logger.info("Failed to reset device " + d.ifname)
+            print str(e)
+            ok = False
+
+    wpas = None
+    try:
+        wpas = WpaSupplicant(global_iface='/tmp/wpas-wlan5')
+        ifaces = wpas.global_request("INTERFACES").splitlines()
+        for iface in ifaces:
+            if iface.startswith("wlan"):
+                wpas.interface_remove(iface)
+    except Exception, e:
+        pass
+    if wpas:
+        wpas.close_ctrl()
+
+    try:
+        hapd = HostapdGlobal()
+        hapd.flush()
+        hapd.remove('wlan3-3')
+        hapd.remove('wlan3-2')
+        for ap in apdev:
+            hapd.remove(ap['ifname'])
+    except Exception, 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, 'r') as f:
+        contents = f.read()
+    if contents is None:
+        return
+    sql = "INSERT INTO logs(test,run,type,contents) VALUES(?, ?, ?, ?)"
+    params = (test, run, type, contents)
+    try:
+        conn.execute(sql, params)
+        conn.commit()
+    except Exception, 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, 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, tracing, dmesg):
+        self._logdir = logdir
+        self._testname = testname
+        self._tracing = tracing
+        self._dmesg = dmesg
+    def __enter__(self):
+        if self._tracing:
+            output = os.path.abspath(os.path.join(self._logdir, '%s.dat' % (self._testname, )))
+            self._trace_cmd = subprocess.Popen(['sudo', 'trace-cmd', 'record', '-o', output, '-e', 'mac80211', '-e', 'cfg80211', '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 not 'STARTED' 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)
+    def __exit__(self, type, value, traceback):
+        if self._tracing:
+            self._trace_cmd.stdin.write('DONE\n')
+            self._trace_cmd.wait()
+        if self._dmesg:
+            output = os.path.join(self._logdir, '%s.dmesg' % (self._testname, ))
+            subprocess.call(['sudo', '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(['sudo', 'chown', '-f', getpass.getuser(), srcname])
+    except Exception, e:
+        logger.info("Failed to rename log files")
+        logger.info(e)
 
 def main():
-    test_file = None
-    error_file = None
-    idx = 1
-    if len(sys.argv) > 1 and sys.argv[1] == '-d':
-        logging.basicConfig(level=logging.DEBUG)
-        idx = idx + 1
-    elif len(sys.argv) > 1 and sys.argv[1] == '-q':
-        logging.basicConfig(level=logging.WARNING)
-        idx = idx + 1
+    tests = []
+    test_modules = []
+    if os.path.exists('run-tests.py'):
+        files = os.listdir(".")
+    else:
+        files = os.listdir("..")
+    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__.iteritems():
+                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('--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)',
+                        choices=[[]] + test_names)
+
+    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.database:
+        import sqlite3
+        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:
-        logging.basicConfig(level=logging.INFO)
+        conn = None
 
-    if len(sys.argv) > idx + 1 and sys.argv[idx] == '-e':
-        error_file = sys.argv[idx + 1]
-        idx = idx + 2
+    if conn:
+        run = int(time.time())
 
-    if len(sys.argv) > idx + 1 and sys.argv[idx] == '-f':
-        test_file = sys.argv[idx + 1]
-        idx = idx + 2
+    # 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)
 
-    if len(sys.argv) > idx:
-        test_filter = sys.argv[idx]
+    tests_to_run = []
+    if args.tests:
+        for selected in args.tests:
+            for t in tests:
+                name = t.__name__.replace('test_', '', 1)
+                if name == selected:
+                    tests_to_run.append(t)
     else:
-        test_filter = None
+        for t in tests:
+            name = t.__name__.replace('test_', '', 1)
+            if args.testmodules:
+                if not t.__module__.replace('test_', '', 1) 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)
+            if t.__doc__ is None:
+                print name + " - MISSING DESCRIPTION"
+            else:
+                print name + " - " + t.__doc__
+            if conn:
+                sql = 'INSERT OR REPLACE INTO tests(test,description) VALUES (?, ?)'
+                params = (name, t.__doc__)
+                try:
+                    conn.execute(sql, params)
+                except Exception, 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)
+    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')
@@ -59,65 +319,231 @@ def main():
 
     for d in dev:
         if not d.ping():
-            print d.ifname + ": No response from wpa_supplicant"
+            logger.info(d.ifname + ": No response from wpa_supplicant")
             return
-        print "DEV: " + d.ifname + ": " + d.p2p_dev_addr()
+        logger.info("DEV: " + d.ifname + ": " + d.p2p_dev_addr())
     for ap in apdev:
-        print "APDEV: " + ap['ifname']
-
-    tests = []
-    for t in os.listdir("."):
-        m = re.match(r'(test_.*)\.py$', t)
-        if m:
-            if test_file and test_file not in t:
-                continue
-            print "Import test cases from " + t
-            mod = __import__(m.group(1))
-            for s in dir(mod):
-                if s.startswith("test_"):
-                    func = mod.__dict__.get(s)
-                    tests.append(func)
+        logger.info("APDEV: " + ap['ifname'])
 
     passed = []
+    skipped = []
     failed = []
 
-    for t in tests:
-        if test_filter:
-            if test_filter != t.__name__:
+    # 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(['sudo', '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)
+    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
-        reset_devs(dev, apdev)
-        print "START " + t.__name__
-        if t.__doc__:
-            print "Test: " + t.__doc__
-        for d in dev:
-            d.request("NOTE TEST-START " + t.__name__)
-        try:
-            if t.func_code.co_argcount > 1:
-                t(dev, apdev)
+        else:
+            if len(tests_to_run) == 0:
+                break
+            t = tests_to_run.pop(0)
+
+        name = t.__name__.replace('test_', '', 1)
+        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)
+            log_handler.setLevel(logging.DEBUG)
+            log_handler.setFormatter(log_formatter)
+            logger.addHandler(log_handler)
+
+        reset_ok = True
+        with DataCollector(args.logdir, name, args.tracing, args.dmesg):
+            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()
+            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, 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)
+            try:
+                if t.func_code.co_argcount > 2:
+                    params = {}
+                    params['logdir'] = args.logdir
+                    params['long'] = args.long
+                    t(dev, apdev, params)
+                elif t.func_code.co_argcount > 1:
+                    t(dev, apdev)
+                else:
+                    t(dev)
+                result = "PASS"
+            except HwsimSkip, e:
+                logger.info("Skip test case: %s" % e)
+                result = "SKIP"
+            except Exception, e:
+                logger.info(e)
+                if args.loglevel == logging.WARNING:
+                    print "Exception: " + str(e)
+                result = "FAIL"
+            for d in dev:
+                try:
+                    d.dump_monitor()
+                    d.request("NOTE TEST-STOP " + name)
+                except Exception, e:
+                    logger.info("Failed to issue TEST-STOP after {} for {}".format(name, d.ifname))
+                    logger.info(e)
+                    result = "FAIL"
+            wpas = None
+            try:
+                wpas = WpaSupplicant(global_iface="/tmp/wpas-wlan5")
+                rename_log(args.logdir, 'log5', name, wpas)
+                if not args.no_reset:
+                    wpas.remove_ifname()
+            except Exception, e:
+                pass
+            if wpas:
+                wpas.close_ctrl()
+            if args.no_reset:
+                print "Leaving devices in current state"
             else:
-                t(dev)
-            passed.append(t.__name__)
-            print "PASS " + t.__name__
-        except Exception, e:
-            print e
-            failed.append(t.__name__)
-            print "FAIL " + t.__name__
-        for d in dev:
-            d.request("NOTE TEST-STOP " + t.__name__)
+                reset_ok = reset_devs(dev, apdev)
+
+            for i in range(0, 3):
+                rename_log(args.logdir, 'log' + str(i), name, dev[i])
+            try:
+                hapd = HostapdGlobal()
+            except Exception, 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)
+
+            wt = Wlantest()
+            rename_log(args.logdir, 'hwsim0.pcapng', name, wt)
+            rename_log(args.logdir, 'hwsim0', name, wt)
+
+        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
+            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)
+        log_handler.setLevel(logging.DEBUG)
+        log_handler.setFormatter(log_formatter)
+        logger.addHandler(log_handler)
 
-    if not test_filter:
-        reset_devs(dev, apdev)
+    if conn:
+        conn.close()
 
-    print
     if len(failed):
-        print "passed " + str(len(passed)) + " test case(s)"
-        print "failed tests: " + str(failed)
-        if error_file:
-            f = open(error_file, 'w')
-            f.write(str(failed) + '\n')
-            f.close()
+        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)
-    print "passed all " + str(len(passed)) + " test case(s)"
+    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()
old mode 100755 (executable)
new mode 100644 (file)
index be5070d..df8a76d
 
 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
+DATE="$(date +%s)"
 
-$DIR/stop-wifi.sh
-sudo modprobe mac80211_hwsim radios=5
-mkdir -p $DIR/logs
-DATE=`date +%s`
+if [ -z "$LOGDIR" ] ; then
+    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
+    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
+
+if test -w "$DIR/logs" ; then
+    rm -rf $DIR/logs/current
+    ln -sf $DATE $DIR/logs/current
+fi
+
+if groups | tr ' ' "\n" | grep -q ^admin$; then
+    GROUP=admin
+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"
+
+if [ "$1" = "valgrind" ]; then
+    VALGRIND=y
+    VALGRIND_WPAS="valgrind --log-file=$LOGDIR/valgrind-wlan%d"
+    VALGRIND_HAPD="valgrind --log-file=$LOGDIR/valgrind-hostapd"
+    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 $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 -f /proc/modules && sudo modprobe mac80211_hwsim radios=6 channels=$NUM_CH support_p2p_device=0
 sudo ifconfig hwsim0 up
-sudo $WLANTEST -i hwsim0 -c -d > $DIR/logs/$DATE-hwsim0 &
-sudo tcpdump -ni hwsim0 -s 2500 -w $DIR/logs/$DATE-hwsim0.dump &
-sudo $WPAS -g /tmp/wpas-wlan0 -Gadmin -Dnl80211 -iwlan0 -c $DIR/p2p0.conf -ddKt > $DIR/logs/$DATE-log0 &
-sudo $WPAS -g /tmp/wpas-wlan1 -Gadmin -Dnl80211 -iwlan1 -c $DIR/p2p1.conf -ddKt > $DIR/logs/$DATE-log1 &
-sudo $WPAS -g /tmp/wpas-wlan2 -Gadmin -Dnl80211 -iwlan2 -c $DIR/p2p2.conf -ddKt > $DIR/logs/$DATE-log2 &
-sudo $HAPD -ddKt -g /var/run/hostapd-global -G admin -ddKt > $DIR/logs/$DATE-hostapd &
-sleep 1
-sudo chown $USER $DIR/logs/$DATE-hwsim0.dump
+sudo $WLANTEST -i hwsim0 -n $LOGDIR/hwsim0.pcapng -c -dt -L $LOGDIR/hwsim0 &
+for i in 0 1 2; do
+    DBUSARG=""
+    if [ $i = "0" -a -r /var/run/dbus/pid -a -r /var/run/dbus/hwsim-test ]; 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 -ddKt -f $LOGDIR/hostapd &
+
+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
+
+for i in unknown revoked; do
+    openssl ocsp -index $DIR/auth_serv/index-$i.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-$i.der >> $LOGDIR/ocsp.log 2>&1
+done
+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
index c206bcf..18e8dd6 100644 (file)
@@ -1,19 +1,21 @@
-#!/usr/bin/python
-#
 # Fast BSS Transition tests
-# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+# 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 os
 import time
 import subprocess
 import logging
-logger = logging.getLogger(__name__)
+logger = logging.getLogger()
 
 import hwsim_utils
 import hostapd
+from utils import HwsimSkip
 from wlantest import Wlantest
+from test_ap_psk import check_mib, find_wpas_process, read_process_memory, verify_not_present, get_key_locations
 
 def ft_base_rsn():
     params = { "wpa": "2",
@@ -62,29 +64,83 @@ def ft_params2(rsn=True, ssid=None, passphrase=None):
     params['r1kh'] = "02:00:00:00:03:00 00:01:02:03:04:05 300102030405060708090a0b0c0d0e0f"
     return params
 
-def run_roams(dev, apdev, ssid, passphrase):
+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 100102030405060708090a0b0c0d0e0f",
+                       "12:00:00:00:04:00 nas2.w1.fi 300102030405060708090a0b0c0d0e0f" ]
+    params['r1kh'] = "12:00:00:00:04:00 10:01:02:03:04:06 200102030405060708090a0b0c0d0e0f"
+    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 200102030405060708090a0b0c0d0ef1",
+                       "02:00:00:00:04:00 nas2.w1.fi 000102030405060708090a0b0c0d0ef2" ]
+    params['r1kh'] = "02:00:00:00:03:00 00:01:02:03:04:05 300102030405060708090a0b0c0d0ef3"
+    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 200102030405060708090a0b0c0d0e0f",
+                       "02:00:00:00:04:00 nas2.w1.fi 000102030405060708090a0b0c0d0e0f" ]
+    params['r1kh'] = "12:00:00:00:03:00 10:01:02:03:04:05 300102030405060708090a0b0c0d0e0f"
+    return params
+
+def run_roams(dev, apdev, hapd0, hapd1, ssid, passphrase, over_ds=False, sae=False, eap=False, fail_test=False, roams=1):
     logger.info("Connect to first AP")
-    dev.connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
-                ieee80211w="1")
+    if eap:
+        dev.connect(ssid, key_mgmt="FT-EAP", proto="WPA2", ieee80211w="1",
+                    eap="GPSK", identity="gpsk user",
+                    password="abcdefghijklmnop0123456789abcdef",
+                    scan_freq="2412")
+    else:
+        if sae:
+            key_mgmt="FT-SAE"
+        else:
+            key_mgmt="FT-PSK"
+        dev.connect(ssid, psk=passphrase, key_mgmt=key_mgmt, proto="WPA2",
+                    ieee80211w="1", scan_freq="2412")
     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]
-    hwsim_utils.test_connectivity(dev.ifname, ap1['ifname'])
+        hapd1ap = hapd1
+        hapd2ap = hapd0
+    hwsim_utils.test_connectivity(dev, hapd1ap)
 
-    logger.info("Roam to the second AP")
-    dev.roam(ap2['bssid'])
-    if dev.get_status_field('bssid') != ap2['bssid']:
-        raise Exception("Did not connect to correct AP")
-    hwsim_utils.test_connectivity(dev.ifname, ap2['ifname'])
+    dev.scan_for_bss(ap2['bssid'], freq="2412")
 
-    logger.info("Roam back to the first AP")
-    dev.roam(ap1['bssid'])
-    if dev.get_status_field('bssid') != ap1['bssid']:
-        raise Exception("Did not connect to correct AP")
-    hwsim_utils.test_connectivity(dev.ifname, ap1['ifname'])
+    for i in range(0, roams):
+        logger.info("Roam to the second AP")
+        if 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:
+            hwsim_utils.test_connectivity(dev, hapd2ap)
+
+        logger.info("Roam back to the first AP")
+        if 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:
+            hwsim_utils.test_connectivity(dev, hapd1ap)
 
 def test_ap_ft(dev, apdev):
     """WPA2-PSK-FT AP"""
@@ -92,11 +148,25 @@ def test_ap_ft(dev, apdev):
     passphrase="12345678"
 
     params = ft_params1(ssid=ssid, passphrase=passphrase)
-    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
     params = ft_params2(ssid=ssid, passphrase=passphrase)
-    hostapd.add_ap(apdev[1]['ifname'], params)
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], 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_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]['ifname'], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
 
-    run_roams(dev[0], apdev, ssid, passphrase)
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, roams=50)
 
 def test_ap_ft_mixed(dev, apdev):
     """WPA2-PSK-FT mixed-mode AP"""
@@ -104,11 +174,15 @@ def test_ap_ft_mixed(dev, apdev):
     passphrase="12345678"
 
     params = ft_params1(rsn=False, ssid=ssid, passphrase=passphrase)
-    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.add_ap(apdev[0]['ifname'], 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)
-    hostapd.add_ap(apdev[1]['ifname'], params)
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
 
-    run_roams(dev[0], apdev, ssid, passphrase)
+    run_roams(dev[0], apdev, hapd, hapd1, ssid, passphrase)
 
 def test_ap_ft_pmf(dev, apdev):
     """WPA2-PSK-FT AP with PMF"""
@@ -117,9 +191,379 @@ def test_ap_ft_pmf(dev, apdev):
 
     params = ft_params1(ssid=ssid, passphrase=passphrase)
     params["ieee80211w"] = "2";
-    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2";
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase)
+
+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]['ifname'], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], 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 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]['ifname'], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+              roams=50)
+
+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]['ifname'], 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)
+
+def test_ap_ft_pmf_over_ds(dev, apdev):
+    """WPA2-PSK-FT AP over DS with PMF"""
+    ssid = "test-ft"
+    passphrase="12345678"
+
+    params = ft_params1(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2";
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2";
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True)
+
+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]['ifname'], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params["pmk_r1_push"] = "0"
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True)
+
+def test_ap_ft_sae(dev, apdev):
+    """WPA2-PSK-FT-SAE AP"""
+    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]['ifname'], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params['wpa_key_mgmt'] = "FT-SAE"
+    hapd = hostapd.add_ap(apdev[1]['ifname'], 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)
+
+def test_ap_ft_sae_over_ds(dev, apdev):
+    """WPA2-PSK-FT-SAE AP over DS"""
+    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]['ifname'], params)
     params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params['wpa_key_mgmt'] = "FT-SAE"
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    dev[0].request("SET sae_groups ")
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, sae=True,
+              over_ds=True)
+
+def test_ap_ft_eap(dev, apdev):
+    """WPA2-EAP-FT AP"""
+    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(radius.items() + params.items())
+    hapd = hostapd.add_ap(apdev[0]['ifname'], 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 = dict(radius.items() + params.items())
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd, hapd1, ssid, passphrase, eap=True)
+    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") ])
+
+def test_ap_ft_eap_pull(dev, apdev):
+    """WPA2-EAP-FT AP (pull PMK)"""
+    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["pmk_r1_push"] = "0"
+    params = dict(radius.items() + params.items())
+    hapd = hostapd.add_ap(apdev[0]['ifname'], 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"
+    params = dict(radius.items() + params.items())
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd, hapd1, ssid, passphrase, eap=True)
+
+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]['ifname'], params)
+    params = ft_params2_incorrect_rrb_key(ssid=ssid, passphrase=passphrase)
     params["ieee80211w"] = "2";
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+              fail_test=True)
+
+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]['ifname'], params)
+    params = ft_params2_incorrect_rrb_key(ssid=ssid, passphrase=passphrase)
+    params["pmk_r1_push"] = "0"
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+              fail_test=True)
+
+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]['ifname'], 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]['ifname'], params)
 
-    run_roams(dev[0], apdev, ssid, passphrase)
+    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(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]['ifname'], params)
+    params = ft_params2_r0kh_mismatch(ssid=ssid, passphrase=passphrase)
+    params["ieee80211w"] = "2";
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+              fail_test=True)
+
+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]['ifname'], params)
+    params = ft_params2(ssid=ssid, passphrase=passphrase)
+    params["pmk_r1_push"] = "0"
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
+
+    run_roams(dev[0], apdev, hapd0, hapd1, ssid, passphrase, over_ds=True,
+              fail_test=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]['ifname'], 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]['ifname'], 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]['ifname'], p)
+    p = ft_params2(ssid=ssid, passphrase=passphrase)
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], p)
+
+    pid = find_wpas_process(dev[0])
+
+    dev[0].connect(ssid, psk=passphrase, key_mgmt="FT-PSK", proto="WPA2",
+                   scan_freq="2412")
+    time.sleep(0.1)
+
+    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")
+    if gtk in buf:
+        raise Exception("GTK 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")
+    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")
index def7bfe..617b4fa 100644 (file)
@@ -1,20 +1,29 @@
-#!/usr/bin/python
-#
 # Hotspot 2.0 tests
-# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+# 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 binascii
+import struct
 import time
 import subprocess
 import logging
-logger = logging.getLogger(__name__)
+logger = logging.getLogger()
+import os
+import os.path
+import socket
+import subprocess
 
 import hostapd
+from utils import HwsimSkip
+import hwsim_utils
+from wlantest import Wlantest
+from wpasupplicant import WpaSupplicant
+from test_ap_eap import check_eap_capa, check_domain_match_full
 
-def hs20_ap_params():
-    params = hostapd.wpa2_params(ssid="test-hs20")
+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"
@@ -42,55 +51,3155 @@ def hs20_ap_params():
     params['anqp_3gpp_cell_net'] = "244,91"
     return params
 
-def test_ap_hs20_select(dev, apdev):
-    """Hotspot 2.0 network selection"""
+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()
+
+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=" + 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 home network")
+
+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])
+    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"""
+    dev[0].flush_scan_cache()
+
     bssid = apdev[0]['bssid']
     params = hs20_ap_params()
     params['hessid'] = bssid
     hostapd.add_ap(apdev[0]['ifname'], params)
 
-    dev[0].request("SET interworking 1")
-    dev[0].request("SET hs20 1")
+    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]['ifname'], 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()
 
-    id = dev[0].add_cred()
-    dev[0].set_cred_quoted(id, "realm", "example.com");
-    dev[0].set_cred_quoted(id, "username", "test");
-    dev[0].set_cred_quoted(id, "password", "secret");
-    dev[0].set_cred_quoted(id, "domain", "example.com");
+    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_nai_home_realm_query(dev, apdev):
+    """NAI Home Realm Query"""
+    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]['ifname'], 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("INTERWORKING_SELECT")
-    ev = dev[0].wait_event(["INTERWORKING-AP", "INTERWORKING-NO-MATCH"],
-                           timeout=15)
+
+    dev[0].request("ANQP_GET " + bssid + " 263")
+    ev = dev[0].wait_event(["RX-ANQP"], timeout=5)
     if ev is None:
-        raise Exception("Network selection timed out");
-    if "INTERWORKING-NO-MATCH" in ev:
-        raise Exception("Matching network not found")
-    if bssid not in ev:
-        raise Exception("Unexpected BSSID in match")
-    if "type=home" not in ev:
-        raise Exception("Home network not recognized")
+        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 "example.com".encode('hex') not in nai1:
+        raise Exception("Home realm not reported")
+    if "example.org".encode('hex') in nai1:
+        raise Exception("Non-home realm reported")
+    if "example.com".encode('hex') not in nai2:
+        raise Exception("Home realm not reported in wildcard query")
+    if "example.org".encode('hex') 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].set_cred_quoted(id, "domain", "no.match.example.com");
     dev[0].dump_monitor()
-    dev[0].request("INTERWORKING_SELECT")
-    ev = dev[0].wait_event(["INTERWORKING-AP", "INTERWORKING-NO-MATCH"],
-                           timeout=15)
+    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("Network selection timed out");
-    if "INTERWORKING-NO-MATCH" in ev:
-        raise Exception("Matching network not found")
-    if bssid not in ev:
-        raise Exception("Unexpected BSSID in match")
-    if "type=roaming" not in ev:
-        raise Exception("Roaming network not recognized")
+        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].set_cred_quoted(id, "realm", "no.match.example.com");
     dev[0].dump_monitor()
-    dev[0].request("INTERWORKING_SELECT")
-    ev = dev[0].wait_event(["INTERWORKING-AP", "INTERWORKING-NO-MATCH"],
-                           timeout=15)
+    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("Network selection timed out");
-    if "INTERWORKING-NO-MATCH" not in ev:
-        raise Exception("Unexpected network match")
+        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)
+
+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
+    hostapd.add_ap(apdev[0]['ifname'], 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]['ifname'], params)
+
+    dev[0].hs20_enable()
+
+    wt = Wlantest()
+    wt.flush()
+
+    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]['ifname'], 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]['ifname'], 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['ifname'], params)
+
+    dev.hs20_enable()
+    dev.add_cred_values({ 'imsi': "555444-333222111", 'eap': method,
+                          'milenage': "5122250214c33e723a5dd523fc145fc0:981d464c7c52eb6e5036234984ad0bcf:000000000123"})
+    interworking_select(dev, "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_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]['ifname'], 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], "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]['ifname'], 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], "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"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    params['disable_dgaf'] = '1'
+    hostapd.add_ap(apdev[0]['ifname'], 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'] != "2":
+        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"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    params['disable_dgaf'] = '1'
+    hostapd.add_ap(apdev[0]['ifname'], 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'] != "2":
+        raise Exception("Unexpected HS 2.0 support indication")
+
+def test_ap_hs20_auto_interworking(dev, apdev):
+    """Hotspot 2.0 connection with auto_interworking=1"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    params['disable_dgaf'] = '1'
+    hostapd.add_ap(apdev[0]['ifname'], 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'] != "2":
+        raise Exception("Unexpected HS 2.0 support indication")
+
+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]['ifname'], { "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")
+
+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]['ifname'], 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):
+    bssid = ap['bssid']
+    params = hs20_ap_params()
+    params['nai_realm'] = [ "0,example.com," + eap_params ]
+    hostapd.add_ap(ap['ifname'], params)
+
+    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)
+
+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]['ifname'], 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"""
+    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)"""
+    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")
+
+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]['ifname'], 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"""
+    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"""
+    eap_test(dev[0], apdev[0], "21[2:3]", "TTLS", "mschap user")
+
+def test_ap_hs20_eap_ttls_eap_mschapv2(dev, apdev):
+    """Hotspot 2.0 connection with TTLS/EAP-MSCHAPv2"""
+    eap_test(dev[0], apdev[0], "21[3:26][6:7][99:99]", "TTLS", "user")
+
+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]['ifname'], 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_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]['ifname'], 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_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]['ifname'], 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]['ifname'], 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], bssid, freq="2412")
+    interworking_connect(dev[0], bssid, "TLS")
+
+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]['ifname'], 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")
+
+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]['ifname'], 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")
+
+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]['ifname'], 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]['ifname'], params)
+
+    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]['ifname'], params)
+
+    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_username_roaming(dev, apdev):
+    """Hotspot 2.0 connection in username/password credential (roaming)"""
+    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]['ifname'], 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)"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    hostapd.add_ap(apdev[0]['ifname'], 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)"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    del params['domain_name']
+    hostapd.add_ap(apdev[0]['ifname'], 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"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    hostapd.add_ap(apdev[0]['ifname'], 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_while_associated_with_pmf(dev, apdev):
+    """Hotspot 2.0 connection with GAS query while associated and using PMF"""
+    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]['ifname'], 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]['ifname'], 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")
+
+    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_frag_while_associated(dev, apdev):
+    """Hotspot 2.0 connection with fragmented GAS query while associated"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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")
+
+    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"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    hostapd.add_ap(apdev[0]['ifname'], 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]['ifname'], 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.translate(None, ':'))
+    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.translate(None, ':'))
+    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):
+    cred = { 'realm': "example.com",
+             'ca_cert': "auth_serv/ca.pem",
+             'username': "hs20-test",
+             'password': "password" }
+    if domain:
+        cred['domain'] = domain
+    return cred
+
+def test_ap_hs20_prefer_home(dev, apdev):
+    """Hotspot 2.0 required roaming consortium"""
+    params = hs20_ap_params()
+    params['domain_name'] = "example.org"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    params = hs20_ap_params()
+    params['ssid'] = "test-hs20-other"
+    params['domain_name'] = "example.com"
+    hostapd.add_ap(apdev[1]['ifname'], 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"""
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    params = hs20_ap_params()
+    params['ssid'] = "test-hs20-other"
+    params['roaming_consortium'] = [ "223344" ]
+    hostapd.add_ap(apdev[1]['ifname'], 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_excluded_ssid(dev, apdev):
+    """Hotspot 2.0 exclusion based on SSID"""
+    params = hs20_ap_params()
+    params['roaming_consortium'] = [ "223344" ]
+    params['anqp_3gpp_cell_net'] = "555,444"
+    hostapd.add_ap(apdev[0]['ifname'], 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]['ifname'], 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"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params(ssid="test-hs20-visited")
+    params['domain_name'] = "visited.example.org"
+    hostapd.add_ap(apdev[0]['ifname'], 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]['ifname'], 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"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], 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_domain_match_full(dev[0])
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], 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"""
+    params = hs20_ap_params()
+    params['domain_name'] = "roaming.example.org"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    params = hs20_ap_params()
+    params['ssid'] = "test-hs20-other"
+    params['domain_name'] = "roaming.example.net"
+    hostapd.add_ap(apdev[1]['ifname'], 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"""
+    params = hs20_ap_params()
+    params['bss_load_test'] = "12:200:20000"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    params = hs20_ap_params()
+    params['ssid'] = "test-hs20-other"
+    params['bss_load_test'] = "5:20:10000"
+    hostapd.add_ap(apdev[1]['ifname'], 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"""
+    params = hs20_ap_params()
+    params['bss_load_test'] = "12:200:20000"
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    params = hs20_ap_params()
+    params['ssid'] = "test-hs20-other"
+    hostapd.add_ap(apdev[1]['ifname'], 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_multi_cred_sp_prio(dev, apdev):
+    """Hotspot 2.0 multi-cred sp_priority"""
+    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]['ifname'], 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"""
+    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]['ifname'], 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]['ifname'], 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 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"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], 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]['ifname'], 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_capab_and_roaming_partner_preference(dev, apdev):
+    """Hotspot 2.0 and req_conn_capab with roaming partner preference"""
+    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]['ifname'], params)
+
+    bssid2 = apdev[1]['bssid']
+    params = hs20_ap_params(ssid="test-hs20-b")
+    params['domain_name'] = "roaming.example.net"
+    hostapd.add_ap(apdev[1]['ifname'], 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)"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], 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]['ifname'], params)
+
+    check_auto_select(dev[0], bssid2)
+
+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"""
+    bssid = apdev[0]['bssid']
+
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": 'secret',
+                                                "ignore_broadcast_ssid": "1" })
+    dev[0].scan_for_bss(bssid, freq=2412)
+    hapd.disable()
+    hapd_global = hostapd.HostapdGlobal()
+    hapd_global.flush()
+    hapd_global.remove(apdev[0]['ifname'])
+
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], 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]['ifname'], 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)"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], 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]['ifname'], 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"""
+    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]['ifname'], params)
+
+    bssid2 = apdev[1]['bssid']
+    params = hs20_ap_params(ssid="test-hs20-b")
+    params['domain_name'] = "roaming.example.net"
+    hostapd.add_ap(apdev[1]['ifname'], 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]['ifname'], 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"""
+    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")
+    eap_test(dev[0], apdev[0], "21[3:26]", "TTLS", "user")
+    dev[0].dump_monitor()
+    addr = dev[0].p2p_interface_addr()
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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"""
+    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")
+    eap_test(dev[0], apdev[0], "21[3:26]", "TTLS", "user")
+    dev[0].dump_monitor()
+    addr = dev[0].p2p_interface_addr()
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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"""
+    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]['ifname'], 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_remediation_required(dev, apdev):
+    """Hotspot 2.0 connection and remediation required from RADIUS"""
+    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]['ifname'], 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"""
+    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].p2p_dev_addr()
+    params = hs20_ap_params()
+    params['nai_realm'] = [ "0,example.com,21[2:4]" ]
+    hapd = hostapd.add_ap(apdev[0]['ifname'], 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")
+
+def test_ap_hs20_session_info(dev, apdev):
+    """Hotspot 2.0 connection and session information from RADIUS"""
+    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]['ifname'], 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]['ifname'], params)
+
+    dev[1].connect("osen", key_mgmt="NONE", scan_freq="2412",
+                   wait_connect=False)
+    dev[2].connect("osen", key_mgmt="NONE", wep_key0='"hello"',
+                   scan_freq="2412", wait_connect=False)
+    dev[0].connect("osen", 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")
+
+    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",
+                 eap="WFA-UNAUTH-TLS", identity="osen@example.com",
+                 ca_cert="auth_serv/ca.pem",
+                 scan_freq="2412")
+    wpas.request("DISCONNECT")
+
+def test_ap_hs20_network_preference(dev, apdev):
+    """Hotspot 2.0 network selection with preferred home network"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], 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]['ifname'], 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"""
+    bssid2 = apdev[1]['bssid']
+    params = hostapd.wpa2_params(ssid="home", passphrase="12345678")
+    hostapd.add_ap(apdev[1]['ifname'], 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]['ifname'], 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)"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], 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]['ifname'], 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"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    hostapd.add_ap(apdev[0]['ifname'], 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]['ifname'], 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_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]['ifname'], 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]['ifname'], params)
+
+    with open("w1fi_logo.png", "r") 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[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")
+        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], "r") 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")
+
+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]['ifname'], 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_ft(dev, apdev):
+    """Hotspot 2.0 connection with FT"""
+    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"
+    hostapd.add_ap(apdev[0]['ifname'], 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")
+
+def test_ap_hs20_remediation_sql(dev, apdev, params):
+    """Hotspot 2.0 connection and remediation required using SQLite for user DB"""
+    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,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]['ifname'], params)
+
+        bssid = apdev[0]['bssid']
+        params = hs20_ap_params()
+        params['auth_server_port'] = "18128"
+        hostapd.add_ap(apdev[0]['ifname'], 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_external_selection(dev, apdev):
+    """Hotspot 2.0 connection using external network selection and creation"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    params['disable_dgaf'] = '1'
+    hostapd.add_ap(apdev[0]['ifname'], params)
+
+    dev[0].hs20_enable()
+    dev[0].connect("test-hs20", proto="RSN", key_mgmt="WPA-EAP", eap="TTLS",
+                   identity="hs20-test", password="password",
+                   ca_cert="auth_serv/ca.pem", phase2="auth=MSCHAPV2",
+                   scan_freq="2412", update_identifier="54321")
+    if dev[0].get_status_field("hs20") != "2":
+        raise Exception("Unexpected hs20 indication")
+
+def test_ap_hs20_random_mac_addr(dev, apdev):
+    """Hotspot 2.0 connection with random MAC address"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    params['disable_dgaf'] = '1'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], 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"""
+    bssid = apdev[0]['bssid']
+    params = hs20_ap_params()
+    params['nai_realm'] = [ "0,example.com,25[3:26]"]
+    hapd = hostapd.add_ap(apdev[0]['ifname'], 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_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]['ifname'], 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 = "\x01\x01" + binascii.unhexlify(addr0.replace(':',''))
+    src_ll_opt1 = "\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)):
+        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)):
+        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)):
+        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"""
+    bssid = apdev[0]['bssid']
+
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": 'secret',
+                                                "ignore_broadcast_ssid": "1" })
+    dev[0].scan_for_bss(bssid, freq=2412)
+    hapd.disable()
+    hapd_global = hostapd.HostapdGlobal()
+    hapd_global.flush()
+    hapd_global.remove(apdev[0]['ifname'])
+
+    params = hs20_ap_params()
+    params['hessid'] = bssid
+    hostapd.add_ap(apdev[0]['ifname'], 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()
+    dev[0].flush_scan_cache()
+
+def test_ap_hs20_proxyarp(dev, apdev):
+    """Hotspot 2.0 and ProxyARP"""
+    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['ap_isolate'] = '1'
+    params['bridge'] = 'ap-br0'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], 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 = "\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)):
+        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)):
+        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)):
+        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)):
+        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)):
+        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"""
+    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"""
+    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 += '\0x00'
+    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 + '\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 = '\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 = '\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 = '\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 = "\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)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+def build_na(src_ll, ip_src, ip_dst, target, opt=None):
+    link_mc = binascii.unhexlify("3333ff000002")
+    _src_ll = binascii.unhexlify(src_ll.replace(':',''))
+    proto = '\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)
+
+    reserved = '\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, 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)):
+        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):
+    _dst_ll = binascii.unhexlify(dst_ll.replace(':',''))
+    _src_ll = binascii.unhexlify(src_ll.replace(':',''))
+    proto = '\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 = '\x00\x00\x00\x00'
+    _yiaddr = socket.inet_pton(socket.AF_INET, yiaddr)
+    _siaddr = '\x00\x00\x00\x00'
+    _giaddr = '\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*'\x00'
+    # magic
+    if wrong_magic:
+        payload += '\x63\x82\x53\x00'
+    else:
+        payload += '\x63\x82\x53\x63'
+    if truncated_opt:
+        payload += '\x22\xff\x00'
+    # Option: DHCP Message Type = ACK
+    payload += '\x35\x01\x05'
+    # Pad Option
+    payload += '\x00'
+    # Option: Subnet Mask
+    payload += '\x01\x04' + _subnet_mask
+    # Option: Time Offset
+    payload += struct.pack('>BBL', 2, 4, 0)
+    # End Option
+    payload += '\xff'
+    # Pad Option
+    payload += '\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*'\x00'
+
+    udp = struct.pack('>HHHH', 67, 68, 8 + len(payload), 0) + 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 + '\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 = '\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)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+def get_permanent_neighbors(ifname):
+    cmd = subprocess.Popen(['ip', 'nei'], stdout=subprocess.PIPE)
+    res = cmd.stdout.read()
+    cmd.stdout.close()
+    return [ line for line in res.splitlines() if "PERMANENT" in line and ifname in line ]
+
+def _test_proxyarp_open(dev, apdev, params):
+    cap_br = os.path.join(params['logdir'], "proxyarp_open.ap-br0.pcap")
+    cap_dev0 = os.path.join(params['logdir'], "proxyarp_open.%s.pcap" % dev[0].ifname)
+    cap_dev1 = os.path.join(params['logdir'], "proxyarp_open.%s.pcap" % dev[1].ifname)
+
+    bssid = apdev[0]['bssid']
+    params = { 'ssid': 'open' }
+    params['proxy_arp'] = '1'
+    hapd = hostapd.add_ap(apdev[0]['ifname'], 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")
+
+    subprocess.call(['brctl', 'setfd', 'ap-br0', '0'])
+    subprocess.call(['ip', 'link', 'set', 'dev', 'ap-br0', 'up'])
+
+    for chain in [ 'FORWARD', 'OUTPUT' ]:
+        subprocess.call(['ebtables', '-A', chain, '-p', 'ARP',
+                         '-d', 'Broadcast', '-o', apdev[0]['ifname'],
+                         '-j', 'DROP'])
+        subprocess.call(['ebtables', '-A', chain, '-d', 'Multicast',
+                         '-p', 'IPv6', '--ip6-protocol', 'ipv6-icmp',
+                         '--ip6-icmp-type', 'neighbor-solicitation',
+                         '-o', apdev[0]['ifname'], '-j', 'DROP'])
+        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'])
+
+    cmd = {}
+    cmd[0] = subprocess.Popen(['tcpdump', '-p', '-U', '-i', 'ap-br0',
+                               '-w', cap_br, '-s', '2000'],
+                              stderr=open('/dev/null', 'w'))
+    cmd[1] = subprocess.Popen(['tcpdump', '-p', '-U', '-i', dev[0].ifname,
+                               '-w', cap_dev0, '-s', '2000'],
+                              stderr=open('/dev/null', 'w'))
+    cmd[2] = subprocess.Popen(['tcpdump', '-p', '-U', '-i', dev[1].ifname,
+                               '-w', cap_dev1, '-s', '2000'],
+                              stderr=open('/dev/null', 'w'))
+
+    dev[0].connect("open", key_mgmt="NONE", scan_freq="2412")
+    dev[1].connect("open", key_mgmt="NONE", scan_freq="2412")
+    time.sleep(0.1)
+
+    addr0 = dev[0].p2p_interface_addr()
+    addr1 = dev[1].p2p_interface_addr()
+
+    src_ll_opt0 = "\x01\x01" + binascii.unhexlify(addr0.replace(':',''))
+    src_ll_opt1 = "\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="\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="\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="\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")
+
+    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)):
+        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)
+    if "OK" not in hapd.request("DATA_TEST_FRAME ifname=ap-br0 " + binascii.hexlify(pkt)):
+        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)):
+        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)):
+        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)):
+        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)):
+        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)):
+        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)):
+        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)):
+        raise Exception("DATA_TEST_FRAME failed")
+
+    matches = get_permanent_neighbors("ap-br0")
+    logger.info("After connect: " + str(matches))
+    if len(matches) != 4:
+        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")
+    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)
+
+    # 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)
+
+    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")
+
+    # ARP Probe from bridge
+    send_arp(hapd, hapd_bssid=bssid, target_ip="192.168.1.130")
+    # 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)
+
+    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 bridge
+    send_arp(hapd, hapd_bssid=bssid, sender_ip="192.168.1.102",
+             target_ip="192.168.1.130")
+
+    # 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")
+
+    time.sleep(0.1)
+
+    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)
+
+    # 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)
+
+    # 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")
+
+    try:
+        hwsim_utils.test_connectivity_iface(dev[0], hapd, "ap-br0")
+    except Exception, 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(3):
+        cmd[i].terminate()
+    matches = get_permanent_neighbors("ap-br0")
+    logger.info("After disconnect: " + str(matches))
+    if len(matches) > 0:
+        raise Exception("Unexpected neighbor entries after disconnect")
+    cmd = subprocess.Popen(['ebtables', '-L', '--Lc'], stdout=subprocess.PIPE)
+    res = cmd.stdout.read()
+    cmd.stdout.close()
+    logger.info("ebtables results:\n" + res)
+
+def test_proxyarp_open(dev, apdev, params):
+    """ProxyARP with open network"""
+    try:
+        _test_proxyarp_open(dev, apdev, params)
+    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'))
index 33e023b..6a83e3d 100644 (file)
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-#
 # Protected management frames tests
 # Copyright (c) 2013, Jouni Malinen <j@w1.fi>
 #
@@ -9,11 +7,13 @@
 import time
 import subprocess
 import logging
-logger = logging.getLogger(__name__)
+logger = logging.getLogger()
 
 import hwsim_utils
 import hostapd
 from wlantest import Wlantest
+from wpasupplicant import WpaSupplicant
+from test_ap_eap import eap_connect
 
 def test_ap_pmf_required(dev, apdev):
     """WPA2-PSK AP with PMF required"""
@@ -24,13 +24,20 @@ def test_ap_pmf_required(dev, apdev):
     params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
     params["wpa_key_mgmt"] = "WPA-PSK-SHA256";
     params["ieee80211w"] = "2";
-    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    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")
-    hwsim_utils.test_connectivity(dev[0].ifname, apdev[0]['ifname'])
+                   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")
-    hwsim_utils.test_connectivity(dev[1].ifname, apdev[0]['ifname'])
+                   key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+                   scan_freq="2412")
+    hwsim_utils.test_connectivity(dev[1], hapd)
     hapd = hostapd.Hostapd(apdev[0]['ifname'])
     hapd.request("SA_QUERY " + dev[0].p2p_interface_addr())
     hapd.request("SA_QUERY " + dev[1].p2p_interface_addr())
@@ -54,13 +61,15 @@ def test_ap_pmf_optional(dev, apdev):
     params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
     params["wpa_key_mgmt"] = "WPA-PSK";
     params["ieee80211w"] = "1";
-    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
     dev[0].connect(ssid, psk="12345678", ieee80211w="1",
-                   key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2")
-    hwsim_utils.test_connectivity(dev[0].ifname, apdev[0]['ifname'])
+                   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")
-    hwsim_utils.test_connectivity(dev[1].ifname, apdev[0]['ifname'])
+                   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())
@@ -74,13 +83,15 @@ def test_ap_pmf_optional_2akm(dev, apdev):
     params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
     params["wpa_key_mgmt"] = "WPA-PSK WPA-PSK-SHA256";
     params["ieee80211w"] = "1";
-    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
     dev[0].connect(ssid, psk="12345678", ieee80211w="1",
-                   key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2")
-    hwsim_utils.test_connectivity(dev[0].ifname, apdev[0]['ifname'])
+                   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")
-    hwsim_utils.test_connectivity(dev[1].ifname, apdev[0]['ifname'])
+                   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(),
@@ -96,15 +107,225 @@ def test_ap_pmf_negative(dev, apdev):
     wt.flush()
     wt.add_passphrase("12345678")
     params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
-    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
     dev[0].connect(ssid, psk="12345678", ieee80211w="1",
-                   key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2")
-    hwsim_utils.test_connectivity(dev[0].ifname, apdev[0]['ifname'])
+                   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")
-        hwsim_utils.test_connectivity(dev[1].ifname, apdev[0]['ifname'])
+                       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, e:
         logger.debug("Ignore expected exception: " + str(e))
     wt.require_ap_no_pmf(apdev[0]['bssid'])
+
+def test_ap_pmf_assoc_comeback(dev, apdev):
+    """WPA2-PSK AP with PMF association comeback"""
+    ssid = "assoc-comeback"
+    wt = Wlantest()
+    wt.flush()
+    wt.add_passphrase("12345678")
+    params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+    params["wpa_key_mgmt"] = "WPA-PSK-SHA256";
+    params["ieee80211w"] = "2";
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    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")
+
+def test_ap_pmf_assoc_comeback2(dev, apdev):
+    """WPA2-PSK AP with PMF association comeback (using DROP_SA)"""
+    ssid = "assoc-comeback"
+    wt = Wlantest()
+    wt.flush()
+    wt.add_passphrase("12345678")
+    params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+    params["wpa_key_mgmt"] = "WPA-PSK";
+    params["ieee80211w"] = "1";
+    hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+    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")
+
+def test_ap_pmf_sta_sa_query(dev, apdev):
+    """WPA2-PSK AP with station using SA Query"""
+    ssid = "assoc-comeback"
+    addr = dev[0].p2p_dev_addr()
+    wt = Wlantest()
+    wt.flush()
+    wt.add_passphrase("12345678")
+
+    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.connect_network(id)
+    bssid = wpas.p2p_dev_addr()
+
+    dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+                   key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+                   scan_freq="2412")
+    wpas.request("DEAUTHENTICATE " + addr + " test=0")
+    wpas.request("DISASSOCIATE " + addr + " test=0")
+    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.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")
+    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(dev, apdev):
+    """WPA2-PSK AP with station using SA Query and getting no response"""
+    ssid = "assoc-comeback"
+    addr = dev[0].p2p_dev_addr()
+
+    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.connect_network(id)
+    bssid = wpas.p2p_dev_addr()
+
+    dev[0].connect(ssid, psk="12345678", ieee80211w="1",
+                   key_mgmt="WPA-PSK WPA-PSK-SHA256", proto="WPA2",
+                   scan_freq="2412")
+    wpas.request("DEAUTHENTICATE " + addr + " test=0")
+    wpas.request("DISASSOCIATE " + addr + " test=0")
+    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.request("DISASSOCIATE " + addr + " reason=7 test=0")
+    dev[0].wait_disconnected()
+    wpas.request("SET ext_mgmt_frame_handling 0")
+    dev[0].wait_connected()
+
+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].p2p_dev_addr()
+    wt = Wlantest()
+    wt.flush()
+    wt.add_passphrase("12345678")
+
+    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.connect_network(id)
+    bssid = wpas.p2p_dev_addr()
+
+    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_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]['ifname'], 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")
+
+def test_ap_pmf_required_sha1(dev, apdev):
+    """WPA2-PSK AP with PMF required with SHA1 AKM"""
+    ssid = "test-pmf-required-sha1"
+    wt = Wlantest()
+    wt.flush()
+    wt.add_passphrase("12345678")
+    params = hostapd.wpa2_params(ssid=ssid, passphrase="12345678")
+    params["wpa_key_mgmt"] = "WPA-PSK";
+    params["ieee80211w"] = "2";
+    hapd = hostapd.add_ap(apdev[0]['ifname'], 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, 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)
index 3a966f7..f41e272 100644 (file)
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-#
 # Roaming tests
 # Copyright (c) 2013, Jouni Malinen <j@w1.fi>
 #
@@ -9,32 +7,61 @@
 import time
 import subprocess
 import logging
-logger = logging.getLogger(__name__)
+logger = logging.getLogger()
 
 import hwsim_utils
 import hostapd
 
 def test_ap_roam_open(dev, apdev):
     """Roam between two open APs"""
-    hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open" })
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open" })
     dev[0].connect("test-open", key_mgmt="NONE")
-    hwsim_utils.test_connectivity(dev[0].ifname, apdev[0]['ifname'])
-    hostapd.add_ap(apdev[1]['ifname'], { "ssid": "test-open" })
+    hwsim_utils.test_connectivity(dev[0], hapd0)
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], { "ssid": "test-open" })
     dev[0].scan(type="ONLY")
     dev[0].roam(apdev[1]['bssid'])
-    hwsim_utils.test_connectivity(dev[0].ifname, apdev[1]['ifname'])
+    hwsim_utils.test_connectivity(dev[0], hapd1)
     dev[0].roam(apdev[0]['bssid'])
-    hwsim_utils.test_connectivity(dev[0].ifname, apdev[0]['ifname'])
+    hwsim_utils.test_connectivity(dev[0], hapd0)
 
 def test_ap_roam_wpa2_psk(dev, apdev):
     """Roam between two WPA2-PSK APs"""
     params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
-    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd0 = hostapd.add_ap(apdev[0]['ifname'], params)
     dev[0].connect("test-wpa2-psk", psk="12345678")
-    hwsim_utils.test_connectivity(dev[0].ifname, apdev[0]['ifname'])
-    hostapd.add_ap(apdev[1]['ifname'], params)
+    hwsim_utils.test_connectivity(dev[0], hapd0)
+    hapd1 = hostapd.add_ap(apdev[1]['ifname'], params)
     dev[0].scan(type="ONLY")
     dev[0].roam(apdev[1]['bssid'])
-    hwsim_utils.test_connectivity(dev[0].ifname, apdev[1]['ifname'])
+    hwsim_utils.test_connectivity(dev[0], hapd1)
     dev[0].roam(apdev[0]['bssid'])
-    hwsim_utils.test_connectivity(dev[0].ifname, apdev[0]['ifname'])
+    hwsim_utils.test_connectivity(dev[0], hapd0)
+
+def test_ap_reassociation_to_same_bss(dev, apdev):
+    """Reassociate to the same BSS"""
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open" })
+    dev[0].connect("test-open", key_mgmt="NONE")
+
+    dev[0].request("REASSOCIATE")
+    dev[0].wait_connected(timeout=10, error="Reassociation timed out")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+    dev[0].request("REATTACH")
+    dev[0].wait_connected(timeout=10, error="Reattach timed out")
+    hwsim_utils.test_connectivity(dev[0], hapd)
+
+def test_ap_roam_set_bssid(dev, apdev):
+    """Roam control"""
+    hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open" })
+    hostapd.add_ap(apdev[1]['ifname'], { "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'])
index 9abada8..64ed7db 100644 (file)
@@ -1,55 +1,59 @@
-#!/usr/bin/python
-#
 # TDLS tests
-# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+# 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 time
 import logging
-logger = logging.getLogger(__name__)
+logger = logging.getLogger()
+import subprocess
 
 import hwsim_utils
 from hostapd import HostapdGlobal
 from hostapd import Hostapd
 import hostapd
+from utils import HwsimSkip
 from wlantest import Wlantest
 
 def start_ap_wpa2_psk(ifname):
     params = hostapd.wpa2_params(ssid="test-wpa2-psk", passphrase="12345678")
-    hostapd.add_ap(ifname, params)
+    return hostapd.add_ap(ifname, params)
 
-def connectivity(dev, ap_ifname):
+def connectivity(dev, hapd):
     hwsim_utils.test_connectivity_sta(dev[0], dev[1])
-    hwsim_utils.test_connectivity(dev[0].ifname, ap_ifname)
-    hwsim_utils.test_connectivity(dev[1].ifname, ap_ifname)
+    hwsim_utils.test_connectivity(dev[0], hapd)
+    hwsim_utils.test_connectivity(dev[1], hapd)
 
-def connect_2sta(dev, ssid, ap_ifname):
-    dev[0].connect(ssid, psk="12345678")
-    dev[1].connect(ssid, psk="12345678")
-    connectivity(dev, ap_ifname)
+def connect_2sta(dev, ssid, hapd):
+    dev[0].connect(ssid, psk="12345678", scan_freq="2412")
+    dev[1].connect(ssid, psk="12345678", scan_freq="2412")
+    connectivity(dev, hapd)
 
-def connect_2sta_wpa2_psk(dev, ap_ifname):
-    connect_2sta(dev, "test-wpa2-psk", ap_ifname)
+def connect_2sta_wpa2_psk(dev, hapd):
+    connect_2sta(dev, "test-wpa2-psk", hapd)
 
-def connect_2sta_wpa_psk(dev, ap_ifname):
-    connect_2sta(dev, "test-wpa-psk", ap_ifname)
+def connect_2sta_wpa_psk(dev, hapd):
+    connect_2sta(dev, "test-wpa-psk", hapd)
 
-def connect_2sta_wpa_psk_mixed(dev, ap_ifname):
-    dev[0].connect("test-wpa-mixed-psk", psk="12345678", proto="WPA")
-    dev[1].connect("test-wpa-mixed-psk", psk="12345678", proto="WPA2")
-    connectivity(dev, ap_ifname)
+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")
+    connectivity(dev, hapd)
 
-def connect_2sta_wep(dev, ap_ifname):
-    dev[0].connect("test-wep", key_mgmt="NONE", wep_key0='"hello"')
-    dev[1].connect("test-wep", key_mgmt="NONE", wep_key0='"hello"')
-    connectivity(dev, ap_ifname)
+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")
+    connectivity(dev, hapd)
 
-def connect_2sta_open(dev, ap_ifname):
-    dev[0].connect("test-open", key_mgmt="NONE")
-    dev[1].connect("test-open", key_mgmt="NONE")
-    connectivity(dev, ap_ifname)
+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)
+    connectivity(dev, hapd)
 
 def wlantest_setup():
     wt = Wlantest()
@@ -93,8 +97,16 @@ def tdls_check_ap(sta0, sta1, bssid, addr0, addr1):
     if inv_ap > 0:
         raise Exception("Invalid frames through AP path")
 
-def setup_tdls(sta0, sta1, bssid, reverse=False, expect_fail=False):
+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, ap, reverse=False, expect_fail=False):
     logger.info("Setup TDLS")
+    hapd = hostapd.Hostapd(ap['ifname'])
+    check_connectivity(sta0, sta1, hapd)
+    bssid = ap['bssid']
     addr0 = sta0.p2p_interface_addr()
     addr1 = sta1.p2p_interface_addr()
     wt = Wlantest()
@@ -112,147 +124,254 @@ def setup_tdls(sta0, sta1, bssid, reverse=False, expect_fail=False):
     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, bssid):
+def teardown_tdls(sta0, sta1, ap, responder=False, wildcard=False):
     logger.info("Teardown TDLS")
+    hapd = hostapd.Hostapd(ap['ifname'])
+    check_connectivity(sta0, sta1, hapd)
+    bssid = ap['bssid']
     addr0 = sta0.p2p_interface_addr()
     addr1 = sta1.p2p_interface_addr()
-    sta0.tdls_teardown(addr1)
+    if responder:
+        sta1.tdls_teardown(addr0)
+    elif wildcard:
+        sta0.tdls_teardown("*")
+    else:
+        sta0.tdls_teardown(addr1)
     time.sleep(1)
     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 test_ap_tdls_discovery(dev, apdev):
+    """WPA2-PSK AP and two stations using TDLS discovery"""
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
+    wlantest_setup()
+    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"""
-    start_ap_wpa2_psk(apdev[0]['ifname'])
-    bssid = apdev[0]['bssid']
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
     wlantest_setup()
-    connect_2sta_wpa2_psk(dev, apdev[0]['ifname'])
-    setup_tdls(dev[0], dev[1], bssid)
-    teardown_tdls(dev[0], dev[1], bssid)
-    setup_tdls(dev[1], dev[0], bssid)
-    #teardown_tdls(dev[0], dev[1], bssid)
+    connect_2sta_wpa2_psk(dev, hapd)
+    setup_tdls(dev[0], dev[1], apdev[0])
+    teardown_tdls(dev[0], dev[1], apdev[0])
+    setup_tdls(dev[1], dev[0], apdev[0])
+    #teardown_tdls(dev[0], dev[1], apdev[0])
 
 def test_ap_wpa2_tdls_concurrent_init(dev, apdev):
     """Concurrent TDLS setup initiation"""
-    start_ap_wpa2_psk(apdev[0]['ifname'])
-    bssid = apdev[0]['bssid']
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
     wlantest_setup()
-    connect_2sta_wpa2_psk(dev, apdev[0]['ifname'])
+    connect_2sta_wpa2_psk(dev, hapd)
     dev[0].request("SET tdls_testing 0x80")
-    setup_tdls(dev[1], dev[0], bssid, reverse=True)
+    setup_tdls(dev[1], dev[0], apdev[0], reverse=True)
 
 def test_ap_wpa2_tdls_concurrent_init2(dev, apdev):
     """Concurrent TDLS setup initiation (reverse)"""
-    start_ap_wpa2_psk(apdev[0]['ifname'])
-    bssid = apdev[0]['bssid']
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
     wlantest_setup()
-    connect_2sta_wpa2_psk(dev, apdev[0]['ifname'])
+    connect_2sta_wpa2_psk(dev, hapd)
     dev[1].request("SET tdls_testing 0x80")
-    setup_tdls(dev[0], dev[1], bssid)
+    setup_tdls(dev[0], dev[1], apdev[0])
 
 def test_ap_wpa2_tdls_decline_resp(dev, apdev):
     """Decline TDLS Setup Response"""
-    start_ap_wpa2_psk(apdev[0]['ifname'])
-    bssid = apdev[0]['bssid']
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
     wlantest_setup()
-    connect_2sta_wpa2_psk(dev, apdev[0]['ifname'])
+    connect_2sta_wpa2_psk(dev, hapd)
     dev[1].request("SET tdls_testing 0x200")
-    setup_tdls(dev[1], dev[0], bssid, expect_fail=True)
+    setup_tdls(dev[1], dev[0], apdev[0], expect_fail=True)
 
 def test_ap_wpa2_tdls_long_lifetime(dev, apdev):
     """TDLS with long TPK lifetime"""
-    start_ap_wpa2_psk(apdev[0]['ifname'])
-    bssid = apdev[0]['bssid']
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
     wlantest_setup()
-    connect_2sta_wpa2_psk(dev, apdev[0]['ifname'])
+    connect_2sta_wpa2_psk(dev, hapd)
     dev[1].request("SET tdls_testing 0x40")
-    setup_tdls(dev[1], dev[0], bssid)
+    setup_tdls(dev[1], dev[0], apdev[0])
 
 def test_ap_wpa2_tdls_long_frame(dev, apdev):
     """TDLS with long setup/teardown frames"""
-    start_ap_wpa2_psk(apdev[0]['ifname'])
-    bssid = apdev[0]['bssid']
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
     wlantest_setup()
-    connect_2sta_wpa2_psk(dev, apdev[0]['ifname'])
+    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], bssid)
-    teardown_tdls(dev[1], dev[0], bssid)
-    setup_tdls(dev[0], dev[1], bssid)
+    setup_tdls(dev[1], dev[0], apdev[0])
+    teardown_tdls(dev[1], dev[0], apdev[0])
+    setup_tdls(dev[0], dev[1], apdev[0])
 
 def test_ap_wpa2_tdls_reneg(dev, apdev):
     """Renegotiate TDLS link"""
-    start_ap_wpa2_psk(apdev[0]['ifname'])
-    bssid = apdev[0]['bssid']
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
     wlantest_setup()
-    connect_2sta_wpa2_psk(dev, apdev[0]['ifname'])
-    setup_tdls(dev[1], dev[0], bssid)
-    setup_tdls(dev[0], dev[1], bssid)
+    connect_2sta_wpa2_psk(dev, hapd)
+    setup_tdls(dev[1], dev[0], apdev[0])
+    setup_tdls(dev[0], dev[1], apdev[0])
 
 def test_ap_wpa2_tdls_wrong_lifetime_resp(dev, apdev):
     """Incorrect TPK lifetime in TDLS Setup Response"""
-    start_ap_wpa2_psk(apdev[0]['ifname'])
-    bssid = apdev[0]['bssid']
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
     wlantest_setup()
-    connect_2sta_wpa2_psk(dev, apdev[0]['ifname'])
+    connect_2sta_wpa2_psk(dev, hapd)
     dev[1].request("SET tdls_testing 0x10")
-    setup_tdls(dev[0], dev[1], bssid, expect_fail=True)
+    setup_tdls(dev[0], dev[1], apdev[0], expect_fail=True)
 
 def test_ap_wpa2_tdls_diff_rsnie(dev, apdev):
     """TDLS with different RSN IEs"""
-    start_ap_wpa2_psk(apdev[0]['ifname'])
-    bssid = apdev[0]['bssid']
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
     wlantest_setup()
-    connect_2sta_wpa2_psk(dev, apdev[0]['ifname'])
+    connect_2sta_wpa2_psk(dev, hapd)
     dev[1].request("SET tdls_testing 0x2")
-    setup_tdls(dev[1], dev[0], bssid)
-    teardown_tdls(dev[1], dev[0], bssid)
+    setup_tdls(dev[1], dev[0], apdev[0])
+    teardown_tdls(dev[1], dev[0], apdev[0])
+
+def test_ap_wpa2_tdls_wrong_tpk_m2_mic(dev, apdev):
+    """Incorrect MIC in TDLS Setup Response"""
+    hapd = start_ap_wpa2_psk(apdev[0]['ifname'])
+    wlantest_setup()
+    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]['ifname'])
+    wlantest_setup()
+    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_wpa_tdls(dev, apdev):
     """WPA-PSK AP and two stations using TDLS"""
-    hostapd.add_ap(apdev[0]['ifname'],
-                   hostapd.wpa_params(ssid="test-wpa-psk",
-                                      passphrase="12345678"))
-    bssid = apdev[0]['bssid']
+    hapd = hostapd.add_ap(apdev[0]['ifname'],
+                          hostapd.wpa_params(ssid="test-wpa-psk",
+                                             passphrase="12345678"))
     wlantest_setup()
-    connect_2sta_wpa_psk(dev, apdev[0]['ifname'])
-    setup_tdls(dev[0], dev[1], bssid)
-    teardown_tdls(dev[0], dev[1], bssid)
-    setup_tdls(dev[1], dev[0], bssid)
+    connect_2sta_wpa_psk(dev, hapd)
+    setup_tdls(dev[0], dev[1], apdev[0])
+    teardown_tdls(dev[0], dev[1], apdev[0])
+    setup_tdls(dev[1], dev[0], apdev[0])
 
 def test_ap_wpa_mixed_tdls(dev, apdev):
     """WPA+WPA2-PSK AP and two stations using TDLS"""
-    hostapd.add_ap(apdev[0]['ifname'],
-                   hostapd.wpa_mixed_params(ssid="test-wpa-mixed-psk",
-                                            passphrase="12345678"))
-    bssid = apdev[0]['bssid']
+    hapd = hostapd.add_ap(apdev[0]['ifname'],
+                          hostapd.wpa_mixed_params(ssid="test-wpa-mixed-psk",
+                                                   passphrase="12345678"))
     wlantest_setup()
-    connect_2sta_wpa_psk_mixed(dev, apdev[0]['ifname'])
-    setup_tdls(dev[0], dev[1], bssid)
-    teardown_tdls(dev[0], dev[1], bssid)
-    setup_tdls(dev[1], dev[0], bssid)
+    connect_2sta_wpa_psk_mixed(dev, hapd)
+    setup_tdls(dev[0], dev[1], apdev[0])
+    teardown_tdls(dev[0], dev[1], apdev[0])
+    setup_tdls(dev[1], dev[0], apdev[0])
 
 def test_ap_wep_tdls(dev, apdev):
     """WEP AP and two stations using TDLS"""
-    hostapd.add_ap(apdev[0]['ifname'],
-                   { "ssid": "test-wep", "wep_key0": '"hello"' })
-    bssid = apdev[0]['bssid']
+    hapd = hostapd.add_ap(apdev[0]['ifname'],
+                          { "ssid": "test-wep", "wep_key0": '"hello"' })
     wlantest_setup()
-    connect_2sta_wep(dev, apdev[0]['ifname'])
-    setup_tdls(dev[0], dev[1], bssid)
-    teardown_tdls(dev[0], dev[1], bssid)
-    setup_tdls(dev[1], dev[0], bssid)
+    connect_2sta_wep(dev, hapd)
+    setup_tdls(dev[0], dev[1], apdev[0])
+    teardown_tdls(dev[0], dev[1], apdev[0])
+    setup_tdls(dev[1], dev[0], apdev[0])
 
 def test_ap_open_tdls(dev, apdev):
     """Open AP and two stations using TDLS"""
-    hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open" })
-    bssid = apdev[0]['bssid']
+    hapd = hostapd.add_ap(apdev[0]['ifname'], { "ssid": "test-open" })
+    wlantest_setup()
+    connect_2sta_open(dev, hapd)
+    setup_tdls(dev[0], dev[1], apdev[0])
+    teardown_tdls(dev[0], dev[1], apdev[0])
+    setup_tdls(dev[1], dev[0], apdev[0])
+    teardown_tdls(dev[1], dev[0], apdev[0], 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]['ifname'], params)
+        hostapd.add_ap(apdev[1]['ifname'], params)
+        wlantest_setup()
+        subprocess.call(['sudo', 'brctl', 'setfd', 'ap-br0', '0'])
+        subprocess.call(['sudo', '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(['sudo', 'ip', 'link', 'set', 'dev', 'ap-br0', 'down'])
+        subprocess.call(['sudo', '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]['ifname'])
     wlantest_setup()
-    connect_2sta_open(dev, apdev[0]['ifname'])
-    setup_tdls(dev[0], dev[1], bssid)
-    teardown_tdls(dev[0], dev[1], bssid)
-    setup_tdls(dev[1], dev[0], bssid)
+    connect_2sta_wpa2_psk(dev, hapd)
+    setup_tdls(dev[0], dev[1], apdev[0])
+    teardown_tdls(dev[0], dev[1], apdev[0], responder=True)
+
+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" }
+    try:
+        hapd = hostapd.add_ap(apdev[0]['ifname'], params)
+        wlantest_setup()
+        connect_2sta_open(dev, hapd, scan_freq="5180")
+        setup_tdls(dev[0], dev[1], apdev[0])
+        teardown_tdls(dev[0], dev[1], apdev[0])
+        setup_tdls(dev[1], dev[0], apdev[0])
+        teardown_tdls(dev[1], dev[0], apdev[0], wildcard=True)
+    finally:
+        dev[0].request("DISCONNECT")
+        dev[1].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['sudo', 'iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+        dev[1].flush_scan_cache()
+
+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]['ifname'], { "ssid": "test-open" })
+    connect_2sta_open(dev, hapd)
+    setup_tdls(dev[0], dev[1], apdev[0])
+    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")
index 84f206f..5cb67ab 100644 (file)
@@ -1,18 +1,26 @@
-#!/usr/bin/python
-#
 # WPS tests
-# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+# 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 subprocess
 import logging
-logger = logging.getLogger(__name__)
+logger = logging.getLogger()
+import re
+import socket
+import httplib
+import urlparse
+import urllib
+import xml.etree.ElementTree as ET
+import StringIO
 
 import hwsim_utils
 import hostapd
+from wpasupplicant import WpaSupplicant
+from utils import HwsimSkip
 
 def test_ap_wps_init(dev, apdev):
     """Initial AP configuration with first WPS Enrollee"""
@@ -22,12 +30,22 @@ def test_ap_wps_init(dev, apdev):
     hapd = hostapd.Hostapd(apdev[0]['ifname'])
     logger.info("WPS provisioning step")
     hapd.request("WPS_PBC")
-    dev[0].request("SET ignore_old_scan_res 1")
-    dev[0].dump_monitor()
+    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")
-    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
-    if ev is None:
-        raise Exception("Association with the AP timed out")
+    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")
@@ -38,6 +56,113 @@ def test_ap_wps_init(dev, apdev):
     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 "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"""
+    ssid = "test-wps"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "1" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hostapd.add_ap(apdev[1]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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"""
+    ssid = "test-wps"
+    params = { "ssid": ssid, "eap_server": "1", "wps_state": "1" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hostapd.add_ap(apdev[1]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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")
+
+def test_ap_wps_init_through_wps_config(dev, apdev):
+    """Initial AP configuration using wps_config command"""
+    ssid = "test-wps-init-config"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "1" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    if "FAIL" in hapd.request("WPS_CONFIG " + ssid.encode("hex") + " WPA2PSK CCMP " + "12345678".encode("hex")):
+        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")
+
 def test_ap_wps_conf(dev, apdev):
     """WPS PBC provisioning with configured AP"""
     ssid = "test-wps-conf"
@@ -48,11 +173,10 @@ def test_ap_wps_conf(dev, apdev):
     hapd = hostapd.Hostapd(apdev[0]['ifname'])
     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")
-    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
-    if ev is None:
-        raise Exception("Association with the AP timed out")
+    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")
@@ -65,6 +189,135 @@ def test_ap_wps_conf(dev, apdev):
     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]['ifname'], params)
+        logger.info("WPS provisioning step")
+        hapd.request("WPS_PBC")
+        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")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['sudo', 'iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+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]['ifname'], params)
+        logger.info("WPS provisioning step")
+        hapd.request("WPS_PBC")
+        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")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['sudo', 'iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+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" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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")
+    hapd_global = hostapd.HostapdGlobal()
+    hapd_global.remove(apdev[0]['ifname'])
+    params['wpa_passphrase'] = 'another passphrase'
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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")
+
+def test_ap_wps_incorrect_pin(dev, apdev):
+    """WPS PIN provisioning with incorrect PIN"""
+    ssid = "test-wps-incorrect-pin"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+
+    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)
+
 def test_ap_wps_conf_pin(dev, apdev):
     """WPS PIN provisioning with configured AP"""
     ssid = "test-wps-conf-pin"
@@ -76,12 +329,10 @@ def test_ap_wps_conf_pin(dev, apdev):
     logger.info("WPS provisioning step")
     pin = dev[0].wps_read_pin()
     hapd.request("WPS_PIN any " + pin)
-    dev[0].request("SET ignore_old_scan_res 1")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq="2412")
     dev[0].dump_monitor()
-    dev[0].request("WPS_PIN any " + pin)
-    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
-    if ev is None:
-        raise Exception("Association with the AP timed out")
+    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")
@@ -92,6 +343,96 @@ def test_ap_wps_conf_pin(dev, apdev):
     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_v1(dev, apdev):
+    """WPS PIN provisioning with configured WPS v1.0 AP"""
+    ssid = "test-wps-conf-pin-v1"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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")
+
+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"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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)
+
+def test_ap_wps_conf_pin_timeout(dev, apdev):
+    """WPS PIN provisioning with configured AP timing out PIN"""
+    ssid = "test-wps-conf-pin"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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"
@@ -102,8 +443,8 @@ def test_ap_wps_reg_connect(dev, apdev):
                      "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP",
                      "ap_pin": appin})
     logger.info("WPS provisioning step")
-    dev[0].request("SET ignore_old_scan_res 1")
     dev[0].dump_monitor()
+    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']:
@@ -115,15 +456,101 @@ def test_ap_wps_reg_connect(dev, apdev):
     if status['key_mgmt'] != 'WPA2-PSK':
         raise Exception("Unexpected key_mgmt")
 
+def test_ap_wps_reg_connect_mixed_mode(dev, apdev):
+    """WPS registrar using AP PIN to connect (WPA+WPA2)"""
+    ssid = "test-wps-reg-ap-pin"
+    appin = "12345670"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "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].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 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"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "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.Hostapd(apdev[0]['ifname'])
+    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].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)
+
 def test_ap_wps_reg_config(dev, apdev):
-    """WPS registrar configuring and AP using AP PIN"""
+    """WPS registrar configuring an AP using AP PIN"""
     ssid = "test-wps-init-ap-pin"
     appin = "12345670"
     hostapd.add_ap(apdev[0]['ifname'],
                    { "ssid": ssid, "eap_server": "1", "wps_state": "2",
                      "ap_pin": appin})
     logger.info("WPS configuration step")
-    dev[0].request("SET ignore_old_scan_res 1")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
     dev[0].dump_monitor()
     new_ssid = "wps-new-ssid"
     new_passphrase = "1234567890"
@@ -139,6 +566,174 @@ def test_ap_wps_reg_config(dev, apdev):
     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]['ifname'], params)
+    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 " + new_ssid.encode("hex") + " WPA2PSK CCMP " + new_passphrase.encode("hex")):
+        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"""
+    ssid = "test-wps-init-ap"
+    appin = "12345670"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "1",
+                     "ap_pin": appin})
+    logger.info("WPS configuration step")
+    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' 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_setup_locked(dev, apdev):
+    """WPS registrar locking up AP setup on AP PIN failures"""
+    ssid = "test-wps-incorrect-ap-pin"
+    appin = "12345670"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "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].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")
+
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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 = hostapd.Hostapd(apdev[0]['ifname'])
+    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"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "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].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")
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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_pbc_overlap_2ap(dev, apdev):
     """WPS PBC session overlap with two active APs"""
     hostapd.add_ap(apdev[0]['ifname'],
@@ -156,7 +751,8 @@ def test_ap_wps_pbc_overlap_2ap(dev, apdev):
     hapd2 = hostapd.Hostapd(apdev[1]['ifname'])
     hapd2.request("WPS_PBC")
     logger.info("WPS provisioning step")
-    dev[0].dump_monitor()
+    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)
     if ev is None:
@@ -172,12 +768,12 @@ def test_ap_wps_pbc_overlap_2sta(dev, apdev):
     hapd = hostapd.Hostapd(apdev[0]['ifname'])
     logger.info("WPS provisioning step")
     hapd.request("WPS_PBC")
-    dev[0].request("SET ignore_old_scan_res 1")
-    dev[1].request("SET ignore_old_scan_res 1")
+    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")
-    dev[1].request("WPS_PBC")
+    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)")
@@ -188,9 +784,59 @@ def test_ap_wps_pbc_overlap_2sta(dev, apdev):
         raise Exception("PBC session overlap not detected (dev1)")
     if "config_error=12" not in ev:
         raise Exception("PBC session overlap not correctly reported (dev1)")
+    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")
+
+def test_ap_wps_cancel(dev, apdev):
+    """WPS AP cancelling enabled config method"""
+    ssid = "test-wps-ap-cancel"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "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']
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+
+    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"
@@ -205,7 +851,7 @@ def test_ap_wps_er_add_enrollee(dev, apdev):
     logger.info("WPS configuration step")
     new_passphrase = "1234567890"
     dev[0].dump_monitor()
-    dev[0].request("SET ignore_old_scan_res 1")
+    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()
@@ -238,27 +884,89 @@ def test_ap_wps_er_add_enrollee(dev, apdev):
         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].request("SET ignore_old_scan_res 1")
+    dev[1].scan_for_bss(apdev[0]['bssid'], freq=2412)
     dev[1].dump_monitor()
-    dev[1].request("WPS_PIN any " + pin)
-    ev = dev[1].wait_event(["WPS-SUCCESS"], timeout=15)
+    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")
-    ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
-    if ev is None:
-        raise Exception("Association with the AP timed out")
+    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_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"
@@ -273,8 +981,8 @@ def test_ap_wps_er_add_enrollee_pbc(dev, apdev):
                      "config_methods": "label push_button",
                      "ap_pin": ap_pin, "uuid": ap_uuid, "upnp_iface": "lo"})
     logger.info("Learn AP configuration")
+    dev[0].scan_for_bss(apdev[0]['bssid'], freq=2412)
     dev[0].dump_monitor()
-    dev[0].request("SET ignore_old_scan_res 1")
     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']:
@@ -288,30 +996,1353 @@ def test_ap_wps_er_add_enrollee_pbc(dev, apdev):
     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")
+    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()
-    enrollee = dev[1].p2p_interface_addr()
-    dev[1].request("SET ignore_old_scan_res 1")
     dev[1].dump_monitor()
     dev[1].request("WPS_PBC")
 
-    ev = dev[0].wait_event(["WPS-ER-ENROLLEE-ADD"], timeout=15)
-    if ev is None:
-        raise Exception("Enrollee discovery timed out")
-    if enrollee not in ev:
-        raise Exception("Expected Enrollee not found")
-    dev[0].request("WPS_ER_PBC " + enrollee)
+    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")
-    ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
-    if ev is None:
-        raise Exception("Association with the AP timed out")
+    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]['ifname'],
+                   { "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].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]['ifname'],
+                   { "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].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")
+
+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]['ifname'],
+                   { "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 + " " +
+                   ssid.encode("hex") + " WPA2PSK CCMP " +
+                   new_passphrase.encode("hex"))
+    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")
+
+def test_ap_wps_fragmentation(dev, apdev):
+    """WPS with fragmentation in EAP-WSC and mixed mode WPA+WPA2"""
+    ssid = "test-wps-fragmentation"
+    appin = "12345670"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "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" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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")
+
+def test_ap_wps_new_version_sta(dev, apdev):
+    """WPS compatibility with new version number on the station"""
+    ssid = "test-wps-ver"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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)
+
+def test_ap_wps_new_version_ap(dev, apdev):
+    """WPS compatibility with new version number on the AP"""
+    ssid = "test-wps-ver"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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")
+
+def test_ap_wps_check_pin(dev, apdev):
+    """Verify PIN checking through control interface"""
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": "wps", "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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_wep_config(dev, apdev):
+    """WPS 2.0 AP rejecting WEP configuration"""
+    ssid = "test-wps-config"
+    appin = "12345670"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "ap_pin": appin})
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "skip_cred_build": "1", "extra_cred": "wps-wep-cred" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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)
+
+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" }
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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
+            (addr,psk) = l.split(' ')
+            psks[addr] = psk
+    return psks
+
+def test_ap_wps_per_station_psk(dev, apdev):
+    """WPS PBC provisioning with per-station PSK"""
+    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
+
+    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]['ifname'], params)
+
+        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 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)
+
+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
+
+    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]['ifname'], 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:
+        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):
+        subprocess.call(['sudo', 'rm', pinfile])
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "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"})
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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:
+        subprocess.call(['sudo', 'rm', pinfile])
+
+def test_ap_wps_auto_setup_with_config_file(dev, apdev):
+    """WPS auto-setup with configuration file"""
+    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")
+        hostapd.add_bss('phy3', ifname, conffile)
+        hapd = hostapd.Hostapd(ifname)
+        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, 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:
+        subprocess.call(['sudo', 'rm', conffile])
+
+def test_ap_wps_pbc_timeout(dev, apdev, params):
+    """wpa_supplicant PBC walk time [long]"""
+    if not params['long']:
+        raise HwsimSkip("Skip test case with long duration due to --long not specified")
+    ssid = "test-wps"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "1" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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")
+    ev = dev[0].wait_event(["WPS-TIMEOUT"], timeout=150)
+    if ev is None:
+        raise Exception("WPS-TIMEOUT not reported")
+
+def add_ssdp_ap(ifname, ap_uuid):
+    ssid = "wps-ssdp"
+    ap_pin = "12345670"
+    hostapd.add_ap(ifname,
+                   { "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" })
+
+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, ("239.255.255.250", 1900))
+    if no_recv:
+        return None
+    return sock.recv(1000)
+
+def ssdp_send_msearch(st):
+    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)
+
+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]['ifname'], 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]['ifname'], 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, ("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, ("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, ("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, ("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, ("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, ("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, ("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, ("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, ("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, ("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, ("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, ("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, ("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, ("239.255.255.250", 1900))
+
+    logger.debug("Invalid message format")
+    sock.sendto("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, ("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, ("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]['ifname'], 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, ("239.255.255.250", 1900))
+    resp = 0
+    while True:
+        try:
+            r = sock.recv(1000)
+            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, ("239.255.255.250", 1900))
+    while True:
+        try:
+            r = sock.recv(1000)
+            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):
+    conn = urllib.urlopen(location)
+    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'] = urlparse.urljoin(location, service.find(urn + 'SCPDURL').text)
+    res['control_url'] = urlparse.urljoin(location, service.find(urn + 'controlURL').text)
+    res['event_sub_url'] = urlparse.urljoin(location, service.find(urn + 'eventSubURL').text)
+    return res
+
+def upnp_soap_action(conn, path, action, include_soap_action=True, soap_action_override=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))
+    tree = ET.ElementTree(root)
+    soap = StringIO.StringIO()
+    tree.write(soap, xml_declaration=True, encoding='utf-8')
+
+    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
+    conn.request("POST", path, soap.getvalue(), 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]['ifname'], ap_uuid)
+
+    location = ssdp_get_location(ap_uuid)
+    urls = upnp_get_urls(location)
+
+    conn = urllib.urlopen(urls['scpd_url'])
+    scpd = conn.read()
+
+    conn = urllib.urlopen(urlparse.urljoin(location, "unknown.html"))
+    if conn.getcode() != 404:
+        raise Exception("Unexpected HTTP response to GET unknown URL")
+
+    url = urlparse.urlparse(location)
+    conn = httplib.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: %s" % resp.status)
+
+    conn.request("UNKNOWN", "hello", "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 501:
+        raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+    headers = { "Content-type": 'text/xml; charset="utf-8"',
+                "SOAPAction": '"urn:some-unknown-action#GetDeviceInfo"' }
+    ctrlurl = urlparse.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: %s" % 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: %s" % 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: %s" % resp.status)
+
+    resp = upnp_soap_action(conn, ctrlurl.path, "GetDeviceInfo")
+    if resp.status != 200:
+        raise Exception("Unexpected HTTP response: %s" % resp.status)
+    dev = resp.read()
+    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: %s" % 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: %s" % 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: %s" % resp.status)
+
+    logger.debug("Unknown action")
+    resp = upnp_soap_action(conn, ctrlurl.path, "Unknown")
+    if resp.status != 401:
+        raise Exception("Unexpected HTTP response: %s" % resp.status)
+
+def test_ap_wps_upnp_subscribe(dev, apdev):
+    """WPS AP and UPnP event subscription"""
+    ap_uuid = "27ea801a-9e5c-4e73-bd82-f89cbcd10d7e"
+    add_ssdp_ap(apdev[0]['ifname'], ap_uuid)
+
+    location = ssdp_get_location(ap_uuid)
+    urls = upnp_get_urls(location)
+    eventurl = urlparse.urlparse(urls['event_sub_url'])
+
+    url = urlparse.urlparse(location)
+    conn = httplib.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: %s" % resp.status)
+
+    conn.request("SUBSCRIBE", eventurl.path, "\r\n\r\n", headers)
+    resp = conn.getresponse()
+    if resp.status != 412:
+        raise Exception("Unexpected HTTP response: %s" % 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: %s" % 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: %s" % 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: %s" % 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: %s" % 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: %s" % 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: %s" % 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: %s" % 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: %s" % 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: %s" % 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: %s" % 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: %s" % 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: %s" % 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: %s" % 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: %s" % 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: %s" % 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: %s" % 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: %s" % 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: %s" % resp.status)
+    sid = resp.getheader("sid")
+    logger.debug("Subscription SID " + sid)
+
+def test_ap_wps_disabled(dev, apdev):
+    """WPS operations while WPS is disabled"""
+    ssid = "test-wps-disabled"
+    hostapd.add_ap(apdev[0]['ifname'], { "ssid": ssid })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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"""
+    ssid = "test-wps-wep"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "skip_cred_build": "1", "extra_cred": "wps-mixed-cred" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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")
+    if pairwise != "CCMP TKIP" and pairwise != "CCMP GCMP TKIP":
+        raise Exception("Unexpected merged pairwise field value: " + pairwise)
+
+def test_ap_wps_while_connected(dev, apdev):
+    """WPS PBC provisioning while connected to another AP"""
+    ssid = "test-wps-conf"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+
+    hostapd.add_ap(apdev[1]['ifname'], { "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")
+
+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"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "2",
+                     "wpa_passphrase": "12345678", "wpa": "2",
+                     "wpa_key_mgmt": "WPA-PSK", "rsn_pairwise": "CCMP"})
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+
+    hostapd.add_ap(apdev[1]['ifname'], { "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")
+
+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]['ifname'],
+                          { "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]['ifname'],
+                          { "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")
+
+    if "OK" not in wpas.request("AP_SCAN 2"):
+        raise Exception("Failed to set AP_SCAN 2")
+
+    wpas.scan_for_bss(apdev[0]['bssid'], freq="2412")
+    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.request("DISCONNECT")
+    wpas.request("BSS_FLUSH 0")
+    wpas.dump_monitor()
+    wpas.request("REASSOCIATE")
+    wpas.wait_connected(timeout=30)
index 9d5b2a4..75d06e2 100644 (file)
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-#
 # WPS+NFC tests
 # Copyright (c) 2013, Jouni Malinen <j@w1.fi>
 #
@@ -9,12 +7,12 @@
 import time
 import subprocess
 import logging
-logger = logging.getLogger(__name__)
+logger = logging.getLogger()
 
 import hwsim_utils
 import hostapd
 
-def check_wpa2_connection(sta, ap, ssid, mixed=False):
+def check_wpa2_connection(sta, ap, hapd, ssid, mixed=False):
     status = sta.get_status()
     if status['wpa_state'] != 'COMPLETED':
         raise Exception("Not fully connected")
@@ -28,7 +26,7 @@ def check_wpa2_connection(sta, ap, ssid, mixed=False):
         raise Exception("Unexpected encryption configuration")
     if status['key_mgmt'] != 'WPA2-PSK':
         raise Exception("Unexpected key_mgmt")
-    hwsim_utils.test_connectivity(sta.ifname, ap['ifname'])
+    hwsim_utils.test_connectivity(sta, hapd)
 
 def ap_wps_params(ssid):
     return { "ssid": ssid, "eap_server": "1", "wps_state": "2",
@@ -37,12 +35,14 @@ def ap_wps_params(ssid):
 
 def test_nfc_wps_password_token_sta(dev, apdev):
     """NFC tag with password token on the station/Enrollee"""
-    dev[0].request("SET ignore_old_scan_res 1")
     ssid = "test-wps-nfc-pw-token-conf"
     params = ap_wps_params(ssid)
     hostapd.add_ap(apdev[0]['ifname'], params)
     hapd = hostapd.Hostapd(apdev[0]['ifname'])
     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")
@@ -53,10 +53,8 @@ def test_nfc_wps_password_token_sta(dev, apdev):
     res = dev[0].request("WPS_NFC")
     if "FAIL" in res:
         raise Exception("Failed to start Enrollee using NFC password token")
-    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
-    if ev is None:
-        raise Exception("Association with the AP timed out")
-    check_wpa2_connection(dev[0], apdev[0], ssid)
+    dev[0].wait_connected(timeout=30)
+    check_wpa2_connection(dev[0], apdev[0], hapd, ssid)
 
 def test_nfc_wps_config_token(dev, apdev):
     """NFC tag with configuration token from AP"""
@@ -72,14 +70,11 @@ def test_nfc_wps_config_token(dev, apdev):
     res = dev[0].request("WPS_NFC_TAG_READ " + conf)
     if "FAIL" in res:
         raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
-    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
-    if ev is None:
-        raise Exception("Association with the AP timed out")
-    check_wpa2_connection(dev[0], apdev[0], ssid)
+    dev[0].wait_connected(timeout=15)
+    check_wpa2_connection(dev[0], apdev[0], hapd, ssid)
 
 def test_nfc_wps_config_token_init(dev, apdev):
     """NFC tag with configuration token from AP with auto configuration"""
-    dev[0].request("SET ignore_old_scan_res 1")
     ssid = "test-wps-nfc-conf-token-init"
     hostapd.add_ap(apdev[0]['ifname'],
                    { "ssid": ssid, "eap_server": "1", "wps_state": "1" })
@@ -92,14 +87,11 @@ def test_nfc_wps_config_token_init(dev, apdev):
     res = dev[0].request("WPS_NFC_TAG_READ " + conf)
     if "FAIL" in res:
         raise Exception("Failed to provide NFC tag contents to wpa_supplicant")
-    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
-    if ev is None:
-        raise Exception("Association with the AP timed out")
-    check_wpa2_connection(dev[0], apdev[0], ssid, mixed=True)
+    dev[0].wait_connected(timeout=15)
+    check_wpa2_connection(dev[0], apdev[0], hapd, ssid, mixed=True)
 
 def test_nfc_wps_password_token_sta_init(dev, apdev):
     """Initial AP configuration with first WPS NFC Enrollee"""
-    dev[0].request("SET ignore_old_scan_res 1")
     ssid = "test-wps-nfc-pw-token-init"
     hostapd.add_ap(apdev[0]['ifname'],
                    { "ssid": ssid, "eap_server": "1", "wps_state": "1" })
@@ -115,14 +107,11 @@ def test_nfc_wps_password_token_sta_init(dev, apdev):
     res = dev[0].request("WPS_NFC")
     if "FAIL" in res:
         raise Exception("Failed to start Enrollee using NFC password token")
-    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
-    if ev is None:
-        raise Exception("Association with the AP timed out")
-    check_wpa2_connection(dev[0], apdev[0], ssid, mixed=True)
+    dev[0].wait_connected(timeout=30)
+    check_wpa2_connection(dev[0], apdev[0], hapd, ssid, mixed=True)
 
 def test_nfc_wps_password_token_ap(dev, apdev):
     """WPS registrar configuring an AP using AP password token"""
-    dev[0].request("SET ignore_old_scan_res 1")
     ssid = "test-wps-nfc-pw-token-init"
     hostapd.add_ap(apdev[0]['ifname'],
                    { "ssid": ssid, "eap_server": "1", "wps_state": "1" })
@@ -143,10 +132,66 @@ def test_nfc_wps_password_token_ap(dev, apdev):
     res = dev[0].request("WPS_REG " + apdev[0]['bssid'] + " nfc-pw " + new_ssid.encode("hex") + " WPA2PSK CCMP " + new_passphrase.encode("hex"))
     if "FAIL" in res:
         raise Exception("Failed to start Registrar using NFC password token")
-    ev = dev[0].wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
-    if ev is None:
-        raise Exception("Association with the AP timed out")
-    check_wpa2_connection(dev[0], apdev[0], new_ssid, mixed=True)
+    dev[0].wait_connected(timeout=30)
+    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")
+
+def test_nfc_wps_handover_init(dev, apdev):
+    """Connect to WPS AP with NFC connection handover and move to configured state"""
+    dev[0].request("SET ignore_old_scan_res 1")
+    ssid = "test-wps-nfc-handover-init"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "1" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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)
+    check_wpa2_connection(dev[0], apdev[0], hapd, ssid, mixed=True)
+
+def test_nfc_wps_handover_errors(dev, apdev):
+    """WPS AP NFC handover report error cases"""
+    ssid = "test-wps-nfc-handover"
+    hostapd.add_ap(apdev[0]['ifname'],
+                   { "ssid": ssid, "eap_server": "1", "wps_state": "1" })
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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")
 
 def test_nfc_wps_handover(dev, apdev):
     """Connect to WPS AP with NFC connection handover"""
@@ -168,7 +213,336 @@ def test_nfc_wps_handover(dev, apdev):
     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"], timeout=15)
+    dev[0].wait_connected(timeout=30)
+    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"""
+    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]['ifname'], 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)
+        check_wpa2_connection(dev[0], apdev[0], hapd, ssid)
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['sudo', 'iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+def test_nfc_wps_handover_chan14(dev, apdev):
+    """Connect to WPS AP with NFC connection handover on channel 14"""
+    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]['ifname'], 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)
+        check_wpa2_connection(dev[0], apdev[0], hapd, ssid)
+    finally:
+        dev[0].request("DISCONNECT")
+        if hapd:
+            hapd.request("DISABLE")
+        subprocess.call(['sudo', 'iw', 'reg', 'set', '00'])
+        dev[0].flush_scan_cache()
+
+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)
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    # 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)
+    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)
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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)
+    hostapd.add_ap(apdev[0]['ifname'], params)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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"
+    hostapd.add_ap(ap['ifname'],
+                   { "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")
+    er.dump_monitor()
+    er.request("SET ignore_old_scan_res 1")
+    er.wps_reg(ap['bssid'], ap_pin)
+
+    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")
+
+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")
+
+def _test_nfc_wps_er_pw_token(dev, apdev):
+    ssid = "wps-nfc-er-pw-token"
+    start_ap_er(dev[0], apdev[0], ssid)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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)
+    check_wpa2_connection(dev[1], apdev[0], hapd, ssid)
+
+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")
+
+def _test_nfc_wps_er_config_token(dev, apdev):
+    ssid = "wps-nfc-er-config-token"
+    start_ap_er(dev[0], apdev[0], ssid)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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)
+    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"
+    start_ap_er(dev[0], apdev[0], ssid)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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)
+    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")
+
+def _test_nfc_wps_er_handover_pk_hash_mismatch_sta(dev, apdev):
+    ssid = "wps-nfc-er-handover-pkhash-sta"
+    start_ap_er(dev[0], apdev[0], ssid)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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")
+
+def _test_nfc_wps_er_handover_pk_hash_mismatch_er(dev, apdev):
+    ssid = "wps-nfc-er-handover-pkhash-er"
+    start_ap_er(dev[0], apdev[0], ssid)
+    hapd = hostapd.Hostapd(apdev[0]['ifname'])
+    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("Association with the AP timed out")
-    check_wpa2_connection(dev[0], apdev[0], ssid)
+        raise Exception("Timed out")
+    if "WPS-FAIL" not in ev:
+        raise Exception("Public key hash mismatch not detected")
index e787570..de42857 100644 (file)
@@ -1,7 +1,5 @@
-#!/usr/bin/python
-#
 # P2P autonomous GO test cases
-# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+# 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 time
 import subprocess
 import logging
-logger = logging.getLogger(__name__)
+logger = logging.getLogger()
 
 import hwsim_utils
+import utils
+from utils import HwsimSkip
 from wlantest import Wlantest
+from wpasupplicant import WpaSupplicant
 
-def autogo(go):
+def autogo(go, freq=None, persistent=None):
     logger.info("Start autonomous GO " + go.ifname)
-    res = go.p2p_start_go()
+    res = go.p2p_start_go(freq=freq, persistent=persistent)
     logger.debug("res: " + str(res))
+    return res
 
-def connect_cli(go, client):
+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)
-    client.p2p_connect_group(go.p2p_dev_addr(), pin, timeout=60)
+    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 test_autogo(dev):
     """P2P autonomous GO and client joining group"""
-    autogo(dev[0])
-    connect_cli(dev[0], dev[1])
+    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)
+    if 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)
+    if bss['id'] != id:
+        raise Exception("Could not find BSS entry based on id")
+    res = dev[1].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_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 30000 102400 20000 102400"):
+        raise Exception("Could not send presence request")
+    ev = dev[1].wait_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_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()
-    try:
-        dev[1].remove_group()
-    except:
-        pass
+    dev[1].wait_go_ending_session()
+
+def test_autogo2(dev):
+    """P2P autonomous GO with a separate group interface and client joining group"""
+    dev[0].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].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].request("SET p2p_no_group_iface 0")
+    dev[1].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].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=10)
+    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)")
+
+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].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])
-    connect_cli(dev[0], dev[1])
-    connect_cli(dev[0], dev[2])
+    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[2].remove_group()
-    dev[1].remove_group()
+    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].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_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_tdls(dev):
     """P2P autonomous GO and two clients using TDLS"""
     wt = Wlantest()
@@ -57,12 +253,12 @@ def test_autogo_tdls(dev):
     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)
+    res = go.p2p_start_go(persistent=id, freq="2462")
     logger.debug("res: " + str(res))
     wt.flush()
     wt.add_passphrase("12345678")
-    connect_cli(go, dev[1])
-    connect_cli(go, dev[2])
+    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()
@@ -97,3 +293,185 @@ def test_autogo_tdls(dev):
     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].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"""
+    autogo(dev[0], freq=2417)
+    connect_cli(dev[0], dev[1])
+    res = dev[0].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_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])
+
+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(['sudo', '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)
+
+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")
+
+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])
+        subprocess.call(['sudo', 'brctl', 'addbr', 'p2p-br0'])
+        subprocess.call(['sudo', 'brctl', 'setfd', 'p2p-br0', '0'])
+        subprocess.call(['sudo', 'brctl', 'addif', 'p2p-br0', dev[0].ifname])
+        subprocess.call(['sudo', 'ip', 'link', 'set', 'dev', 'p2p-br0', 'up'])
+        time.sleep(0.1)
+        subprocess.call(['sudo', 'brctl', 'delif', 'p2p-br0', dev[0].ifname])
+        time.sleep(0.1)
+        subprocess.call(['sudo', 'ip', 'link', 'set', 'dev', 'p2p-br0', 'down'])
+        time.sleep(0.1)
+        subprocess.call(['sudo', '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_status_field('wpa_state') != "COMPLETED":
+            raise Exception("Unexpected wpa_state")
+        dev[0].remove_group()
+    finally:
+        dev[0].request("AUTOSCAN ")
+        subprocess.Popen(['sudo', 'brctl', 'delif', 'p2p-br0', dev[0].ifname],
+                         stderr=open('/dev/null', 'w'))
+        subprocess.Popen(['sudo', 'ip', 'link', 'set', 'dev', 'p2p-br0', 'down'],
+                         stderr=open('/dev/null', 'w'))
+        subprocess.Popen(['sudo', 'brctl', 'delbr', 'p2p-br0'],
+                         stderr=open('/dev/null', 'w'))
+
+def test_presence_req_on_group_interface(dev):
+    """P2P_PRESENCE_REQ on group interface"""
+    dev[1].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()
index f1029c8..9b97d39 100644 (file)
@@ -1,5 +1,3 @@
-#!/usr/bin/python
-#
 # P2P device discovery test cases
 # Copyright (c) 2013, Jouni Malinen <j@w1.fi>
 #
@@ -7,16 +5,18 @@
 # See README for more details.
 
 import logging
-logger = logging.getLogger(__name__)
+logger = logging.getLogger()
+import time
 
 import hwsim_utils
+from wpasupplicant import WpaSupplicant
 
 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)
+    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):
@@ -75,6 +75,32 @@ def test_discovery(dev):
     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].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")
+
+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_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)
@@ -87,5 +113,324 @@ def test_discovery_group_client(dev):
     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):
-        raise Exception("Could not discover group client")
+    if not dev[2].discover_peer(dev[1].p2p_dev_addr(), social=False, timeout=10):
+        if not dev[2].discover_peer(dev[1].p2p_dev_addr(), social=False, timeout=10):
+            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")
+    dev[1].wait_disconnected(timeout=10)
+    dev[2].request("P2P_CONNECT {} {} display".format(dev[1].p2p_dev_addr(),
+                                                      pin))
+    ev = dev[1].wait_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].request("REASSOCIATE")
+    ev = dev[1].wait_event(["CTRL-EVENT-CONNECTED", "P2P-GO-NEG-REQUEST"])
+    if ev is None:
+        raise Exception("Timeout on reconnection to group")
+    if "P2P-GO-NEG-REQUEST" not in ev:
+        ev = dev[1].wait_event(["P2P-GO-NEG-REQUEST"])
+        if ev is None:
+            raise Exception("Timeout on waiting for GO Negotiation Request")
+
+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_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_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_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_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_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_event(['P2P-DEVICE-FOUND'], timeout=2)
+    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_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_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):
+    """P2P device discovery with interface getting didabled"""
+    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(1)
+
+        # 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_auto(dev):
+    """P2P device discovery and provision discovery with auto GO/dev selection"""
+    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].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_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_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()
+
+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")
index 6bba55b..ac432dc 100644 (file)
@@ -1,18 +1,21 @@
-#!/usr/bin/python
-#
 # P2P group formation test cases
-# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+# 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 logging
-logger = logging.getLogger(__name__)
+logger = logging.getLogger()
 import time
 import threading
 import Queue
+import os
 
+import hostapd
 import hwsim_utils
+import utils
+from utils import HwsimSkip
+from wpasupplicant import WpaSupplicant
 
 def check_grpform_results(i_res, r_res):
     if i_res['result'] != 'success' or r_res['result'] != 'success':
@@ -20,14 +23,26 @@ def check_grpform_results(i_res, r_res):
     if i_res['ssid'] != r_res['ssid']:
         raise Exception("SSID mismatch")
     if i_res['freq'] != r_res['freq']:
-        raise Exception("SSID mismatch")
+        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")
-    i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), pin, i_method, timeout=15, go_intent=i_intent)
-    logger.debug("i_res: " + str(i_res))
+    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, 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'):
@@ -45,23 +60,26 @@ def go_neg_pin(i_dev, r_dev, i_intent=None, r_intent=None, i_method='enter', r_m
         raise Exception("GO Negotiation timed out")
     r_dev.dump_monitor()
     logger.debug("Re-initiate GO Negotiation from r_dev")
-    r_res = r_dev.p2p_go_neg_init(i_dev.p2p_dev_addr(), pin, r_method, go_intent=r_intent, timeout=15)
+    r_res = r_dev.p2p_go_neg_init(i_dev.p2p_dev_addr(), pin, r_method, go_intent=r_intent, timeout=20)
     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'):
-    r_dev.p2p_listen()
+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_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)
-    i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), pin, i_method, timeout=15, go_intent=i_intent, expect_failure=expect_failure)
+    r_dev.p2p_go_neg_auth(i_dev.p2p_dev_addr(), pin, r_method, go_intent=r_intent, freq=r_freq)
+    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)
     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))
@@ -75,65 +93,171 @@ def go_neg_pin_authorized(i_dev, r_dev, i_intent=None, r_intent=None, expect_fai
     if expect_failure:
         return
     logger.info("Group formed")
-    hwsim_utils.test_connectivity_p2p(r_dev, i_dev)
+    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):
+def go_neg_init_pbc(i_dev, r_dev, i_intent, res, freq, provdisc):
     logger.debug("Initiate GO Negotiation from i_dev")
-    i_res = i_dev.p2p_go_neg_init(r_dev.p2p_dev_addr(), None, "pbc", timeout=15, go_intent=i_intent)
-    logger.debug("i_res: " + str(i_res))
+    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, 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):
-    r_dev.p2p_find(social=True)
+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.Queue()
-    t = threading.Thread(target=go_neg_init_pbc, args=(i_dev, r_dev, i_intent, res))
+    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:
         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")
-    r_res = r_dev.p2p_go_neg_init(i_dev.p2p_dev_addr(), None, "pbc", go_intent=r_intent, timeout=15)
+    r_res = r_dev.p2p_go_neg_init(i_dev.p2p_dev_addr(), None, "pbc",
+                                  go_intent=r_intent, timeout=20, freq=r_freq)
     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):
+    dev1.remove_group()
+    try:
+        dev2.remove_group()
+    except:
+        pass
+
 def test_grpform(dev):
     """P2P group formation using PIN and authorized connection (init -> GO)"""
-    go_neg_pin_authorized(i_dev=dev[0], i_intent=15, r_dev=dev[1], r_intent=0)
-    dev[0].remove_group()
     try:
+        dev[0].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()
-    except:
-        pass
+        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].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].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 utils.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].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)
+    remove_group(dev[0], dev[1])
+    if r_res['ifname'] in utils.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].request("SET p2p_no_group_iface 0")
+    dev[1].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 utils.get_ifnames():
+        raise Exception("Group interface netdev was not removed")
+    if r_res['ifname'] in utils.get_ifnames():
+        raise Exception("Group interface netdev was not removed")
 
 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)
-    dev[0].remove_group()
-    try:
-        dev[1].remove_group()
-    except:
-        pass
+    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].request("SET p2p_no_group_iface 0")
+    dev[1].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 utils.get_ifnames():
+        raise Exception("Group interface netdev was not removed")
+    if r_res['ifname'] in utils.get_ifnames():
+        raise Exception("Group interface netdev was not removed")
 
 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)
-    dev[0].remove_group()
-    try:
-        dev[1].remove_group()
-    except:
-        pass
+    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].request("SET p2p_no_group_iface 0")
+    dev[1].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 utils.get_ifnames():
+        raise Exception("Group interface netdev was not removed")
+    if r_res['ifname'] in utils.get_ifnames():
+        raise Exception("Group interface netdev was not removed")
 
 def test_grpform_pbc(dev):
     """P2P group formation using PBC and re-init GO Negotiation"""
@@ -141,11 +265,41 @@ def test_grpform_pbc(dev):
     check_grpform_results(i_res, r_res)
     if i_res['role'] != 'GO' or r_res['role'] != 'client':
         raise Exception("Unexpected device roles")
-    dev[0].remove_group()
+    remove_group(dev[0], dev[1])
+
+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"""
     try:
-        dev[1].remove_group()
-    except:
-        pass
+        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 100 50000"):
+            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)
+        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'] != "50000":
+            raise Exception("Extended listen interval not discovered correctly")
+        if peer0['ext_listen_period'] != "100":
+            raise Exception("Extended listen period not discovered correctly")
+        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_both_go_intent_15(dev):
     """P2P GO Negotiation with both devices using GO intent 15"""
@@ -158,3 +312,619 @@ def test_both_go_neg_display(dev):
 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')
+
+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)
+
+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].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].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()
+
+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])
+
+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])
+
+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)
+
+def test_grpform_pref_chan_go(dev):
+    """P2P group formation preferred channel selection by GO"""
+    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])
+
+def test_grpform_pref_chan_go_overridden(dev):
+    """P2P group formation preferred channel selection by GO overridden by client"""
+    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])
+
+def test_grpform_no_go_freq_forcing_chan(dev):
+    """P2P group formation with no-GO freq forcing channel"""
+    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])
+
+def test_grpform_no_go_freq_conflict(dev):
+    """P2P group formation fails due to no-GO range forced by client"""
+    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)
+
+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])
+
+def test_grpform_no_5ghz_add_cli(dev):
+    """P2P group formation with passive scan 5 GHz and p2p_add_cli_chan=1"""
+    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])
+
+def test_grpform_no_5ghz_add_cli2(dev):
+    """P2P group formation with passive scan 5 GHz and p2p_add_cli_chan=1 (reverse)"""
+    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])
+
+def test_grpform_no_5ghz_add_cli3(dev):
+    """P2P group formation with passive scan 5 GHz and p2p_add_cli_chan=1 (intent 15)"""
+    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])
+
+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)"""
+    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])
+
+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].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].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_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_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_event(["P2P-GROUP-FORMATION-FAILURE"], timeout=10)
+    if ev is None:
+        raise Exception("Group formation failure timed out")
+    ev = dev[0].wait_event(["P2P-GROUP-FORMATION-FAILURE"], timeout=5)
+    if ev is None:
+        raise Exception("Group formation failure timed out")
+
+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(["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")
+
+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].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].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(["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, ifname):
+    hapd_global = hostapd.HostapdGlobal()
+    hapd_global.remove(ifname)
+    dev[0].request("P2P_CANCEL")
+    dev[1].request("P2P_CANCEL")
+    dev[0].p2p_stop_find()
+    dev[1].p2p_stop_find()
+    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)
+
+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]['ifname'], params)
+    hapd.request("WPS_PBC")
+    time.sleep(0.1)
+
+    # Since P2P Client scan case is now optimzied 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 enoguh 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].request("P2P_CONNECT " + addr1 + " pbc auth go_intent=0"):
+        raise Exception("Failed to authorize GO Neg")
+    if "OK" not in dev[1].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"], timeout=15)
+    if ev is None:
+        raise Exception("PBC overlap not reported")
+
+    clear_pbc_overlap(dev, apdev[0]['ifname'])
+
+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]['ifname'], 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].request("P2P_CONNECT " + addr1 + " pbc auth go_intent=0"):
+        raise Exception("Failed to authorize GO Neg")
+    if "OK" not in dev[1].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)
+    if ev is None or "WPS-OVERLAP-DETECTED" not in ev:
+        # Do not report this as failure since the P2P group formation case
+        # using a separate group interface has limited chances of "seeing" the
+        # overlapping AP due to a per-SSID scan and no prior scan operations on
+        # the group interface.
+        logger.info("PBC overlap not reported")
+
+    clear_pbc_overlap(dev, apdev[0]['ifname'])
+
+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")
+
+def test_grpform_cred_ready_timeout(dev, apdev, params):
+    """P2P GO Negotiation wait for credentials to become ready [long]"""
+    if not params['long']:
+        raise HwsimSkip("Skip test case with long duration due to --long not specified")
+
+    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()
+    ev = dev[2].wait_global_event(["P2P-GO-NEG-FAILURE"], timeout=100)
+    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()
+    wpas.p2p_stop_find()
+
+    # 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()
+
+        if mode != "P2P GO - group formation":
+            raise Exception("Unexpected mode on GO during group formation: " + mode)
+
+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].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()
+
+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-CHANNEL-UNSUPPORTED" not in dev[0].request("P2P_CONNECT 00:11:22:33:44:55 pin freq=3000"):
+        raise Exception("Unsupported channel not reported")
+
+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")
+
+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()
diff --git a/tests/hwsim/tnc/.gitignore b/tests/hwsim/tnc/.gitignore
new file mode 100644 (file)
index 0000000..2f88962
--- /dev/null
@@ -0,0 +1,4 @@
+libhostap2_imc.so
+libhostap2_imv.so
+libhostap_imc.so
+libhostap_imv.so
diff --git a/tests/hwsim/tnc/Makefile b/tests/hwsim/tnc/Makefile
new file mode 100644 (file)
index 0000000..64ba0ca
--- /dev/null
@@ -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/tests/hwsim/tnc/hostap2_imc.c b/tests/hwsim/tnc/hostap2_imc.c
new file mode 100644 (file)
index 0000000..3818c17
--- /dev/null
@@ -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/tests/hwsim/tnc/hostap2_imv.c b/tests/hwsim/tnc/hostap2_imv.c
new file mode 100644 (file)
index 0000000..652888a
--- /dev/null
@@ -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/tests/hwsim/tnc/hostap_imc.c b/tests/hwsim/tnc/hostap_imc.c
new file mode 100644 (file)
index 0000000..d28183a
--- /dev/null
@@ -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/tests/hwsim/tnc/hostap_imv.c b/tests/hwsim/tnc/hostap_imv.c
new file mode 100644 (file)
index 0000000..0f4f9c8
--- /dev/null
@@ -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/tests/hwsim/tnc/tnc_config b/tests/hwsim/tnc/tnc_config
new file mode 100644 (file)
index 0000000..613783a
--- /dev/null
@@ -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/tests/hwsim/vm/.gitignore b/tests/hwsim/vm/.gitignore
new file mode 100644 (file)
index 0000000..b1ce1b1
--- /dev/null
@@ -0,0 +1 @@
+vm-config
diff --git a/tests/hwsim/vm/README b/tests/hwsim/vm/README
new file mode 100644 (file)
index 0000000..8fef72c
--- /dev/null
@@ -0,0 +1,66 @@
+These scripts allow you to run the hwsim tests inside a KVM virtual machine.
+
+To set it up, first compile a kernel with the kernel-config 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.
+
+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.sh <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/tests/hwsim/vm/build-codecov.sh b/tests/hwsim/vm/build-codecov.sh
new file mode 100644 (file)
index 0000000..e67ef2e
--- /dev/null
@@ -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/tests/hwsim/vm/combine-codecov.sh b/tests/hwsim/vm/combine-codecov.sh
new file mode 100644 (file)
index 0000000..3fe8443
--- /dev/null
@@ -0,0 +1,36 @@
+#!/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:.*\/common\/wpa_ctrl.c$\)/\1/};/^SF:.*\/common\/wpa_ctrl.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/tests/hwsim/vm/dbus.conf b/tests/hwsim/vm/dbus.conf
new file mode 100644 (file)
index 0000000..e64e44f
--- /dev/null
@@ -0,0 +1,37 @@
+<!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.epitest.hostap.WPASupplicant"/>
+    <allow send_destination="fi.epitest.hostap.WPASupplicant"/>
+    <allow send_interface="fi.epitest.hostap.WPASupplicant"/>
+    <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/tests/hwsim/vm/inside.sh b/tests/hwsim/vm/inside.sh
new file mode 100644 (file)
index 0000000..ffab4ee
--- /dev/null
@@ -0,0 +1,117 @@
+#!/bin/sh
+
+# 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
+mount tmpfs -t tmpfs /tmp
+# 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
+
+# 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)
+ARGS=$(sed 's/.*ARGS=//' /proc/cmdline)
+
+# create /dev entries we need
+mknod -m 660 /dev/ttyS0 c 4 64
+mknod -m 660 /dev/random c 1 8
+mknod -m 660 /dev/urandom c 1 9
+mknod -m 666 /dev/null c 1 3
+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
+
+# 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
+
+# local network is needed for some tests
+ip link set lo up
+
+# create logs mountpoint and mount the logshare
+mkdir /tmp/logs
+mount -t 9p -o trans=virtio,rw logshare /tmp/logs
+
+if [ "$TIMEWARP" = "1" ] ; then
+    (
+        while sleep 1 ; do
+            date --set "@$(($(date +%s) + 19))"
+        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 $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/tests/hwsim/vm/kernel-config b/tests/hwsim/vm/kernel-config
new file mode 100644 (file)
index 0000000..487c4fd
--- /dev/null
@@ -0,0 +1,1712 @@
+#
+# Automatically generated file; DO NOT EDIT.
+# Linux/x86 3.12.0-rc1 Kernel Configuration
+#
+CONFIG_64BIT=y
+CONFIG_X86_64=y
+CONFIG_X86=y
+CONFIG_INSTRUCTION_DECODER=y
+CONFIG_OUTPUT_FORMAT="elf64-x86-64"
+CONFIG_ARCH_DEFCONFIG="arch/x86/configs/x86_64_defconfig"
+CONFIG_LOCKDEP_SUPPORT=y
+CONFIG_STACKTRACE_SUPPORT=y
+CONFIG_HAVE_LATENCYTOP_SUPPORT=y
+CONFIG_MMU=y
+CONFIG_NEED_DMA_MAP_STATE=y
+CONFIG_NEED_SG_DMA_LENGTH=y
+CONFIG_GENERIC_BUG=y
+CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y
+CONFIG_GENERIC_HWEIGHT=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_ARCH_HAS_CPU_RELAX=y
+CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y
+CONFIG_ARCH_HAS_CPU_AUTOPROBE=y
+CONFIG_HAVE_SETUP_PER_CPU_AREA=y
+CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y
+CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK=y
+CONFIG_ARCH_HIBERNATION_POSSIBLE=y
+CONFIG_ARCH_SUSPEND_POSSIBLE=y
+CONFIG_ARCH_WANT_HUGE_PMD_SHARE=y
+CONFIG_ARCH_WANT_GENERAL_HUGETLB=y
+CONFIG_ZONE_DMA32=y
+CONFIG_AUDIT_ARCH=y
+CONFIG_ARCH_SUPPORTS_OPTIMIZED_INLINING=y
+CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y
+CONFIG_X86_64_SMP=y
+CONFIG_X86_HT=y
+CONFIG_ARCH_HWEIGHT_CFLAGS="-fcall-saved-rdi -fcall-saved-rsi -fcall-saved-rdx -fcall-saved-rcx -fcall-saved-r8 -fcall-saved-r9 -fcall-saved-r10 -fcall-saved-r11"
+CONFIG_ARCH_SUPPORTS_UPROBES=y
+CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config"
+CONFIG_IRQ_WORK=y
+CONFIG_BUILDTIME_EXTABLE_SORT=y
+
+#
+# General setup
+#
+CONFIG_INIT_ENV_ARG_LIMIT=32
+CONFIG_CROSS_COMPILE=""
+# CONFIG_COMPILE_TEST is not set
+CONFIG_LOCALVERSION=""
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_HAVE_KERNEL_GZIP=y
+CONFIG_HAVE_KERNEL_BZIP2=y
+CONFIG_HAVE_KERNEL_LZMA=y
+CONFIG_HAVE_KERNEL_XZ=y
+CONFIG_HAVE_KERNEL_LZO=y
+CONFIG_HAVE_KERNEL_LZ4=y
+# CONFIG_KERNEL_GZIP is not set
+CONFIG_KERNEL_BZIP2=y
+# CONFIG_KERNEL_LZMA is not set
+# CONFIG_KERNEL_XZ is not set
+# CONFIG_KERNEL_LZO is not set
+# CONFIG_KERNEL_LZ4 is not set
+CONFIG_DEFAULT_HOSTNAME="(none)"
+# CONFIG_SWAP is not set
+CONFIG_SYSVIPC=y
+CONFIG_SYSVIPC_SYSCTL=y
+# CONFIG_POSIX_MQUEUE is not set
+# CONFIG_FHANDLE is not set
+# CONFIG_AUDIT is not set
+
+#
+# IRQ subsystem
+#
+CONFIG_GENERIC_IRQ_PROBE=y
+CONFIG_GENERIC_IRQ_SHOW=y
+CONFIG_GENERIC_PENDING_IRQ=y
+CONFIG_IRQ_FORCED_THREADING=y
+CONFIG_SPARSE_IRQ=y
+CONFIG_CLOCKSOURCE_WATCHDOG=y
+CONFIG_ARCH_CLOCKSOURCE_DATA=y
+CONFIG_GENERIC_TIME_VSYSCALL=y
+CONFIG_GENERIC_CLOCKEVENTS=y
+CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
+CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
+CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST=y
+CONFIG_GENERIC_CMOS_UPDATE=y
+
+#
+# Timers subsystem
+#
+CONFIG_TICK_ONESHOT=y
+CONFIG_NO_HZ_COMMON=y
+# CONFIG_HZ_PERIODIC is not set
+CONFIG_NO_HZ_IDLE=y
+# CONFIG_NO_HZ_FULL is not set
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+
+#
+# CPU/Task time and stats accounting
+#
+CONFIG_TICK_CPU_ACCOUNTING=y
+# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set
+# CONFIG_IRQ_TIME_ACCOUNTING is not set
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+
+#
+# RCU Subsystem
+#
+CONFIG_TREE_PREEMPT_RCU=y
+CONFIG_PREEMPT_RCU=y
+CONFIG_RCU_STALL_COMMON=y
+# CONFIG_RCU_USER_QS is not set
+CONFIG_RCU_FANOUT=64
+CONFIG_RCU_FANOUT_LEAF=16
+# CONFIG_RCU_FANOUT_EXACT is not set
+# CONFIG_RCU_FAST_NO_HZ is not set
+# CONFIG_TREE_RCU_TRACE is not set
+# CONFIG_RCU_BOOST is not set
+# CONFIG_RCU_NOCB_CPU is not set
+# CONFIG_IKCONFIG is not set
+CONFIG_LOG_BUF_SHIFT=21
+CONFIG_HAVE_UNSTABLE_SCHED_CLOCK=y
+CONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y
+CONFIG_ARCH_WANTS_PROT_NUMA_PROT_NONE=y
+# CONFIG_CGROUPS is not set
+# CONFIG_CHECKPOINT_RESTORE is not set
+# CONFIG_NAMESPACES is not set
+# CONFIG_UIDGID_STRICT_TYPE_CHECKS is not set
+# CONFIG_SCHED_AUTOGROUP is not set
+# CONFIG_SYSFS_DEPRECATED is not set
+# CONFIG_RELAY is not set
+# CONFIG_BLK_DEV_INITRD is not set
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+CONFIG_SYSCTL=y
+CONFIG_ANON_INODES=y
+CONFIG_SYSCTL_EXCEPTION_TRACE=y
+CONFIG_HAVE_PCSPKR_PLATFORM=y
+CONFIG_EXPERT=y
+# CONFIG_SYSCTL_SYSCALL is not set
+CONFIG_KALLSYMS=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_PRINTK=y
+CONFIG_BUG=y
+CONFIG_ELF_CORE=y
+CONFIG_PCSPKR_PLATFORM=y
+CONFIG_BASE_FULL=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_SIGNALFD=y
+CONFIG_TIMERFD=y
+CONFIG_EVENTFD=y
+CONFIG_SHMEM=y
+CONFIG_AIO=y
+CONFIG_PCI_QUIRKS=y
+CONFIG_EMBEDDED=y
+CONFIG_HAVE_PERF_EVENTS=y
+
+#
+# Kernel Performance Events And Counters
+#
+CONFIG_PERF_EVENTS=y
+# CONFIG_DEBUG_PERF_USE_VMALLOC is not set
+CONFIG_VM_EVENT_COUNTERS=y
+CONFIG_SLUB_DEBUG=y
+# CONFIG_COMPAT_BRK is not set
+# CONFIG_SLAB is not set
+CONFIG_SLUB=y
+# CONFIG_SLOB is not set
+CONFIG_SLUB_CPU_PARTIAL=y
+# CONFIG_PROFILING is not set
+CONFIG_TRACEPOINTS=y
+CONFIG_HAVE_OPROFILE=y
+CONFIG_OPROFILE_NMI_TIMER=y
+CONFIG_JUMP_LABEL=y
+# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set
+CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y
+CONFIG_ARCH_USE_BUILTIN_BSWAP=y
+CONFIG_HAVE_IOREMAP_PROT=y
+CONFIG_HAVE_KPROBES=y
+CONFIG_HAVE_KRETPROBES=y
+CONFIG_HAVE_OPTPROBES=y
+CONFIG_HAVE_KPROBES_ON_FTRACE=y
+CONFIG_HAVE_ARCH_TRACEHOOK=y
+CONFIG_HAVE_DMA_ATTRS=y
+CONFIG_USE_GENERIC_SMP_HELPERS=y
+CONFIG_GENERIC_SMP_IDLE_THREAD=y
+CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
+CONFIG_HAVE_DMA_API_DEBUG=y
+CONFIG_HAVE_HW_BREAKPOINT=y
+CONFIG_HAVE_MIXED_BREAKPOINTS_REGS=y
+CONFIG_HAVE_USER_RETURN_NOTIFIER=y
+CONFIG_HAVE_PERF_EVENTS_NMI=y
+CONFIG_HAVE_PERF_REGS=y
+CONFIG_HAVE_PERF_USER_STACK_DUMP=y
+CONFIG_HAVE_ARCH_JUMP_LABEL=y
+CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y
+CONFIG_HAVE_ALIGNED_STRUCT_PAGE=y
+CONFIG_HAVE_CMPXCHG_LOCAL=y
+CONFIG_HAVE_CMPXCHG_DOUBLE=y
+CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
+CONFIG_HAVE_CONTEXT_TRACKING=y
+CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
+CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y
+CONFIG_HAVE_ARCH_SOFT_DIRTY=y
+CONFIG_MODULES_USE_ELF_RELA=y
+
+#
+# GCOV-based kernel profiling
+#
+# CONFIG_GCOV_KERNEL is not set
+# CONFIG_HAVE_GENERIC_DMA_COHERENT is not set
+CONFIG_SLABINFO=y
+CONFIG_RT_MUTEXES=y
+CONFIG_BASE_SMALL=0
+# CONFIG_MODULES is not set
+CONFIG_BLOCK=y
+# CONFIG_BLK_DEV_BSG is not set
+# CONFIG_BLK_DEV_BSGLIB is not set
+# CONFIG_BLK_DEV_INTEGRITY is not set
+# CONFIG_CMDLINE_PARSER is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+# CONFIG_AIX_PARTITION is not set
+# CONFIG_OSF_PARTITION is not set
+# CONFIG_AMIGA_PARTITION is not set
+# CONFIG_ATARI_PARTITION is not set
+CONFIG_MAC_PARTITION=y
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_BSD_DISKLABEL is not set
+# CONFIG_MINIX_SUBPARTITION is not set
+# CONFIG_SOLARIS_X86_PARTITION is not set
+# CONFIG_UNIXWARE_DISKLABEL is not set
+# CONFIG_LDM_PARTITION is not set
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_ULTRIX_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+# CONFIG_KARMA_PARTITION is not set
+CONFIG_EFI_PARTITION=y
+# CONFIG_SYSV68_PARTITION is not set
+# CONFIG_CMDLINE_PARTITION is not set
+
+#
+# IO Schedulers
+#
+CONFIG_IOSCHED_NOOP=y
+# CONFIG_IOSCHED_DEADLINE is not set
+# CONFIG_IOSCHED_CFQ is not set
+CONFIG_DEFAULT_NOOP=y
+CONFIG_DEFAULT_IOSCHED="noop"
+CONFIG_UNINLINE_SPIN_UNLOCK=y
+# CONFIG_FREEZER is not set
+
+#
+# Processor type and features
+#
+CONFIG_ZONE_DMA=y
+CONFIG_SMP=y
+CONFIG_X86_MPPARSE=y
+# CONFIG_X86_EXTENDED_PLATFORM is not set
+# CONFIG_X86_INTEL_LPSS is not set
+CONFIG_SCHED_OMIT_FRAME_POINTER=y
+CONFIG_HYPERVISOR_GUEST=y
+CONFIG_PARAVIRT=y
+# CONFIG_PARAVIRT_DEBUG is not set
+CONFIG_PARAVIRT_SPINLOCKS=y
+# CONFIG_XEN is not set
+# CONFIG_XEN_PRIVILEGED_GUEST is not set
+CONFIG_KVM_GUEST=y
+# CONFIG_KVM_DEBUG_FS is not set
+# CONFIG_PARAVIRT_TIME_ACCOUNTING is not set
+CONFIG_PARAVIRT_CLOCK=y
+CONFIG_NO_BOOTMEM=y
+# CONFIG_MEMTEST is not set
+# CONFIG_MK8 is not set
+# CONFIG_MPSC is not set
+CONFIG_MCORE2=y
+# CONFIG_MATOM is not set
+# CONFIG_GENERIC_CPU is not set
+CONFIG_X86_INTERNODE_CACHE_SHIFT=6
+CONFIG_X86_L1_CACHE_SHIFT=6
+CONFIG_X86_INTEL_USERCOPY=y
+CONFIG_X86_USE_PPRO_CHECKSUM=y
+CONFIG_X86_P6_NOP=y
+CONFIG_X86_TSC=y
+CONFIG_X86_CMPXCHG64=y
+CONFIG_X86_CMOV=y
+CONFIG_X86_MINIMUM_CPU_FAMILY=64
+CONFIG_X86_DEBUGCTLMSR=y
+# CONFIG_PROCESSOR_SELECT is not set
+CONFIG_CPU_SUP_INTEL=y
+CONFIG_CPU_SUP_AMD=y
+CONFIG_CPU_SUP_CENTAUR=y
+CONFIG_HPET_TIMER=y
+CONFIG_DMI=y
+CONFIG_GART_IOMMU=y
+# CONFIG_CALGARY_IOMMU is not set
+CONFIG_SWIOTLB=y
+CONFIG_IOMMU_HELPER=y
+# CONFIG_MAXSMP is not set
+CONFIG_NR_CPUS=4
+# CONFIG_SCHED_SMT is not set
+CONFIG_SCHED_MC=y
+# CONFIG_PREEMPT_NONE is not set
+# CONFIG_PREEMPT_VOLUNTARY is not set
+CONFIG_PREEMPT=y
+CONFIG_PREEMPT_COUNT=y
+CONFIG_X86_LOCAL_APIC=y
+CONFIG_X86_IO_APIC=y
+# CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS is not set
+# CONFIG_X86_MCE is not set
+# CONFIG_I8K is not set
+# CONFIG_MICROCODE is not set
+# CONFIG_MICROCODE_INTEL_EARLY is not set
+# CONFIG_MICROCODE_AMD_EARLY is not set
+# CONFIG_X86_MSR is not set
+# CONFIG_X86_CPUID is not set
+CONFIG_ARCH_PHYS_ADDR_T_64BIT=y
+CONFIG_ARCH_DMA_ADDR_T_64BIT=y
+CONFIG_DIRECT_GBPAGES=y
+# CONFIG_NUMA is not set
+CONFIG_ARCH_SPARSEMEM_ENABLE=y
+CONFIG_ARCH_SPARSEMEM_DEFAULT=y
+CONFIG_ARCH_SELECT_MEMORY_MODEL=y
+CONFIG_ARCH_PROC_KCORE_TEXT=y
+CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000
+CONFIG_SELECT_MEMORY_MODEL=y
+CONFIG_SPARSEMEM_MANUAL=y
+CONFIG_SPARSEMEM=y
+CONFIG_HAVE_MEMORY_PRESENT=y
+CONFIG_SPARSEMEM_EXTREME=y
+CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y
+CONFIG_SPARSEMEM_ALLOC_MEM_MAP_TOGETHER=y
+CONFIG_SPARSEMEM_VMEMMAP=y
+CONFIG_HAVE_MEMBLOCK=y
+CONFIG_HAVE_MEMBLOCK_NODE_MAP=y
+CONFIG_ARCH_DISCARD_MEMBLOCK=y
+# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set
+# CONFIG_MEMORY_HOTPLUG is not set
+CONFIG_PAGEFLAGS_EXTENDED=y
+CONFIG_SPLIT_PTLOCK_CPUS=999999
+# CONFIG_COMPACTION is not set
+CONFIG_PHYS_ADDR_T_64BIT=y
+CONFIG_ZONE_DMA_FLAG=1
+# CONFIG_BOUNCE is not set
+CONFIG_VIRT_TO_BUS=y
+# CONFIG_KSM is not set
+CONFIG_DEFAULT_MMAP_MIN_ADDR=65536
+# CONFIG_TRANSPARENT_HUGEPAGE is not set
+# CONFIG_CROSS_MEMORY_ATTACH is not set
+# CONFIG_CLEANCACHE is not set
+# CONFIG_CMA is not set
+# CONFIG_ZBUD is not set
+# CONFIG_X86_CHECK_BIOS_CORRUPTION is not set
+CONFIG_X86_RESERVE_LOW=64
+CONFIG_MTRR=y
+# CONFIG_MTRR_SANITIZER is not set
+CONFIG_X86_PAT=y
+CONFIG_ARCH_USES_PG_UNCACHED=y
+CONFIG_ARCH_RANDOM=y
+CONFIG_X86_SMAP=y
+# CONFIG_EFI is not set
+# CONFIG_SECCOMP is not set
+# CONFIG_CC_STACKPROTECTOR is not set
+CONFIG_HZ_100=y
+# CONFIG_HZ_250 is not set
+# CONFIG_HZ_300 is not set
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=100
+CONFIG_SCHED_HRTICK=y
+# CONFIG_KEXEC is not set
+# CONFIG_CRASH_DUMP is not set
+CONFIG_PHYSICAL_START=0x1000000
+# CONFIG_RELOCATABLE is not set
+CONFIG_PHYSICAL_ALIGN=0x1000000
+# CONFIG_HOTPLUG_CPU is not set
+# CONFIG_CMDLINE_BOOL is not set
+CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
+
+#
+# Power management and ACPI options
+#
+# CONFIG_SUSPEND is not set
+# CONFIG_PM_RUNTIME is not set
+CONFIG_ACPI=y
+# CONFIG_ACPI_PROCFS is not set
+# CONFIG_ACPI_PROCFS_POWER is not set
+# CONFIG_ACPI_EC_DEBUGFS 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_ACPI_DOCK is not set
+# CONFIG_ACPI_PROCESSOR is not set
+# CONFIG_ACPI_CUSTOM_DSDT is not set
+CONFIG_ACPI_BLACKLIST_YEAR=0
+# CONFIG_ACPI_DEBUG is not set
+# CONFIG_ACPI_PCI_SLOT is not set
+CONFIG_X86_PM_TIMER=y
+# CONFIG_ACPI_CONTAINER is not set
+# CONFIG_ACPI_SBS is not set
+# CONFIG_ACPI_HED is not set
+# CONFIG_ACPI_CUSTOM_METHOD is not set
+# CONFIG_ACPI_APEI is not set
+# CONFIG_SFI is not set
+
+#
+# CPU Frequency scaling
+#
+# CONFIG_CPU_FREQ is not set
+
+#
+# CPU Idle
+#
+CONFIG_CPU_IDLE=y
+# CONFIG_CPU_IDLE_MULTIPLE_DRIVERS is not set
+CONFIG_CPU_IDLE_GOV_LADDER=y
+CONFIG_CPU_IDLE_GOV_MENU=y
+# CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED is not set
+# CONFIG_INTEL_IDLE is not set
+
+#
+# Memory power savings
+#
+# CONFIG_I7300_IDLE is not set
+
+#
+# Bus options (PCI etc.)
+#
+CONFIG_PCI=y
+CONFIG_PCI_DIRECT=y
+# CONFIG_PCI_MMCONFIG is not set
+CONFIG_PCI_DOMAINS=y
+# CONFIG_PCI_CNB20LE_QUIRK is not set
+# CONFIG_PCIEPORTBUS is not set
+# CONFIG_PCI_MSI is not set
+# CONFIG_PCI_DEBUG is not set
+# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set
+# CONFIG_PCI_STUB is not set
+# CONFIG_HT_IRQ is not set
+# CONFIG_PCI_IOV is not set
+# CONFIG_PCI_PRI is not set
+# CONFIG_PCI_PASID is not set
+# CONFIG_PCI_IOAPIC is not set
+CONFIG_PCI_LABEL=y
+
+#
+# PCI host controller drivers
+#
+# CONFIG_ISA_DMA_API is not set
+CONFIG_AMD_NB=y
+# CONFIG_PCCARD is not set
+# CONFIG_HOTPLUG_PCI is not set
+# CONFIG_RAPIDIO is not set
+# CONFIG_X86_SYSFB is not set
+
+#
+# Executable file formats / Emulations
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y
+CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
+CONFIG_BINFMT_SCRIPT=y
+# CONFIG_HAVE_AOUT is not set
+# CONFIG_BINFMT_MISC is not set
+CONFIG_COREDUMP=y
+# CONFIG_IA32_EMULATION is not set
+CONFIG_X86_DEV_DMA_OPS=y
+CONFIG_NET=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_DIAG is not set
+CONFIG_UNIX=y
+# CONFIG_UNIX_DIAG is not set
+CONFIG_XFRM=y
+# CONFIG_XFRM_USER is not set
+# CONFIG_XFRM_SUB_POLICY is not set
+# CONFIG_XFRM_MIGRATE is not set
+# CONFIG_XFRM_STATISTICS is not set
+# CONFIG_NET_KEY is not set
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+# CONFIG_IP_FIB_TRIE_STATS is not set
+CONFIG_IP_MULTIPLE_TABLES=y
+# CONFIG_IP_ROUTE_MULTIPATH is not set
+# CONFIG_IP_ROUTE_VERBOSE is not set
+# CONFIG_IP_PNP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE_DEMUX is not set
+CONFIG_NET_IP_TUNNEL=y
+# CONFIG_IP_MROUTE is not set
+# CONFIG_SYN_COOKIES is not set
+# CONFIG_INET_AH is not set
+# CONFIG_INET_ESP is not set
+# CONFIG_INET_IPCOMP is not set
+# CONFIG_INET_XFRM_TUNNEL is not set
+CONFIG_INET_TUNNEL=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+# CONFIG_INET_DIAG is not set
+# CONFIG_TCP_CONG_ADVANCED is not set
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_DEFAULT_TCP_CONG="cubic"
+# CONFIG_TCP_MD5SIG is not set
+CONFIG_IPV6=y
+CONFIG_IPV6_PRIVACY=y
+# CONFIG_IPV6_ROUTER_PREF is not set
+# CONFIG_IPV6_OPTIMISTIC_DAD is not set
+# CONFIG_INET6_AH is not set
+# CONFIG_INET6_ESP is not set
+# CONFIG_INET6_IPCOMP is not set
+# CONFIG_IPV6_MIP6 is not set
+# CONFIG_INET6_XFRM_TUNNEL is not set
+# CONFIG_INET6_TUNNEL is not set
+CONFIG_INET6_XFRM_MODE_TRANSPORT=y
+CONFIG_INET6_XFRM_MODE_TUNNEL=y
+CONFIG_INET6_XFRM_MODE_BEET=y
+# CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION is not set
+CONFIG_IPV6_SIT=y
+# CONFIG_IPV6_SIT_6RD is not set
+CONFIG_IPV6_NDISC_NODETYPE=y
+# CONFIG_IPV6_TUNNEL is not set
+# CONFIG_IPV6_GRE is not set
+# CONFIG_IPV6_MULTIPLE_TABLES is not set
+# CONFIG_IPV6_MROUTE is not set
+# CONFIG_NETWORK_SECMARK is not set
+# CONFIG_NETWORK_PHY_TIMESTAMPING is not set
+# CONFIG_NETFILTER is not set
+# CONFIG_IP_DCCP is not set
+# CONFIG_IP_SCTP is not set
+# CONFIG_RDS is not set
+# CONFIG_TIPC is not set
+# CONFIG_ATM is not set
+# CONFIG_L2TP is not set
+CONFIG_STP=y
+CONFIG_BRIDGE=y
+CONFIG_BRIDGE_IGMP_SNOOPING=y
+CONFIG_HAVE_NET_DSA=y
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_DECNET is not set
+CONFIG_LLC=y
+# CONFIG_LLC2 is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_PHONET is not set
+# CONFIG_IEEE802154 is not set
+# CONFIG_NET_SCHED is not set
+# CONFIG_DCB is not set
+# CONFIG_BATMAN_ADV is not set
+# CONFIG_OPENVSWITCH is not set
+# CONFIG_VSOCKETS is not set
+# CONFIG_NETLINK_MMAP is not set
+# CONFIG_NETLINK_DIAG is not set
+# CONFIG_NET_MPLS_GSO is not set
+CONFIG_RPS=y
+CONFIG_RFS_ACCEL=y
+CONFIG_XPS=y
+CONFIG_NET_RX_BUSY_POLL=y
+CONFIG_BQL=y
+CONFIG_NET_FLOW_LIMIT=y
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+# CONFIG_NET_DROP_MONITOR is not set
+# CONFIG_HAMRADIO is not set
+# CONFIG_CAN is not set
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_AF_RXRPC is not set
+CONFIG_FIB_RULES=y
+CONFIG_WIRELESS=y
+CONFIG_CFG80211=y
+# CONFIG_NL80211_TESTMODE is not set
+CONFIG_CFG80211_DEVELOPER_WARNINGS=y
+# CONFIG_CFG80211_REG_DEBUG is not set
+# CONFIG_CFG80211_CERTIFICATION_ONUS is not set
+CONFIG_CFG80211_DEFAULT_PS=y
+CONFIG_CFG80211_DEBUGFS=y
+# CONFIG_CFG80211_INTERNAL_REGDB is not set
+CONFIG_CFG80211_WEXT=y
+# CONFIG_LIB80211 is not set
+CONFIG_MAC80211=y
+CONFIG_MAC80211_HAS_RC=y
+# CONFIG_MAC80211_RC_PID is not set
+CONFIG_MAC80211_RC_MINSTREL=y
+CONFIG_MAC80211_RC_MINSTREL_HT=y
+CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y
+CONFIG_MAC80211_RC_DEFAULT="minstrel_ht"
+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_MPL_DEBUG is not set
+# CONFIG_MAC80211_MPATH_DEBUG is not set
+# CONFIG_MAC80211_MHWMP_DEBUG is not set
+# CONFIG_MAC80211_MESH_SYNC_DEBUG is not set
+# CONFIG_MAC80211_MESH_CSA_DEBUG is not set
+# CONFIG_MAC80211_MESH_PS_DEBUG is not set
+CONFIG_MAC80211_TDLS_DEBUG=y
+# CONFIG_MAC80211_DEBUG_COUNTERS is not set
+# CONFIG_WIMAX is not set
+CONFIG_RFKILL=y
+# CONFIG_RFKILL_INPUT is not set
+CONFIG_NET_9P=y
+CONFIG_NET_9P_VIRTIO=y
+# CONFIG_NET_9P_DEBUG is not set
+# CONFIG_CAIF is not set
+# CONFIG_CEPH_LIB is not set
+# CONFIG_NFC is not set
+CONFIG_HAVE_BPF_JIT=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
+# CONFIG_DEVTMPFS is not set
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+CONFIG_FW_LOADER=y
+CONFIG_FIRMWARE_IN_KERNEL=y
+CONFIG_EXTRA_FIRMWARE=""
+CONFIG_FW_LOADER_USER_HELPER=y
+# CONFIG_DEBUG_DRIVER is not set
+# CONFIG_DEBUG_DEVRES is not set
+# CONFIG_SYS_HYPERVISOR is not set
+# CONFIG_GENERIC_CPU_DEVICES is not set
+# CONFIG_DMA_SHARED_BUFFER is not set
+
+#
+# Bus devices
+#
+# CONFIG_CONNECTOR is not set
+# CONFIG_MTD is not set
+# CONFIG_PARPORT is not set
+CONFIG_PNP=y
+# CONFIG_PNP_DEBUG_MESSAGES is not set
+
+#
+# Protocols
+#
+CONFIG_PNPACPI=y
+# CONFIG_BLK_DEV is not set
+
+#
+# Misc devices
+#
+# CONFIG_SENSORS_LIS3LV02D is not set
+# CONFIG_DUMMY_IRQ is not set
+# CONFIG_IBM_ASM is not set
+# CONFIG_PHANTOM is not set
+# CONFIG_SGI_IOC4 is not set
+# CONFIG_TIFM_CORE is not set
+# CONFIG_ATMEL_SSC is not set
+# CONFIG_ENCLOSURE_SERVICES is not set
+# CONFIG_HP_ILO is not set
+# CONFIG_VMWARE_BALLOON is not set
+# CONFIG_PCH_PHUB is not set
+# CONFIG_SRAM is not set
+# CONFIG_C2PORT is not set
+
+#
+# EEPROM support
+#
+# CONFIG_EEPROM_93CX6 is not set
+# CONFIG_CB710_CORE is not set
+
+#
+# Texas Instruments shared transport line discipline
+#
+
+#
+# Altera FPGA firmware download module
+#
+# CONFIG_VMWARE_VMCI is not set
+CONFIG_HAVE_IDE=y
+# CONFIG_IDE is not set
+
+#
+# SCSI device support
+#
+CONFIG_SCSI_MOD=y
+# CONFIG_RAID_ATTRS is not set
+# CONFIG_SCSI is not set
+# CONFIG_SCSI_DMA is not set
+# CONFIG_SCSI_NETLINK is not set
+# CONFIG_ATA is not set
+# CONFIG_MD is not set
+# CONFIG_FUSION is not set
+
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_FIREWIRE is not set
+# CONFIG_FIREWIRE_NOSY is not set
+# CONFIG_I2O is not set
+# CONFIG_MACINTOSH_DRIVERS is not set
+CONFIG_NETDEVICES=y
+# CONFIG_NET_CORE is not set
+# CONFIG_ARCNET is not set
+
+#
+# CAIF transport drivers
+#
+
+#
+# Distributed Switch Architecture drivers
+#
+# CONFIG_NET_DSA_MV88E6XXX is not set
+# CONFIG_NET_DSA_MV88E6060 is not set
+# CONFIG_NET_DSA_MV88E6XXX_NEED_PPU is not set
+# CONFIG_NET_DSA_MV88E6131 is not set
+# CONFIG_NET_DSA_MV88E6123_61_65 is not set
+# CONFIG_ETHERNET is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_NET_SB1000 is not set
+# CONFIG_PHYLIB is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+CONFIG_WLAN=y
+# CONFIG_LIBERTAS_THINFIRM is not set
+# CONFIG_ATMEL is not set
+# CONFIG_PRISM54 is not set
+# CONFIG_RTL8180 is not set
+# CONFIG_ADM8211 is not set
+CONFIG_MAC80211_HWSIM=y
+# CONFIG_MWL8K is not set
+# CONFIG_ATH_CARDS is not set
+# CONFIG_B43 is not set
+# CONFIG_B43LEGACY is not set
+# CONFIG_BRCMFMAC is not set
+# CONFIG_HOSTAP is not set
+# CONFIG_IPW2100 is not set
+# CONFIG_IWLWIFI is not set
+# CONFIG_IWL4965 is not set
+# CONFIG_IWL3945 is not set
+# CONFIG_LIBERTAS is not set
+# CONFIG_P54_COMMON is not set
+# CONFIG_RT2X00 is not set
+CONFIG_RTL_CARDS=y
+# CONFIG_RTL8192CE is not set
+# CONFIG_RTL8192SE is not set
+# CONFIG_RTL8192DE is not set
+# CONFIG_RTL8723AE is not set
+# CONFIG_RTL8188EE is not set
+# CONFIG_WL_TI is not set
+# CONFIG_MWIFIEX is not set
+# CONFIG_CW1200 is not set
+
+#
+# Enable WiMAX (Networking options) to see the WiMAX drivers
+#
+# CONFIG_WAN is not set
+# CONFIG_VMXNET3 is not set
+# CONFIG_ISDN is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+# CONFIG_INPUT_FF_MEMLESS is not set
+# CONFIG_INPUT_POLLDEV is not set
+# CONFIG_INPUT_SPARSEKMAP is not set
+# CONFIG_INPUT_MATRIXKMAP is not set
+
+#
+# Userland interfaces
+#
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_EVBUG is not set
+
+#
+# Input Device Drivers
+#
+# CONFIG_INPUT_KEYBOARD is not set
+# CONFIG_INPUT_MOUSE is not set
+# CONFIG_INPUT_JOYSTICK is not set
+# CONFIG_INPUT_TABLET is not set
+# CONFIG_INPUT_TOUCHSCREEN is not set
+# CONFIG_INPUT_MISC is not set
+
+#
+# Hardware I/O ports
+#
+CONFIG_SERIO=y
+CONFIG_SERIO_I8042=y
+CONFIG_SERIO_SERPORT=y
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_PCIPS2 is not set
+# CONFIG_SERIO_LIBPS2 is not set
+# CONFIG_SERIO_RAW is not set
+# CONFIG_SERIO_ALTERA_PS2 is not set
+# CONFIG_SERIO_PS2MULT is not set
+# CONFIG_SERIO_ARC_PS2 is not set
+# CONFIG_GAMEPORT is not set
+
+#
+# Character devices
+#
+CONFIG_TTY=y
+CONFIG_VT=y
+CONFIG_CONSOLE_TRANSLATIONS=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_UNIX98_PTYS=y
+# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set
+# CONFIG_LEGACY_PTYS is not set
+# CONFIG_SERIAL_NONSTANDARD is not set
+# CONFIG_NOZOMI is not set
+# CONFIG_N_GSM is not set
+# CONFIG_TRACE_SINK is not set
+# CONFIG_DEVKMEM is not set
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y
+# CONFIG_SERIAL_8250_PNP is not set
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_FIX_EARLYCON_MEM=y
+CONFIG_SERIAL_8250_PCI=y
+CONFIG_SERIAL_8250_NR_UARTS=4
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+# CONFIG_SERIAL_8250_EXTENDED is not set
+# CONFIG_SERIAL_8250_DW is not set
+
+#
+# Non-8250 serial port support
+#
+# CONFIG_SERIAL_MFD_HSU is not set
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+# CONFIG_SERIAL_JSM is not set
+# CONFIG_SERIAL_SCCNXP is not set
+# CONFIG_SERIAL_TIMBERDALE is not set
+# CONFIG_SERIAL_ALTERA_JTAGUART is not set
+# CONFIG_SERIAL_ALTERA_UART is not set
+# CONFIG_SERIAL_PCH_UART is not set
+# CONFIG_SERIAL_ARC is not set
+# CONFIG_SERIAL_RP2 is not set
+# CONFIG_SERIAL_FSL_LPUART is not set
+# CONFIG_SERIAL_ST_ASC is not set
+# CONFIG_TTY_PRINTK is not set
+# CONFIG_VIRTIO_CONSOLE is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_HW_RANDOM is not set
+# CONFIG_NVRAM is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+# CONFIG_MWAVE is not set
+# CONFIG_RAW_DRIVER is not set
+# CONFIG_HPET is not set
+# CONFIG_HANGCHECK_TIMER is not set
+# CONFIG_TCG_TPM is not set
+# CONFIG_TELCLOCK is not set
+CONFIG_DEVPORT=y
+# CONFIG_I2C is not set
+# CONFIG_SPI is not set
+# CONFIG_HSI is not set
+
+#
+# PPS support
+#
+# CONFIG_PPS is not set
+
+#
+# PPS generators support
+#
+
+#
+# PTP clock support
+#
+# CONFIG_PTP_1588_CLOCK is not set
+
+#
+# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks.
+#
+# CONFIG_PTP_1588_CLOCK_PCH is not set
+CONFIG_ARCH_WANT_OPTIONAL_GPIOLIB=y
+CONFIG_GPIO_DEVRES=y
+# CONFIG_GPIOLIB is not set
+# CONFIG_W1 is not set
+CONFIG_POWER_SUPPLY=y
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+# CONFIG_PDA_POWER is not set
+# CONFIG_TEST_POWER is not set
+# CONFIG_BATTERY_DS2780 is not set
+# CONFIG_BATTERY_DS2781 is not set
+# CONFIG_BATTERY_BQ27x00 is not set
+# CONFIG_CHARGER_MAX8903 is not set
+# CONFIG_POWER_RESET is not set
+# CONFIG_POWER_AVS is not set
+# CONFIG_HWMON is not set
+CONFIG_THERMAL=y
+CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
+# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set
+# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set
+# CONFIG_THERMAL_GOV_FAIR_SHARE is not set
+CONFIG_THERMAL_GOV_STEP_WISE=y
+# CONFIG_THERMAL_GOV_USER_SPACE is not set
+# CONFIG_THERMAL_EMULATION is not set
+# CONFIG_INTEL_POWERCLAMP is not set
+
+#
+# Texas Instruments thermal drivers
+#
+# CONFIG_WATCHDOG is not set
+CONFIG_SSB_POSSIBLE=y
+
+#
+# Sonics Silicon Backplane
+#
+# CONFIG_SSB is not set
+CONFIG_BCMA_POSSIBLE=y
+
+#
+# Broadcom specific AMBA
+#
+# CONFIG_BCMA is not set
+
+#
+# Multifunction device drivers
+#
+# CONFIG_MFD_CORE is not set
+# CONFIG_MFD_CS5535 is not set
+# CONFIG_MFD_CROS_EC is not set
+# CONFIG_HTC_PASIC3 is not set
+# CONFIG_LPC_ICH is not set
+# CONFIG_LPC_SCH is not set
+# CONFIG_MFD_JANZ_CMODIO is not set
+# CONFIG_MFD_KEMPLD is not set
+# CONFIG_MFD_RDC321X is not set
+# CONFIG_MFD_RTSX_PCI is not set
+# CONFIG_MFD_SM501 is not set
+# CONFIG_ABX500_CORE is not set
+# CONFIG_MFD_SYSCON is not set
+# CONFIG_MFD_TI_AM335X_TSCADC is not set
+# CONFIG_MFD_TMIO is not set
+# CONFIG_MFD_VX855 is not set
+# CONFIG_REGULATOR is not set
+# CONFIG_MEDIA_SUPPORT is not set
+
+#
+# Graphics support
+#
+# CONFIG_AGP is not set
+CONFIG_VGA_ARB=y
+CONFIG_VGA_ARB_MAX_GPUS=16
+# CONFIG_VGA_SWITCHEROO is not set
+# CONFIG_DRM is not set
+# CONFIG_VGASTATE is not set
+CONFIG_VIDEO_OUTPUT_CONTROL=y
+CONFIG_FB=y
+# CONFIG_FIRMWARE_EDID is not set
+# CONFIG_FB_DDC is not set
+CONFIG_FB_BOOT_VESA_SUPPORT=y
+CONFIG_FB_CFB_FILLRECT=y
+CONFIG_FB_CFB_COPYAREA=y
+CONFIG_FB_CFB_IMAGEBLIT=y
+# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set
+# CONFIG_FB_SYS_FILLRECT is not set
+# CONFIG_FB_SYS_COPYAREA is not set
+# CONFIG_FB_SYS_IMAGEBLIT is not set
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+# CONFIG_FB_SYS_FOPS is not set
+# CONFIG_FB_SVGALIB is not set
+# CONFIG_FB_MACMODES is not set
+# CONFIG_FB_BACKLIGHT is not set
+CONFIG_FB_MODE_HELPERS=y
+# CONFIG_FB_TILEBLITTING is not set
+
+#
+# Frame buffer hardware drivers
+#
+# CONFIG_FB_CIRRUS is not set
+# CONFIG_FB_PM2 is not set
+# CONFIG_FB_CYBER2000 is not set
+# CONFIG_FB_ARC is not set
+# CONFIG_FB_ASILIANT is not set
+# CONFIG_FB_IMSTT is not set
+# CONFIG_FB_VGA16 is not set
+CONFIG_FB_VESA=y
+# CONFIG_FB_N411 is not set
+# CONFIG_FB_HGA is not set
+# CONFIG_FB_S1D13XXX is not set
+# CONFIG_FB_NVIDIA is not set
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_I740 is not set
+# CONFIG_FB_LE80578 is not set
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_RADEON is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_S3 is not set
+# CONFIG_FB_SAVAGE is not set
+# CONFIG_FB_SIS is not set
+# CONFIG_FB_VIA is not set
+# CONFIG_FB_NEOMAGIC is not set
+# CONFIG_FB_KYRO is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_VT8623 is not set
+# CONFIG_FB_TRIDENT is not set
+# CONFIG_FB_ARK is not set
+# CONFIG_FB_PM3 is not set
+# CONFIG_FB_CARMINE is not set
+# CONFIG_FB_GOLDFISH is not set
+# CONFIG_FB_VIRTUAL is not set
+# CONFIG_FB_METRONOME is not set
+# CONFIG_FB_MB862XX is not set
+# CONFIG_FB_BROADSHEET is not set
+# CONFIG_FB_AUO_K190X is not set
+# CONFIG_FB_SIMPLE is not set
+# CONFIG_EXYNOS_VIDEO is not set
+# CONFIG_BACKLIGHT_LCD_SUPPORT is not set
+
+#
+# Console display driver support
+#
+CONFIG_VGA_CONSOLE=y
+CONFIG_VGACON_SOFT_SCROLLBACK=y
+CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=64
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
+# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
+# CONFIG_LOGO is not set
+# CONFIG_SOUND is not set
+
+#
+# HID support
+#
+CONFIG_HID=y
+# CONFIG_HID_BATTERY_STRENGTH is not set
+CONFIG_HIDRAW=y
+# CONFIG_UHID is not set
+CONFIG_HID_GENERIC=y
+
+#
+# Special HID drivers
+#
+# CONFIG_HID_A4TECH is not set
+# CONFIG_HID_ACRUX is not set
+# CONFIG_HID_APPLE is not set
+# CONFIG_HID_AUREAL is not set
+# CONFIG_HID_BELKIN is not set
+# CONFIG_HID_CHERRY is not set
+# CONFIG_HID_CHICONY is not set
+# CONFIG_HID_CYPRESS is not set
+# CONFIG_HID_DRAGONRISE is not set
+# CONFIG_HID_EMS_FF is not set
+# CONFIG_HID_ELECOM is not set
+# CONFIG_HID_EZKEY is not set
+# CONFIG_HID_KEYTOUCH is not set
+# CONFIG_HID_KYE is not set
+# CONFIG_HID_UCLOGIC is not set
+# CONFIG_HID_WALTOP is not set
+# CONFIG_HID_GYRATION is not set
+# CONFIG_HID_ICADE is not set
+# CONFIG_HID_TWINHAN is not set
+# CONFIG_HID_KENSINGTON is not set
+# CONFIG_HID_LCPOWER is not set
+# CONFIG_HID_LOGITECH is not set
+# CONFIG_HID_MAGICMOUSE is not set
+# CONFIG_HID_MICROSOFT is not set
+# CONFIG_HID_MONTEREY is not set
+# CONFIG_HID_MULTITOUCH is not set
+# CONFIG_HID_ORTEK is not set
+# CONFIG_HID_PANTHERLORD is not set
+# CONFIG_HID_PETALYNX is not set
+# CONFIG_HID_PICOLCD is not set
+# CONFIG_HID_PRIMAX is not set
+# CONFIG_HID_SAITEK is not set
+# CONFIG_HID_SAMSUNG is not set
+# CONFIG_HID_SPEEDLINK is not set
+# CONFIG_HID_STEELSERIES is not set
+# CONFIG_HID_SUNPLUS is not set
+# CONFIG_HID_GREENASIA is not set
+# CONFIG_HID_SMARTJOYPLUS is not set
+# CONFIG_HID_TIVO is not set
+# CONFIG_HID_TOPSEED is not set
+# CONFIG_HID_THRUSTMASTER is not set
+# CONFIG_HID_XINMO is not set
+# CONFIG_HID_ZEROPLUS is not set
+# CONFIG_HID_ZYDACRON is not set
+# CONFIG_HID_SENSOR_HUB is not set
+CONFIG_USB_OHCI_LITTLE_ENDIAN=y
+# CONFIG_USB_SUPPORT is not set
+# CONFIG_UWB is not set
+# CONFIG_MMC is not set
+# CONFIG_MEMSTICK is not set
+# CONFIG_NEW_LEDS is not set
+# CONFIG_ACCESSIBILITY is not set
+# CONFIG_INFINIBAND is not set
+# CONFIG_EDAC is not set
+CONFIG_RTC_LIB=y
+# CONFIG_RTC_CLASS is not set
+# CONFIG_DMADEVICES is not set
+# CONFIG_AUXDISPLAY is not set
+# CONFIG_UIO is not set
+CONFIG_VIRT_DRIVERS=y
+CONFIG_VIRTIO=y
+
+#
+# Virtio drivers
+#
+CONFIG_VIRTIO_PCI=y
+# CONFIG_VIRTIO_BALLOON is not set
+# CONFIG_VIRTIO_MMIO is not set
+
+#
+# Microsoft Hyper-V guest support
+#
+# CONFIG_HYPERV is not set
+# CONFIG_STAGING is not set
+# CONFIG_X86_PLATFORM_DEVICES is not set
+
+#
+# Hardware Spinlock drivers
+#
+CONFIG_CLKEVT_I8253=y
+CONFIG_I8253_LOCK=y
+CONFIG_CLKBLD_I8253=y
+# CONFIG_MAILBOX is not set
+# CONFIG_IOMMU_SUPPORT is not set
+
+#
+# Remoteproc drivers
+#
+# CONFIG_STE_MODEM_RPROC is not set
+
+#
+# Rpmsg drivers
+#
+# CONFIG_PM_DEVFREQ is not set
+# CONFIG_EXTCON is not set
+# CONFIG_MEMORY is not set
+# CONFIG_IIO is not set
+# CONFIG_NTB is not set
+# CONFIG_VME_BUS is not set
+# CONFIG_PWM is not set
+# CONFIG_IPACK_BUS is not set
+# CONFIG_RESET_CONTROLLER is not set
+# CONFIG_FMC is not set
+
+#
+# Firmware Drivers
+#
+# CONFIG_EDD is not set
+CONFIG_FIRMWARE_MEMMAP=y
+# CONFIG_DELL_RBU is not set
+# CONFIG_DCDBAS is not set
+# CONFIG_DMIID is not set
+# CONFIG_DMI_SYSFS is not set
+# CONFIG_ISCSI_IBFT_FIND is not set
+# CONFIG_GOOGLE_FIRMWARE is not set
+
+#
+# File systems
+#
+CONFIG_DCACHE_WORD_ACCESS=y
+# CONFIG_EXT2_FS is not set
+# CONFIG_EXT3_FS is not set
+# CONFIG_EXT4_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_GFS2_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_BTRFS_FS is not set
+# CONFIG_NILFS2_FS is not set
+CONFIG_FS_POSIX_ACL=y
+CONFIG_FILE_LOCKING=y
+# CONFIG_FSNOTIFY is not set
+# CONFIG_DNOTIFY is not set
+# CONFIG_INOTIFY_USER is not set
+# CONFIG_FANOTIFY is not set
+# CONFIG_QUOTA is not set
+# CONFIG_QUOTACTL is not set
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_FUSE_FS is not set
+CONFIG_GENERIC_ACL=y
+
+#
+# Caches
+#
+# CONFIG_FSCACHE is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=y
+# CONFIG_JOLIET is not set
+# CONFIG_ZISOFS is not set
+# CONFIG_UDF_FS is not set
+
+#
+# DOS/FAT/NT Filesystems
+#
+# CONFIG_MSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_PROC_SYSCTL=y
+CONFIG_PROC_PAGE_MONITOR=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_TMPFS_XATTR=y
+# CONFIG_HUGETLBFS is not set
+# CONFIG_HUGETLB_PAGE is not set
+CONFIG_CONFIGFS_FS=y
+# CONFIG_MISC_FILESYSTEMS is not set
+CONFIG_NETWORK_FILESYSTEMS=y
+# CONFIG_NFS_FS is not set
+# CONFIG_NFSD is not set
+# CONFIG_CEPH_FS is not set
+# CONFIG_CIFS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_AFS_FS is not set
+CONFIG_9P_FS=y
+CONFIG_9P_FS_POSIX_ACL=y
+# CONFIG_9P_FS_SECURITY is not set
+CONFIG_NLS=y
+CONFIG_NLS_DEFAULT="iso8859-1"
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ASCII is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_MAC_ROMAN is not set
+# CONFIG_NLS_MAC_CELTIC is not set
+# CONFIG_NLS_MAC_CENTEURO is not set
+# CONFIG_NLS_MAC_CROATIAN is not set
+# CONFIG_NLS_MAC_CYRILLIC is not set
+# CONFIG_NLS_MAC_GAELIC is not set
+# CONFIG_NLS_MAC_GREEK is not set
+# CONFIG_NLS_MAC_ICELAND is not set
+# CONFIG_NLS_MAC_INUIT is not set
+# CONFIG_NLS_MAC_ROMANIAN is not set
+# CONFIG_NLS_MAC_TURKISH is not set
+# CONFIG_NLS_UTF8 is not set
+# CONFIG_DLM is not set
+
+#
+# Kernel hacking
+#
+CONFIG_TRACE_IRQFLAGS_SUPPORT=y
+
+#
+# printk and dmesg options
+#
+CONFIG_PRINTK_TIME=y
+CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4
+# CONFIG_BOOT_PRINTK_DELAY is not set
+# CONFIG_DYNAMIC_DEBUG is not set
+
+#
+# Compile-time checks and compiler options
+#
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_INFO_REDUCED=y
+CONFIG_ENABLE_WARN_DEPRECATED=y
+CONFIG_ENABLE_MUST_CHECK=y
+CONFIG_FRAME_WARN=1024
+# CONFIG_STRIP_ASM_SYMS is not set
+# CONFIG_READABLE_ASM is not set
+# CONFIG_UNUSED_SYMBOLS is not set
+CONFIG_DEBUG_FS=y
+# CONFIG_HEADERS_CHECK is not set
+CONFIG_DEBUG_SECTION_MISMATCH=y
+CONFIG_ARCH_WANT_FRAME_POINTERS=y
+CONFIG_FRAME_POINTER=y
+# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_KERNEL=y
+
+#
+# Memory Debugging
+#
+CONFIG_DEBUG_PAGEALLOC=y
+CONFIG_WANT_PAGE_DEBUG_FLAGS=y
+CONFIG_PAGE_GUARD=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_DEBUG_OBJECTS_ENABLE_DEFAULT=1
+CONFIG_SLUB_DEBUG_ON=y
+# CONFIG_SLUB_STATS is not set
+CONFIG_HAVE_DEBUG_KMEMLEAK=y
+CONFIG_DEBUG_KMEMLEAK=y
+CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE=400
+# CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF is not set
+CONFIG_DEBUG_STACK_USAGE=y
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_VIRTUAL is not set
+# CONFIG_DEBUG_MEMORY_INIT is not set
+# CONFIG_DEBUG_PER_CPU_MAPS is not set
+CONFIG_HAVE_DEBUG_STACKOVERFLOW=y
+# CONFIG_DEBUG_STACKOVERFLOW is not set
+CONFIG_HAVE_ARCH_KMEMCHECK=y
+# CONFIG_DEBUG_SHIRQ is not set
+
+#
+# Debug Lockups and Hangs
+#
+CONFIG_LOCKUP_DETECTOR=y
+CONFIG_HARDLOCKUP_DETECTOR=y
+# CONFIG_BOOTPARAM_HARDLOCKUP_PANIC is not set
+CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE=0
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
+CONFIG_DETECT_HUNG_TASK=y
+CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120
+# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set
+CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0
+CONFIG_PANIC_ON_OOPS=y
+CONFIG_PANIC_ON_OOPS_VALUE=1
+CONFIG_SCHED_DEBUG=y
+CONFIG_SCHEDSTATS=y
+CONFIG_TIMER_STATS=y
+CONFIG_DEBUG_PREEMPT=y
+
+#
+# Lock Debugging (spinlocks, mutexes, etc...)
+#
+CONFIG_DEBUG_RT_MUTEXES=y
+CONFIG_DEBUG_PI_LIST=y
+# CONFIG_RT_MUTEX_TESTER is not set
+CONFIG_DEBUG_SPINLOCK=y
+CONFIG_DEBUG_MUTEXES=y
+# CONFIG_DEBUG_WW_MUTEX_SLOWPATH is not set
+CONFIG_DEBUG_LOCK_ALLOC=y
+CONFIG_PROVE_LOCKING=y
+CONFIG_LOCKDEP=y
+CONFIG_LOCK_STAT=y
+CONFIG_DEBUG_LOCKDEP=y
+CONFIG_DEBUG_ATOMIC_SLEEP=y
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+CONFIG_TRACE_IRQFLAGS=y
+CONFIG_STACKTRACE=y
+CONFIG_DEBUG_KOBJECT=y
+CONFIG_DEBUG_KOBJECT_RELEASE=y
+CONFIG_DEBUG_BUGVERBOSE=y
+# CONFIG_DEBUG_WRITECOUNT is not set
+CONFIG_DEBUG_LIST=y
+# CONFIG_DEBUG_SG is not set
+CONFIG_DEBUG_NOTIFIERS=y
+# CONFIG_DEBUG_CREDENTIALS is not set
+
+#
+# RCU Debugging
+#
+CONFIG_PROVE_RCU=y
+CONFIG_PROVE_RCU_REPEATEDLY=y
+# CONFIG_PROVE_RCU_DELAY is not set
+CONFIG_SPARSE_RCU_POINTER=y
+# CONFIG_RCU_TORTURE_TEST is not set
+CONFIG_RCU_CPU_STALL_TIMEOUT=60
+CONFIG_RCU_CPU_STALL_VERBOSE=y
+# CONFIG_RCU_CPU_STALL_INFO is not set
+# CONFIG_RCU_TRACE is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+# CONFIG_NOTIFIER_ERROR_INJECTION is not set
+# CONFIG_FAULT_INJECTION is not set
+CONFIG_LATENCYTOP=y
+CONFIG_ARCH_HAS_DEBUG_STRICT_USER_COPY_CHECKS=y
+CONFIG_DEBUG_STRICT_USER_COPY_CHECKS=y
+CONFIG_USER_STACKTRACE_SUPPORT=y
+CONFIG_NOP_TRACER=y
+CONFIG_HAVE_FUNCTION_TRACER=y
+CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
+CONFIG_HAVE_FUNCTION_GRAPH_FP_TEST=y
+CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST=y
+CONFIG_HAVE_DYNAMIC_FTRACE=y
+CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y
+CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
+CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
+CONFIG_HAVE_FENTRY=y
+CONFIG_HAVE_C_RECORDMCOUNT=y
+CONFIG_TRACE_CLOCK=y
+CONFIG_RING_BUFFER=y
+CONFIG_EVENT_TRACING=y
+CONFIG_CONTEXT_SWITCH_TRACER=y
+CONFIG_TRACING=y
+CONFIG_GENERIC_TRACER=y
+CONFIG_TRACING_SUPPORT=y
+CONFIG_FTRACE=y
+CONFIG_FUNCTION_TRACER=y
+CONFIG_FUNCTION_GRAPH_TRACER=y
+# CONFIG_IRQSOFF_TRACER is not set
+# CONFIG_PREEMPT_TRACER is not set
+# CONFIG_SCHED_TRACER is not set
+# CONFIG_FTRACE_SYSCALLS is not set
+# CONFIG_TRACER_SNAPSHOT is not set
+CONFIG_BRANCH_PROFILE_NONE=y
+# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set
+# CONFIG_PROFILE_ALL_BRANCHES is not set
+# CONFIG_STACK_TRACER is not set
+# CONFIG_BLK_DEV_IO_TRACE is not set
+# CONFIG_UPROBE_EVENT is not set
+# CONFIG_PROBE_EVENTS is not set
+CONFIG_DYNAMIC_FTRACE=y
+CONFIG_DYNAMIC_FTRACE_WITH_REGS=y
+# CONFIG_FUNCTION_PROFILER is not set
+CONFIG_FTRACE_MCOUNT_RECORD=y
+# CONFIG_FTRACE_STARTUP_TEST is not set
+# CONFIG_MMIOTRACE is not set
+# CONFIG_RING_BUFFER_BENCHMARK is not set
+# CONFIG_RING_BUFFER_STARTUP_TEST is not set
+
+#
+# Runtime Testing
+#
+# CONFIG_LKDTM is not set
+# CONFIG_TEST_LIST_SORT is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+# CONFIG_RBTREE_TEST is not set
+# CONFIG_ATOMIC64_SELFTEST is not set
+# CONFIG_TEST_STRING_HELPERS is not set
+# CONFIG_TEST_KSTRTOX is not set
+# CONFIG_PROVIDE_OHCI1394_DMA_INIT is not set
+# CONFIG_DMA_API_DEBUG is not set
+# CONFIG_SAMPLES is not set
+CONFIG_HAVE_ARCH_KGDB=y
+# CONFIG_KGDB is not set
+# CONFIG_STRICT_DEVMEM is not set
+# CONFIG_X86_VERBOSE_BOOTUP is not set
+CONFIG_EARLY_PRINTK=y
+# CONFIG_EARLY_PRINTK_DBGP is not set
+# CONFIG_X86_PTDUMP is not set
+# CONFIG_DEBUG_RODATA is not set
+CONFIG_DOUBLEFAULT=y
+# CONFIG_DEBUG_TLBFLUSH is not set
+# CONFIG_IOMMU_DEBUG is not set
+# CONFIG_IOMMU_STRESS is not set
+CONFIG_HAVE_MMIOTRACE_SUPPORT=y
+CONFIG_IO_DELAY_TYPE_0X80=0
+CONFIG_IO_DELAY_TYPE_0XED=1
+CONFIG_IO_DELAY_TYPE_UDELAY=2
+CONFIG_IO_DELAY_TYPE_NONE=3
+CONFIG_IO_DELAY_0X80=y
+# CONFIG_IO_DELAY_0XED is not set
+# CONFIG_IO_DELAY_UDELAY is not set
+# CONFIG_IO_DELAY_NONE is not set
+CONFIG_DEFAULT_IO_DELAY_TYPE=0
+# CONFIG_DEBUG_BOOT_PARAMS is not set
+# CONFIG_CPA_DEBUG is not set
+# CONFIG_OPTIMIZE_INLINING is not set
+# CONFIG_DEBUG_NMI_SELFTEST is not set
+# CONFIG_X86_DEBUG_STATIC_CPU_HAS is not set
+
+#
+# Security options
+#
+# CONFIG_KEYS is not set
+# CONFIG_SECURITY_DMESG_RESTRICT is not set
+# CONFIG_SECURITY is not set
+# CONFIG_SECURITYFS is not set
+CONFIG_DEFAULT_SECURITY_DAC=y
+CONFIG_DEFAULT_SECURITY=""
+CONFIG_CRYPTO=y
+
+#
+# Crypto core or helper
+#
+CONFIG_CRYPTO_ALGAPI=y
+CONFIG_CRYPTO_ALGAPI2=y
+CONFIG_CRYPTO_AEAD=y
+CONFIG_CRYPTO_AEAD2=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_BLKCIPHER2=y
+CONFIG_CRYPTO_HASH=y
+CONFIG_CRYPTO_HASH2=y
+CONFIG_CRYPTO_RNG=y
+CONFIG_CRYPTO_RNG2=y
+CONFIG_CRYPTO_PCOMP2=y
+CONFIG_CRYPTO_MANAGER=y
+CONFIG_CRYPTO_MANAGER2=y
+# CONFIG_CRYPTO_USER is not set
+CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y
+# CONFIG_CRYPTO_GF128MUL is not set
+# CONFIG_CRYPTO_NULL is not set
+# CONFIG_CRYPTO_PCRYPT is not set
+CONFIG_CRYPTO_WORKQUEUE=y
+# CONFIG_CRYPTO_CRYPTD is not set
+# CONFIG_CRYPTO_AUTHENC is not set
+
+#
+# Authenticated Encryption with Associated Data
+#
+CONFIG_CRYPTO_CCM=y
+# CONFIG_CRYPTO_GCM is not set
+CONFIG_CRYPTO_SEQIV=y
+
+#
+# Block modes
+#
+# CONFIG_CRYPTO_CBC is not set
+CONFIG_CRYPTO_CTR=y
+# CONFIG_CRYPTO_CTS is not set
+# CONFIG_CRYPTO_ECB is not set
+# CONFIG_CRYPTO_LRW is not set
+# CONFIG_CRYPTO_PCBC is not set
+# CONFIG_CRYPTO_XTS is not set
+
+#
+# Hash modes
+#
+# CONFIG_CRYPTO_CMAC is not set
+# CONFIG_CRYPTO_HMAC is not set
+# CONFIG_CRYPTO_XCBC is not set
+# CONFIG_CRYPTO_VMAC is not set
+
+#
+# Digest
+#
+# CONFIG_CRYPTO_CRC32C is not set
+# CONFIG_CRYPTO_CRC32C_INTEL is not set
+# CONFIG_CRYPTO_CRC32 is not set
+# CONFIG_CRYPTO_CRC32_PCLMUL is not set
+CONFIG_CRYPTO_CRCT10DIF=y
+# CONFIG_CRYPTO_GHASH is not set
+# CONFIG_CRYPTO_MD4 is not set
+# CONFIG_CRYPTO_MD5 is not set
+# CONFIG_CRYPTO_MICHAEL_MIC is not set
+# CONFIG_CRYPTO_RMD128 is not set
+# CONFIG_CRYPTO_RMD160 is not set
+# CONFIG_CRYPTO_RMD256 is not set
+# CONFIG_CRYPTO_RMD320 is not set
+# CONFIG_CRYPTO_SHA1 is not set
+# CONFIG_CRYPTO_SHA1_SSSE3 is not set
+# CONFIG_CRYPTO_SHA256_SSSE3 is not set
+# CONFIG_CRYPTO_SHA512_SSSE3 is not set
+# CONFIG_CRYPTO_SHA256 is not set
+# CONFIG_CRYPTO_SHA512 is not set
+# CONFIG_CRYPTO_TGR192 is not set
+# CONFIG_CRYPTO_WP512 is not set
+# CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL is not set
+
+#
+# Ciphers
+#
+CONFIG_CRYPTO_AES=y
+# CONFIG_CRYPTO_AES_X86_64 is not set
+# CONFIG_CRYPTO_AES_NI_INTEL is not set
+# CONFIG_CRYPTO_ANUBIS is not set
+CONFIG_CRYPTO_ARC4=y
+# CONFIG_CRYPTO_BLOWFISH is not set
+# CONFIG_CRYPTO_BLOWFISH_X86_64 is not set
+# CONFIG_CRYPTO_CAMELLIA is not set
+# CONFIG_CRYPTO_CAMELLIA_X86_64 is not set
+# CONFIG_CRYPTO_CAMELLIA_AESNI_AVX_X86_64 is not set
+# CONFIG_CRYPTO_CAMELLIA_AESNI_AVX2_X86_64 is not set
+# CONFIG_CRYPTO_CAST5 is not set
+# CONFIG_CRYPTO_CAST5_AVX_X86_64 is not set
+# CONFIG_CRYPTO_CAST6 is not set
+# CONFIG_CRYPTO_CAST6_AVX_X86_64 is not set
+# CONFIG_CRYPTO_DES is not set
+# CONFIG_CRYPTO_FCRYPT is not set
+# CONFIG_CRYPTO_KHAZAD is not set
+# CONFIG_CRYPTO_SALSA20 is not set
+# CONFIG_CRYPTO_SALSA20_X86_64 is not set
+# CONFIG_CRYPTO_SEED is not set
+# CONFIG_CRYPTO_SERPENT is not set
+# CONFIG_CRYPTO_SERPENT_SSE2_X86_64 is not set
+# CONFIG_CRYPTO_SERPENT_AVX_X86_64 is not set
+# CONFIG_CRYPTO_SERPENT_AVX2_X86_64 is not set
+# CONFIG_CRYPTO_TEA is not set
+# CONFIG_CRYPTO_TWOFISH is not set
+# CONFIG_CRYPTO_TWOFISH_X86_64 is not set
+# CONFIG_CRYPTO_TWOFISH_X86_64_3WAY is not set
+# CONFIG_CRYPTO_TWOFISH_AVX_X86_64 is not set
+
+#
+# Compression
+#
+# CONFIG_CRYPTO_DEFLATE is not set
+# CONFIG_CRYPTO_ZLIB is not set
+# CONFIG_CRYPTO_LZO is not set
+# CONFIG_CRYPTO_LZ4 is not set
+# CONFIG_CRYPTO_LZ4HC is not set
+
+#
+# Random Number Generation
+#
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_USER_API_HASH is not set
+# CONFIG_CRYPTO_USER_API_SKCIPHER is not set
+# CONFIG_CRYPTO_HW is not set
+CONFIG_HAVE_KVM=y
+# CONFIG_VIRTUALIZATION is not set
+CONFIG_BINARY_PRINTF=y
+
+#
+# Library routines
+#
+CONFIG_BITREVERSE=y
+CONFIG_GENERIC_STRNCPY_FROM_USER=y
+CONFIG_GENERIC_STRNLEN_USER=y
+CONFIG_GENERIC_NET_UTILS=y
+CONFIG_GENERIC_FIND_FIRST_BIT=y
+CONFIG_GENERIC_PCI_IOMAP=y
+CONFIG_GENERIC_IOMAP=y
+CONFIG_GENERIC_IO=y
+CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y
+# CONFIG_CRC_CCITT is not set
+# CONFIG_CRC16 is not set
+# CONFIG_CRC_T10DIF is not set
+# CONFIG_CRC_ITU_T is not set
+CONFIG_CRC32=y
+# CONFIG_CRC32_SELFTEST is not set
+CONFIG_CRC32_SLICEBY8=y
+# CONFIG_CRC32_SLICEBY4 is not set
+# CONFIG_CRC32_SARWATE is not set
+# CONFIG_CRC32_BIT is not set
+# CONFIG_CRC7 is not set
+# CONFIG_LIBCRC32C is not set
+# CONFIG_CRC8 is not set
+# CONFIG_XZ_DEC is not set
+# CONFIG_XZ_DEC_BCJ is not set
+CONFIG_HAS_IOMEM=y
+CONFIG_HAS_IOPORT=y
+CONFIG_HAS_DMA=y
+CONFIG_CPU_RMAP=y
+CONFIG_DQL=y
+CONFIG_NLATTR=y
+CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y
+CONFIG_AVERAGE=y
+# CONFIG_CORDIC is not set
+# CONFIG_DDR is not set
+CONFIG_FONT_SUPPORT=y
+# CONFIG_FONTS is not set
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
diff --git a/tests/hwsim/vm/parallel-vm.py b/tests/hwsim/vm/parallel-vm.py
new file mode 100644 (file)
index 0000000..339ad87
--- /dev/null
@@ -0,0 +1,452 @@
+#!/usr/bin/env python2
+#
+# Parallel VM test case executor
+# 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 curses
+import fcntl
+import logging
+import os
+import subprocess
+import sys
+import time
+
+logger = logging.getLogger()
+
+def get_failed(vm):
+    failed = []
+    for i in range(num_servers):
+        failed += vm[i]['failed']
+    return failed
+
+def vm_read_stdout(vm, i):
+    global total_started, total_passed, total_failed, total_skipped
+    global rerun_failures
+
+    ready = False
+    try:
+        out = vm['proc'].stdout.read()
+    except:
+        return False
+    logger.debug("VM[%d] stdout.read[%s]" % (i, out))
+    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]" % (i, line))
+        if line.startswith("READY"):
+            ready = True
+        elif line.startswith("PASS"):
+            ready = True
+            total_passed += 1
+        elif line.startswith("FAIL"):
+            ready = True
+            total_failed += 1
+            name = line.split(' ')[1]
+            logger.debug("VM[%d] test case failed: %s" % (i, name))
+            vm['failed'].append(name)
+        elif line.startswith("NOT-FOUND"):
+            ready = True
+            total_failed += 1
+            logger.info("VM[%d] test case not found" % i)
+        elif line.startswith("SKIP"):
+            ready = True
+            total_skipped += 1
+        elif line.startswith("START"):
+            total_started += 1
+        vm['out'] += line + '\n'
+        lines.append(line)
+    vm['pending'] = pending
+    return ready
+
+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
+
+    total_tests = len(tests)
+    logger.info("Total tests: %d" % total_tests)
+
+    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)
+        scr.addstr(i + 1, 10, "starting VM")
+    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()
+
+    completed_first_pass = False
+    rerun_tests = []
+
+    while True:
+        running = False
+        first_running = False
+        updated = False
+
+        for i in range(0, num_servers):
+            if completed_first_pass:
+                continue
+            if vm[i]['first_run_done']:
+                continue
+            if not vm[i]['proc']:
+                continue
+            if vm[i]['proc'].poll() is not None:
+                vm[i]['proc'] = None
+                scr.move(i + 1, 10)
+                scr.clrtoeol()
+                log = '{}/{}.srv.{}/console'.format(dir, timestamp, i + 1)
+                with open(log, 'r') as f:
+                    if "Kernel panic" in f.read():
+                        scr.addstr("kernel panic")
+                        logger.info("VM[%d] kernel panic" % i)
+                    else:
+                        scr.addstr("unexpected exit")
+                        logger.info("VM[%d] unexpected exit" % i)
+                updated = True
+                continue
+
+            running = True
+            first_running = True
+            try:
+                err = vm[i]['proc'].stderr.read()
+                vm[i]['err'] += err
+                logger.debug("VM[%d] stderr.read[%s]" % (i, err))
+            except:
+                pass
+
+            if vm_read_stdout(vm[i], i):
+                scr.move(i + 1, 10)
+                scr.clrtoeol()
+                updated = True
+                if not tests:
+                    vm[i]['first_run_done'] = True
+                    scr.addstr("completed first round")
+                    logger.info("VM[%d] completed first round" % i)
+                    continue
+                else:
+                    name = tests.pop(0)
+                    vm[i]['proc'].stdin.write(name + '\n')
+                    scr.addstr(name)
+                    logger.debug("VM[%d] start test %s" % (i, name))
+
+        if not first_running and not completed_first_pass:
+            logger.info("First round of testing completed")
+            if tests:
+                logger.info("Unexpected test cases remaining from first round: " + str(tests))
+                raise Exception("Unexpected test cases remaining from first round")
+            completed_first_pass = True
+            for name in get_failed(vm):
+                if rerun_failures:
+                    rerun_tests.append(name)
+                first_run_failures.append(name)
+
+        for i in range(num_servers):
+            if not completed_first_pass:
+                continue
+            if not vm[i]['proc']:
+                continue
+            if vm[i]['proc'].poll() is not None:
+                vm[i]['proc'] = None
+                scr.move(i + 1, 10)
+                scr.clrtoeol()
+                log = '{}/{}.srv.{}/console'.format(dir, timestamp, i + 1)
+                with open(log, 'r') as f:
+                    if "Kernel panic" in f.read():
+                        scr.addstr("kernel panic")
+                        logger.info("VM[%d] kernel panic" % i)
+                    else:
+                        scr.addstr("completed run")
+                        logger.info("VM[%d] completed run" % i)
+                updated = True
+                continue
+
+            running = True
+            try:
+                err = vm[i]['proc'].stderr.read()
+                vm[i]['err'] += err
+                logger.debug("VM[%d] stderr.read[%s]" % (i, err))
+            except:
+                pass
+
+            ready = False
+            if vm[i]['first_run_done']:
+                vm[i]['first_run_done'] = False
+                ready = True
+            else:
+                ready = vm_read_stdout(vm[i], i)
+            if ready:
+                scr.move(i + 1, 10)
+                scr.clrtoeol()
+                updated = True
+                if not rerun_tests:
+                    vm[i]['proc'].stdin.write('\n')
+                    scr.addstr("shutting down")
+                    logger.info("VM[%d] shutting down" % i)
+                else:
+                    name = rerun_tests.pop(0)
+                    vm[i]['proc'].stdin.write(name + '\n')
+                    scr.addstr(name + "(*)")
+                    logger.debug("VM[%d] start test %s (*)" % (i, name))
+
+        if not running:
+            break
+
+        if updated:
+            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.move(0, 35)
+            scr.clrtoeol()
+            if rerun_tests:
+                scr.addstr("(RETRY FAILED %d)" % len(rerun_tests))
+            elif rerun_failures:
+                pass
+            elif first_run_failures:
+                scr.addstr("(RETRY FAILED)")
+
+            scr.refresh()
+
+        time.sleep(0.25)
+
+    scr.refresh()
+    time.sleep(0.3)
+
+def main():
+    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
+
+    if len(sys.argv) < 2:
+        sys.exit("Usage: %s <number of VMs> [-1] [--debug] [--codecov] [params..]" % sys.argv[0])
+    num_servers = int(sys.argv[1])
+    if num_servers < 1:
+        sys.exit("Too small number of VMs")
+
+    timestamp = int(time.time())
+
+    idx = 2
+
+    if len(sys.argv) > idx and sys.argv[idx] == "-1":
+        idx += 1
+        rerun_failures = False
+
+    if len(sys.argv) > idx and sys.argv[idx] == "--debug":
+        idx += 1
+        debug_level = logging.DEBUG
+
+    if len(sys.argv) > idx and sys.argv[idx] == "--codecov":
+        idx += 1
+        print "Code coverage - build separate binaries"
+        logdir = "/tmp/hwsim-test-logs/" + str(timestamp)
+        os.makedirs(logdir)
+        subprocess.check_call(['./build-codecov.sh', logdir])
+        codecov_args = ['--codecov_dir', logdir]
+        codecov = True
+    else:
+        codecov_args = []
+        codecov = False
+
+    first_run_failures = []
+    tests = []
+    cmd = [ '../run-tests.py', '-L' ] + sys.argv[idx:]
+    lst = subprocess.Popen(cmd, stdout=subprocess.PIPE)
+    for l in lst.stdout.readlines():
+        name = l.split(' ')[0]
+        tests.append(name)
+    if len(tests) == 0:
+        sys.exit("No test cases selected")
+    if '-f' in sys.argv[idx:]:
+        extra_args = sys.argv[idx:]
+    else:
+        extra_args = [x for x in sys.argv[idx:] if x not in tests]
+
+    dir = '/tmp/hwsim-test-logs'
+    try:
+        os.mkdir(dir)
+    except:
+        pass
+
+    if 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.
+        long = [ "ap_roam_open",
+                 "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",
+                 "dfs_radar",
+                 "dfs",
+                 "grpform_cred_ready_timeout",
+                 "ap_wps_pbc_timeout" ]
+        for l in long:
+            if l in tests:
+                tests.remove(l)
+                tests.insert(0, l)
+
+    logger.setLevel(debug_level)
+    log_handler = logging.FileHandler('parallel-vm.log')
+    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):
+        print("\rStarting virtual machine {}/{}".format(i + 1, num_servers)),
+        logger.info("Starting virtual machine {}/{}".format(i + 1, num_servers))
+        cmd = ['./vm-run.sh', '--delay', str(i), '--timestamp', str(timestamp),
+               '--ext', 'srv.%d' % (i + 1),
+               '-i'] + codecov_args + extra_args
+        vm[i] = {}
+        vm[i]['first_run_done'] = False
+        vm[i]['proc'] = subprocess.Popen(cmd,
+                                         stdin=subprocess.PIPE,
+                                         stdout=subprocess.PIPE,
+                                         stderr=subprocess.PIPE)
+        vm[i]['out'] = ""
+        vm[i]['pending'] = ""
+        vm[i]['err'] = ""
+        vm[i]['failed'] = []
+        for stream in [ vm[i]['proc'].stdout, vm[i]['proc'].stderr ]:
+            fd = stream.fileno()
+            fl = fcntl.fcntl(fd, fcntl.F_GETFL)
+            fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
+    print
+
+    curses.wrapper(show_progress)
+
+    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, vm[i]['out'], vm[i]['err']))
+
+    failed = get_failed(vm)
+
+    if first_run_failures:
+        print "Failed test cases:"
+        for f in first_run_failures:
+            print f,
+            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,
+            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))
+
+    for i in range(0, num_servers):
+        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)
+
+    if codecov:
+        print "Code coverage - preparing report"
+        for i in range(num_servers):
+            subprocess.check_call(['./process-codecov.sh',
+                                   logdir + ".srv.%d" % (i + 1),
+                                   str(i)])
+        subprocess.check_call(['./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/tests/hwsim/vm/parallel-vm.sh b/tests/hwsim/vm/parallel-vm.sh
new file mode 100644 (file)
index 0000000..b2fd078
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+cd "$(dirname $0)"
+
+NUM=$1
+if [ -z "$NUM" ]; then
+    echo "usage: $0 <num servers> [params..]"
+    exit 1
+fi
+shift
+
+LOGS=/tmp/hwsim-test-logs
+mkdir -p $LOGS
+DATE=$(date +%s)
+
+for i in `seq 1 $NUM`; do
+    printf "\rStarting virtual machine $i/$NUM"
+    ./vm-run.sh --timestamp $DATE --ext srv.$i --split $i/$NUM $* >> $LOGS/parallel-$DATE.srv.$i 2>&1 &
+done
+echo
+
+echo "Waiting for virtual machines to complete testing"
+count=$NUM
+for i in `seq 1 $NUM`; do
+    printf "\r$count VM(s) remaining   "
+    wait -n
+    count=$((count-1))
+done
+printf "\rTesting completed       "
+echo
+
+echo -n "PASS count: "
+grep ^PASS $LOGS/parallel-$DATE.srv.* | wc -l
+cat $LOGS/parallel-$DATE.srv.* | grep FAIL | sort
diff --git a/tests/hwsim/vm/process-codecov.sh b/tests/hwsim/vm/process-codecov.sh
new file mode 100644 (file)
index 0000000..d932aa2
--- /dev/null
@@ -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/tests/hwsim/vm/uevent.sh b/tests/hwsim/vm/uevent.sh
new file mode 100644 (file)
index 0000000..d52f7fc
--- /dev/null
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+# 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/tests/hwsim/vm/vm-run.sh b/tests/hwsim/vm/vm-run.sh
new file mode 100644 (file)
index 0000000..77703bf
--- /dev/null
@@ -0,0 +1,126 @@
+#!/bin/bash
+
+cd "$(dirname $0)"
+
+if [ -z "$TESTDIR" ] ; then
+       TESTDIR=$(pwd)/../
+fi
+
+LOGS=/tmp/hwsim-test-logs
+
+# increase the memory size if you want to run with valgrind, 512 MB works
+MEMORY=128
+
+# 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
+       KERNEL=$KERNELDIR/arch/x86_64/boot/bzImage
+fi
+
+
+CMD=$TESTDIR/vm/inside.sh
+
+unset RUN_TEST_ARGS
+TIMESTAMP=$(date +%s)
+DATE=$TIMESTAMP
+CODECOV=no
+TIMEWARP=0
+DELAY=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
+                       ;;
+               --delay ) shift
+                       DELAY=$1
+                       shift
+                       ;;
+               * )
+                       RUN_TEST_ARGS="$RUN_TEST_ARGS$1 "
+                       shift
+                       ;;
+       esac
+done
+
+LOGDIR=$LOGS/$DATE
+mkdir -p $LOGDIR
+
+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
+
+if [ $DELAY -gt 0 ]; then
+    echo "Wait $DELAY seconds before starting VM"
+    sleep $DELAY
+fi
+
+echo "Starting test run in a virtual machine"
+
+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 \
+       -append "mac80211_hwsim.support_p2p_device=0 mac80211_hwsim.channels=$CHANNELS mac80211_hwsim.radios=6 init=$CMD testdir=$TESTDIR timewarp=$TIMEWARP console=$KVMOUT root=/dev/root rootflags=trans=virtio,version=9p2000.u ro rootfstype=9p EPATH=$EPATH ARGS=$RUN_TEST_ARGS"
+
+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"
+if [ $CODECOV = "yes" ]; then
+    echo "Code coverage report:"
+    echo "file://$LOGDIR/lcov/index.html"
+fi
index cf7c9fb..85ecb88 100644 (file)
@@ -1,7 +1,5 @@
-#!/usr/bin/python
-#
 # Python class for controlling wlantest
-# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+# 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.
@@ -12,7 +10,7 @@ import subprocess
 import logging
 import wpaspy
 
-logger = logging.getLogger(__name__)
+logger = logging.getLogger()
 
 class Wlantest:
     def __init__(self):
@@ -26,6 +24,11 @@ class Wlantest:
         if "FAIL" in res:
             raise Exception("wlantest_cli flush failed")
 
+    def relog(self):
+        res = subprocess.check_output([self.wlantest_cli, "relog"])
+        if "FAIL" in res:
+            raise Exception("wlantest_cli relog failed")
+
     def add_passphrase(self, passphrase):
         res = subprocess.check_output([self.wlantest_cli, "add_passphrase",
                                        passphrase])
@@ -44,6 +47,20 @@ class Wlantest:
             raise Exception("Could not get BSS info from wlantest for " + bssid)
         return res
 
+    def get_bss_counter(self, field, bssid):
+        try:
+            res = subprocess.check_output([self.wlantest_cli, "get_bss_counter",
+                                           field, bssid]);
+        except Exception, e:
+            return 0
+        if "FAIL" in res:
+            return 0
+        return int(res)
+
+    def clear_bss_counters(self, bssid):
+        subprocess.call([self.wlantest_cli, "clear_bss_counters", bssid],
+                        stdout=open('/dev/null', 'w'));
+
     def info_sta(self, field, bssid, addr):
         res = subprocess.check_output([self.wlantest_cli, "info_sta",
                                        field, bssid, addr])
@@ -58,9 +75,15 @@ class Wlantest:
             raise Exception("wlantest_cli command failed")
         return int(res)
 
+    def clear_sta_counters(self, bssid, addr):
+        res = subprocess.check_output([self.wlantest_cli, "clear_sta_counters",
+                                       bssid, addr]);
+        if "FAIL" in res:
+            raise Exception("wlantest_cli command failed")
+
     def tdls_clear(self, bssid, addr1, addr2):
-        subprocess.call([self.wlantest_cli, "clear_tdls_counters", bssid, addr1,
-                         addr2]);
+        res = subprocess.check_output([self.wlantest_cli, "clear_tdls_counters",
+                                       bssid, addr1, addr2]);
 
     def get_tdls_counter(self, field, bssid, addr1, addr2):
         res = subprocess.check_output([self.wlantest_cli, "get_tdls_counter",
@@ -109,3 +132,25 @@ class Wlantest:
         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 = subprocess.check_output([self.wlantest_cli, "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 = subprocess.check_output([self.wlantest_cli, "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 ]
index 3e8036e..08fd687 100644 (file)
@@ -1,7 +1,5 @@
-#!/usr/bin/python
-#
 # Python class for controlling wpa_supplicant
-# Copyright (c) 2013, Jouni Malinen <j@w1.fi>
+# 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.
@@ -9,25 +7,68 @@
 import os
 import time
 import logging
+import binascii
 import re
+import struct
+import subprocess
 import wpaspy
 
-logger = logging.getLogger(__name__)
+logger = logging.getLogger()
 wpas_ctrl = '/var/run/wpa_supplicant'
 
 class WpaSupplicant:
-    def __init__(self, ifname, global_iface=None):
-        self.ifname = ifname
+    def __init__(self, ifname=None, global_iface=None):
         self.group_ifname = None
-        self.ctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
-        self.mon = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
-        self.mon.attach()
+        self.gctrl_mon = None
+        if ifname:
+            self.set_ifname(ifname)
+        else:
+            self.ifname = None
 
         self.global_iface = global_iface
         if global_iface:
             self.global_ctrl = wpaspy.Ctrl(global_iface)
             self.global_mon = wpaspy.Ctrl(global_iface)
             self.global_mon.attach()
+        else:
+            self.global_mon = None
+
+    def close_ctrl(self):
+        if self.global_mon:
+            self.global_mon.detach()
+            self.global_mon = None
+            self.global_ctrl = None
+        self.remove_ifname()
+
+    def set_ifname(self, ifname):
+        self.ifname = ifname
+        self.ctrl = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
+        self.mon = wpaspy.Ctrl(os.path.join(wpas_ctrl, ifname))
+        self.mon.attach()
+
+    def remove_ifname(self):
+        if self.ifname:
+            self.mon.detach()
+            self.mon = None
+            self.ctrl = None
+            self.ifname = None
+
+    def interface_add(self, ifname, config="", driver="nl80211", drv_params=None):
+        try:
+            groups = subprocess.check_output(["id"])
+            group = "admin" if "(admin)" in groups else "adm"
+        except Exception, e:
+            group = "admin"
+        cmd = "INTERFACE_ADD " + ifname + "\t" + config + "\t" + driver + "\tDIR=/var/run/wpa_supplicant GROUP=" + group
+        if drv_params:
+            cmd = cmd + '\t' + drv_params
+        if "FAIL" in self.global_request(cmd):
+            raise Exception("Failed to add a dynamic wpa_supplicant interface")
+        self.set_ifname(ifname)
+
+    def interface_remove(self, ifname):
+        self.remove_ifname()
+        self.global_request("INTERFACE_REMOVE " + ifname)
 
     def request(self, cmd):
         logger.debug(self.ifname + ": CTRL: " + cmd)
@@ -37,7 +78,8 @@ class WpaSupplicant:
         if self.global_iface is None:
             self.request(cmd)
         else:
-            logger.debug(self.ifname + ": CTRL: " + cmd)
+            ifname = self.ifname or self.global_iface
+            logger.debug(ifname + ": CTRL(global): " + cmd)
             return self.global_ctrl.request(cmd)
 
     def group_request(self, cmd):
@@ -50,12 +92,62 @@ class WpaSupplicant:
     def ping(self):
         return "PONG" in self.request("PING")
 
+    def global_ping(self):
+        return "PONG" in self.global_request("PING")
+
     def reset(self):
-        self.request("FLUSH")
+        self.dump_monitor()
+        res = self.request("FLUSH")
+        if not "OK" in res:
+            logger.info("FLUSH to " + self.ifname + " failed: " + res)
+        self.request("SET p2p_add_cli_chan 0")
+        self.request("SET p2p_no_go_freq ")
+        self.request("SET p2p_pref_chan ")
+        self.request("SET p2p_no_group_iface 1")
+        self.request("SET p2p_go_intent 7")
         self.request("SET ignore_old_scan_res 0")
+        if self.gctrl_mon:
+            try:
+                self.gctrl_mon.detach()
+            except:
+                pass
+            self.gctrl_mon = None
         self.group_ifname = None
         self.dump_monitor()
 
+        iter = 0
+        while iter < 60:
+            state = self.get_driver_status_field("scan_state")
+            if "SCAN_STARTED" in state or "SCAN_REQUESTED" in state:
+                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"
+            try:
+                cmd = ["sudo", "ifconfig", self.ifname, "down"]
+                subprocess.call(cmd)
+            except subprocess.CalledProcessError, e:
+                logger.info("ifconfig failed: " + str(e.returncode))
+                logger.info(e.output)
+            try:
+                cmd = ["sudo", "ifconfig", self.ifname, "up"]
+                subprocess.call(cmd)
+            except subprocess.CalledProcessError, e:
+                logger.info("ifconfig failed: " + str(e.returncode))
+                logger.info(e.output)
+        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 add_network(self):
         id = self.request("ADD_NETWORK")
         if "FAIL" in id:
@@ -68,6 +160,12 @@ class WpaSupplicant:
             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:
@@ -80,6 +178,30 @@ class WpaSupplicant:
             raise Exception("SET_NETWORK failed")
         return None
 
+    def list_networks(self):
+        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 add_cred(self):
         id = self.request("ADD_CRED")
         if "FAIL" in id:
@@ -104,70 +226,177 @@ class WpaSupplicant:
             raise Exception("SET_CRED failed")
         return None
 
-    def select_network(self, id):
-        id = self.request("SELECT_NETWORK " + str(id))
+    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" ]
+        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=" + freq
+        else:
+            extra = ""
+        id = self.request("SELECT_NETWORK " + str(id) + extra)
         if "FAIL" in id:
             raise Exception("SELECT_NETWORK failed")
         return None
 
-    def connect_network(self, id):
+    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=10):
         self.dump_monitor()
         self.select_network(id)
-        ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=10)
-        if ev is None:
-            raise Exception("Association with the AP timed out")
+        self.wait_connected(timeout=timeout)
         self.dump_monitor()
 
-    def get_status(self):
-        res = self.request("STATUS")
+    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:
-            [name,value] = l.split('=', 1)
+            try:
+                [name,value] = l.split('=', 1)
+                vals[name] = value
+            except ValueError, 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_status_field(self, field):
-        vals = self.get_status()
+    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_group_status(self):
-        res = self.group_request("STATUS")
+    def get_driver_status(self):
+        res = self.request("STATUS-DRIVER")
         lines = res.splitlines()
         vals = dict()
         for l in lines:
-            [name,value] = l.split('=', 1)
+            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_group_status_field(self, field):
-        vals = self.get_group_status()
+    def get_driver_status_field(self, field):
+        vals = self.get_driver_status()
         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, 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 p2p_listen(self):
         return self.global_request("P2P_LISTEN")
 
-    def p2p_find(self, social=False):
+    def p2p_find(self, social=False, progressive=False, dev_id=None,
+                 dev_type=None, delay=None):
+        cmd = "P2P_FIND"
         if social:
-            return self.global_request("P2P_FIND type=social")
-        return self.global_request("P2P_FIND")
+            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)
+        return self.global_request(cmd)
 
     def p2p_stop_find(self):
         return self.global_request("P2P_STOP_FIND")
 
     def wps_read_pin(self):
-        #TODO: make this random
-        self.pin = "12345670"
+        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):
@@ -178,20 +407,32 @@ class WpaSupplicant:
             return True
         return "[PROBE_REQ_ONLY]" not in res
 
-    def discover_peer(self, peer, full=True, timeout=15, social=True):
+    def discover_peer(self, peer, full=True, timeout=15, social=True, force_find=False):
         logger.info(self.ifname + ": Trying to discover peer " + peer)
-        if self.peer_known(peer, full):
+        if not force_find and self.peer_known(peer, full):
             return True
         self.p2p_find(social)
         count = 0
-        while count < timeout:
-            time.sleep(1)
+        while count < timeout * 4:
+            time.sleep(0.25)
             count = count + 1
             if self.peer_known(peer, full):
                 return True
         return False
 
-    def group_form_result(self, ev, expect_failure=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")
@@ -207,17 +448,30 @@ class WpaSupplicant:
         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:]*)'
+        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) < 8:
-            raise Exception("Could not parse P2P-GROUP-STARTED")
+        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:
+            self.gctrl_mon = wpaspy.Ctrl(os.path.join(wpas_ctrl, self.group_ifname))
+            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)
@@ -225,29 +479,61 @@ class WpaSupplicant:
         if p:
             res['passphrase'] = p.group(1)
         res['go_dev_addr'] = s[7]
+
+        if len(s) > 8 and len(s[8]) > 0:
+            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):
+    def p2p_go_neg_auth(self, peer, pin, method, go_intent=None, persistent=False, freq=None):
         if not self.discover_peer(peer):
             raise Exception("Peer " + peer + " not found")
         self.dump_monitor()
-        cmd = "P2P_CONNECT " + peer + " " + pin + " " + method + " auth"
+        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 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=1, expect_failure=False):
-        ev = self.wait_global_event(["P2P-GROUP-STARTED","P2P-GO-NEG-FAILURE"], timeout);
+        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)
+        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):
+    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):
         if not self.discover_peer(peer):
             raise Exception("Peer " + peer + " not found")
         self.dump_monitor()
@@ -257,61 +543,132 @@ class WpaSupplicant:
             cmd = "P2P_CONNECT " + peer + " " + method
         if go_intent:
             cmd = cmd + ' go_intent=' + str(go_intent)
+        if freq:
+            cmd = cmd + ' freq=' + str(freq)
+        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:
                 self.dump_monitor()
                 return None
-            ev = self.wait_global_event(["P2P-GROUP-STARTED","P2P-GO-NEG-FAILURE"], timeout)
+            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)
+            return self.group_form_result(ev, expect_failure, go_neg_res)
         raise Exception("P2P_CONNECT failed")
 
-    def wait_event(self, events, timeout):
-        count = 0
-        while count < timeout * 10:
-            count = count + 1
-            time.sleep(0.1)
+    def wait_event(self, events, timeout=10):
+        start = os.times()[4]
+        while True:
             while self.mon.pending():
                 ev = self.mon.recv()
                 logger.debug(self.ifname + ": " + 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_global_event(self, events, timeout):
         if self.global_iface is None:
             self.wait_event(events, timeout)
         else:
-            count = 0
-            while count < timeout * 10:
-                count = count + 1
-                time.sleep(0.1)
+            start = os.times()[4]
+            while True:
                 while self.global_mon.pending():
                     ev = self.global_mon.recv()
-                    logger.debug(self.ifname + ": " + ev)
+                    logger.debug(self.ifname + "(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.global_mon.pending(timeout=remaining):
+                    break
         return None
 
+    def wait_group_event(self, events, timeout=10):
+        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_ifname + ": " + 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):
+        if self.gctrl_mon:
+            try:
+                self.gctrl_mon.detach()
+            except:
+                pass
+            self.gctrl_mon = None
+        ev = self.wait_event(["P2P-GROUP-REMOVED"], timeout=3)
+        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):
         while self.mon.pending():
             ev = self.mon.recv()
             logger.debug(self.ifname + ": " + ev)
+        while self.global_mon and self.global_mon.pending():
+            ev = self.global_mon.recv()
+            logger.debug(self.ifname + "(global): " + ev)
 
     def remove_group(self, ifname=None):
+        if self.gctrl_mon:
+            try:
+                self.gctrl_mon.detach()
+            except:
+                pass
+            self.gctrl_mon = None
         if ifname is None:
-            ifname = self.group_ifname if self.group_ifname else self.iname
+            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):
+    def p2p_start_go(self, persistent=None, freq=None, no_event_clear=False):
         self.dump_monitor()
         cmd = "P2P_GROUP_ADD"
         if persistent is None:
@@ -321,12 +678,13 @@ class WpaSupplicant:
         else:
             cmd = cmd + " persistent=" + str(persistent)
         if freq:
-            cmd = cmd + " freq=" + 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")
-            self.dump_monitor()
+            if not no_event_clear:
+                self.dump_monitor()
             return self.group_form_result(ev)
         raise Exception("P2P_GROUP_ADD failed")
 
@@ -336,12 +694,22 @@ class WpaSupplicant:
             raise Exception("Failed to authorize client connection on GO")
         return None
 
-    def p2p_connect_group(self, go_addr, pin, timeout=0):
+    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=False):
-            raise Exception("GO " + go_addr + " not found")
+        if not self.discover_peer(go_addr, social=social):
+            if social or not self.discover_peer(go_addr, social=social):
+                raise Exception("GO " + go_addr + " not found")
         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()
@@ -365,58 +733,300 @@ class WpaSupplicant:
             raise Exception("Failed to request TDLS teardown")
         return None
 
-    def connect(self, ssid, psk=None, proto=None, key_mgmt=None, wep_key0=None, ieee80211w=None):
+    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.iteritems():
+            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 not (tsid, up) 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()
-        self.set_network_quoted(id, "ssid", ssid)
-        if psk:
-            self.set_network_quoted(id, "psk", psk)
-        if proto:
-            self.set_network(id, "proto", proto)
-        if key_mgmt:
-            self.set_network(id, "key_mgmt", key_mgmt)
-        if ieee80211w:
-            self.set_network(id, "ieee80211w", ieee80211w)
-        if wep_key0:
-            self.set_network(id, "wep_key0", wep_key0)
-        self.connect_network(id)
-
-    def scan(self, type=None):
+        if ssid:
+            self.set_network_quoted(id, "ssid", ssid)
+        elif ssid2:
+            self.set_network(id, "ssid", ssid2)
+
+        quoted = [ "psk", "identity", "anonymous_identity", "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" ]
+        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", "eap",
+                       "eapol_flags", "fragment_size", "scan_ssid", "auth_alg",
+                       "wpa_ptk_rekey", "disable_ht", "disable_vht", "bssid",
+                       "disable_max_amsdu", "ampdu_factor", "ampdu_density",
+                       "disable_ht40", "disable_sgi", "disable_ldpc",
+                       "ht40_intolerant", "update_identifier", "mac_addr",
+                       "erp", "bg_scan_period", "bssid_blacklist",
+                       "bssid_whitelist" ]
+        for field in not_quoted:
+            if field in kwargs and kwargs[field]:
+                self.set_network(id, field, kwargs[field])
+
+        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):
         if type:
             cmd = "SCAN TYPE=" + type
         else:
             cmd = "SCAN"
-        self.dump_monitor()
+        if freq:
+            cmd = cmd + " freq=" + str(freq)
+        if only_new:
+            cmd += " only_new=1"
+        if not no_wait:
+            self.dump_monitor()
         if not "OK" in self.request(cmd):
             raise Exception("Failed to trigger scan")
+        if no_wait:
+            return
         ev = self.wait_event(["CTRL-EVENT-SCAN-RESULTS"], 15)
         if ev is None:
             raise Exception("Scan timed out")
 
-    def roam(self, bssid):
+    def scan_for_bss(self, bssid, freq=None, force_scan=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")
+            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)
+
+    def roam(self, bssid, fail_test=False):
         self.dump_monitor()
-        self.request("ROAM " + bssid)
-        ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=10)
-        if ev is None:
-            raise Exception("Roaming with the AP timed out")
+        if "OK" not in self.request("ROAM " + bssid):
+            raise Exception("ROAM 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
+        self.wait_connected(timeout=10, error="Roaming with the AP timed out")
+        self.dump_monitor()
+
+    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
+        self.wait_connected(timeout=10, error="Roaming with the AP timed out")
         self.dump_monitor()
 
     def wps_reg(self, bssid, pin, new_ssid=None, key_mgmt=None, cipher=None,
-                new_passphrase=None):
+                new_passphrase=None, no_wait=False):
         self.dump_monitor()
         if new_ssid:
             self.request("WPS_REG " + bssid + " " + pin + " " +
                          new_ssid.encode("hex") + " " + key_mgmt + " " +
                          cipher + " " + new_passphrase.encode("hex"))
+            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")
-        ev = self.wait_event(["CTRL-EVENT-CONNECTED"], timeout=15)
+        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):
+        res = self.request("BSS " + bssid)
+        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()
+            [index,aa,pmkid,expiration,opportunistic] = l.split(' ')
+            vals['index'] = index
+            vals['pmkid'] = pmkid
+            vals['expiration'] = expiration
+            vals['opportunistic'] = opportunistic
+            return vals
+        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
+        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=10, error="Disconnection timed out"):
+        ev = self.wait_event(["CTRL-EVENT-DISCONNECTED"], timeout=timeout)
         if ev is None:
-            raise Exception("Association with the AP timed out")
+            raise Exception(error)
+        return ev
index 84c959c..9d76c07 100644 (file)
@@ -51,115 +51,6 @@ static void test_aes_perf(void)
 }
 
 
-static int test_eax(void)
-{
-       u8 msg[] = { 0xF7, 0xFB };
-       u8 key[] = { 0x91, 0x94, 0x5D, 0x3F, 0x4D, 0xCB, 0xEE, 0x0B,
-                    0xF4, 0x5E, 0xF5, 0x22, 0x55, 0xF0, 0x95, 0xA4 };
-       u8 nonce[] = { 0xBE, 0xCA, 0xF0, 0x43, 0xB0, 0xA2, 0x3D, 0x84,
-                      0x31, 0x94, 0xBA, 0x97, 0x2C, 0x66, 0xDE, 0xBD };
-       u8 hdr[] = { 0xFA, 0x3B, 0xFD, 0x48, 0x06, 0xEB, 0x53, 0xFA };
-       u8 cipher[] = { 0x19, 0xDD, 0x5C, 0x4C, 0x93, 0x31, 0x04, 0x9D,
-                       0x0B, 0xDA, 0xB0, 0x27, 0x74, 0x08, 0xF6, 0x79,
-                       0x67, 0xE5 };
-       u8 data[sizeof(msg)], tag[BLOCK_SIZE];
-
-       memcpy(data, msg, sizeof(msg));
-       if (aes_128_eax_encrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr),
-                               data, sizeof(data), tag)) {
-               printf("AES-128 EAX mode encryption failed\n");
-               return 1;
-       }
-       if (memcmp(data, cipher, sizeof(data)) != 0) {
-               printf("AES-128 EAX mode encryption returned invalid cipher "
-                      "text\n");
-               return 1;
-       }
-       if (memcmp(tag, cipher + sizeof(data), BLOCK_SIZE) != 0) {
-               printf("AES-128 EAX mode encryption returned invalid tag\n");
-               return 1;
-       }
-
-       if (aes_128_eax_decrypt(key, nonce, sizeof(nonce), hdr, sizeof(hdr),
-                               data, sizeof(data), tag)) {
-               printf("AES-128 EAX mode decryption failed\n");
-               return 1;
-       }
-       if (memcmp(data, msg, sizeof(data)) != 0) {
-               printf("AES-128 EAX mode decryption returned invalid plain "
-                      "text\n");
-               return 1;
-       }
-
-       return 0;
-}
-
-
-static int test_cbc(void)
-{
-       struct cbc_test_vector {
-               u8 key[16];
-               u8 iv[16];
-               u8 plain[32];
-               u8 cipher[32];
-               size_t len;
-       } vectors[] = {
-               {
-                       { 0x06, 0xa9, 0x21, 0x40, 0x36, 0xb8, 0xa1, 0x5b,
-                         0x51, 0x2e, 0x03, 0xd5, 0x34, 0x12, 0x00, 0x06 },
-                       { 0x3d, 0xaf, 0xba, 0x42, 0x9d, 0x9e, 0xb4, 0x30,
-                         0xb4, 0x22, 0xda, 0x80, 0x2c, 0x9f, 0xac, 0x41 },
-                       "Single block msg",
-                       { 0xe3, 0x53, 0x77, 0x9c, 0x10, 0x79, 0xae, 0xb8,
-                         0x27, 0x08, 0x94, 0x2d, 0xbe, 0x77, 0x18, 0x1a },
-                       16
-               },
-               {
-                       { 0xc2, 0x86, 0x69, 0x6d, 0x88, 0x7c, 0x9a, 0xa0,
-                         0x61, 0x1b, 0xbb, 0x3e, 0x20, 0x25, 0xa4, 0x5a },
-                       { 0x56, 0x2e, 0x17, 0x99, 0x6d, 0x09, 0x3d, 0x28,
-                         0xdd, 0xb3, 0xba, 0x69, 0x5a, 0x2e, 0x6f, 0x58 },
-                       { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-                         0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
-                         0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
-                         0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f },
-                       { 0xd2, 0x96, 0xcd, 0x94, 0xc2, 0xcc, 0xcf, 0x8a,
-                         0x3a, 0x86, 0x30, 0x28, 0xb5, 0xe1, 0xdc, 0x0a,
-                         0x75, 0x86, 0x60, 0x2d, 0x25, 0x3c, 0xff, 0xf9,
-                         0x1b, 0x82, 0x66, 0xbe, 0xa6, 0xd6, 0x1a, 0xb1 },
-                       32
-               }
-       };
-       int ret = 0;
-       u8 *buf;
-       unsigned int i;
-
-       for (i = 0; i < sizeof(vectors) / sizeof(vectors[0]); i++) {
-               struct cbc_test_vector *tv = &vectors[i];
-               buf = malloc(tv->len);
-               if (buf == NULL) {
-                       ret++;
-                       break;
-               }
-               memcpy(buf, tv->plain, tv->len);
-               if (aes_128_cbc_encrypt(tv->key, tv->iv, buf, tv->len) ||
-                   memcmp(buf, tv->cipher, tv->len) != 0) {
-                       printf("AES-CBC encrypt %d failed\n", i);
-                       ret++;
-               }
-               memcpy(buf, tv->cipher, tv->len);
-               if (aes_128_cbc_decrypt(tv->key, tv->iv, buf, tv->len) ||
-                   memcmp(buf, tv->plain, tv->len) != 0) {
-                       printf("AES-CBC decrypt %d failed\n", i);
-                       ret++;
-               }
-               free(buf);
-       }
-
-       return ret;
-}
-
-
 /*
  * GCM test vectors from
  * http://csrc.nist.gov/groups/ST/toolkit/BCM/documents/proposedmodes/gcm/gcm-spec.pdf
@@ -347,7 +238,7 @@ static int test_gcm(void)
        u8 p[64], c[64], tmp[64];
        size_t k_len, p_len, aad_len, iv_len;
 
-       for (i = 0; i < sizeof(gcm_tests) / sizeof(gcm_tests[0]); i++) {
+       for (i = 0; i < ARRAY_SIZE(gcm_tests); i++) {
                const struct gcm_test_vector *tc = &gcm_tests[i];
 
                k_len = os_strlen(tc->k) / 2;
@@ -438,140 +329,291 @@ static int test_gcm(void)
 }
 
 
-/* OMAC1 AES-128 test vectors from
- * http://csrc.nist.gov/CryptoToolkit/modes/proposedmodes/omac/omac-ad.pdf
- * which are same as the examples from NIST SP800-38B
- * http://csrc.nist.gov/CryptoToolkit/modes/800-38_Series_Publications/SP800-38B.pdf
- */
+static int test_nist_key_wrap_ae(const char *fname)
+{
+       FILE *f;
+       int ret = 0;
+       char buf[15000], *pos, *pos2;
+       u8 bin[2000], k[32], p[1024], c[1024 + 8], result[1024 + 8];
+       size_t bin_len, k_len = 0, p_len = 0, c_len = 0;
+       int ok = 0;
 
-struct omac1_test_vector {
-       u8 k[16];
-       u8 msg[64];
-       int msg_len;
-       u8 tag[16];
-};
+       printf("NIST KW AE tests from %s\n", fname);
 
-static struct omac1_test_vector test_vectors[] =
-{
-       {
-               { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
-                 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
-               { },
-               0,
-               { 0xbb, 0x1d, 0x69, 0x29, 0xe9, 0x59, 0x37, 0x28,
-                 0x7f, 0xa3, 0x7d, 0x12, 0x9b, 0x75, 0x67, 0x46 }
-       },
-       {
-               { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
-                 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
-               { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
-                 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a},
-               16,
-               { 0x07, 0x0a, 0x16, 0xb4, 0x6b, 0x4d, 0x41, 0x44,
-                 0xf7, 0x9b, 0xdd, 0x9d, 0xd0, 0x4a, 0x28, 0x7c }
-       },
-       {
-               { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
-                 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
-               { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
-                 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
-                 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
-                 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
-                 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11 },
-               40,
-               { 0xdf, 0xa6, 0x67, 0x47, 0xde, 0x9a, 0xe6, 0x30,
-                 0x30, 0xca, 0x32, 0x61, 0x14, 0x97, 0xc8, 0x27 }
-       },
-       {
-               { 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
-                 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c },
-               { 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
-                 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
-                 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
-                 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
-                 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
-                 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
-                 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
-                 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 },
-               64,
-               { 0x51, 0xf0, 0xbe, 0xbf, 0x7e, 0x3b, 0x9d, 0x92,
-                 0xfc, 0x49, 0x74, 0x17, 0x79, 0x36, 0x3c, 0xfe }
-       },
-};
+       f = fopen(fname, "r");
+       if (f == NULL) {
+               printf("%s does not exist - cannot validate test vectors\n",
+                      fname);
+               return 1;
+       }
 
+       while (fgets(buf, sizeof(buf), f)) {
+               if (buf[0] == '#')
+                       continue;
+               pos = os_strchr(buf, '=');
+               if (pos == NULL)
+                       continue;
+               pos2 = pos - 1;
+               while (pos2 >= buf && *pos2 == ' ')
+                       *pos2-- = '\0';
+               *pos++ = '\0';
+               while (*pos == ' ')
+                       *pos++ = '\0';
+               pos2 = os_strchr(pos, '\r');
+               if (!pos2)
+                       pos2 = os_strchr(pos, '\n');
+               if (pos2)
+                       *pos2 = '\0';
+               else
+                       pos2 = pos + os_strlen(pos);
+
+               if (buf[0] == '[') {
+                       printf("%s = %s\n", buf, pos);
+                       continue;
+               }
 
-int main(int argc, char *argv[])
-{
-       u8 kek[] = {
-               0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-               0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
-       };
-       u8 plain[] = {
-               0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
-               0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff
-       };
-       u8 crypt[] = {
-               0x1F, 0xA6, 0x8B, 0x0A, 0x81, 0x12, 0xB4, 0x47,
-               0xAE, 0xF3, 0x4B, 0xD8, 0xFB, 0x5A, 0x7B, 0x82,
-               0x9D, 0x3E, 0x86, 0x23, 0x71, 0xD2, 0xCF, 0xE5
-       };
-       u8 result[24];
-       int ret = 0;
-       unsigned int i;
-       struct omac1_test_vector *tv;
+               if (os_strcmp(buf, "COUNT") == 0) {
+                       printf("Test %s - ", pos);
+                       continue;
+               }
 
-       if (aes_wrap(kek, 2, plain, result)) {
-               printf("AES-WRAP-128-128 reported failure\n");
-               ret++;
-       }
-       if (memcmp(result, crypt, 24) != 0) {
-               printf("AES-WRAP-128-128 failed\n");
-               ret++;
-       }
-       if (aes_unwrap(kek, 2, crypt, result)) {
-               printf("AES-UNWRAP-128-128 reported failure\n");
-               ret++;
+               bin_len = os_strlen(pos);
+               if (bin_len > sizeof(bin) * 2) {
+                       printf("Too long binary data (%s)\n", buf);
+                       return 1;
+               }
+               if (bin_len & 0x01) {
+                       printf("Odd number of hexstring values (%s)\n",
+                               buf);
+                       return 1;
+               }
+               bin_len /= 2;
+               if (hexstr2bin(pos, bin, bin_len) < 0) {
+                       printf("Invalid hex string '%s' (%s)\n", pos, buf);
+                       return 1;
+               }
+
+               if (os_strcmp(buf, "K") == 0) {
+                       if (bin_len > sizeof(k)) {
+                               printf("Too long K (%u)\n", (unsigned) bin_len);
+                               return 1;
+                       }
+                       os_memcpy(k, bin, bin_len);
+                       k_len = bin_len;
+                       continue;
+               }
+
+               if (os_strcmp(buf, "P") == 0) {
+                       if (bin_len > sizeof(p)) {
+                               printf("Too long P (%u)\n", (unsigned) bin_len);
+                               return 1;
+                       }
+                       os_memcpy(p, bin, bin_len);
+                       p_len = bin_len;
+                       continue;
+               }
+
+               if (os_strcmp(buf, "C") != 0) {
+                       printf("Unexpected field '%s'\n", buf);
+                       continue;
+               }
+
+               if (bin_len > sizeof(c)) {
+                       printf("Too long C (%u)\n", (unsigned) bin_len);
+                       return 1;
+               }
+               os_memcpy(c, bin, bin_len);
+               c_len = bin_len;
+
+               if (p_len % 8 != 0 || c_len % 8 != 0 || c_len - p_len != 8) {
+                       printf("invalid parameter length (p_len=%u c_len=%u)\n",
+                              (unsigned) p_len, (unsigned) c_len);
+                       continue;
+               }
+
+               if (aes_wrap(k, k_len, p_len / 8, p, result)) {
+                       printf("aes_wrap() failed\n");
+                       ret++;
+                       continue;
+               }
+
+               if (os_memcmp(c, result, c_len) == 0) {
+                       printf("OK\n");
+                       ok++;
+               } else {
+                       printf("FAIL\n");
+                       ret++;
+               }
        }
-       if (memcmp(result, plain, 16) != 0) {
-               printf("AES-UNWRAP-128-128 failed\n");
-               ret++;
-               for (i = 0; i < 16; i++)
-                       printf(" %02x", result[i]);
-               printf("\n");
+
+       fclose(f);
+
+       if (ret)
+               printf("Test case failed\n");
+       else
+               printf("%d test vectors OK\n", ok);
+
+       return ret;
+}
+
+
+static int test_nist_key_wrap_ad(const char *fname)
+{
+       FILE *f;
+       int ret = 0;
+       char buf[15000], *pos, *pos2;
+       u8 bin[2000], k[32], p[1024], c[1024 + 8], result[1024 + 8];
+       size_t bin_len, k_len = 0, p_len = 0, c_len = 0;
+       int ok = 0;
+       int fail;
+
+       printf("NIST KW AD tests from %s\n", fname);
+
+       f = fopen(fname, "r");
+       if (f == NULL) {
+               printf("%s does not exist - cannot validate test vectors\n",
+                      fname);
+               return 1;
        }
 
-       test_aes_perf();
+       while (fgets(buf, sizeof(buf), f)) {
+               if (buf[0] == '#')
+                       continue;
+               fail = 0;
+               pos = os_strchr(buf, '=');
+               if (pos == NULL) {
+                       if (os_strncmp(buf, "FAIL", 4) == 0) {
+                               fail = 1;
+                               goto skip_val_parse;
+                       }
+                       continue;
+               }
+               pos2 = pos - 1;
+               while (pos2 >= buf && *pos2 == ' ')
+                       *pos2-- = '\0';
+               *pos++ = '\0';
+               while (*pos == ' ')
+                       *pos++ = '\0';
+               pos2 = os_strchr(pos, '\r');
+               if (!pos2)
+                       pos2 = os_strchr(pos, '\n');
+               if (pos2)
+                       *pos2 = '\0';
+               else
+                       pos2 = pos + os_strlen(pos);
+
+               if (buf[0] == '[') {
+                       printf("%s = %s\n", buf, pos);
+                       continue;
+               }
 
-       for (i = 0; i < sizeof(test_vectors) / sizeof(test_vectors[0]); i++) {
-               tv = &test_vectors[i];
-               if (omac1_aes_128(tv->k, tv->msg, tv->msg_len, result) ||
-                   memcmp(result, tv->tag, 16) != 0) {
-                       printf("OMAC1-AES-128 test vector %d failed\n", i);
-                       ret++;
+               if (os_strcmp(buf, "COUNT") == 0) {
+                       printf("Test %s - ", pos);
+                       continue;
                }
 
-               if (tv->msg_len > 1) {
-                       const u8 *addr[2];
-                       size_t len[2];
+               bin_len = os_strlen(pos);
+               if (bin_len > sizeof(bin) * 2) {
+                       printf("Too long binary data (%s)\n", buf);
+                       return 1;
+               }
+               if (bin_len & 0x01) {
+                       printf("Odd number of hexstring values (%s)\n",
+                               buf);
+                       return 1;
+               }
+               bin_len /= 2;
+               if (hexstr2bin(pos, bin, bin_len) < 0) {
+                       printf("Invalid hex string '%s' (%s)\n", pos, buf);
+                       return 1;
+               }
 
-                       addr[0] = tv->msg;
-                       len[0] = 1;
-                       addr[1] = tv->msg + 1;
-                       len[1] = tv->msg_len - 1;
+               if (os_strcmp(buf, "K") == 0) {
+                       if (bin_len > sizeof(k)) {
+                               printf("Too long K (%u)\n", (unsigned) bin_len);
+                               return 1;
+                       }
+                       os_memcpy(k, bin, bin_len);
+                       k_len = bin_len;
+                       continue;
+               }
 
-                       if (omac1_aes_128_vector(tv->k, 2, addr, len,
-                                                result) ||
-                           memcmp(result, tv->tag, 16) != 0) {
-                               printf("OMAC1-AES-128(vector) test vector %d "
-                                      "failed\n", i);
-                               ret++;
+               if (os_strcmp(buf, "C") == 0) {
+                       if (bin_len > sizeof(c)) {
+                               printf("Too long C (%u)\n", (unsigned) bin_len);
+                               return 1;
+                       }
+                       os_memcpy(c, bin, bin_len);
+                       c_len = bin_len;
+                       continue;
+               }
+
+       skip_val_parse:
+               if (!fail) {
+                       if (os_strcmp(buf, "P") != 0) {
+                               printf("Unexpected field '%s'\n", buf);
+                               continue;
+                       }
+
+                       if (bin_len > sizeof(p)) {
+                               printf("Too long P (%u)\n", (unsigned) bin_len);
+                               return 1;
+                       }
+                       os_memcpy(p, bin, bin_len);
+                       p_len = bin_len;
+
+                       if (p_len % 8 != 0 || c_len % 8 != 0 ||
+                           c_len - p_len != 8) {
+                               printf("invalid parameter length (p_len=%u c_len=%u)\n",
+                                      (unsigned) p_len, (unsigned) c_len);
+                               continue;
                        }
                }
+
+               if (aes_unwrap(k, k_len, (c_len / 8) - 1, c, result)) {
+                       if (fail) {
+                               printf("OK (fail reported)\n");
+                               ok++;
+                               continue;
+                       }
+                       printf("aes_unwrap() failed\n");
+                       ret++;
+                       continue;
+               }
+
+               if (fail) {
+                       printf("FAIL (mismatch not reported)\n");
+                       ret++;
+               } else if (os_memcmp(p, result, p_len) == 0) {
+                       printf("OK\n");
+                       ok++;
+               } else {
+                       printf("FAIL\n");
+                       ret++;
+               }
        }
 
-       ret += test_eax();
+       fclose(f);
 
-       ret += test_cbc();
+       if (ret)
+               printf("Test case failed\n");
+       else
+               printf("%d test vectors OK\n", ok);
+
+       return ret;
+}
+
+
+int main(int argc, char *argv[])
+{
+       int ret = 0;
+
+       if (argc >= 3 && os_strcmp(argv[1], "NIST-KW-AE") == 0)
+               ret += test_nist_key_wrap_ae(argv[2]);
+       else if (argc >= 3 && os_strcmp(argv[1], "NIST-KW-AD") == 0)
+               ret += test_nist_key_wrap_ad(argv[2]);
+
+       test_aes_perf();
 
        ret += test_gcm();
 
index 7b6f7be..c6957fd 100644 (file)
@@ -11,8 +11,6 @@
 #include "common.h"
 #include "tls/asn1.h"
 
-extern int wpa_debug_level;
-
 
 static const char * asn1_class_str(int class)
 {
index 2f41500..a72e56f 100644 (file)
@@ -12,9 +12,6 @@
 #include "common.h"
 #include "crypto/tls.h"
 
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-
 
 static void https_tls_event_cb(void *ctx, enum tls_event ev,
                               union tls_event_data *data)
index 903fd10..e3e63ed 100644 (file)
@@ -61,7 +61,7 @@ int main(int argc, char *argv[])
        size_t len[2];
        int errors = 0;
 
-       for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+       for (i = 0; i < ARRAY_SIZE(tests); i++) {
                printf("MD4 test case %d:", i);
 
                addr[0] = (u8 *) tests[i].data;
index 5ae0671..b5246a4 100644 (file)
@@ -61,7 +61,7 @@ int main(int argc, char *argv[])
        size_t len[2];
        int errors = 0;
 
-       for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
+       for (i = 0; i < ARRAY_SIZE(tests); i++) {
                printf("MD5 test case %d:", i);
 
                addr[0] = (u8 *) tests[i].data;
index b41e1a7..7c4be09 100644 (file)
@@ -5,9 +5,6 @@
 #include "crypto/milenage.h"
 
 
-extern int wpa_debug_level;
-
-
 /**
  * milenage_opc - Determine OPc from OP and K
  * @op: OP = 128-bit operator variant algorithm configuration field
@@ -249,7 +246,7 @@ static const struct gsm_milenage_test_set gsm_test_sets[] =
        }
 };
 
-#define NUM_GSM_TESTS (sizeof(gsm_test_sets) / sizeof(gsm_test_sets[0]))
+#define NUM_GSM_TESTS ARRAY_SIZE(gsm_test_sets)
 
 
 struct milenage_test_set {
@@ -693,7 +690,7 @@ static const struct milenage_test_set test_sets[] =
        }
 };
 
-#define NUM_TESTS (sizeof(test_sets) / sizeof(test_sets[0]))
+#define NUM_TESTS ARRAY_SIZE(test_sets)
 
 
 int main(int argc, char *argv[])
index 6ac2ef3..99f5592 100644 (file)
@@ -198,7 +198,7 @@ static const struct rc4_test_vector tests[] = {
        }
 };
 
-#define NUM_TESTS (sizeof(tests) / sizeof(tests[0]))
+#define NUM_TESTS ARRAY_SIZE(tests)
 
 
 static int run_test(unsigned int i, const u8 *key, size_t key_len,
index 4141c49..3269d4d 100644 (file)
 
 #include "common.h"
 #include "crypto/crypto.h"
-#include "crypto/md5.h"
-#include "crypto/sha1.h"
 
 
-static int test_eap_fast(void)
+static int cavp_shavs(const char *fname)
 {
-       /* RFC 4851, Appendix B.1 */
-       const u8 pac_key[] = {
-               0x0B, 0x97, 0x39, 0x0F, 0x37, 0x51, 0x78, 0x09,
-               0x81, 0x1E, 0xFD, 0x9C, 0x6E, 0x65, 0x94, 0x2B,
-               0x63, 0x2C, 0xE9, 0x53, 0x89, 0x38, 0x08, 0xBA,
-               0x36, 0x0B, 0x03, 0x7C, 0xD1, 0x85, 0xE4, 0x14
-       };
-       const u8 seed[] = {
-               0x3F, 0xFB, 0x11, 0xC4, 0x6C, 0xBF, 0xA5, 0x7A,
-               0x54, 0x40, 0xDA, 0xE8, 0x22, 0xD3, 0x11, 0xD3,
-               0xF7, 0x6D, 0xE4, 0x1D, 0xD9, 0x33, 0xE5, 0x93,
-               0x70, 0x97, 0xEB, 0xA9, 0xB3, 0x66, 0xF4, 0x2A,
-               0x00, 0x00, 0x00, 0x02, 0x6A, 0x66, 0x43, 0x2A,
-               0x8D, 0x14, 0x43, 0x2C, 0xEC, 0x58, 0x2D, 0x2F,
-               0xC7, 0x9C, 0x33, 0x64, 0xBA, 0x04, 0xAD, 0x3A,
-               0x52, 0x54, 0xD6, 0xA5, 0x79, 0xAD, 0x1E, 0x00
-       };
-       const u8 master_secret[] = {
-               0x4A, 0x1A, 0x51, 0x2C, 0x01, 0x60, 0xBC, 0x02,
-               0x3C, 0xCF, 0xBC, 0x83, 0x3F, 0x03, 0xBC, 0x64,
-               0x88, 0xC1, 0x31, 0x2F, 0x0B, 0xA9, 0xA2, 0x77,
-               0x16, 0xA8, 0xD8, 0xE8, 0xBD, 0xC9, 0xD2, 0x29,
-               0x38, 0x4B, 0x7A, 0x85, 0xBE, 0x16, 0x4D, 0x27,
-               0x33, 0xD5, 0x24, 0x79, 0x87, 0xB1, 0xC5, 0xA2  
-       };
-       const u8 key_block[] = {
-               0x59, 0x59, 0xBE, 0x8E, 0x41, 0x3A, 0x77, 0x74,
-               0x8B, 0xB2, 0xE5, 0xD3, 0x60, 0xAC, 0x4D, 0x35,
-               0xDF, 0xFB, 0xC8, 0x1E, 0x9C, 0x24, 0x9C, 0x8B,
-               0x0E, 0xC3, 0x1D, 0x72, 0xC8, 0x84, 0x9D, 0x57,
-               0x48, 0x51, 0x2E, 0x45, 0x97, 0x6C, 0x88, 0x70,
-               0xBE, 0x5F, 0x01, 0xD3, 0x64, 0xE7, 0x4C, 0xBB,
-               0x11, 0x24, 0xE3, 0x49, 0xE2, 0x3B, 0xCD, 0xEF,
-               0x7A, 0xB3, 0x05, 0x39, 0x5D, 0x64, 0x8A, 0x44,
-               0x11, 0xB6, 0x69, 0x88, 0x34, 0x2E, 0x8E, 0x29,
-               0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05,
-               0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96,
-               0x8F, 0x0B, 0x5E, 0x06, 0x46, 0x7A, 0x44, 0x84,
-               0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98,
-               0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71
-       };
-       const u8 sks[] = {
-               0xD6, 0x4B, 0x7D, 0x72, 0x17, 0x59, 0x28, 0x05,
-               0xAF, 0xF9, 0xB7, 0xFF, 0x66, 0x6D, 0xA1, 0x96,
-               0x8F, 0x0B, 0x5E, 0x06, 0x46, 0x7A, 0x44, 0x84,
-               0x64, 0xC1, 0xC8, 0x0C, 0x96, 0x44, 0x09, 0x98,
-               0xFF, 0x92, 0xA8, 0xB4, 0xC6, 0x42, 0x28, 0x71
-       };
-       const u8 isk[] = {
-               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-               0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-       };
-       const u8 imck[] = {
-               0x16, 0x15, 0x3C, 0x3F, 0x21, 0x55, 0xEF, 0xD9,
-               0x7F, 0x34, 0xAE, 0xC8, 0x1A, 0x4E, 0x66, 0x80,
-               0x4C, 0xC3, 0x76, 0xF2, 0x8A, 0xA9, 0x6F, 0x96,
-               0xC2, 0x54, 0x5F, 0x8C, 0xAB, 0x65, 0x02, 0xE1,
-               0x18, 0x40, 0x7B, 0x56, 0xBE, 0xEA, 0xA7, 0xC5,
-               0x76, 0x5D, 0x8F, 0x0B, 0xC5, 0x07, 0xC6, 0xB9,
-               0x04, 0xD0, 0x69, 0x56, 0x72, 0x8B, 0x6B, 0xB8,
-               0x15, 0xEC, 0x57, 0x7B
-       };
-       const u8 msk[] = {
-               0x4D, 0x83, 0xA9, 0xBE, 0x6F, 0x8A, 0x74, 0xED,
-               0x6A, 0x02, 0x66, 0x0A, 0x63, 0x4D, 0x2C, 0x33,
-               0xC2, 0xDA, 0x60, 0x15, 0xC6, 0x37, 0x04, 0x51,
-               0x90, 0x38, 0x63, 0xDA, 0x54, 0x3E, 0x14, 0xB9,
-               0x27, 0x99, 0x18, 0x1E, 0x07, 0xBF, 0x0F, 0x5A,
-               0x5E, 0x3C, 0x32, 0x93, 0x80, 0x8C, 0x6C, 0x49,
-               0x67, 0xED, 0x24, 0xFE, 0x45, 0x40, 0xA0, 0x59,
-               0x5E, 0x37, 0xC2, 0xE9, 0xD0, 0x5D, 0x0A, 0xE3
-       };
-       const u8 emsk[] = {
-               0x3A, 0xD4, 0xAB, 0xDB, 0x76, 0xB2, 0x7F, 0x3B,
-               0xEA, 0x32, 0x2C, 0x2B, 0x74, 0xF4, 0x28, 0x55,
-               0xEF, 0x2D, 0xBA, 0x78, 0xC9, 0x57, 0x2F, 0x0D,
-               0x06, 0xCD, 0x51, 0x7C, 0x20, 0x93, 0x98, 0xA9,
-               0x76, 0xEA, 0x70, 0x21, 0xD7, 0x0E, 0x25, 0x54,
-               0x97, 0xED, 0xB2, 0x8A, 0xF6, 0xED, 0xFD, 0x0A,
-               0x2A, 0xE7, 0xA1, 0x58, 0x90, 0x10, 0x50, 0x44,
-               0xB3, 0x82, 0x85, 0xDB, 0x06, 0x14, 0xD2, 0xF9
-       };
-       /* RFC 4851, Appendix B.2 */
-       u8 tlv[] = {
-               0x80, 0x0C, 0x00, 0x38, 0x00, 0x01, 0x01, 0x00,
-               0xD8, 0x6A, 0x8C, 0x68, 0x3C, 0x32, 0x31, 0xA8,
-               0x56, 0x63, 0xB6, 0x40, 0x21, 0xFE, 0x21, 0x14,
-               0x4E, 0xE7, 0x54, 0x20, 0x79, 0x2D, 0x42, 0x62,
-               0xC9, 0xBF, 0x53, 0x7F, 0x54, 0xFD, 0xAC, 0x58,
-               0x43, 0x24, 0x6E, 0x30, 0x92, 0x17, 0x6D, 0xCF,
-               0xE6, 0xE0, 0x69, 0xEB, 0x33, 0x61, 0x6A, 0xCC,
-               0x05, 0xC5, 0x5B, 0xB7
-       };
-       const u8 compound_mac[] = {
-               0x43, 0x24, 0x6E, 0x30, 0x92, 0x17, 0x6D, 0xCF,
-               0xE6, 0xE0, 0x69, 0xEB, 0x33, 0x61, 0x6A, 0xCC,
-               0x05, 0xC5, 0x5B, 0xB7
-       };
-       u8 buf[512];
-       const u8 *simck, *cmk;
-       int errors = 0;
-
-       printf("EAP-FAST test cases\n");
-
-       printf("- T-PRF (SHA1) test case / master_secret\n");
-       sha1_t_prf(pac_key, sizeof(pac_key), "PAC to master secret label hash",
-                  seed, sizeof(seed), buf, sizeof(master_secret));
-       if (memcmp(master_secret, buf, sizeof(master_secret)) != 0) {
-               printf("T-PRF test - FAILED!\n");
-               errors++;
-       }
-
-       printf("- PRF (TLS, SHA1/MD5) test case / key_block\n");
-       if (tls_prf_sha1_md5(master_secret, sizeof(master_secret),
-                            "key expansion", seed, sizeof(seed),
-                            buf, sizeof(key_block)) ||
-           memcmp(key_block, buf, sizeof(key_block)) != 0) {
-               printf("PRF test - FAILED!\n");
-               errors++;
-       }
-
-       printf("- T-PRF (SHA1) test case / IMCK\n");
-       sha1_t_prf(sks, sizeof(sks), "Inner Methods Compound Keys",
-                  isk, sizeof(isk), buf, sizeof(imck));
-       if (memcmp(imck, buf, sizeof(imck)) != 0) {
-               printf("T-PRF test - FAILED!\n");
-               errors++;
+       FILE *f;
+       int ret = 0;
+       char buf[15000], *pos, *pos2;
+       u8 msg[6400];
+       int msg_len = 0, tmp_len;
+       u8 md[20], hash[20];
+       int ok = 0;
+
+       printf("CAVP SHAVS test vectors from %s\n", fname);
+
+       f = fopen(fname, "r");
+       if (f == NULL) {
+               printf("%s does not exist - cannot validate CAVP SHAVS test vectors\n",
+                       fname);
+               return 0;
        }
 
-       simck = imck;
-       cmk = imck + 40;
-
-       printf("- T-PRF (SHA1) test case / MSK\n");
-       sha1_t_prf(simck, 40, "Session Key Generating Function",
-                  (u8 *) "", 0, buf, sizeof(msk));
-       if (memcmp(msk, buf, sizeof(msk)) != 0) {
-               printf("T-PRF test - FAILED!\n");
-               errors++;
+       while (fgets(buf, sizeof(buf), f)) {
+               pos = os_strchr(buf, '=');
+               if (pos == NULL)
+                       continue;
+               pos2 = pos - 1;
+               while (pos2 >= buf && *pos2 == ' ')
+                       *pos2-- = '\0';
+               *pos++ = '\0';
+               while (*pos == ' ')
+                       *pos++ = '\0';
+               pos2 = os_strchr(pos, '\r');
+               if (!pos2)
+                       pos2 = os_strchr(pos, '\n');
+               if (pos2)
+                       *pos2 = '\0';
+               else
+                       pos2 = pos + os_strlen(pos);
+
+               if (os_strcmp(buf, "Len") == 0) {
+                       msg_len = atoi(pos);
+               } else if (os_strcmp(buf, "Msg") == 0) {
+                       tmp_len = os_strlen(pos);
+                       if (msg_len == 0 && tmp_len == 2)
+                               tmp_len = 0;
+                       if (msg_len != tmp_len * 4) {
+                               printf("Unexpected Msg length (msg_len=%u tmp_len=%u, Msg='%s'\n",
+                                      msg_len, tmp_len, pos);
+                               ret++;
+                               break;
+                       }
+
+                       if (hexstr2bin(pos, msg, msg_len / 8) < 0) {
+                               printf("Invalid hex string '%s'\n", pos);
+                               ret++;
+                               break;
+                       }
+               } else if (os_strcmp(buf, "MD") == 0) {
+                       const u8 *addr[1];
+                       size_t len[1];
+
+                       tmp_len = os_strlen(pos);
+                       if (tmp_len != 2 * 20) {
+                               printf("Unexpected MD length (MD='%s'\n",
+                                      pos);
+                               ret++;
+                               break;
+                       }
+
+                       if (hexstr2bin(pos, md, 20) < 0) {
+                               printf("Invalid hex string '%s'\n", pos);
+                               ret++;
+                               break;
+                       }
+
+                       addr[0] = msg;
+                       len[0] = msg_len / 8;
+                       if (sha1_vector(1, addr, len, hash) < 0 ||
+                           os_memcmp(hash, md, 20) != 0)
+                               ret++;
+                       else
+                               ok++;
+               }
        }
 
-       printf("- T-PRF (SHA1) test case / EMSK\n");
-       sha1_t_prf(simck, 40, "Extended Session Key Generating Function",
-                  (u8 *) "", 0, buf, sizeof(msk));
-       if (memcmp(emsk, buf, sizeof(emsk)) != 0) {
-               printf("T-PRF test - FAILED!\n");
-               errors++;
-       }
+       fclose(f);
 
-       printf("- Compound MAC test case\n");
-       memset(tlv + sizeof(tlv) - 20, 0, 20);
-       hmac_sha1(cmk, 20, tlv, sizeof(tlv), tlv + sizeof(tlv) - 20);
-       if (memcmp(tlv + sizeof(tlv) - 20, compound_mac, sizeof(compound_mac))
-           != 0) {
-               printf("Compound MAC test - FAILED!\n");
-               errors++;
-       }
+       if (ret)
+               printf("Test case failed\n");
+       else
+               printf("%d test vectors OK\n", ok);
 
-       return errors;
+       return ret;
 }
 
 
-static u8 key0[] =
-{
-       0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
-       0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
-       0x0b, 0x0b, 0x0b, 0x0b
-};
-static u8 data0[] = "Hi There";
-static u8 prf0[] =
-{
-       0xbc, 0xd4, 0xc6, 0x50, 0xb3, 0x0b, 0x96, 0x84,
-       0x95, 0x18, 0x29, 0xe0, 0xd7, 0x5f, 0x9d, 0x54,
-       0xb8, 0x62, 0x17, 0x5e, 0xd9, 0xf0, 0x06, 0x06,
-       0xe1, 0x7d, 0x8d, 0xa3, 0x54, 0x02, 0xff, 0xee,
-       0x75, 0xdf, 0x78, 0xc3, 0xd3, 0x1e, 0x0f, 0x88,
-       0x9f, 0x01, 0x21, 0x20, 0xc0, 0x86, 0x2b, 0xeb,
-       0x67, 0x75, 0x3e, 0x74, 0x39, 0xae, 0x24, 0x2e,
-       0xdb, 0x83, 0x73, 0x69, 0x83, 0x56, 0xcf, 0x5a
-};
-
-static u8 key1[] = "Jefe";
-static u8 data1[] = "what do ya want for nothing?";
-static u8 prf1[] =
-{
-       0x51, 0xf4, 0xde, 0x5b, 0x33, 0xf2, 0x49, 0xad,
-       0xf8, 0x1a, 0xeb, 0x71, 0x3a, 0x3c, 0x20, 0xf4,
-       0xfe, 0x63, 0x14, 0x46, 0xfa, 0xbd, 0xfa, 0x58,
-       0x24, 0x47, 0x59, 0xae, 0x58, 0xef, 0x90, 0x09,
-       0xa9, 0x9a, 0xbf, 0x4e, 0xac, 0x2c, 0xa5, 0xfa,
-       0x87, 0xe6, 0x92, 0xc4, 0x40, 0xeb, 0x40, 0x02,
-       0x3e, 0x7b, 0xab, 0xb2, 0x06, 0xd6, 0x1d, 0xe7,
-       0xb9, 0x2f, 0x41, 0x52, 0x90, 0x92, 0xb8, 0xfc
-};
-
-
-static u8 key2[] =
-{
-       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-       0xaa, 0xaa, 0xaa, 0xaa
-};
-static u8 data2[] =
-{
-       0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
-       0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
-       0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
-       0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
-       0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
-       0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
-       0xdd, 0xdd
-};
-static u8 prf2[] =
-{
-       0xe1, 0xac, 0x54, 0x6e, 0xc4, 0xcb, 0x63, 0x6f,
-       0x99, 0x76, 0x48, 0x7b, 0xe5, 0xc8, 0x6b, 0xe1,
-       0x7a, 0x02, 0x52, 0xca, 0x5d, 0x8d, 0x8d, 0xf1,
-       0x2c, 0xfb, 0x04, 0x73, 0x52, 0x52, 0x49, 0xce,
-       0x9d, 0xd8, 0xd1, 0x77, 0xea, 0xd7, 0x10, 0xbc,
-       0x9b, 0x59, 0x05, 0x47, 0x23, 0x91, 0x07, 0xae,
-       0xf7, 0xb4, 0xab, 0xd4, 0x3d, 0x87, 0xf0, 0xa6,
-       0x8f, 0x1c, 0xbd, 0x9e, 0x2b, 0x6f, 0x76, 0x07
-};
-
-
-struct passphrase_test {
-       char *passphrase;
-       char *ssid;
-       char psk[32];
-};
-
-static struct passphrase_test passphrase_tests[] =
-{
-       {
-               "password",
-               "IEEE",
-               {
-                       0xf4, 0x2c, 0x6f, 0xc5, 0x2d, 0xf0, 0xeb, 0xef,
-                       0x9e, 0xbb, 0x4b, 0x90, 0xb3, 0x8a, 0x5f, 0x90,
-                       0x2e, 0x83, 0xfe, 0x1b, 0x13, 0x5a, 0x70, 0xe2,
-                       0x3a, 0xed, 0x76, 0x2e, 0x97, 0x10, 0xa1, 0x2e
-               }
-       },
-       {
-               "ThisIsAPassword",
-               "ThisIsASSID",
-               {
-                       0x0d, 0xc0, 0xd6, 0xeb, 0x90, 0x55, 0x5e, 0xd6,
-                       0x41, 0x97, 0x56, 0xb9, 0xa1, 0x5e, 0xc3, 0xe3,
-                       0x20, 0x9b, 0x63, 0xdf, 0x70, 0x7d, 0xd5, 0x08,
-                       0xd1, 0x45, 0x81, 0xf8, 0x98, 0x27, 0x21, 0xaf
-               }
-       },
-       {
-               "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
-               "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ",
-               {
-                       0xbe, 0xcb, 0x93, 0x86, 0x6b, 0xb8, 0xc3, 0x83,
-                       0x2c, 0xb7, 0x77, 0xc2, 0xf5, 0x59, 0x80, 0x7c,
-                       0x8c, 0x59, 0xaf, 0xcb, 0x6e, 0xae, 0x73, 0x48,
-                       0x85, 0x00, 0x13, 0x00, 0xa9, 0x81, 0xcc, 0x62
-               }
-       },
-};
-
-#define NUM_PASSPHRASE_TESTS \
-(sizeof(passphrase_tests) / sizeof(passphrase_tests[0]))
-
-
-struct rfc6070_test {
-       char *p;
-       char *s;
-       int c;
-       char dk[32];
-       size_t dk_len;
-};
-
-static struct rfc6070_test rfc6070_tests[] =
-{
-       {
-               "password",
-               "salt",
-               1,
-               {
-                       0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71,
-                       0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06,
-                       0x2f, 0xe0, 0x37, 0xa6
-               },
-               20
-       },
-       {
-               "password",
-               "salt",
-               2,
-               {
-                       0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c,
-                       0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0,
-                       0xd8, 0xde, 0x89, 0x57
-               },
-               20
-       },
-       {
-               "password",
-               "salt",
-               4096,
-               {
-                       0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a,
-                       0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0,
-                       0x65, 0xa4, 0x29, 0xc1
-               },
-               20
-       },
-#if 0 /* This takes quite long to derive.. */
-       {
-               "password",
-               "salt",
-               16777216,
-               {
-                       0xee, 0xfe, 0x3d, 0x61, 0xcd, 0x4d, 0xa4, 0xe4,
-                       0xe9, 0x94, 0x5b, 0x3d, 0x6b, 0xa2, 0x15, 0x8c,
-                       0x26, 0x34, 0xe9, 0x84
-               },
-               20
-       },
-#endif
-       {
-               "passwordPASSWORDpassword",
-               "saltSALTsaltSALTsaltSALTsaltSALTsalt",
-               4096,
-               {
-                       0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b,
-                       0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a,
-                       0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70,
-                       0x38
-               },
-               25
-       },
-#if 0 /* \0 not currently supported in passphrase parameters.. */
-       {
-               "pass\0word",
-               "sa\0lt",
-               4096,
-               {
-                       0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d,
-                       0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3
-               },
-               16
-       },
-#endif
-};
-
-#define NUM_RFC6070_TESTS \
-(sizeof(rfc6070_tests) / sizeof(rfc6070_tests[0]))
-
-
 int main(int argc, char *argv[])
 {
-       u8 res[512];
        int ret = 0;
-       unsigned int i;
-
-       printf("PRF-SHA1 test cases:\n");
 
-       sha1_prf(key0, sizeof(key0), "prefix", data0, sizeof(data0) - 1,
-                res, sizeof(prf0));
-       if (memcmp(res, prf0, sizeof(prf0)) == 0)
-               printf("Test case 0 - OK\n");
-       else {
-               printf("Test case 0 - FAILED!\n");
+       if (cavp_shavs("CAVP/SHA1ShortMsg.rsp"))
                ret++;
-       }
-
-       sha1_prf(key1, sizeof(key1) - 1, "prefix", data1, sizeof(data1) - 1,
-                res, sizeof(prf1));
-       if (memcmp(res, prf1, sizeof(prf1)) == 0)
-               printf("Test case 1 - OK\n");
-       else {
-               printf("Test case 1 - FAILED!\n");
-               ret++;
-       }
-
-       sha1_prf(key2, sizeof(key2), "prefix", data2, sizeof(data2),
-                res, sizeof(prf2));
-       if (memcmp(res, prf2, sizeof(prf2)) == 0)
-               printf("Test case 2 - OK\n");
-       else {
-               printf("Test case 2 - FAILED!\n");
+       if (cavp_shavs("CAVP/SHA1LongMsg.rsp"))
                ret++;
-       }
-
-       ret += test_eap_fast();
-
-       printf("PBKDF2-SHA1 Passphrase test cases:\n");
-       for (i = 0; i < NUM_PASSPHRASE_TESTS; i++) {
-               u8 psk[32];
-               struct passphrase_test *test = &passphrase_tests[i];
-               pbkdf2_sha1(test->passphrase,
-                           test->ssid, strlen(test->ssid),
-                           4096, psk, 32);
-               if (memcmp(psk, test->psk, 32) == 0)
-                       printf("Test case %d - OK\n", i);
-               else {
-                       printf("Test case %d - FAILED!\n", i);
-                       ret++;
-               }
-       }
-
-       printf("PBKDF2-SHA1 test cases (RFC 6070):\n");
-       for (i = 0; i < NUM_RFC6070_TESTS; i++) {
-               u8 dk[25];
-               struct rfc6070_test *test = &rfc6070_tests[i];
-               pbkdf2_sha1(test->p, test->s, strlen(test->s), test->c,
-                           dk, test->dk_len);
-               if (memcmp(dk, test->dk, test->dk_len) == 0)
-                       printf("Test case %d - OK\n", i);
-               else {
-                       printf("Test case %d - FAILED!\n", i);
-                       ret++;
-               }
-       }
 
        return ret;
 }
index 9d45439..741351a 100644 (file)
 #include "includes.h"
 
 #include "common.h"
-#include "crypto/sha256.h"
 #include "crypto/crypto.h"
 
-struct {
-       char *data;
-       u8 hash[32];
-} tests[] = {
-       {
-               "abc",
-               {
-                       0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea,
-                       0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
-                       0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c,
-                       0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad
-               }
-       },
-       {
-               "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
-               {
-                       0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8,
-                       0xe5, 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39,
-                       0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67,
-                       0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1
-               }
-       }
-};
-
-struct hmac_test {
-       u8 key[80];
-       size_t key_len;
-       u8 data[128];
-       size_t data_len;
-       u8 hash[32];
-} hmac_tests[] = {
-       /* draft-ietf-ipsec-ciph-sha-256-01.txt */
-       {
-               {
-                       0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
-                       0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
-                       0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
-                       0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20
-               },
-               32,
-               "abc", 3,
-               {
-                       0xa2, 0x1b, 0x1f, 0x5d, 0x4c, 0xf4, 0xf7, 0x3a,
-                       0x4d, 0xd9, 0x39, 0x75, 0x0f, 0x7a, 0x06, 0x6a,
-                       0x7f, 0x98, 0xcc, 0x13, 0x1c, 0xb1, 0x6a, 0x66,
-                       0x92, 0x75, 0x90, 0x21, 0xcf, 0xab, 0x81, 0x81
-               }
-       },
-       {
-               {
-                       0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
-                       0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
-                       0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
-                       0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20
-               },
-               32,
-               "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
-               56,
-               {
-                       0x10, 0x4f, 0xdc, 0x12, 0x57, 0x32, 0x8f, 0x08,
-                       0x18, 0x4b, 0xa7, 0x31, 0x31, 0xc5, 0x3c, 0xae,
-                       0xe6, 0x98, 0xe3, 0x61, 0x19, 0x42, 0x11, 0x49,
-                       0xea, 0x8c, 0x71, 0x24, 0x56, 0x69, 0x7d, 0x30
-               }
-       },
-       {
-               {
-                       0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
-                       0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
-                       0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
-                       0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20
-               },
-               32,
-               "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
-               "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq",
-               112,
-               {
-                       0x47, 0x03, 0x05, 0xfc, 0x7e, 0x40, 0xfe, 0x34,
-                       0xd3, 0xee, 0xb3, 0xe7, 0x73, 0xd9, 0x5a, 0xab,
-                       0x73, 0xac, 0xf0, 0xfd, 0x06, 0x04, 0x47, 0xa5,
-                       0xeb, 0x45, 0x95, 0xbf, 0x33, 0xa9, 0xd1, 0xa3
-               }
-       },
-       {
-               {
-                       0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
-                       0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
-                       0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
-                       0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
-               },
-               32,
-               "Hi There",
-               8,
-               {
-                       0x19, 0x8a, 0x60, 0x7e, 0xb4, 0x4b, 0xfb, 0xc6,
-                       0x99, 0x03, 0xa0, 0xf1, 0xcf, 0x2b, 0xbd, 0xc5,
-                       0xba, 0x0a, 0xa3, 0xf3, 0xd9, 0xae, 0x3c, 0x1c,
-                       0x7a, 0x3b, 0x16, 0x96, 0xa0, 0xb6, 0x8c, 0xf7
-               }
-       },
-       {
-               "Jefe",
-               4,
-               "what do ya want for nothing?",
-               28,
-               {
-                       0x5b, 0xdc, 0xc1, 0x46, 0xbf, 0x60, 0x75, 0x4e,
-                       0x6a, 0x04, 0x24, 0x26, 0x08, 0x95, 0x75, 0xc7,
-                       0x5a, 0x00, 0x3f, 0x08, 0x9d, 0x27, 0x39, 0x83,
-                       0x9d, 0xec, 0x58, 0xb9, 0x64, 0xec, 0x38, 0x43
-               }
-       },
-       {
-               {
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
-               },
-               32,
-               {
-                       0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
-                       0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
-                       0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
-                       0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
-                       0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
-                       0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd, 0xdd,
-                       0xdd, 0xdd
-               },
-               50,
-               {
-                       0xcd, 0xcb, 0x12, 0x20, 0xd1, 0xec, 0xcc, 0xea,
-                       0x91, 0xe5, 0x3a, 0xba, 0x30, 0x92, 0xf9, 0x62,
-                       0xe5, 0x49, 0xfe, 0x6c, 0xe9, 0xed, 0x7f, 0xdc,
-                       0x43, 0x19, 0x1f, 0xbd, 0xe4, 0x5c, 0x30, 0xb0
-               }
-       },
-       {
-               {
-                       0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
-                       0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
-                       0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
-                       0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
-                       0x21, 0x22, 0x23, 0x24, 0x25
-               },
-               37,
-               {
-                       0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
-                       0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
-                       0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
-                       0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
-                       0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
-                       0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
-                       0xcd, 0xcd
-               },
-               50,
-               {
-                       0xd4, 0x63, 0x3c, 0x17, 0xf6, 0xfb, 0x8d, 0x74,
-                       0x4c, 0x66, 0xde, 0xe0, 0xf8, 0xf0, 0x74, 0x55,
-                       0x6e, 0xc4, 0xaf, 0x55, 0xef, 0x07, 0x99, 0x85,
-                       0x41, 0x46, 0x8e, 0xb4, 0x9b, 0xd2, 0xe9, 0x17
-               }
-       },
-       {
-               {
-                       0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
-                       0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
-                       0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c,
-                       0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c
-               },
-               32,
-               "Test With Truncation",
-               20,
-               {
-                       0x75, 0x46, 0xaf, 0x01, 0x84, 0x1f, 0xc0, 0x9b,
-                       0x1a, 0xb9, 0xc3, 0x74, 0x9a, 0x5f, 0x1c, 0x17,
-                       0xd4, 0xf5, 0x89, 0x66, 0x8a, 0x58, 0x7b, 0x27,
-                       0x00, 0xa9, 0xc9, 0x7c, 0x11, 0x93, 0xcf, 0x42
-               }
-       },
-       {
-               {
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
-               },
-               80,
-               "Test Using Larger Than Block-Size Key - Hash Key First",
-               54,
-               {
-                       0x69, 0x53, 0x02, 0x5e, 0xd9, 0x6f, 0x0c, 0x09,
-                       0xf8, 0x0a, 0x96, 0xf7, 0x8e, 0x65, 0x38, 0xdb,
-                       0xe2, 0xe7, 0xb8, 0x20, 0xe3, 0xdd, 0x97, 0x0e,
-                       0x7d, 0xdd, 0x39, 0x09, 0x1b, 0x32, 0x35, 0x2f
-               }
-       },
-       {
-               {
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
-                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa
-               },
-               80,
-               "Test Using Larger Than Block-Size Key and Larger Than One "
-               "Block-Size Data",
-               73,
-               {
-                       0x63, 0x55, 0xac, 0x22, 0xe8, 0x90, 0xd0, 0xa3,
-                       0xc8, 0x48, 0x1a, 0x5c, 0xa4, 0x82, 0x5b, 0xc8,
-                       0x84, 0xd3, 0xe7, 0xa1, 0xff, 0x98, 0xa2, 0xfc,
-                       0x2a, 0xc7, 0xd8, 0xe0, 0x64, 0xc3, 0xb2, 0xe6
-               }
-       }
-};
 
-
-int main(int argc, char *argv[])
+static int cavp_shavs(const char *fname)
 {
+       FILE *f;
+       int ret = 0;
+       char buf[15000], *pos, *pos2;
+       u8 msg[6400];
+       int msg_len = 0, tmp_len;
+       u8 md[32], hash[32];
+       int ok = 0;
+
+       printf("CAVP SHAVS test vectors from %s\n", fname);
+
+       f = fopen(fname, "r");
+       if (f == NULL) {
+               printf("%s does not exist - cannot validate CAVP SHAVS test vectors\n",
+                       fname);
+               return 0;
+       }
 
-       unsigned int i;
-       u8 hash[32];
-       const u8 *addr[2];
-       size_t len[2];
-       int errors = 0;
-
-       for (i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
-               printf("SHA256 test case %d:", i + 1);
-
-               addr[0] = (u8 *) tests[i].data;
-               len[0] = strlen(tests[i].data);
-               sha256_vector(1, addr, len, hash);
-               if (memcmp(hash, tests[i].hash, 32) != 0) {
-                       printf(" FAIL");
-                       errors++;
-               } else
-                       printf(" OK");
-
-               if (len[0]) {
-                       addr[0] = (u8 *) tests[i].data;
-                       len[0] = 1;
-                       addr[1] = (u8 *) tests[i].data + 1;
-                       len[1] = strlen(tests[i].data) - 1;
-                       sha256_vector(2, addr, len, hash);
-                       if (memcmp(hash, tests[i].hash, 32) != 0) {
-                               printf(" FAIL");
-                               errors++;
-                       } else
-                               printf(" OK");
+       while (fgets(buf, sizeof(buf), f)) {
+               pos = os_strchr(buf, '=');
+               if (pos == NULL)
+                       continue;
+               pos2 = pos - 1;
+               while (pos2 >= buf && *pos2 == ' ')
+                       *pos2-- = '\0';
+               *pos++ = '\0';
+               while (*pos == ' ')
+                       *pos++ = '\0';
+               pos2 = os_strchr(pos, '\r');
+               if (!pos2)
+                       pos2 = os_strchr(pos, '\n');
+               if (pos2)
+                       *pos2 = '\0';
+               else
+                       pos2 = pos + os_strlen(pos);
+
+               if (os_strcmp(buf, "Len") == 0) {
+                       msg_len = atoi(pos);
+               } else if (os_strcmp(buf, "Msg") == 0) {
+                       tmp_len = os_strlen(pos);
+                       if (msg_len == 0 && tmp_len == 2)
+                               tmp_len = 0;
+                       if (msg_len != tmp_len * 4) {
+                               printf("Unexpected Msg length (msg_len=%u tmp_len=%u, Msg='%s'\n",
+                                      msg_len, tmp_len, pos);
+                               ret++;
+                               break;
+                       }
+
+                       if (hexstr2bin(pos, msg, msg_len / 8) < 0) {
+                               printf("Invalid hex string '%s'\n", pos);
+                               ret++;
+                               break;
+                       }
+               } else if (os_strcmp(buf, "MD") == 0) {
+                       const u8 *addr[1];
+                       size_t len[1];
+
+                       tmp_len = os_strlen(pos);
+                       if (tmp_len != 2 * 32) {
+                               printf("Unexpected MD length (MD='%s'\n",
+                                      pos);
+                               ret++;
+                               break;
+                       }
+
+                       if (hexstr2bin(pos, md, 32) < 0) {
+                               printf("Invalid hex string '%s'\n", pos);
+                               ret++;
+                               break;
+                       }
+
+                       addr[0] = msg;
+                       len[0] = msg_len / 8;
+                       if (sha256_vector(1, addr, len, hash) < 0 ||
+                           os_memcmp(hash, md, 32) != 0)
+                               ret++;
+                       else
+                               ok++;
                }
-
-               printf("\n");
        }
 
-       for (i = 0; i < sizeof(hmac_tests) / sizeof(hmac_tests[0]); i++) {
-               struct hmac_test *t = &hmac_tests[i];
-               printf("HMAC-SHA256 test case %d:", i + 1);
+       fclose(f);
 
-               hmac_sha256(t->key, t->key_len, t->data, t->data_len, hash);
-               if (memcmp(hash, t->hash, 32) != 0) {
-                       printf(" FAIL");
-                       errors++;
-               } else
-                       printf(" OK");
+       if (ret)
+               printf("Test case failed\n");
+       else
+               printf("%d test vectors OK\n", ok);
 
-               addr[0] = t->data;
-               len[0] = t->data_len;
-               hmac_sha256_vector(t->key, t->key_len, 1, addr, len, hash);
-               if (memcmp(hash, t->hash, 32) != 0) {
-                       printf(" FAIL");
-                       errors++;
-               } else
-                       printf(" OK");
+       return ret;
+}
 
-               if (len[0]) {
-                       addr[0] = t->data;
-                       len[0] = 1;
-                       addr[1] = t->data + 1;
-                       len[1] = t->data_len - 1;
-                       hmac_sha256_vector(t->key, t->key_len, 2, addr, len,
-                                          hash);
-                       if (memcmp(hash, t->hash, 32) != 0) {
-                               printf(" FAIL");
-                               errors++;
-                       } else
-                               printf(" OK");
-               }
 
-               printf("\n");
-       }
+int main(int argc, char *argv[])
+{
+       int errors = 0;
 
-       printf("Test IEEE 802.11r KDF\n");
-       sha256_prf((u8 *) "abc", 3, "KDF test", (u8 *) "data", 4,
-                  hash, sizeof(hash));
-       /* TODO: add proper test case for this */
+       if (cavp_shavs("CAVP/SHA256ShortMsg.rsp"))
+               errors++;
+       if (cavp_shavs("CAVP/SHA256LongMsg.rsp"))
+               errors++;
 
        return errors;
 }
index e92ea61..194f229 100644 (file)
@@ -11,8 +11,6 @@
 #include "common.h"
 #include "tls/x509v3.h"
 
-extern int wpa_debug_level;
-
 
 int main(int argc, char *argv[])
 {
index 1290b5c..bfb0698 100644 (file)
@@ -12,8 +12,6 @@
 #include "tls/asn1.h"
 #include "tls/x509v3.h"
 
-extern int wpa_debug_level;
-
 
 int main(int argc, char *argv[])
 {
old mode 100755 (executable)
new mode 100644 (file)
old mode 100755 (executable)
new mode 100644 (file)
index 572bd9d..ec34a8b
@@ -56,8 +56,17 @@ function run_test
                    OK=0
                fi
            else
-               echo "$NUM failed - expected validation failure; other type of error detected"
-               OK=0
+               if [ $RES -eq -1 ]; then
+                   if grep -q "Failed to parse X.509 certificate" $TMPOUT.$NUM; then
+                       OK=1
+                   else
+                       echo "$NUM failed - expected parsing failure; other type of error detected"
+                       OK=0
+                   fi
+               else
+                   echo "$NUM failed - expected validation failure; other type of error detected"
+                   OK=0
+               fi
            fi
        fi
     fi
@@ -153,6 +162,9 @@ run_test 4.8.18 0 UserNoticeQualifierTest18EE.crt PoliciesP12CACert.crt
 run_test 4.8.19 0 UserNoticeQualifierTest19EE.crt TrustAnchorRootCertificate.crt
 run_test 4.8.20 0 CPSPointerQualifierTest20EE.crt GoodCACert.crt
 
+run_test 4.16.1 0 ValidUnknownNotCriticalCertificateExtensionTest1EE.crt
+run_test 4.16.2 -1 InvalidUnknownCriticalCertificateExtensionTest2EE.crt
+
 if false; then
 # DSA tests
 run_test 4.1.4 0 ValidDSASignaturesTest4EE.crt DSACACert.crt
index 6594b34..320fdbb 100644 (file)
@@ -19,6 +19,8 @@ CFLAGS += -I.
 CFLAGS += -I../src
 CFLAGS += -I../src/utils
 
+# glibc < 2.17 needs -lrt for clock_gettime()
+LIBS += -lrt
 
 ifndef LDO
 LDO=$(CC)
@@ -30,6 +32,10 @@ ifeq ($(V), 1)
 Q=
 E=true
 endif
+ifeq ($(QUIET), 1)
+Q=@
+E=true
+endif
 
 %.o: %.c
        $(Q)$(CC) -c -o $@ $(CFLAGS) $<
@@ -42,6 +48,8 @@ OBJS_lib += ../src/crypto/libcrypto.a
 CFLAGS += -DCONFIG_PEERKEY
 CFLAGS += -DCONFIG_IEEE80211W
 CFLAGS += -DCONFIG_IEEE80211R
+CFLAGS += -DCONFIG_HS20
+CFLAGS += -DCONFIG_DEBUG_FILE
 
 OBJS += ../src/common/ieee802_11_common.o
 OBJS += ../src/common/wpa_common.o
@@ -68,6 +76,7 @@ OBJS += ctrl.o
 OBJS += inject.o
 OBJS += wep.o
 OBJS += bip.o
+OBJS += gcmp.o
 
 LIBS += -lpcap
 
@@ -111,7 +120,7 @@ wlantest: $(OBJS) $(LIBWLANTEST)
        $(LDO) $(LDFLAGS) -o wlantest $(OBJS) -L. -lwlantest $(LIBS)
 
 wlantest_cli: $(OBJS_cli) $(LIBWLANTEST)
-       $(LDO) $(LDFLAGS) -o wlantest_cli $(OBJS_cli) -L. -lwlantest
+       $(LDO) $(LDFLAGS) -o wlantest_cli $(OBJS_cli) -L. -lwlantest $(LIBS)
 
 test_vectors: $(TOBJS) $(LIBWLANTEST)
        $(LDO) $(LDFLAGS) -o test_vectors $(TOBJS) -L. -lwlantest $(LIBS)
index d9a52e9..bda8036 100644 (file)
@@ -14,8 +14,8 @@
 #include "wlantest.h"
 
 
-u8 * bip_protect(const u8 *igtk, u8 *frame, size_t len, u8 *ipn, int keyid,
-                size_t *prot_len)
+u8 * bip_protect(const u8 *igtk, size_t igtk_len, u8 *frame, size_t len,
+                u8 *ipn, int keyid, size_t *prot_len)
 {
        u8 *prot, *pos, *buf;
        u8 mic[16];
@@ -23,19 +23,19 @@ u8 * bip_protect(const u8 *igtk, u8 *frame, size_t len, u8 *ipn, int keyid,
        struct ieee80211_hdr *hdr;
        size_t plen;
 
-       plen = len + 18;
+       plen = len + igtk_len == 32 ? 26 : 18;
        prot = os_malloc(plen);
        if (prot == NULL)
                return NULL;
        os_memcpy(prot, frame, len);
        pos = prot + len;
        *pos++ = WLAN_EID_MMIE;
-       *pos++ = 16;
+       *pos++ = igtk_len == 32 ? 24 : 16;
        WPA_PUT_LE16(pos, keyid);
        pos += 2;
        os_memcpy(pos, ipn, 6);
        pos += 6;
-       os_memset(pos, 0, 8); /* MIC */
+       os_memset(pos, 0, igtk_len == 32 ? 16 : 8); /* MIC */
 
        buf = os_malloc(plen + 20 - 24);
        if (buf == NULL) {
@@ -59,8 +59,8 @@ u8 * bip_protect(const u8 *igtk, u8 *frame, size_t len, u8 *ipn, int keyid,
        }
        os_free(buf);
 
-       os_memcpy(pos, mic, 8);
-       wpa_hexdump(MSG_DEBUG, "BIP MMIE MIC", pos, 8);
+       os_memcpy(pos, mic, igtk_len == 32 ? 16 : 8);
+       wpa_hexdump(MSG_DEBUG, "BIP MMIE MIC", pos, igtk_len == 32 ? 16 : 8);
 
        *prot_len = plen;
        return prot;
index 98d98ef..61e2960 100644 (file)
@@ -154,6 +154,26 @@ void bss_update(struct wlantest *wt, struct wlantest_bss *bss,
                bss_add_pmk(wt, bss);
        }
 
+       if (elems->osen == NULL) {
+               if (bss->osenie[0]) {
+                       add_note(wt, MSG_INFO, "BSS " MACSTR
+                                " - OSEN IE removed", MAC2STR(bss->bssid));
+                       bss->rsnie[0] = 0;
+                       update = 1;
+               }
+       } else {
+               if (bss->osenie[0] == 0 ||
+                   os_memcmp(bss->osenie, elems->osen - 2,
+                             elems->osen_len + 2) != 0) {
+                       wpa_printf(MSG_INFO, "BSS " MACSTR " - OSEN IE "
+                                  "stored", MAC2STR(bss->bssid));
+                       wpa_hexdump(MSG_DEBUG, "OSEN IE", elems->osen - 2,
+                                   elems->osen_len + 2);
+                       update = 1;
+               }
+               os_memcpy(bss->osenie, elems->osen - 2,
+                         elems->osen_len + 2);
+       }
 
        if (elems->rsn_ie == NULL) {
                if (bss->rsnie[0]) {
@@ -238,37 +258,60 @@ void bss_update(struct wlantest *wt, struct wlantest_bss *bss,
                }
        }
 
+       if (bss->osenie[0]) {
+               bss->proto |= WPA_PROTO_OSEN;
+               bss->pairwise_cipher |= WPA_CIPHER_CCMP;
+               bss->group_cipher |= WPA_CIPHER_CCMP;
+               bss->key_mgmt |= WPA_KEY_MGMT_OSEN;
+       }
+
        if (!(bss->proto & WPA_PROTO_RSN) ||
            !(bss->rsn_capab & WPA_CAPABILITY_MFPC))
                bss->mgmt_group_cipher = 0;
 
-       if (!bss->wpaie[0] && !bss->rsnie[0] &&
+       if (!bss->wpaie[0] && !bss->rsnie[0] && !bss->osenie[0] &&
            (bss->capab_info & WLAN_CAPABILITY_PRIVACY))
                bss->group_cipher = WPA_CIPHER_WEP40;
 
        wpa_printf(MSG_INFO, "BSS " MACSTR
-                  " proto=%s%s%s"
-                  "pairwise=%s%s%s%s"
-                  "group=%s%s%s%s%s%s"
-                  "mgmt_group_cipher=%s"
-                  "key_mgmt=%s%s%s%s%s%s%s%s"
+                  " proto=%s%s%s%s"
+                  "pairwise=%s%s%s%s%s%s%s"
+                  "group=%s%s%s%s%s%s%s%s%s"
+                  "mgmt_group_cipher=%s%s%s%s%s"
+                  "key_mgmt=%s%s%s%s%s%s%s%s%s"
                   "rsn_capab=%s%s%s%s%s",
                   MAC2STR(bss->bssid),
                   bss->proto == 0 ? "OPEN " : "",
                   bss->proto & WPA_PROTO_WPA ? "WPA " : "",
                   bss->proto & WPA_PROTO_RSN ? "WPA2 " : "",
+                  bss->proto & WPA_PROTO_OSEN ? "OSEN " : "",
                   bss->pairwise_cipher == 0 ? "N/A " : "",
                   bss->pairwise_cipher & WPA_CIPHER_NONE ? "NONE " : "",
                   bss->pairwise_cipher & WPA_CIPHER_TKIP ? "TKIP " : "",
                   bss->pairwise_cipher & WPA_CIPHER_CCMP ? "CCMP " : "",
+                  bss->pairwise_cipher & WPA_CIPHER_CCMP_256 ? "CCMP-256 " :
+                  "",
+                  bss->pairwise_cipher & WPA_CIPHER_GCMP ? "GCMP " : "",
+                  bss->pairwise_cipher & WPA_CIPHER_GCMP_256 ? "GCMP-256 " :
+                  "",
                   bss->group_cipher == 0 ? "N/A " : "",
                   bss->group_cipher & WPA_CIPHER_NONE ? "NONE " : "",
                   bss->group_cipher & WPA_CIPHER_WEP40 ? "WEP40 " : "",
                   bss->group_cipher & WPA_CIPHER_WEP104 ? "WEP104 " : "",
                   bss->group_cipher & WPA_CIPHER_TKIP ? "TKIP " : "",
                   bss->group_cipher & WPA_CIPHER_CCMP ? "CCMP " : "",
-                  bss->mgmt_group_cipher & WPA_CIPHER_AES_128_CMAC ? "BIP " :
-                  "N/A ",
+                  bss->group_cipher & WPA_CIPHER_CCMP_256 ? "CCMP-256 " : "",
+                  bss->group_cipher & WPA_CIPHER_GCMP ? "GCMP " : "",
+                  bss->group_cipher & WPA_CIPHER_GCMP_256 ? "GCMP-256 " : "",
+                  bss->mgmt_group_cipher == 0 ? "N/A " : "",
+                  bss->mgmt_group_cipher & WPA_CIPHER_AES_128_CMAC ?
+                  "BIP " : "",
+                  bss->mgmt_group_cipher & WPA_CIPHER_BIP_GMAC_128 ?
+                  "BIP-GMAC-128 " : "",
+                  bss->mgmt_group_cipher & WPA_CIPHER_BIP_GMAC_256 ?
+                  "BIP-GMAC-256 " : "",
+                  bss->mgmt_group_cipher & WPA_CIPHER_BIP_CMAC_256 ?
+                  "BIP-CMAC-256 " : "",
                   bss->key_mgmt == 0 ? "N/A " : "",
                   bss->key_mgmt & WPA_KEY_MGMT_IEEE8021X ? "EAP " : "",
                   bss->key_mgmt & WPA_KEY_MGMT_PSK ? "PSK " : "",
@@ -279,6 +322,7 @@ void bss_update(struct wlantest *wt, struct wlantest_bss *bss,
                   "EAP-SHA256 " : "",
                   bss->key_mgmt & WPA_KEY_MGMT_PSK_SHA256 ?
                   "PSK-SHA256 " : "",
+                  bss->key_mgmt & WPA_KEY_MGMT_OSEN ? "OSEN " : "",
                   bss->rsn_capab & WPA_CAPABILITY_PREAUTH ? "PREAUTH " : "",
                   bss->rsn_capab & WPA_CAPABILITY_NO_PAIRWISE ?
                   "NO_PAIRWISE " : "",
index bed5d6c..7de0a8a 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * wlantest control interface
- * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2010-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -290,6 +290,8 @@ static void ctrl_clear_sta_counters(struct wlantest *wt, int sock, u8 *cmd,
        }
 
        os_memset(sta->counters, 0, sizeof(sta->counters));
+       os_memset(sta->tx_tid, 0, sizeof(sta->tx_tid));
+       os_memset(sta->rx_tid, 0, sizeof(sta->rx_tid));
        ctrl_send_simple(wt, sock, WLANTEST_CTRL_SUCCESS);
 }
 
@@ -904,6 +906,15 @@ static void info_print_cipher(char *buf, size_t len, int cipher)
        if (cipher & WPA_CIPHER_AES_128_CMAC)
                pos += os_snprintf(pos, end - pos, "%sBIP",
                                   pos == buf ? "" : " ");
+       if (cipher & WPA_CIPHER_BIP_GMAC_128)
+               pos += os_snprintf(pos, end - pos, "%sBIP-GMAC-128",
+                                  pos == buf ? "" : " ");
+       if (cipher & WPA_CIPHER_BIP_GMAC_256)
+               pos += os_snprintf(pos, end - pos, "%sBIP-GMAC-256",
+                                  pos == buf ? "" : " ");
+       if (cipher & WPA_CIPHER_BIP_CMAC_256)
+               pos += os_snprintf(pos, end - pos, "%sBIP-CMAC-256",
+                                  pos == buf ? "" : " ");
 }
 
 
@@ -940,6 +951,12 @@ static void info_print_key_mgmt(char *buf, size_t len, int key_mgmt)
        if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
                pos += os_snprintf(pos, end - pos, "%sPSK-SHA256",
                                   pos == buf ? "" : " ");
+       if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+               pos += os_snprintf(pos, end - pos, "%sEAP-SUITE-B",
+                                  pos == buf ? "" : " ");
+       if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+               pos += os_snprintf(pos, end - pos, "%sEAP-SUITE-B-192",
+                                  pos == buf ? "" : " ");
 }
 
 
@@ -1187,6 +1204,84 @@ static void ctrl_send_(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
 }
 
 
+static void ctrl_relog(struct wlantest *wt, int sock)
+{
+       int res = wlantest_relog(wt);
+       ctrl_send_simple(wt, sock, res ? WLANTEST_CTRL_FAILURE :
+                        WLANTEST_CTRL_SUCCESS);
+}
+
+
+static void ctrl_get_tx_tid(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+       u8 *addr;
+       size_t addr_len;
+       struct wlantest_bss *bss;
+       struct wlantest_sta *sta;
+       u32 counter;
+       u8 buf[4 + 12], *end, *pos;
+
+       bss = ctrl_get_bss(wt, sock, cmd, clen);
+       sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+       if (sta == NULL)
+               return;
+
+       addr = attr_get(cmd, clen, WLANTEST_ATTR_TID, &addr_len);
+       if (addr == NULL || addr_len != 4) {
+               ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+               return;
+       }
+       counter = WPA_GET_BE32(addr);
+       if (counter >= 16 + 1) {
+               ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+               return;
+       }
+
+       pos = buf;
+       end = buf + sizeof(buf);
+       WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+       pos += 4;
+       pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER,
+                           sta->tx_tid[counter]);
+       ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
+static void ctrl_get_rx_tid(struct wlantest *wt, int sock, u8 *cmd, size_t clen)
+{
+       u8 *addr;
+       size_t addr_len;
+       struct wlantest_bss *bss;
+       struct wlantest_sta *sta;
+       u32 counter;
+       u8 buf[4 + 12], *end, *pos;
+
+       bss = ctrl_get_bss(wt, sock, cmd, clen);
+       sta = ctrl_get_sta(wt, sock, cmd, clen, bss);
+       if (sta == NULL)
+               return;
+
+       addr = attr_get(cmd, clen, WLANTEST_ATTR_TID, &addr_len);
+       if (addr == NULL || addr_len != 4) {
+               ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+               return;
+       }
+       counter = WPA_GET_BE32(addr);
+       if (counter >= 16 + 1) {
+               ctrl_send_simple(wt, sock, WLANTEST_CTRL_INVALID_CMD);
+               return;
+       }
+
+       pos = buf;
+       end = buf + sizeof(buf);
+       WPA_PUT_BE32(pos, WLANTEST_CTRL_SUCCESS);
+       pos += 4;
+       pos = attr_add_be32(pos, end, WLANTEST_ATTR_COUNTER,
+                           sta->rx_tid[counter]);
+       ctrl_send(wt, sock, buf, pos - buf);
+}
+
+
 static void ctrl_read(int sock, void *eloop_ctx, void *sock_ctx)
 {
        struct wlantest *wt = eloop_ctx;
@@ -1270,6 +1365,15 @@ static void ctrl_read(int sock, void *eloop_ctx, void *sock_ctx)
        case WLANTEST_CTRL_SEND:
                ctrl_send_(wt, sock, buf + 4, len - 4);
                break;
+       case WLANTEST_CTRL_RELOG:
+               ctrl_relog(wt, sock);
+               break;
+       case WLANTEST_CTRL_GET_TX_TID:
+               ctrl_get_tx_tid(wt, sock, buf + 4, len - 4);
+               break;
+       case WLANTEST_CTRL_GET_RX_TID:
+               ctrl_get_rx_tid(wt, sock, buf + 4, len - 4);
+               break;
        default:
                ctrl_send_simple(wt, sock, WLANTEST_CTRL_UNKNOWN_CMD);
                break;
index d8535d0..161b8a7 100644 (file)
@@ -41,7 +41,6 @@ static void gcmp_aad_nonce(const struct ieee80211_hdr *hdr, const u8 *data,
        }
 
        fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA);
-       fc |= WLAN_FC_ISWEP;
        WPA_PUT_LE16(aad, fc);
        pos = aad + 2;
        os_memcpy(pos, hdr->addr1, 3 * ETH_ALEN);
@@ -131,7 +130,6 @@ u8 * gcmp_encrypt(const u8 *tk, size_t tk_len, u8 *frame, size_t len,
 
        os_memcpy(crypt, frame, hdrlen);
        hdr = (struct ieee80211_hdr *) crypt;
-       hdr->frame_control |= host_to_le16(WLAN_FC_ISWEP);
        pos = crypt + hdrlen;
        *pos++ = pn[5]; /* PN0 */
        *pos++ = pn[4]; /* PN1 */
index 5b14ca7..ed25033 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * wlantest frame injection
- * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2010-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -50,7 +50,7 @@ static int inject_frame(int s, const void *data, size_t len)
 
        ret = sendmsg(s, &msg, 0);
        if (ret < 0)
-               perror("sendmsg");
+               wpa_printf(MSG_ERROR, "sendmsg: %s", strerror(errno));
        return ret;
 }
 
@@ -82,17 +82,18 @@ static int wlantest_inject_bip(struct wlantest *wt, struct wlantest_bss *bss,
                               u8 *frame, size_t len, int incorrect_key)
 {
        u8 *prot;
-       u8 dummy[16];
+       u8 dummy[32];
        int ret;
        size_t plen;
 
-       if (!bss->igtk_set[bss->igtk_idx])
+       if (!bss->igtk_len[bss->igtk_idx])
                return -1;
 
        os_memset(dummy, 0x11, sizeof(dummy));
        inc_byte_array(bss->ipn[bss->igtk_idx], 6);
 
        prot = bip_protect(incorrect_key ? dummy : bss->igtk[bss->igtk_idx],
+                          bss->igtk_len[bss->igtk_idx],
                           frame, len, bss->ipn[bss->igtk_idx],
                           bss->igtk_idx, &plen);
        if (prot == NULL)
@@ -247,11 +248,11 @@ static int wlantest_inject_prot(struct wlantest *wt, struct wlantest_bss *bss,
                                     frame, len, hdrlen, qos, pn, 0,
                                     &crypt_len);
        else if (sta->pairwise_cipher == WPA_CIPHER_TKIP)
-               crypt = tkip_encrypt(incorrect_key ? dummy : sta->ptk.tk1,
+               crypt = tkip_encrypt(incorrect_key ? dummy : sta->ptk.tk,
                                     frame, len, hdrlen, qos, pn, 0,
                                     &crypt_len);
        else
-               crypt = ccmp_encrypt(incorrect_key ? dummy : sta->ptk.tk1,
+               crypt = ccmp_encrypt(incorrect_key ? dummy : sta->ptk.tk,
                                     frame, len, hdrlen, qos, pn, 0,
                                     &crypt_len);
 
@@ -300,7 +301,7 @@ int wlantest_inject(struct wlantest *wt, struct wlantest_bss *bss,
             prot == WLANTEST_INJECT_INCORRECT_KEY) && bss) {
                if (!sta &&
                    ((WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
-                     !bss->igtk_set[bss->igtk_idx]) ||
+                     !bss->igtk_len[bss->igtk_idx]) ||
                     (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_DATA &&
                      !bss->gtk_len[bss->gtk_idx]))) {
                        wpa_printf(MSG_INFO, "No GTK/IGTK known for "
@@ -323,7 +324,7 @@ int wlantest_inject(struct wlantest *wt, struct wlantest_bss *bss,
                            bss->gtk_len[bss->gtk_idx])
                                protect = 1;
                        if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT &&
-                           bss->igtk_set[bss->igtk_idx])
+                           bss->igtk_len[bss->igtk_idx])
                                protect = 1;
                }
        }
index 5e75445..802d0af 100644 (file)
@@ -12,6 +12,7 @@
 #include "utils/radiotap.h"
 #include "utils/radiotap_iter.h"
 #include "common/ieee802_11_defs.h"
+#include "common/qca-vendor.h"
 #include "wlantest.h"
 
 
@@ -276,7 +277,7 @@ void wlantest_process(struct wlantest *wt, const u8 *data, size_t len)
 
        wpa_hexdump(MSG_EXCESSIVE, "Process data", data, len);
 
-       if (ieee80211_radiotap_iterator_init(&iter, (void *) data, len)) {
+       if (ieee80211_radiotap_iterator_init(&iter, (void *) data, len, NULL)) {
                add_note(wt, MSG_INFO, "Invalid radiotap frame");
                return;
        }
@@ -305,16 +306,18 @@ void wlantest_process(struct wlantest *wt, const u8 *data, size_t len)
                        failed = le_to_host16((*(u16 *) iter.this_arg)) &
                                IEEE80211_RADIOTAP_F_TX_FAIL;
                        break;
-
+               case IEEE80211_RADIOTAP_VENDOR_NAMESPACE:
+                       if (WPA_GET_BE24(iter.this_arg) == OUI_QCA &&
+                           iter.this_arg[3] == QCA_RADIOTAP_VID_WLANTEST) {
+                               add_note(wt, MSG_DEBUG,
+                                        "Skip frame inserted by wlantest");
+                               return;
+                       }
                }
        }
 
-       if (iter.max_length == 8) {
-               add_note(wt, MSG_DEBUG, "Skip frame inserted by wlantest");
-               return;
-       }
-       frame = data + iter.max_length;
-       frame_len = len - iter.max_length;
+       frame = data + iter._max_length;
+       frame_len = len - iter._max_length;
 
        if (fcs && frame_len >= 4) {
                frame_len -= 4;
index 9d439be..4c55e7d 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Received Data frame processing
- * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2010-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -93,6 +93,49 @@ static void rx_data_process(struct wlantest *wt, const u8 *bssid,
 }
 
 
+static u8 * try_all_ptk(struct wlantest *wt, int pairwise_cipher,
+                       const struct ieee80211_hdr *hdr,
+                       const u8 *data, size_t data_len, size_t *decrypted_len)
+{
+       struct wlantest_ptk *ptk;
+       u8 *decrypted;
+       int prev_level = wpa_debug_level;
+
+       wpa_debug_level = MSG_WARNING;
+       dl_list_for_each(ptk, &wt->ptk, struct wlantest_ptk, list) {
+               unsigned int tk_len = ptk->ptk_len - 32;
+               decrypted = NULL;
+               if ((pairwise_cipher == WPA_CIPHER_CCMP ||
+                    pairwise_cipher == 0) && tk_len == 16) {
+                       decrypted = ccmp_decrypt(ptk->ptk.tk, hdr, data,
+                                                data_len, decrypted_len);
+               } else if ((pairwise_cipher == WPA_CIPHER_CCMP_256 ||
+                           pairwise_cipher == 0) && tk_len == 32) {
+                       decrypted = ccmp_256_decrypt(ptk->ptk.tk, hdr, data,
+                                                    data_len, decrypted_len);
+               } else if ((pairwise_cipher == WPA_CIPHER_GCMP ||
+                           pairwise_cipher == WPA_CIPHER_GCMP_256 ||
+                           pairwise_cipher == 0) &&
+                          (tk_len == 16 || tk_len == 32)) {
+                       decrypted = gcmp_decrypt(ptk->ptk.tk, tk_len, hdr,
+                                                data, data_len, decrypted_len);
+               } else if ((pairwise_cipher == WPA_CIPHER_TKIP ||
+                           pairwise_cipher == 0) && tk_len == 32) {
+                       decrypted = tkip_decrypt(ptk->ptk.tk, hdr, data,
+                                                data_len, decrypted_len);
+               }
+               if (decrypted) {
+                       wpa_debug_level = prev_level;
+                       add_note(wt, MSG_DEBUG, "Found PTK match from list of all known PTKs");
+                       return decrypted;
+               }
+       }
+       wpa_debug_level = prev_level;
+
+       return NULL;
+}
+
+
 static void rx_data_bss_prot_group(struct wlantest *wt,
                                   const struct ieee80211_hdr *hdr,
                                   const u8 *qos, const u8 *dst, const u8 *src,
@@ -100,7 +143,7 @@ static void rx_data_bss_prot_group(struct wlantest *wt,
 {
        struct wlantest_bss *bss;
        int keyid;
-       u8 *decrypted;
+       u8 *decrypted = NULL;
        size_t dlen;
        u8 pn[6];
 
@@ -159,11 +202,13 @@ static void rx_data_bss_prot_group(struct wlantest *wt,
        if (os_memcmp(pn, bss->rsc[keyid], 6) <= 0) {
                u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
                add_note(wt, MSG_INFO, "CCMP/TKIP replay detected: A1=" MACSTR
-                        " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u",
+                        " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u%s",
                         MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
                         MAC2STR(hdr->addr3),
                         WLAN_GET_SEQ_SEQ(seq_ctrl),
-                        WLAN_GET_SEQ_FRAG(seq_ctrl));
+                        WLAN_GET_SEQ_FRAG(seq_ctrl),
+                        (le_to_host16(hdr->frame_control) & WLAN_FC_RETRY) ?
+                        " Retry" : "");
                wpa_hexdump(MSG_INFO, "RX PN", pn, 6);
                wpa_hexdump(MSG_INFO, "RSC", bss->rsc[keyid], 6);
        }
@@ -174,9 +219,17 @@ skip_replay_det:
                                         &dlen);
        else if (bss->group_cipher == WPA_CIPHER_WEP40)
                decrypted = wep_decrypt(wt, hdr, data, len, &dlen);
-       else
+       else if (bss->group_cipher == WPA_CIPHER_CCMP)
                decrypted = ccmp_decrypt(bss->gtk[keyid], hdr, data, len,
                                         &dlen);
+       else if (bss->group_cipher == WPA_CIPHER_CCMP_256)
+               decrypted = ccmp_256_decrypt(bss->gtk[keyid], hdr, data, len,
+                                            &dlen);
+       else if (bss->group_cipher == WPA_CIPHER_GCMP ||
+                bss->group_cipher == WPA_CIPHER_GCMP_256)
+               decrypted = gcmp_decrypt(bss->gtk[keyid], bss->gtk_len[keyid],
+                                        hdr, data, len, &dlen);
+
        if (decrypted) {
                rx_data_process(wt, bss->bssid, NULL, dst, src, decrypted,
                                dlen, 1, NULL);
@@ -204,6 +257,8 @@ static void rx_data_bss_prot(struct wlantest *wt,
        u8 pn[6], *rsc;
        struct wlantest_tdls *tdls = NULL, *found;
        const u8 *tk = NULL;
+       int ptk_iter_done = 0;
+       int try_ptk_iter = 0;
 
        if (hdr->addr1[0] & 0x01) {
                rx_data_bss_prot_group(wt, hdr, qos, dst, src, data, len);
@@ -253,7 +308,9 @@ static void rx_data_bss_prot(struct wlantest *wt,
             (!sta->ptk_set && sta->pairwise_cipher != WPA_CIPHER_WEP40)) &&
            tk == NULL) {
                add_note(wt, MSG_MSGDUMP, "No PTK known to decrypt the frame");
-               return;
+               if (dl_list_empty(&wt->ptk))
+                       return;
+               try_ptk_iter = 1;
        }
 
        if (len < 4) {
@@ -261,6 +318,8 @@ static void rx_data_bss_prot(struct wlantest *wt,
                return;
        }
 
+       if (sta == NULL)
+               return;
        if (sta->pairwise_cipher & (WPA_CIPHER_TKIP | WPA_CIPHER_CCMP) &&
            !(data[3] & 0x20)) {
                add_note(wt, MSG_INFO, "Expected TKIP/CCMP frame from "
@@ -297,10 +356,19 @@ static void rx_data_bss_prot(struct wlantest *wt,
                         keyid, MAC2STR(hdr->addr2));
        }
 
-       if (qos)
+       if (qos) {
                tid = qos[0] & 0x0f;
-       else
+               if (fc & WLAN_FC_TODS)
+                       sta->tx_tid[tid]++;
+               else
+                       sta->rx_tid[tid]++;
+       } else {
                tid = 0;
+               if (fc & WLAN_FC_TODS)
+                       sta->tx_tid[16]++;
+               else
+                       sta->rx_tid[16]++;
+       }
        if (tk) {
                if (os_memcmp(hdr->addr2, tdls->init->addr, ETH_ALEN) == 0)
                        rsc = tdls->rsc_init[tid];
@@ -321,24 +389,54 @@ static void rx_data_bss_prot(struct wlantest *wt,
        if (os_memcmp(pn, rsc, 6) <= 0) {
                u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
                add_note(wt, MSG_INFO, "CCMP/TKIP replay detected: A1=" MACSTR
-                        " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u",
+                        " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u%s",
                         MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
                         MAC2STR(hdr->addr3),
                         WLAN_GET_SEQ_SEQ(seq_ctrl),
-                        WLAN_GET_SEQ_FRAG(seq_ctrl));
+                        WLAN_GET_SEQ_FRAG(seq_ctrl),
+                        (le_to_host16(hdr->frame_control) &  WLAN_FC_RETRY) ?
+                        " Retry" : "");
                wpa_hexdump(MSG_INFO, "RX PN", pn, 6);
                wpa_hexdump(MSG_INFO, "RSC", rsc, 6);
        }
 
 skip_replay_det:
-       if (tk)
-               decrypted = ccmp_decrypt(tk, hdr, data, len, &dlen);
-       else if (sta->pairwise_cipher == WPA_CIPHER_TKIP)
-               decrypted = tkip_decrypt(sta->ptk.tk1, hdr, data, len, &dlen);
-       else if (sta->pairwise_cipher == WPA_CIPHER_WEP40)
+       if (tk) {
+               if (sta->pairwise_cipher == WPA_CIPHER_CCMP_256)
+                       decrypted = ccmp_256_decrypt(tk, hdr, data, len, &dlen);
+               else if (sta->pairwise_cipher == WPA_CIPHER_GCMP ||
+                        sta->pairwise_cipher == WPA_CIPHER_GCMP_256)
+                       decrypted = gcmp_decrypt(tk, sta->tk_len, hdr, data,
+                                                len, &dlen);
+               else
+                       decrypted = ccmp_decrypt(tk, hdr, data, len, &dlen);
+       } else if (sta->pairwise_cipher == WPA_CIPHER_TKIP) {
+               decrypted = tkip_decrypt(sta->ptk.tk, hdr, data, len, &dlen);
+       } else if (sta->pairwise_cipher == WPA_CIPHER_WEP40) {
                decrypted = wep_decrypt(wt, hdr, data, len, &dlen);
-       else
-               decrypted = ccmp_decrypt(sta->ptk.tk1, hdr, data, len, &dlen);
+       } else if (sta->ptk_set) {
+               if (sta->pairwise_cipher == WPA_CIPHER_CCMP_256)
+                       decrypted = ccmp_256_decrypt(sta->ptk.tk, hdr, data,
+                                                    len, &dlen);
+               else if (sta->pairwise_cipher == WPA_CIPHER_GCMP ||
+                        sta->pairwise_cipher == WPA_CIPHER_GCMP_256)
+                       decrypted = gcmp_decrypt(sta->ptk.tk, sta->tk_len,
+                                                hdr, data, len, &dlen);
+               else
+                       decrypted = ccmp_decrypt(sta->ptk.tk, hdr, data, len,
+                                                &dlen);
+       } else {
+               decrypted = try_all_ptk(wt, sta->pairwise_cipher, hdr, data,
+                                       len, &dlen);
+               ptk_iter_done = 1;
+       }
+       if (!decrypted && !ptk_iter_done) {
+               decrypted = try_all_ptk(wt, sta->pairwise_cipher, hdr, data,
+                                       len, &dlen);
+               if (decrypted) {
+                       add_note(wt, MSG_DEBUG, "Current PTK did not work, but found a match from all known PTKs");
+               }
+       }
        if (decrypted) {
                u16 fc = le_to_host16(hdr->frame_control);
                const u8 *peer_addr = NULL;
@@ -349,7 +447,7 @@ skip_replay_det:
                                dlen, 1, peer_addr);
                write_pcap_decrypted(wt, (const u8 *) hdr, 24 + (qos ? 2 : 0),
                                     decrypted, dlen);
-       } else
+       } else if (!try_ptk_iter)
                add_note(wt, MSG_DEBUG, "Failed to decrypt frame");
        os_free(decrypted);
 }
@@ -383,6 +481,8 @@ static void rx_data_bss(struct wlantest *wt, const struct ieee80211_hdr *hdr,
                rx_data_bss_prot(wt, hdr, qos, dst, src, data, len);
        else {
                const u8 *bssid, *sta_addr, *peer_addr;
+               struct wlantest_bss *bss;
+
                if (fc & WLAN_FC_TODS) {
                        bssid = hdr->addr1;
                        sta_addr = hdr->addr2;
@@ -396,6 +496,27 @@ static void rx_data_bss(struct wlantest *wt, const struct ieee80211_hdr *hdr,
                        sta_addr = hdr->addr2;
                        peer_addr = hdr->addr1;
                }
+
+               bss = bss_get(wt, bssid);
+               if (bss) {
+                       struct wlantest_sta *sta = sta_get(bss, sta_addr);
+
+                       if (sta) {
+                               if (qos) {
+                                       int tid = qos[0] & 0x0f;
+                                       if (fc & WLAN_FC_TODS)
+                                               sta->tx_tid[tid]++;
+                                       else
+                                               sta->rx_tid[tid]++;
+                               } else {
+                                       if (fc & WLAN_FC_TODS)
+                                               sta->tx_tid[16]++;
+                                       else
+                                               sta->rx_tid[16]++;
+                               }
+                       }
+               }
+
                rx_data_process(wt, bssid, sta_addr, dst, src, data, len, 0,
                                peer_addr);
        }
index b18b3cf..04ea6ac 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Received Data frame processing for EAPOL messages
- * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2010-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -31,13 +31,15 @@ static int is_zero(const u8 *buf, size_t len)
 }
 
 
-static int check_mic(const u8 *kck, int ver, const u8 *data, size_t len)
+static int check_mic(const u8 *kck, size_t kck_len, int akmp, int ver,
+                    const u8 *data, size_t len)
 {
        u8 *buf;
        int ret = -1;
        struct ieee802_1x_hdr *hdr;
        struct wpa_eapol_key *key;
-       u8 rx_mic[16];
+       u8 rx_mic[WPA_EAPOL_KEY_MIC_MAX_LEN];
+       size_t mic_len = 16;
 
        buf = os_malloc(len);
        if (buf == NULL)
@@ -46,11 +48,12 @@ static int check_mic(const u8 *kck, int ver, const u8 *data, size_t len)
        hdr = (struct ieee802_1x_hdr *) buf;
        key = (struct wpa_eapol_key *) (hdr + 1);
 
-       os_memcpy(rx_mic, key->key_mic, 16);
-       os_memset(key->key_mic, 0, 16);
+       os_memcpy(rx_mic, key->key_mic, mic_len);
+       os_memset(key->key_mic, 0, mic_len);
 
-       if (wpa_eapol_key_mic(kck, ver, buf, len, key->key_mic) == 0 &&
-           os_memcmp(rx_mic, key->key_mic, 16) == 0)
+       if (wpa_eapol_key_mic(kck, kck_len, akmp, ver, buf, len,
+                             key->key_mic) == 0 &&
+           os_memcmp(rx_mic, key->key_mic, mic_len) == 0)
                ret = 0;
 
        os_free(buf);
@@ -96,15 +99,15 @@ static int try_pmk(struct wlantest *wt, struct wlantest_bss *bss,
                   struct wlantest_pmk *pmk)
 {
        struct wpa_ptk ptk;
-       size_t ptk_len = sta->pairwise_cipher == WPA_CIPHER_TKIP ? 64 : 48;
-       wpa_pmk_to_ptk(pmk->pmk, sizeof(pmk->pmk),
-                      "Pairwise key expansion",
-                      bss->bssid, sta->addr, sta->anonce, sta->snonce,
-                      (u8 *) &ptk, ptk_len,
-                      wpa_key_mgmt_sha256(sta->key_mgmt));
-       if (check_mic(ptk.kck, ver, data, len) < 0)
+
+       if (wpa_pmk_to_ptk(pmk->pmk, sizeof(pmk->pmk),
+                          "Pairwise key expansion",
+                          bss->bssid, sta->addr, sta->anonce, sta->snonce,
+                          &ptk, sta->key_mgmt, sta->pairwise_cipher) < 0 ||
+           check_mic(ptk.kck, ptk.kck_len, sta->key_mgmt, ver, data, len) < 0)
                return -1;
 
+       sta->tk_len = wpa_cipher_key_len(sta->pairwise_cipher);
        wpa_printf(MSG_INFO, "Derived PTK for STA " MACSTR " BSSID " MACSTR,
                   MAC2STR(sta->addr), MAC2STR(bss->bssid));
        sta->counters[WLANTEST_STA_COUNTER_PTK_LEARNED]++;
@@ -115,22 +118,20 @@ static int try_pmk(struct wlantest *wt, struct wlantest_bss *bss,
                 */
                add_note(wt, MSG_DEBUG, "Derived PTK during rekeying");
                os_memcpy(&sta->tptk, &ptk, sizeof(ptk));
-               wpa_hexdump(MSG_DEBUG, "TPTK:KCK", sta->tptk.kck, 16);
-               wpa_hexdump(MSG_DEBUG, "TPTK:KEK", sta->tptk.kek, 16);
-               wpa_hexdump(MSG_DEBUG, "TPTK:TK1", sta->tptk.tk1, 16);
-               if (ptk_len > 48)
-                       wpa_hexdump(MSG_DEBUG, "TPTK:TK2", sta->tptk.u.tk2,
-                                   16);
+               wpa_hexdump(MSG_DEBUG, "TPTK:KCK",
+                           sta->tptk.kck, sta->tptk.kck_len);
+               wpa_hexdump(MSG_DEBUG, "TPTK:KEK",
+                           sta->tptk.kek, sta->tptk.kek_len);
+               wpa_hexdump(MSG_DEBUG, "TPTK:TK",
+                           sta->tptk.tk, sta->tptk.tk_len);
                sta->tptk_set = 1;
                return 0;
        }
        add_note(wt, MSG_DEBUG, "Derived new PTK");
        os_memcpy(&sta->ptk, &ptk, sizeof(ptk));
-       wpa_hexdump(MSG_DEBUG, "PTK:KCK", sta->ptk.kck, 16);
-       wpa_hexdump(MSG_DEBUG, "PTK:KEK", sta->ptk.kek, 16);
-       wpa_hexdump(MSG_DEBUG, "PTK:TK1", sta->ptk.tk1, 16);
-       if (ptk_len > 48)
-               wpa_hexdump(MSG_DEBUG, "PTK:TK2", sta->ptk.u.tk2, 16);
+       wpa_hexdump(MSG_DEBUG, "PTK:KCK", sta->ptk.kck, sta->ptk.kck_len);
+       wpa_hexdump(MSG_DEBUG, "PTK:KEK", sta->ptk.kek, sta->ptk.kek_len);
+       wpa_hexdump(MSG_DEBUG, "PTK:TK", sta->ptk.tk, sta->ptk.tk_len);
        sta->ptk_set = 1;
        os_memset(sta->rsc_tods, 0, sizeof(sta->rsc_tods));
        os_memset(sta->rsc_fromds, 0, sizeof(sta->rsc_fromds));
@@ -144,8 +145,8 @@ static void derive_ptk(struct wlantest *wt, struct wlantest_bss *bss,
 {
        struct wlantest_pmk *pmk;
 
-       wpa_printf(MSG_DEBUG, "Trying to derive PTK for " MACSTR,
-                  MAC2STR(sta->addr));
+       wpa_printf(MSG_DEBUG, "Trying to derive PTK for " MACSTR " (ver %u)",
+                  MAC2STR(sta->addr), ver);
        dl_list_for_each(pmk, &bss->pmk, struct wlantest_pmk, list) {
                wpa_printf(MSG_DEBUG, "Try per-BSS PMK");
                if (try_pmk(wt, bss, sta, ver, data, len, pmk) == 0)
@@ -157,6 +158,36 @@ static void derive_ptk(struct wlantest *wt, struct wlantest_bss *bss,
                if (try_pmk(wt, bss, sta, ver, data, len, pmk) == 0)
                        return;
        }
+
+       if (!sta->ptk_set) {
+               struct wlantest_ptk *ptk;
+               int prev_level = wpa_debug_level;
+
+               wpa_debug_level = MSG_WARNING;
+               dl_list_for_each(ptk, &wt->ptk, struct wlantest_ptk, list) {
+                       if (check_mic(ptk->ptk.kck, ptk->ptk.kck_len,
+                                     sta->key_mgmt, ver, data, len) < 0)
+                               continue;
+                       wpa_printf(MSG_INFO, "Pre-set PTK matches for STA "
+                                  MACSTR " BSSID " MACSTR,
+                                  MAC2STR(sta->addr), MAC2STR(bss->bssid));
+                       add_note(wt, MSG_DEBUG, "Using pre-set PTK");
+                       ptk->ptk_len = 32 +
+                               wpa_cipher_key_len(sta->pairwise_cipher);
+                       os_memcpy(&sta->ptk, &ptk->ptk, sizeof(ptk->ptk));
+                       wpa_hexdump(MSG_DEBUG, "PTK:KCK",
+                                   sta->ptk.kck, sta->ptk.kck_len);
+                       wpa_hexdump(MSG_DEBUG, "PTK:KEK",
+                                   sta->ptk.kek, sta->ptk.kek_len);
+                       wpa_hexdump(MSG_DEBUG, "PTK:TK",
+                                   sta->ptk.tk, sta->ptk.tk_len);
+                       sta->ptk_set = 1;
+                       os_memset(sta->rsc_tods, 0, sizeof(sta->rsc_tods));
+                       os_memset(sta->rsc_fromds, 0, sizeof(sta->rsc_fromds));
+               }
+               wpa_debug_level = prev_level;
+       }
+
        add_note(wt, MSG_DEBUG, "No matching PMK found to derive PTK");
 }
 
@@ -169,6 +200,7 @@ static void rx_data_eapol_key_2_of_4(struct wlantest *wt, const u8 *dst,
        const struct ieee802_1x_hdr *eapol;
        const struct wpa_eapol_key *hdr;
        const u8 *key_data, *kck;
+       size_t kck_len;
        u16 key_info, key_data_len;
        struct wpa_eapol_ie_parse ie;
 
@@ -203,12 +235,15 @@ static void rx_data_eapol_key_2_of_4(struct wlantest *wt, const u8 *dst,
        }
 
        kck = sta->ptk.kck;
+       kck_len = sta->ptk.kck_len;
        if (sta->tptk_set) {
                add_note(wt, MSG_DEBUG,
                         "Use TPTK for validation EAPOL-Key MIC");
                kck = sta->tptk.kck;
+               kck_len = sta->tptk.kck_len;
        }
-       if (check_mic(kck, key_info & WPA_KEY_INFO_TYPE_MASK, data, len) < 0) {
+       if (check_mic(kck, kck_len, sta->key_mgmt,
+                     key_info & WPA_KEY_INFO_TYPE_MASK, data, len) < 0) {
                add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 2/4 MIC");
                return;
        }
@@ -322,7 +357,7 @@ static u8 * decrypt_eapol_key_data_aes(struct wlantest *wt, const u8 *kek,
        buf = os_malloc(keydatalen);
        if (buf == NULL)
                return NULL;
-       if (aes_unwrap(kek, keydatalen / 8, (u8 *) (hdr + 1), buf)) {
+       if (aes_unwrap(kek, 16, keydatalen / 8, (u8 *) (hdr + 1), buf)) {
                os_free(buf);
                add_note(wt, MSG_INFO,
                         "AES unwrap failed - could not decrypt EAPOL-Key "
@@ -335,16 +370,22 @@ static u8 * decrypt_eapol_key_data_aes(struct wlantest *wt, const u8 *kek,
 }
 
 
-static u8 * decrypt_eapol_key_data(struct wlantest *wt, const u8 *kek, u16 ver,
+static u8 * decrypt_eapol_key_data(struct wlantest *wt, const u8 *kek,
+                                  size_t kek_len, u16 ver,
                                   const struct wpa_eapol_key *hdr,
                                   size_t *len)
 {
+       if (kek_len != 16)
+               return NULL;
        switch (ver) {
        case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4:
                return decrypt_eapol_key_data_rc4(wt, kek, hdr, len);
        case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES:
        case WPA_KEY_INFO_TYPE_AES_128_CMAC:
                return decrypt_eapol_key_data_aes(wt, kek, hdr, len);
+       case WPA_KEY_INFO_TYPE_AKM_DEFINED:
+               /* For now, assume this is OSEN */
+               return decrypt_eapol_key_data_aes(wt, kek, hdr, len);
        default:
                add_note(wt, MSG_INFO,
                         "Unsupported EAPOL-Key Key Descriptor Version %u",
@@ -381,8 +422,8 @@ static void learn_kde_keys(struct wlantest *wt, struct wlantest_bss *bss,
                if (ie.gtk_len >= 2 && ie.gtk_len <= 2 + 32) {
                        int id;
                        id = ie.gtk[0] & 0x03;
-                       wpa_printf(MSG_DEBUG, "GTK KeyID=%u tx=%u",
-                                  id, !!(ie.gtk[0] & 0x04));
+                       add_note(wt, MSG_DEBUG, "GTK KeyID=%u tx=%u",
+                                id, !!(ie.gtk[0] & 0x04));
                        if ((ie.gtk[0] & 0xf8) || ie.gtk[1]) {
                                add_note(wt, MSG_INFO,
                                         "GTK KDE: Reserved field set: "
@@ -420,12 +461,35 @@ static void learn_kde_keys(struct wlantest *wt, struct wlantest_bss *bss,
                                         "Unexpected IGTK KeyID %u", id);
                        } else {
                                const u8 *ipn;
-                               wpa_printf(MSG_DEBUG, "IGTK KeyID %u", id);
+                               add_note(wt, MSG_DEBUG, "IGTK KeyID %u", id);
                                wpa_hexdump(MSG_DEBUG, "IPN", ie.igtk + 2, 6);
                                wpa_hexdump(MSG_DEBUG, "IGTK", ie.igtk + 8,
                                            16);
                                os_memcpy(bss->igtk[id], ie.igtk + 8, 16);
-                               bss->igtk_set[id] = 1;
+                               bss->igtk_len[id] = 16;
+                               ipn = ie.igtk + 2;
+                               bss->ipn[id][0] = ipn[5];
+                               bss->ipn[id][1] = ipn[4];
+                               bss->ipn[id][2] = ipn[3];
+                               bss->ipn[id][3] = ipn[2];
+                               bss->ipn[id][4] = ipn[1];
+                               bss->ipn[id][5] = ipn[0];
+                               bss->igtk_idx = id;
+                       }
+               } else if (ie.igtk_len == 40) {
+                       u16 id;
+                       id = WPA_GET_LE16(ie.igtk);
+                       if (id > 5) {
+                               add_note(wt, MSG_INFO,
+                                        "Unexpected IGTK KeyID %u", id);
+                       } else {
+                               const u8 *ipn;
+                               add_note(wt, MSG_DEBUG, "IGTK KeyID %u", id);
+                               wpa_hexdump(MSG_DEBUG, "IPN", ie.igtk + 2, 6);
+                               wpa_hexdump(MSG_DEBUG, "IGTK", ie.igtk + 8,
+                                           32);
+                               os_memcpy(bss->igtk[id], ie.igtk + 8, 32);
+                               bss->igtk_len[id] = 32;
                                ipn = ie.igtk + 2;
                                bss->ipn[id][0] = ipn[5];
                                bss->ipn[id][1] = ipn[4];
@@ -451,6 +515,7 @@ static void rx_data_eapol_key_3_of_4(struct wlantest *wt, const u8 *dst,
        const struct ieee802_1x_hdr *eapol;
        const struct wpa_eapol_key *hdr;
        const u8 *key_data, *kck, *kek;
+       size_t kck_len, kek_len;
        int recalc = 0;
        u16 key_info, ver;
        u8 *decrypted_buf = NULL;
@@ -489,14 +554,19 @@ static void rx_data_eapol_key_3_of_4(struct wlantest *wt, const u8 *dst,
        }
 
        kek = sta->ptk.kek;
+       kek_len = sta->ptk.kek_len;
        kck = sta->ptk.kck;
+       kck_len = sta->ptk.kck_len;
        if (sta->tptk_set) {
                add_note(wt, MSG_DEBUG,
                         "Use TPTK for validation EAPOL-Key MIC");
                kck = sta->tptk.kck;
+               kck_len = sta->tptk.kck_len;
                kek = sta->tptk.kek;
+               kek_len = sta->tptk.kek_len;
        }
-       if (check_mic(kck, key_info & WPA_KEY_INFO_TYPE_MASK, data, len) < 0) {
+       if (check_mic(kck, kck_len, sta->key_mgmt,
+                     key_info & WPA_KEY_INFO_TYPE_MASK, data, len) < 0) {
                add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 3/4 MIC");
                return;
        }
@@ -511,8 +581,8 @@ static void rx_data_eapol_key_3_of_4(struct wlantest *wt, const u8 *dst,
                decrypted_len = WPA_GET_BE16(hdr->key_data_length);
        } else {
                ver = key_info & WPA_KEY_INFO_TYPE_MASK;
-               decrypted_buf = decrypt_eapol_key_data(wt, kek, ver, hdr,
-                                                      &decrypted_len);
+               decrypted_buf = decrypt_eapol_key_data(wt, kek, kek_len, ver,
+                                                      hdr, &decrypted_len);
                if (decrypted_buf == NULL) {
                        add_note(wt, MSG_INFO,
                                 "Failed to decrypt EAPOL-Key Key Data");
@@ -611,6 +681,7 @@ static void rx_data_eapol_key_4_of_4(struct wlantest *wt, const u8 *dst,
        const struct wpa_eapol_key *hdr;
        u16 key_info;
        const u8 *kck;
+       size_t kck_len;
 
        wpa_printf(MSG_DEBUG, "EAPOL-Key 4/4 " MACSTR " -> " MACSTR,
                   MAC2STR(src), MAC2STR(dst));
@@ -624,28 +695,33 @@ static void rx_data_eapol_key_4_of_4(struct wlantest *wt, const u8 *dst,
        eapol = (const struct ieee802_1x_hdr *) data;
        hdr = (const struct wpa_eapol_key *) (eapol + 1);
        if (!is_zero(hdr->key_rsc, 8)) {
-               wpa_printf(MSG_INFO, "EAPOL-Key 4/4 from " MACSTR " used "
-                          "non-zero Key RSC", MAC2STR(src));
+               add_note(wt, MSG_INFO, "EAPOL-Key 4/4 from " MACSTR " used "
+                        "non-zero Key RSC", MAC2STR(src));
        }
        key_info = WPA_GET_BE16(hdr->key_info);
 
        if (!sta->ptk_set && !sta->tptk_set) {
-               wpa_printf(MSG_DEBUG, "No PTK known to process EAPOL-Key 4/4");
+               add_note(wt, MSG_DEBUG,
+                        "No PTK known to process EAPOL-Key 4/4");
                return;
        }
 
        kck = sta->ptk.kck;
+       kck_len = sta->ptk.kck_len;
        if (sta->tptk_set) {
-               wpa_printf(MSG_DEBUG, "Use TPTK for validation EAPOL-Key MIC");
+               add_note(wt, MSG_DEBUG,
+                        "Use TPTK for validation EAPOL-Key MIC");
                kck = sta->tptk.kck;
+               kck_len = sta->tptk.kck_len;
        }
-       if (check_mic(kck, key_info & WPA_KEY_INFO_TYPE_MASK, data, len) < 0) {
-               wpa_printf(MSG_INFO, "Mismatch in EAPOL-Key 4/4 MIC");
+       if (check_mic(kck, kck_len, sta->key_mgmt,
+                     key_info & WPA_KEY_INFO_TYPE_MASK, data, len) < 0) {
+               add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 4/4 MIC");
                return;
        }
-       wpa_printf(MSG_DEBUG, "Valid MIC found in EAPOL-Key 4/4");
+       add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 4/4");
        if (sta->tptk_set) {
-               wpa_printf(MSG_DEBUG, "Update PTK (rekeying)");
+               add_note(wt, MSG_DEBUG, "Update PTK (rekeying)");
                os_memcpy(&sta->ptk, &sta->tptk, sizeof(sta->ptk));
                sta->ptk_set = 1;
                sta->tptk_set = 0;
@@ -680,28 +756,30 @@ static void rx_data_eapol_key_1_of_2(struct wlantest *wt, const u8 *dst,
        key_info = WPA_GET_BE16(hdr->key_info);
 
        if (!sta->ptk_set) {
-               wpa_printf(MSG_DEBUG, "No PTK known to process EAPOL-Key 1/2");
+               add_note(wt, MSG_DEBUG,
+                        "No PTK known to process EAPOL-Key 1/2");
                return;
        }
 
        if (sta->ptk_set &&
-           check_mic(sta->ptk.kck, key_info & WPA_KEY_INFO_TYPE_MASK,
+           check_mic(sta->ptk.kck, sta->ptk.kck_len, sta->key_mgmt,
+                     key_info & WPA_KEY_INFO_TYPE_MASK,
                      data, len) < 0) {
-               wpa_printf(MSG_INFO, "Mismatch in EAPOL-Key 1/2 MIC");
+               add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 1/2 MIC");
                return;
        }
-       wpa_printf(MSG_DEBUG, "Valid MIC found in EAPOL-Key 1/2");
+       add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 1/2");
 
        if (sta->proto & WPA_PROTO_RSN &&
            !(key_info & WPA_KEY_INFO_ENCR_KEY_DATA)) {
-               wpa_printf(MSG_INFO, "EAPOL-Key 1/2 without EncrKeyData bit");
+               add_note(wt, MSG_INFO, "EAPOL-Key 1/2 without EncrKeyData bit");
                return;
        }
        ver = key_info & WPA_KEY_INFO_TYPE_MASK;
-       decrypted = decrypt_eapol_key_data(wt, sta->ptk.kek, ver, hdr,
-                                          &decrypted_len);
+       decrypted = decrypt_eapol_key_data(wt, sta->ptk.kek, sta->ptk.kek_len,
+                                          ver, hdr, &decrypted_len);
        if (decrypted == NULL) {
-               wpa_printf(MSG_INFO, "Failed to decrypt EAPOL-Key Key Data");
+               add_note(wt, MSG_INFO, "Failed to decrypt EAPOL-Key Key Data");
                return;
        }
        wpa_hexdump(MSG_DEBUG, "Decrypted EAPOL-Key Key Data",
@@ -754,7 +832,7 @@ static void rx_data_eapol_key_1_of_2(struct wlantest *wt, const u8 *dst,
                        int id;
                        id = (key_info & WPA_KEY_INFO_KEY_INDEX_MASK) >>
                                WPA_KEY_INFO_KEY_INDEX_SHIFT;
-                       wpa_printf(MSG_DEBUG, "GTK key index %d", id);
+                       add_note(wt, MSG_DEBUG, "GTK key index %d", id);
                        wpa_hexdump(MSG_DEBUG, "GTK", decrypted,
                                    decrypted_len);
                        bss->gtk_len[id] = decrypted_len;
@@ -767,9 +845,9 @@ static void rx_data_eapol_key_1_of_2(struct wlantest *wt, const u8 *dst,
                        bss->rsc[id][5] = rsc[0];
                        wpa_hexdump(MSG_DEBUG, "RSC", bss->rsc[id], 6);
                } else {
-                       wpa_printf(MSG_INFO, "Unexpected WPA Key Data length "
-                                  "in Group Key msg 1/2 from " MACSTR,
-                                  MAC2STR(src));
+                       add_note(wt, MSG_INFO, "Unexpected WPA Key Data length "
+                                "in Group Key msg 1/2 from " MACSTR,
+                                MAC2STR(src));
                }
        }
        os_free(decrypted);
@@ -797,23 +875,25 @@ static void rx_data_eapol_key_2_of_2(struct wlantest *wt, const u8 *dst,
        eapol = (const struct ieee802_1x_hdr *) data;
        hdr = (const struct wpa_eapol_key *) (eapol + 1);
        if (!is_zero(hdr->key_rsc, 8)) {
-               wpa_printf(MSG_INFO, "EAPOL-Key 2/2 from " MACSTR " used "
-                          "non-zero Key RSC", MAC2STR(src));
+               add_note(wt, MSG_INFO, "EAPOL-Key 2/2 from " MACSTR " used "
+                        "non-zero Key RSC", MAC2STR(src));
        }
        key_info = WPA_GET_BE16(hdr->key_info);
 
        if (!sta->ptk_set) {
-               wpa_printf(MSG_DEBUG, "No PTK known to process EAPOL-Key 2/2");
+               add_note(wt, MSG_DEBUG,
+                        "No PTK known to process EAPOL-Key 2/2");
                return;
        }
 
        if (sta->ptk_set &&
-           check_mic(sta->ptk.kck, key_info & WPA_KEY_INFO_TYPE_MASK,
+           check_mic(sta->ptk.kck, sta->ptk.kck_len, sta->key_mgmt,
+                     key_info & WPA_KEY_INFO_TYPE_MASK,
                      data, len) < 0) {
-               wpa_printf(MSG_INFO, "Mismatch in EAPOL-Key 2/2 MIC");
+               add_note(wt, MSG_INFO, "Mismatch in EAPOL-Key 2/2 MIC");
                return;
        }
-       wpa_printf(MSG_DEBUG, "Valid MIC found in EAPOL-Key 2/2");
+       add_note(wt, MSG_DEBUG, "Valid MIC found in EAPOL-Key 2/2");
 }
 
 
@@ -832,8 +912,8 @@ static void rx_data_eapol_key(struct wlantest *wt, const u8 *dst,
        wpa_hexdump(MSG_MSGDUMP, "EAPOL-Key",
                    (const u8 *) hdr, len - sizeof(*eapol));
        if (len < sizeof(*hdr)) {
-               wpa_printf(MSG_INFO, "Too short EAPOL-Key frame from " MACSTR,
-                          MAC2STR(src));
+               add_note(wt, MSG_INFO, "Too short EAPOL-Key frame from " MACSTR,
+                        MAC2STR(src));
                return;
        }
 
@@ -856,8 +936,8 @@ static void rx_data_eapol_key(struct wlantest *wt, const u8 *dst,
        key_data_length = WPA_GET_BE16(hdr->key_data_length);
        key_data = (const u8 *) (hdr + 1);
        if (key_data + key_data_length > data + len) {
-               wpa_printf(MSG_INFO, "Truncated EAPOL-Key from " MACSTR,
-                          MAC2STR(src));
+               add_note(wt, MSG_INFO, "Truncated EAPOL-Key from " MACSTR,
+                        MAC2STR(src));
                return;
        }
        if (key_data + key_data_length < data + len) {
@@ -885,7 +965,8 @@ static void rx_data_eapol_key(struct wlantest *wt, const u8 *dst,
 
        if (ver != WPA_KEY_INFO_TYPE_HMAC_MD5_RC4 &&
            ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES &&
-           ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
+           ver != WPA_KEY_INFO_TYPE_AES_128_CMAC &&
+           ver != WPA_KEY_INFO_TYPE_AKM_DEFINED) {
                wpa_printf(MSG_INFO, "Unsupported EAPOL-Key Key Descriptor "
                           "Version %u from " MACSTR, ver, MAC2STR(src));
                return;
index a2df0e9..5f60abe 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * Received Management frame processing
- * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2010-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -9,6 +9,7 @@
 #include "utils/includes.h"
 
 #include "utils/common.h"
+#include "common/defs.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "crypto/aes_wrap.h"
@@ -86,6 +87,7 @@ static void rx_mgmt_probe_resp(struct wlantest *wt, const u8 *data, size_t len)
        if (bss == NULL)
                return;
 
+       bss->counters[WLANTEST_BSS_COUNTER_PROBE_RESPONSE]++;
        bss->capab_info = le_to_host16(mgmt->u.probe_resp.capab_info);
        if (ieee802_11_parse_elems(mgmt->u.probe_resp.variable,
                                   len - (mgmt->u.probe_resp.variable - data),
@@ -752,12 +754,22 @@ static void rx_mgmt_action(struct wlantest *wt, const u8 *data, size_t len,
 }
 
 
-static int check_mmie_mic(const u8 *igtk, const u8 *data, size_t len)
+static int check_mmie_mic(unsigned int mgmt_group_cipher,
+                         const u8 *igtk, size_t igtk_len,
+                         const u8 *data, size_t len)
 {
        u8 *buf;
        u8 mic[16];
        u16 fc;
        const struct ieee80211_hdr *hdr;
+       int ret, mic_len;
+
+       if (!mgmt_group_cipher || igtk_len < 16)
+               return -1;
+       mic_len = mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC ? 8 : 16;
+
+       if (len < 24 || len - 24 < mic_len)
+               return -1;
 
        buf = os_malloc(len + 20 - 24);
        if (buf == NULL)
@@ -771,19 +783,45 @@ static int check_mmie_mic(const u8 *igtk, const u8 *data, size_t len)
        os_memcpy(buf + 2, hdr->addr1, 3 * ETH_ALEN);
 
        /* Frame body with MMIE MIC masked to zero */
-       os_memcpy(buf + 20, data + 24, len - 24 - 8);
-       os_memset(buf + 20 + len - 24 - 8, 0, 8);
+       os_memcpy(buf + 20, data + 24, len - 24 - mic_len);
+       os_memset(buf + 20 + len - 24 - mic_len, 0, mic_len);
 
        wpa_hexdump(MSG_MSGDUMP, "BIP: AAD|Body(masked)", buf, len + 20 - 24);
        /* MIC = L(AES-128-CMAC(AAD || Frame Body(masked)), 0, 64) */
-       if (omac1_aes_128(igtk, buf, len + 20 - 24, mic) < 0) {
+       if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
+               ret = omac1_aes_128(igtk, buf, len + 20 - 24, mic);
+       } else if (mgmt_group_cipher == WPA_CIPHER_BIP_CMAC_256) {
+               ret = omac1_aes_256(igtk, buf, len + 20 - 24, mic);
+       } else if (mgmt_group_cipher == WPA_CIPHER_BIP_GMAC_128 ||
+                mgmt_group_cipher == WPA_CIPHER_BIP_GMAC_256) {
+               u8 nonce[12], *npos;
+               const u8 *ipn;
+
+               ipn = data + len - mic_len - 6;
+
+               /* Nonce: A2 | IPN */
+               os_memcpy(nonce, hdr->addr2, ETH_ALEN);
+               npos = nonce + ETH_ALEN;
+               *npos++ = ipn[5];
+               *npos++ = ipn[4];
+               *npos++ = ipn[3];
+               *npos++ = ipn[2];
+               *npos++ = ipn[1];
+               *npos++ = ipn[0];
+
+               ret = aes_gmac(igtk, igtk_len, nonce, sizeof(nonce),
+                              buf, len + 20 - 24, mic);
+       } else {
+               ret = -1;
+       }
+       if (ret < 0) {
                os_free(buf);
                return -1;
        }
 
        os_free(buf);
 
-       if (os_memcmp(data + len - 8, mic, 8) != 0)
+       if (os_memcmp(data + len - mic_len, mic, mic_len) != 0)
                return -1;
 
        return 0;
@@ -797,6 +835,7 @@ static int check_bip(struct wlantest *wt, const u8 *data, size_t len)
        const u8 *mmie;
        u16 keyid;
        struct wlantest_bss *bss;
+       size_t mic_len;
 
        mgmt = (const struct ieee80211_mgmt *) data;
        fc = le_to_host16(mgmt->frame_control);
@@ -813,8 +852,11 @@ static int check_bip(struct wlantest *wt, const u8 *data, size_t len)
        if (bss == NULL)
                return 0; /* No key known yet */
 
-       if (len < 24 + 18 || data[len - 18] != WLAN_EID_MMIE ||
-           data[len - 17] != 16) {
+       mic_len = bss->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC ? 8 : 16;
+
+       if (len < 24 + 10 + mic_len ||
+           data[len - (10 + mic_len)] != WLAN_EID_MMIE ||
+           data[len - (10 + mic_len - 1)] != 8 + mic_len) {
                /* No MMIE */
                if (bss->rsn_capab & WPA_CAPABILITY_MFPC) {
                        add_note(wt, MSG_INFO, "Robust group-addressed "
@@ -826,7 +868,7 @@ static int check_bip(struct wlantest *wt, const u8 *data, size_t len)
                return 0;
        }
 
-       mmie = data + len - 16;
+       mmie = data + len - (8 + mic_len);
        keyid = WPA_GET_LE16(mmie);
        if (keyid & 0xf000) {
                add_note(wt, MSG_INFO, "MMIE KeyID reserved bits not zero "
@@ -841,9 +883,9 @@ static int check_bip(struct wlantest *wt, const u8 *data, size_t len)
        }
        wpa_printf(MSG_DEBUG, "MMIE KeyID %u", keyid);
        wpa_hexdump(MSG_MSGDUMP, "MMIE IPN", mmie + 2, 6);
-       wpa_hexdump(MSG_MSGDUMP, "MMIE MIC", mmie + 8, 8);
+       wpa_hexdump(MSG_MSGDUMP, "MMIE MIC", mmie + 8, mic_len);
 
-       if (!bss->igtk_set[keyid]) {
+       if (!bss->igtk_len[keyid]) {
                add_note(wt, MSG_DEBUG, "No IGTK known to validate BIP frame");
                return 0;
        }
@@ -855,7 +897,8 @@ static int check_bip(struct wlantest *wt, const u8 *data, size_t len)
                wpa_hexdump(MSG_INFO, "Last RX IPN", bss->ipn[keyid], 6);
        }
 
-       if (check_mmie_mic(bss->igtk[keyid], data, len) < 0) {
+       if (check_mmie_mic(bss->mgmt_group_cipher, bss->igtk[keyid],
+                          bss->igtk_len[keyid], data, len) < 0) {
                add_note(wt, MSG_INFO, "Invalid MMIE MIC in a frame from "
                         MACSTR, MAC2STR(mgmt->sa));
                bss->counters[WLANTEST_BSS_COUNTER_INVALID_BIP_MMIE]++;
@@ -929,16 +972,18 @@ static u8 * mgmt_ccmp_decrypt(struct wlantest *wt, const u8 *data, size_t len,
        if (os_memcmp(pn, rsc, 6) <= 0) {
                u16 seq_ctrl = le_to_host16(hdr->seq_ctrl);
                add_note(wt, MSG_INFO, "CCMP/TKIP replay detected: A1=" MACSTR
-                        " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u",
+                        " A2=" MACSTR " A3=" MACSTR " seq=%u frag=%u%s",
                         MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
                         MAC2STR(hdr->addr3),
                         WLAN_GET_SEQ_SEQ(seq_ctrl),
-                        WLAN_GET_SEQ_FRAG(seq_ctrl));
+                        WLAN_GET_SEQ_FRAG(seq_ctrl),
+                        (le_to_host16(hdr->frame_control) & WLAN_FC_RETRY) ?
+                        " Retry" : "");
                wpa_hexdump(MSG_INFO, "RX PN", pn, 6);
                wpa_hexdump(MSG_INFO, "RSC", rsc, 6);
        }
 
-       decrypted = ccmp_decrypt(sta->ptk.tk1, hdr, data + 24, len - 24, dlen);
+       decrypted = ccmp_decrypt(sta->ptk.tk, hdr, data + 24, len - 24, dlen);
        if (decrypted) {
                os_memcpy(rsc, pn, 6);
                frame = os_malloc(24 + *dlen);
index 115ef8a..1268b8a 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * STA list
- * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2010-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -82,6 +82,14 @@ void sta_update_assoc(struct wlantest_sta *sta, struct ieee802_11_elems *elems)
                elems->rsn_ie = NULL;
        }
 
+       if (elems->osen && !bss->osenie[0]) {
+               wpa_printf(MSG_INFO, "OSEN IE included in Association Request "
+                          "frame from " MACSTR " even though BSS does not "
+                          "use OSEN - ignore IE",
+                          MAC2STR(sta->addr));
+               elems->osen = NULL;
+       }
+
        if (elems->wpa_ie && elems->rsn_ie) {
                wpa_printf(MSG_INFO, "Both WPA IE and RSN IE included in "
                           "Association Request frame from " MACSTR,
@@ -108,6 +116,15 @@ void sta_update_assoc(struct wlantest_sta *sta, struct ieee802_11_elems *elems)
                        wpa_printf(MSG_INFO, "Failed to parse WPA IE from "
                                   MACSTR, MAC2STR(sta->addr));
                }
+       } else if (elems->osen) {
+               wpa_hexdump(MSG_DEBUG, "OSEN IE", elems->osen - 2,
+                           elems->osen_len + 2);
+               os_memcpy(sta->osenie, elems->osen - 2, elems->osen_len + 2);
+               sta->proto = WPA_PROTO_OSEN;
+               sta->pairwise_cipher = WPA_CIPHER_CCMP;
+               sta->key_mgmt = WPA_KEY_MGMT_OSEN;
+               sta->rsn_capab = 0;
+               goto skip_rsn_wpa;
        } else {
                sta->rsnie[0] = 0;
                sta->proto = 0;
@@ -151,18 +168,24 @@ void sta_update_assoc(struct wlantest_sta *sta, struct ieee802_11_elems *elems)
 
 skip_rsn_wpa:
        wpa_printf(MSG_INFO, "STA " MACSTR
-                  " proto=%s%s%s"
-                  "pairwise=%s%s%s%s"
-                  "key_mgmt=%s%s%s%s%s%s%s%s"
+                  " proto=%s%s%s%s"
+                  "pairwise=%s%s%s%s%s%s%s"
+                  "key_mgmt=%s%s%s%s%s%s%s%s%s%s%s"
                   "rsn_capab=%s%s%s%s%s",
                   MAC2STR(sta->addr),
                   sta->proto == 0 ? "OPEN " : "",
                   sta->proto & WPA_PROTO_WPA ? "WPA " : "",
                   sta->proto & WPA_PROTO_RSN ? "WPA2 " : "",
+                  sta->proto & WPA_PROTO_OSEN ? "OSEN " : "",
                   sta->pairwise_cipher == 0 ? "N/A " : "",
                   sta->pairwise_cipher & WPA_CIPHER_NONE ? "NONE " : "",
                   sta->pairwise_cipher & WPA_CIPHER_TKIP ? "TKIP " : "",
                   sta->pairwise_cipher & WPA_CIPHER_CCMP ? "CCMP " : "",
+                  bss->pairwise_cipher & WPA_CIPHER_CCMP_256 ? "CCMP-256 " :
+                  "",
+                  bss->pairwise_cipher & WPA_CIPHER_GCMP ? "GCMP " : "",
+                  bss->pairwise_cipher & WPA_CIPHER_GCMP_256 ? "GCMP-256 " :
+                  "",
                   sta->key_mgmt == 0 ? "N/A " : "",
                   sta->key_mgmt & WPA_KEY_MGMT_IEEE8021X ? "EAP " : "",
                   sta->key_mgmt & WPA_KEY_MGMT_PSK ? "PSK " : "",
@@ -173,6 +196,11 @@ skip_rsn_wpa:
                   "EAP-SHA256 " : "",
                   sta->key_mgmt & WPA_KEY_MGMT_PSK_SHA256 ?
                   "PSK-SHA256 " : "",
+                  sta->key_mgmt & WPA_KEY_MGMT_OSEN ? "OSEN " : "",
+                  sta->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B ?
+                  "EAP-SUITE-B " : "",
+                  sta->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 ?
+                  "EAP-SUITE-B-192 " : "",
                   sta->rsn_capab & WPA_CAPABILITY_PREAUTH ? "PREAUTH " : "",
                   sta->rsn_capab & WPA_CAPABILITY_NO_PAIRWISE ?
                   "NO_PAIRWISE " : "",
index 6c716c4..321d930 100644 (file)
 #include "wlantest.h"
 
 
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-
-
 static void test_vector_tkip(void)
 {
        u8 tk[] = {
@@ -163,7 +159,8 @@ static void test_vector_bip(void)
        wpa_hexdump(MSG_INFO, "IPN", ipn, sizeof(ipn));
        wpa_hexdump(MSG_INFO, "Plaintext frame", frame, sizeof(frame));
 
-       prot = bip_protect(igtk, frame, sizeof(frame), ipn, 4, &prot_len);
+       prot = bip_protect(igtk, sizeof(igtk), frame, sizeof(frame),
+                          ipn, 4, &prot_len);
        if (prot == NULL) {
                wpa_printf(MSG_ERROR, "Failed to protect BIP frame");
                return;
@@ -225,69 +222,219 @@ static void test_vector_ccmp_mgmt(void)
 }
 
 
-static void test_vector_gcmp(void)
+struct gcmp_test {
+       u8 tk[16];
+       u8 pn[6];
+       u8 frame[300];
+       size_t hdr_len;
+       size_t payload_len;
+       u8 mic[16];
+       u8 encr[300];
+};
+
+static struct gcmp_test gcmp_vectors[] =
+{
+       {
+               .tk = { 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
+                       0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
+               .pn = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 },
+               .frame = {
+                       0x20, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+                       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+               },
+               .hdr_len = 24,
+               .payload_len = 256,
+               .mic = {
+                       0x80, 0xCB, 0x06, 0x62, 0xEA, 0x71, 0xAB, 0xFD,
+                       0x9F, 0x04, 0xC7, 0xF8, 0x72, 0xF5, 0x80, 0x90 },
+               .encr = {
+                       0x5F, 0x55, 0x78, 0xC1, 0x8F, 0x13, 0x7A, 0xD2,
+                       0x79, 0xBF, 0x3F, 0x2B, 0x24, 0xC7, 0xBD, 0x8F,
+                       0x27, 0x7A, 0x1B, 0xE6, 0x77, 0x0D, 0xA1, 0xD9,
+                       0x8B, 0x70, 0xC6, 0xD2, 0x8A, 0xE0, 0x1C, 0x55,
+                       0x9E, 0xCB, 0xA6, 0xA0, 0x1D, 0xB0, 0x67, 0xC5,
+                       0xA2, 0x7E, 0x4D, 0xB0, 0x8C, 0xDA, 0xDC, 0x77,
+                       0x52, 0xAD, 0x63, 0x7E, 0xAF, 0x0A, 0x18, 0xED,
+                       0x13, 0xFB, 0xAA, 0x14, 0x3B, 0xAF, 0xEF, 0x18,
+                       0xF8, 0xFB, 0xCE, 0x4C, 0x65, 0xE8, 0x6B, 0xD0,
+                       0x2A, 0x87, 0xB6, 0x01, 0xB7, 0xEA, 0xB9, 0x3F,
+                       0x2B, 0xBC, 0x87, 0x4C, 0x8A, 0x71, 0x05, 0x80,
+                       0xF5, 0x02, 0x34, 0x1A, 0x6A, 0x53, 0x39, 0x31,
+                       0x43, 0xDE, 0x4C, 0x9E, 0xC6, 0xA2, 0x86, 0xF1,
+                       0x25, 0x71, 0x83, 0x78, 0xAE, 0xDC, 0x84, 0xEB,
+                       0xA2, 0xB3, 0x0F, 0x5C, 0x28, 0xBB, 0x5D, 0x75,
+                       0xC6, 0xB0, 0x25, 0x46, 0x6D, 0x06, 0x51, 0xC7,
+                       0x22, 0xDC, 0x71, 0x15, 0x1F, 0x21, 0x2D, 0x68,
+                       0x87, 0x82, 0x8A, 0x03, 0x82, 0xE9, 0x28, 0x8A,
+                       0x7F, 0x43, 0xD5, 0x2B, 0x7D, 0x25, 0x08, 0x61,
+                       0x57, 0x64, 0x69, 0x54, 0xBB, 0x43, 0xB5, 0x7E,
+                       0xA5, 0x87, 0xA0, 0x25, 0xF4, 0x0C, 0xE7, 0x45,
+                       0x11, 0xE4, 0xDD, 0x22, 0x85, 0xB4, 0x0B, 0xA3,
+                       0xF3, 0xB9, 0x62, 0x62, 0xCB, 0xC2, 0x8C, 0x6A,
+                       0xA7, 0xBE, 0x44, 0x3E, 0x7B, 0x41, 0xE1, 0xEB,
+                       0xFF, 0x52, 0x48, 0x57, 0xA6, 0x81, 0x68, 0x97,
+                       0x75, 0x01, 0x15, 0xB0, 0x23, 0x1A, 0xB7, 0xC2,
+                       0x84, 0x72, 0xC0, 0x6D, 0xD0, 0xB4, 0x9B, 0xE9,
+                       0xF3, 0x69, 0xA8, 0xC3, 0x9C, 0xCD, 0x0D, 0xB7,
+                       0x98, 0x35, 0x10, 0xE1, 0xAE, 0x8F, 0x05, 0xD7,
+                       0x75, 0x45, 0xE0, 0x23, 0x5C, 0xDB, 0xD6, 0x12,
+                       0xF3, 0x15, 0x07, 0x54, 0xCE, 0xE5, 0xCE, 0x6A,
+                       0x12, 0x25, 0xD9, 0x95, 0x25, 0x02, 0x6F, 0x74
+               }
+       },
+       {
+               .tk = { 0xc9, 0x7c, 0x1f, 0x67, 0xce, 0x37, 0x11, 0x85,
+                       0x51, 0x4a, 0x8a, 0x19, 0xf2, 0xbd, 0xd5, 0x2f },
+               .pn = { 0x00, 0x89, 0x5F, 0x5F, 0x2B, 0x08 },
+               .frame = {
+                       0x88, 0x48, 0x0b, 0x00, 0x0f, 0xd2, 0xe1, 0x28,
+                       0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+                       0x50, 0x30, 0xf1, 0x84, 0x44, 0x08, 0x80, 0x33,
+                       0x03, 0x00,
+
+                       0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                       0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+                       0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+                       0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+                       0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27
+               },
+               .hdr_len = 26,
+               .payload_len = 40,
+               .mic = {
+                       0xde, 0xf6, 0x19, 0xc2, 0xa3, 0x74, 0xb6, 0xdf,
+                       0x66, 0xff, 0xa5, 0x3b, 0x6c, 0x69, 0xd7, 0x9e },
+               .encr = {
+                       0x60, 0xe9, 0x70, 0x0c, 0xc4, 0xd4, 0x0a, 0xc6,
+                       0xd2, 0x88, 0xb2, 0x01, 0xc3, 0x8f, 0x5b, 0xf0,
+                       0x8b, 0x80, 0x74, 0x42, 0x64, 0x0a, 0x15, 0x96,
+                       0xe5, 0xdb, 0xda, 0xd4, 0x1d, 0x1f, 0x36, 0x23,
+                       0xf4, 0x5d, 0x7a, 0x12, 0xdb, 0x7a, 0xfb, 0x23
+               }
+       }
+};
+
+
+static int run_gcmp(int idx, struct gcmp_test *vector)
 {
-       u8 tk[] = { 0xc9, 0x7c, 0x1f, 0x67, 0xce, 0x37, 0x11, 0x85,
-                   0x51, 0x4a, 0x8a, 0x19, 0xf2, 0xbd, 0xd5, 0x2f };
-       u8 pn[] = {
-               0x00, 0x89, 0x5F, 0x5F, 0x2B, 0x08
-       };
-       u8 frame[] = {
-               0x88, 0x48, 0x0b, 0x00, 0x0f, 0xd2, 0xe1, 0x28,
-               0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
-               0x50, 0x30, 0xf1, 0x84, 0x44, 0x08, 0x80, 0x33,
-               0x03, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
-               0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
-               0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
-               0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
-               0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
-               0x26, 0x27
-       };
        u8 *enc, *plain;
        size_t enc_len, plain_len;
        u8 fcs[4];
-
-       wpa_printf(MSG_INFO, "\nIEEE P802.11ad/D9.0, M.11.1 GCMP test "
-                  "vector #2\n");
-
-       wpa_hexdump(MSG_INFO, "TK", tk, sizeof(tk));
-       wpa_hexdump(MSG_INFO, "PN", pn, sizeof(pn));
-       wpa_hexdump(MSG_INFO, "802.11 Header", frame, 26);
-       wpa_hexdump(MSG_INFO, "Plaintext Data", frame + 26, sizeof(frame) - 26);
-
-       enc = gcmp_encrypt(tk, sizeof(tk), frame, sizeof(frame), 26, frame + 24,
-                          pn, 0, &enc_len);
+       int err = 0;
+
+       wpa_printf(MSG_INFO,
+                  "\nIEEE Std 802.11ad-2012, M.11.1 GCMP test mpdu #%d\n",
+                  idx);
+
+       wpa_hexdump(MSG_INFO, "TK", vector->tk, sizeof(vector->tk));
+       wpa_hexdump(MSG_INFO, "PN", vector->pn, sizeof(vector->pn));
+       wpa_hexdump(MSG_INFO, "802.11 Header", vector->frame, vector->hdr_len);
+       wpa_hexdump(MSG_INFO, "Plaintext Data",
+                   vector->frame + vector->hdr_len,
+                   vector->payload_len);
+
+       enc = gcmp_encrypt(vector->tk, sizeof(vector->tk),
+                          vector->frame,
+                          vector->hdr_len + vector->payload_len,
+                          vector->hdr_len,
+                          vector->hdr_len == 26 ?
+                          vector->frame + vector->hdr_len - 2 : NULL,
+                          vector->pn, 0, &enc_len);
        if (enc == NULL) {
                wpa_printf(MSG_ERROR, "Failed to encrypt GCMP frame");
-               return;
+               return 1;
        }
 
        wpa_hexdump(MSG_INFO, "Encrypted MPDU (without FCS)", enc, enc_len);
+       if (os_memcmp(vector->encr, enc + vector->hdr_len + 8,
+                     vector->payload_len) != 0) {
+               wpa_printf(MSG_ERROR, "GCMP test mpdu #%d enctypted data mismatch",
+                          idx);
+               err++;
+       }
+       if (os_memcmp(vector->mic, enc + enc_len - sizeof(vector->mic),
+                     sizeof(vector->mic)) != 0) {
+               wpa_printf(MSG_ERROR, "GCMP test mpdu #%d MIC mismatch", idx);
+               err++;
+       }
        WPA_PUT_LE32(fcs, crc32(enc, enc_len));
        wpa_hexdump(MSG_INFO, "FCS", fcs, sizeof(fcs));
 
        wpa_debug_level = MSG_INFO;
-       plain = gcmp_decrypt(tk, sizeof(tk), (const struct ieee80211_hdr *) enc,
-                            enc + 26, enc_len - 26, &plain_len);
+       plain = gcmp_decrypt(vector->tk, sizeof(vector->tk),
+                            (const struct ieee80211_hdr *) enc,
+                            enc + vector->hdr_len,
+                            enc_len - vector->hdr_len, &plain_len);
        wpa_debug_level = MSG_EXCESSIVE;
        os_free(enc);
 
        if (plain == NULL) {
                wpa_printf(MSG_ERROR, "Failed to decrypt GCMP frame");
-               return;
+               return 1;
        }
 
-       if (plain_len != sizeof(frame) - 26 ||
-           os_memcmp(plain, frame + 26, plain_len) != 0) {
+       if (plain_len != vector->payload_len ||
+           os_memcmp(plain, vector->frame + vector->hdr_len, plain_len) != 0) {
                wpa_hexdump(MSG_ERROR, "Decryption result did not match",
                            plain, plain_len);
+               err++;
        }
 
        os_free(plain);
+
+       return err;
 }
 
 
-static void test_vector_gcmp_256(void)
+static int test_vector_gcmp(void)
+{
+       int err = 0;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(gcmp_vectors); i++) {
+               if (run_gcmp(i + 1, &gcmp_vectors[i]))
+                       err++;
+
+       }
+
+       return err;
+}
+
+
+static int test_vector_gcmp_256(void)
 {
        u8 tk[] = { 0xc9, 0x7c, 0x1f, 0x67, 0xce, 0x37, 0x11, 0x85,
                    0x51, 0x4a, 0x8a, 0x19, 0xf2, 0xbd, 0xd5, 0x2f,
@@ -307,11 +454,26 @@ static void test_vector_gcmp_256(void)
                0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
                0x26, 0x27
        };
+       u8 encr[] = {
+               0x88, 0x48, 0x0b, 0x00, 0x0f, 0xd2, 0xe1, 0x28,
+               0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+               0x50, 0x30, 0xf1, 0x84, 0x44, 0x08, 0x80, 0x33,
+               0x03, 0x00, 0x08, 0x2b, 0x00, 0x20, 0x5f, 0x5f,
+               0x89, 0x00, 0x65, 0x83, 0x43, 0xc8, 0xb1, 0x44,
+               0x47, 0xd9, 0x21, 0x1d, 0xef, 0xd4, 0x6a, 0xd8,
+               0x9c, 0x71, 0x0c, 0x6f, 0xc3, 0x33, 0x33, 0x23,
+               0x6e, 0x39, 0x97, 0xb9, 0x17, 0x6a, 0x5a, 0x8b,
+               0xe7, 0x79, 0xb2, 0x12, 0x66, 0x55, 0x5e, 0x70,
+               0xad, 0x79, 0x11, 0x43, 0x16, 0x85, 0x90, 0x95,
+               0x47, 0x3d, 0x5b, 0x1b, 0xd5, 0x96, 0xb3, 0xde,
+               0xa3, 0xbf
+       };
        u8 *enc, *plain;
        size_t enc_len, plain_len;
        u8 fcs[4];
+       int err = 0;
 
-       wpa_printf(MSG_INFO, "\nGCMP-256 test vector\n");
+       wpa_printf(MSG_INFO, "\nIEEE P802.11ac/D7.0, M.11.1 GCMP-256 test vector\n");
 
        wpa_hexdump(MSG_INFO, "TK", tk, sizeof(tk));
        wpa_hexdump(MSG_INFO, "PN", pn, sizeof(pn));
@@ -322,10 +484,14 @@ static void test_vector_gcmp_256(void)
                           pn, 0, &enc_len);
        if (enc == NULL) {
                wpa_printf(MSG_ERROR, "Failed to encrypt GCMP frame");
-               return;
+               return 1;
        }
 
        wpa_hexdump(MSG_INFO, "Encrypted MPDU (without FCS)", enc, enc_len);
+       if (enc_len != sizeof(encr) || os_memcmp(enc, encr, enc_len) != 0) {
+               wpa_printf(MSG_ERROR, "GCMP-256 test vector mismatch");
+               err++;
+       }
        WPA_PUT_LE32(fcs, crc32(enc, enc_len));
        wpa_hexdump(MSG_INFO, "FCS", fcs, sizeof(fcs));
 
@@ -337,20 +503,23 @@ static void test_vector_gcmp_256(void)
 
        if (plain == NULL) {
                wpa_printf(MSG_ERROR, "Failed to decrypt GCMP frame");
-               return;
+               return 1;
        }
 
        if (plain_len != sizeof(frame) - 26 ||
            os_memcmp(plain, frame + 26, plain_len) != 0) {
                wpa_hexdump(MSG_ERROR, "Decryption result did not match",
                            plain, plain_len);
+               err++;
        }
 
        os_free(plain);
+
+       return err;
 }
 
 
-static void test_vector_ccmp_256(void)
+static int test_vector_ccmp_256(void)
 {
        u8 tk[] = { 0xc9, 0x7c, 0x1f, 0x67, 0xce, 0x37, 0x11, 0x85,
                    0x51, 0x4a, 0x8a, 0x19, 0xf2, 0xbd, 0xd5, 0x2f,
@@ -365,11 +534,24 @@ static void test_vector_ccmp_256(void)
                0x96, 0x7b, 0xb6, 0x2f, 0xb6, 0xcd, 0xa8, 0xeb,
                0x7e, 0x78, 0xa0, 0x50
        };
+       u8 encr[] = {
+               0x08, 0x48, 0xc3, 0x2c, 0x0f, 0xd2, 0xe1, 0x28,
+               0xa5, 0x7c, 0x50, 0x30, 0xf1, 0x84, 0x44, 0x08,
+               0xab, 0xae, 0xa5, 0xb8, 0xfc, 0xba, 0x80, 0x33,
+               0x0c, 0xe7, 0x00, 0x20, 0x76, 0x97, 0x03, 0xb5,
+               0x6d, 0x15, 0x5d, 0x88, 0x32, 0x66, 0x82, 0x56,
+               0xd6, 0xa9, 0x2b, 0x78, 0xe1, 0x1d, 0x8e, 0x54,
+               0x49, 0x5d, 0xd1, 0x74, 0x80, 0xaa, 0x56, 0xc9,
+               0x49, 0x2e, 0x88, 0x2b, 0x97, 0x64, 0x2f, 0x80,
+               0xd5, 0x0f, 0xe9, 0x7b
+
+       };
        u8 *enc, *plain;
        size_t enc_len, plain_len;
        u8 fcs[4];
+       int err = 0;
 
-       wpa_printf(MSG_INFO, "\nCCMP-256 test vector\n");
+       wpa_printf(MSG_INFO, "\nIEEE P802.11ac/D7.0, M.6.4 CCMP-256 test vector\n");
 
        wpa_hexdump(MSG_INFO, "TK", tk, sizeof(tk));
        wpa_hexdump(MSG_INFO, "PN", pn, sizeof(pn));
@@ -380,10 +562,14 @@ static void test_vector_ccmp_256(void)
                               &enc_len);
        if (enc == NULL) {
                wpa_printf(MSG_ERROR, "Failed to encrypt CCMP frame");
-               return;
+               return 1;
        }
 
        wpa_hexdump(MSG_INFO, "Encrypted MPDU (without FCS)", enc, enc_len);
+       if (enc_len != sizeof(encr) || os_memcmp(enc, encr, enc_len) != 0) {
+               wpa_printf(MSG_ERROR, "CCMP-256 test vector mismatch");
+               err++;
+       }
        WPA_PUT_LE32(fcs, crc32(enc, enc_len));
        wpa_hexdump(MSG_INFO, "FCS", fcs, sizeof(fcs));
 
@@ -395,20 +581,23 @@ static void test_vector_ccmp_256(void)
 
        if (plain == NULL) {
                wpa_printf(MSG_ERROR, "Failed to decrypt CCMP-256 frame");
-               return;
+               return 1;
        }
 
        if (plain_len != sizeof(frame) - 24 ||
            os_memcmp(plain, frame + 24, plain_len) != 0) {
                wpa_hexdump(MSG_ERROR, "Decryption result did not match",
                            plain, plain_len);
+               err++;
        }
 
        os_free(plain);
+
+       return err;
 }
 
 
-static void test_vector_bip_gmac_128(void)
+static int test_vector_bip_gmac_128(void)
 {
        u8 igtk[] = {
                0x4e, 0xa9, 0x54, 0x3e, 0x09, 0xcf, 0x2b, 0x1e,
@@ -421,10 +610,20 @@ static void test_vector_bip_gmac_128(void)
                0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
                0x02, 0x00
        };
+       u8 res[] = {
+               0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+               0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+               0x02, 0x00, 0x4c, 0x18, 0x04, 0x00, 0x04, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x3e, 0xd8, 0x62, 0xfb,
+               0x0f, 0x33, 0x38, 0xdd, 0x33, 0x86, 0xc8, 0x97,
+               0xe2, 0xed, 0x05, 0x3d
+       };
        u8 *prot;
        size_t prot_len;
+       int err = 0;
 
-       wpa_printf(MSG_INFO, "\nBIP-GMAC-128 with broadcast "
+       wpa_printf(MSG_INFO, "\nIEEE P802.11ac/D7.0, M.9.1 BIP-GMAC-128 with broadcast "
                   "Deauthentication frame\n");
 
        wpa_hexdump(MSG_INFO, "IGTK", igtk, sizeof(igtk));
@@ -435,15 +634,21 @@ static void test_vector_bip_gmac_128(void)
                                ipn, 4, &prot_len);
        if (prot == NULL) {
                wpa_printf(MSG_ERROR, "Failed to protect BIP-GMAC-128 frame");
-               return;
+               return 1;
        }
 
        wpa_hexdump(MSG_INFO, "Protected MPDU (without FCS)", prot, prot_len);
+       if (prot_len != sizeof(res) || os_memcmp(res, prot, prot_len) != 0) {
+               wpa_printf(MSG_ERROR, "BIP-GMAC-128 test vector mismatch");
+               err++;
+       }
        os_free(prot);
+
+       return err;
 }
 
 
-static void test_vector_bip_gmac_256(void)
+static int test_vector_bip_gmac_256(void)
 {
        u8 igtk[] = {
                0x4e, 0xa9, 0x54, 0x3e, 0x09, 0xcf, 0x2b, 0x1e,
@@ -458,11 +663,20 @@ static void test_vector_bip_gmac_256(void)
                0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
                0x02, 0x00
        };
+       u8 res[] = {
+               0xc0, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+               0xff, 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00,
+               0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+               0x02, 0x00, 0x4c, 0x18, 0x04, 0x00, 0x04, 0x00,
+               0x00, 0x00, 0x00, 0x00, 0x23, 0xbe, 0x59, 0xdc,
+               0xc7, 0x02, 0x2e, 0xe3, 0x83, 0x62, 0x7e, 0xbb,
+               0x10, 0x17, 0xdd, 0xfc
+       };
        u8 *prot;
        size_t prot_len;
+       int err = 0;
 
-       wpa_printf(MSG_INFO, "\nBIP-GMAC-256 with broadcast "
-                  "Deauthentication frame\n");
+       wpa_printf(MSG_INFO, "\nIEEE P802.11ac/D7.0, M.9.1 BIP-GMAC-256 with broadcast Deauthentication frame\n");
 
        wpa_hexdump(MSG_INFO, "IGTK", igtk, sizeof(igtk));
        wpa_hexdump(MSG_INFO, "IPN", ipn, sizeof(ipn));
@@ -472,16 +686,24 @@ static void test_vector_bip_gmac_256(void)
                                ipn, 4, &prot_len);
        if (prot == NULL) {
                wpa_printf(MSG_ERROR, "Failed to protect BIP-GMAC-256 frame");
-               return;
+               return 1;
        }
 
        wpa_hexdump(MSG_INFO, "Protected MPDU (without FCS)", prot, prot_len);
+       if (prot_len != sizeof(res) || os_memcmp(res, prot, prot_len) != 0) {
+               wpa_printf(MSG_ERROR, "BIP-GMAC-128 test vector mismatch");
+               err++;
+       }
        os_free(prot);
+
+       return err;
 }
 
 
 int main(int argc, char *argv[])
 {
+       int errors = 0;
+
        wpa_debug_level = MSG_EXCESSIVE;
        wpa_debug_show_keys = 1;
 
@@ -492,13 +714,15 @@ int main(int argc, char *argv[])
        test_vector_ccmp();
        test_vector_bip();
        test_vector_ccmp_mgmt();
-       test_vector_gcmp();
-       test_vector_gcmp_256();
-       test_vector_ccmp_256();
-       test_vector_bip_gmac_128();
-       test_vector_bip_gmac_256();
-
+       errors += test_vector_gcmp();
+       errors += test_vector_gcmp_256();
+       errors += test_vector_ccmp_256();
+       errors += test_vector_bip_gmac_128();
+       errors += test_vector_bip_gmac_256();
+
+       if (errors)
+               wpa_printf(MSG_INFO, "One or more test vectors failed");
        os_program_deinit();
 
-       return 0;
+       return errors ? -1 : 0;
 }
index 5b691fa..ab3b2fc 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * wlantest - IEEE 802.11 protocol monitoring and testing tool
- * Copyright (c) 2010-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2010-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 #include "wlantest.h"
 
 
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-
-
 static void wlantest_terminate(int sig, void *signal_ctx)
 {
        eloop_terminate();
@@ -25,12 +21,13 @@ static void wlantest_terminate(int sig, void *signal_ctx)
 
 static void usage(void)
 {
-       printf("wlantest [-cddhqqF] [-i<ifname>] [-r<pcap file>] "
+       printf("wlantest [-cddhqqFt] [-i<ifname>] [-r<pcap file>] "
               "[-p<passphrase>]\n"
               "         [-I<wired ifname>] [-R<wired pcap file>] "
               "[-P<RADIUS shared secret>]\n"
               "         [-n<write pcapng file>]\n"
-              "         [-w<write pcap file>] [-f<MSK/PMK file>]\n");
+              "         [-w<write pcap file>] [-f<MSK/PMK file>]\n"
+              "         [-L<log file>] [-T<PTK file>]\n");
 }
 
 
@@ -61,6 +58,7 @@ static void wlantest_init(struct wlantest *wt)
        dl_list_init(&wt->secret);
        dl_list_init(&wt->radius);
        dl_list_init(&wt->pmk);
+       dl_list_init(&wt->ptk);
        dl_list_init(&wt->wep);
 }
 
@@ -72,12 +70,20 @@ void radius_deinit(struct wlantest_radius *r)
 }
 
 
+static void ptk_deinit(struct wlantest_ptk *ptk)
+{
+       dl_list_del(&ptk->list);
+       os_free(ptk);
+}
+
+
 static void wlantest_deinit(struct wlantest *wt)
 {
        struct wlantest_passphrase *p, *pn;
        struct wlantest_radius_secret *s, *sn;
        struct wlantest_radius *r, *rn;
        struct wlantest_pmk *pmk, *np;
+       struct wlantest_ptk *ptk, *npt;
        struct wlantest_wep *wep, *nw;
 
        if (wt->ctrl_sock >= 0)
@@ -95,6 +101,8 @@ static void wlantest_deinit(struct wlantest *wt)
                radius_deinit(r);
        dl_list_for_each_safe(pmk, np, &wt->pmk, struct wlantest_pmk, list)
                pmk_deinit(pmk);
+       dl_list_for_each_safe(ptk, npt, &wt->ptk, struct wlantest_ptk, list)
+               ptk_deinit(ptk);
        dl_list_for_each_safe(wep, nw, &wt->wep, struct wlantest_wep, list)
                os_free(wep);
        write_pcap_deinit(wt);
@@ -170,6 +178,59 @@ static int add_pmk_file(struct wlantest *wt, const char *pmk_file)
 }
 
 
+static int add_ptk_file(struct wlantest *wt, const char *ptk_file)
+{
+       FILE *f;
+       u8 ptk[64];
+       size_t ptk_len;
+       char buf[300], *pos;
+       struct wlantest_ptk *p;
+
+       f = fopen(ptk_file, "r");
+       if (f == NULL) {
+               wpa_printf(MSG_ERROR, "Could not open '%s'", ptk_file);
+               return -1;
+       }
+
+       while (fgets(buf, sizeof(buf), f)) {
+               pos = buf;
+               while (*pos && *pos != '\r' && *pos != '\n')
+                       pos++;
+               *pos = '\0';
+               ptk_len = pos - buf;
+               if (ptk_len & 1)
+                       continue;
+               ptk_len /= 2;
+               if (ptk_len != 16 && ptk_len != 32 &&
+                   ptk_len != 48 && ptk_len != 64)
+                       continue;
+               if (hexstr2bin(buf, ptk, ptk_len) < 0)
+                       continue;
+               p = os_zalloc(sizeof(*p));
+               if (p == NULL)
+                       break;
+               if (ptk_len < 48) {
+                       os_memcpy(p->ptk.tk, ptk, ptk_len);
+                       p->ptk.tk_len = ptk_len;
+                       p->ptk_len = 32 + ptk_len;
+               } else {
+                       os_memcpy(p->ptk.kck, ptk, 16);
+                       p->ptk.kck_len = 16;
+                       os_memcpy(p->ptk.kek, ptk + 16, 16);
+                       p->ptk.kek_len = 16;
+                       os_memcpy(p->ptk.tk, ptk + 32, ptk_len - 32);
+                       p->ptk.tk_len = ptk_len - 32;
+                       p->ptk_len = ptk_len;
+               }
+               dl_list_add(&wt->ptk, &p->list);
+               wpa_hexdump(MSG_DEBUG, "Added PTK from file", ptk, ptk_len);
+       }
+
+       fclose(f);
+       return 0;
+}
+
+
 int add_wep(struct wlantest *wt, const char *key)
 {
        struct wlantest_wep *w;
@@ -245,15 +306,38 @@ size_t notes_len(struct wlantest *wt, size_t hdrlen)
 }
 
 
+int wlantest_relog(struct wlantest *wt)
+{
+       int ret = 0;
+
+       wpa_printf(MSG_INFO, "Re-open log/capture files");
+       if (wpa_debug_reopen_file())
+               ret = -1;
+
+       if (wt->write_file) {
+               write_pcap_deinit(wt);
+               if (write_pcap_init(wt, wt->write_file) < 0)
+                       ret = -1;
+       }
+
+       if (wt->pcapng_file) {
+               write_pcapng_deinit(wt);
+               if (write_pcapng_init(wt, wt->pcapng_file) < 0)
+                       ret = -1;
+       }
+
+       return ret;
+}
+
+
 int main(int argc, char *argv[])
 {
        int c;
        const char *read_file = NULL;
        const char *read_wired_file = NULL;
-       const char *write_file = NULL;
        const char *ifname = NULL;
        const char *ifname_wired = NULL;
-       const char *pcapng_file = NULL;
+       const char *logfile = NULL;
        struct wlantest wt;
        int ctrl_iface = 0;
 
@@ -266,7 +350,7 @@ int main(int argc, char *argv[])
        wlantest_init(&wt);
 
        for (;;) {
-               c = getopt(argc, argv, "cdf:Fhi:I:n:p:P:qr:R:w:W:");
+               c = getopt(argc, argv, "cdf:Fhi:I:L:n:p:P:qr:R:tT:w:W:");
                if (c < 0)
                        break;
                switch (c) {
@@ -293,8 +377,11 @@ int main(int argc, char *argv[])
                case 'I':
                        ifname_wired = optarg;
                        break;
+               case 'L':
+                       logfile = optarg;
+                       break;
                case 'n':
-                       pcapng_file = optarg;
+                       wt.pcapng_file = optarg;
                        break;
                case 'p':
                        add_passphrase(&wt, optarg);
@@ -311,8 +398,15 @@ int main(int argc, char *argv[])
                case 'R':
                        read_wired_file = optarg;
                        break;
+               case 't':
+                       wpa_debug_timestamp = 1;
+                       break;
+               case 'T':
+                       if (add_ptk_file(&wt, optarg) < 0)
+                               return -1;
+                       break;
                case 'w':
-                       write_file = optarg;
+                       wt.write_file = optarg;
                        break;
                case 'W':
                        if (add_wep(&wt, optarg) < 0)
@@ -333,10 +427,13 @@ int main(int argc, char *argv[])
        if (eloop_init())
                return -1;
 
-       if (write_file && write_pcap_init(&wt, write_file) < 0)
+       if (logfile)
+               wpa_debug_open_file(logfile);
+
+       if (wt.write_file && write_pcap_init(&wt, wt.write_file) < 0)
                return -1;
 
-       if (pcapng_file && write_pcapng_init(&wt, pcapng_file) < 0)
+       if (wt.pcapng_file && write_pcapng_init(&wt, wt.pcapng_file) < 0)
                return -1;
 
        if (read_wired_file && read_wired_cap_file(&wt, read_wired_file) < 0)
@@ -364,6 +461,7 @@ int main(int argc, char *argv[])
 
        wlantest_deinit(&wt);
 
+       wpa_debug_close_file();
        eloop_destroy();
        os_program_deinit();
 
index 1ff3229..1d8088f 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * wlantest - IEEE 802.11 protocol monitoring and testing tool
- * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2010-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -38,6 +38,12 @@ struct wlantest_pmk {
        u8 pmk[32];
 };
 
+struct wlantest_ptk {
+       struct dl_list list;
+       struct wpa_ptk ptk;
+       size_t ptk_len;
+};
+
 struct wlantest_wep {
        struct dl_list list;
        size_t key_len;
@@ -55,6 +61,7 @@ struct wlantest_sta {
        } state;
        u16 aid;
        u8 rsnie[257]; /* WPA/RSN IE */
+       u8 osenie[257]; /* OSEN IE */
        int proto;
        int pairwise_cipher;
        int group_cipher;
@@ -63,6 +70,7 @@ struct wlantest_sta {
        u8 anonce[32]; /* ANonce from the previous EAPOL-Key msg 1/4 or 3/4 */
        u8 snonce[32]; /* SNonce from the previous EAPOL-Key msg 2/4 */
        struct wpa_ptk ptk; /* Derived PTK */
+       size_t tk_len;
        int ptk_set;
        struct wpa_ptk tptk; /* Derived PTK during rekeying */
        int tptk_set;
@@ -91,6 +99,9 @@ struct wlantest_sta {
        u8 gtk[32];
        size_t gtk_len;
        int gtk_idx;
+
+       u32 tx_tid[16 + 1];
+       u32 rx_tid[16 + 1];
 };
 
 struct wlantest_tdls {
@@ -121,6 +132,7 @@ struct wlantest_bss {
        int parse_error_reported;
        u8 wpaie[257];
        u8 rsnie[257];
+       u8 osenie[257];
        int proto;
        int pairwise_cipher;
        int group_cipher;
@@ -133,8 +145,8 @@ struct wlantest_bss {
        size_t gtk_len[4];
        int gtk_idx;
        u8 rsc[4][6];
-       u8 igtk[6][16];
-       int igtk_set[6];
+       u8 igtk[6][32];
+       size_t igtk_len[6];
        int igtk_idx;
        u8 ipn[6][6];
        u32 counters[NUM_WLANTEST_BSS_COUNTER];
@@ -164,6 +176,7 @@ struct wlantest {
        struct dl_list secret; /* struct wlantest_radius_secret */
        struct dl_list radius; /* struct wlantest_radius */
        struct dl_list pmk; /* struct wlantest_pmk */
+       struct dl_list ptk; /* struct wlantest_ptk */
        struct dl_list wep; /* struct wlantest_wep */
 
        unsigned int rx_mgmt;
@@ -188,6 +201,9 @@ struct wlantest {
 
        char *notes[MAX_NOTES];
        size_t num_notes;
+
+       const char *write_file;
+       const char *pcapng_file;
 };
 
 void add_note(struct wlantest *wt, int level, const char *fmt, ...)
@@ -268,8 +284,8 @@ void tkip_get_pn(u8 *pn, const u8 *data);
 u8 * wep_decrypt(struct wlantest *wt, const struct ieee80211_hdr *hdr,
                 const u8 *data, size_t data_len, size_t *decrypted_len);
 
-u8 * bip_protect(const u8 *igtk, u8 *frame, size_t len, u8 *ipn, int keyid,
-                size_t *prot_len);
+u8 * bip_protect(const u8 *igtk, size_t igtk_len, u8 *frame, size_t len,
+                u8 *ipn, int keyid, size_t *prot_len);
 u8 * bip_gmac_protect(const u8 *igtk, size_t igtk_len, u8 *frame, size_t len,
                      u8 *ipn, int keyid, size_t *prot_len);
 
@@ -286,4 +302,6 @@ int wlantest_inject(struct wlantest *wt, struct wlantest_bss *bss,
                    struct wlantest_sta *sta, u8 *frame, size_t len,
                    enum wlantest_inject_protection prot);
 
+int wlantest_relog(struct wlantest *wt);
+
 #endif /* WLANTEST_H */
index a531b60..ad5a48d 100644 (file)
@@ -622,7 +622,7 @@ static char ** complete_get_sta_counter(int s, const char *str, int pos)
        switch (arg) {
        case 1:
                /* counter list */
-               count = sizeof(sta_counters) / sizeof(sta_counters[0]);
+               count = ARRAY_SIZE(sta_counters);
                res = os_calloc(count, sizeof(char *));
                if (res == NULL)
                        return NULL;
@@ -657,6 +657,7 @@ static const struct bss_counters bss_counters[] = {
        { "missing_bip_mmie", WLANTEST_BSS_COUNTER_MISSING_BIP_MMIE },
        { "bip_deauth", WLANTEST_BSS_COUNTER_BIP_DEAUTH },
        { "bip_disassoc", WLANTEST_BSS_COUNTER_BIP_DISASSOC },
+       { "probe_response", WLANTEST_BSS_COUNTER_PROBE_RESPONSE },
        { NULL, 0 }
 };
 
@@ -721,7 +722,7 @@ static char ** complete_get_bss_counter(int s, const char *str, int pos)
        switch (arg) {
        case 1:
                /* counter list */
-               count = sizeof(bss_counters) / sizeof(bss_counters[0]);
+               count = ARRAY_SIZE(bss_counters);
                res = os_calloc(count, sizeof(char *));
                if (res == NULL)
                        return NULL;
@@ -740,6 +741,12 @@ static char ** complete_get_bss_counter(int s, const char *str, int pos)
 }
 
 
+static int cmd_relog(int s, int argc, char *argv[])
+{
+       return cmd_simple(s, WLANTEST_CTRL_RELOG);
+}
+
+
 struct tdls_counters {
        const char *name;
        enum wlantest_tdls_counter num;
@@ -835,7 +842,7 @@ static char ** complete_get_tdls_counter(int s, const char *str, int pos)
        switch (arg) {
        case 1:
                /* counter list */
-               count = sizeof(tdls_counters) / sizeof(tdls_counters[0]);
+               count = ARRAY_SIZE(tdls_counters);
                res = os_calloc(count, sizeof(char *));
                if (res == NULL)
                        return NULL;
@@ -971,7 +978,7 @@ static char ** complete_inject(int s, const char *str, int pos)
        switch (arg) {
        case 1:
                /* frame list */
-               count = sizeof(inject_frames) / sizeof(inject_frames[0]);
+               count = ARRAY_SIZE(inject_frames);
                res = os_calloc(count, sizeof(char *));
                if (res == NULL)
                        break;
@@ -1322,7 +1329,7 @@ static char ** complete_info_sta(int s, const char *str, int pos)
        switch (arg) {
        case 1:
                /* counter list */
-               count = sizeof(sta_infos) / sizeof(sta_infos[0]);
+               count = ARRAY_SIZE(sta_infos);
                res = os_calloc(count, sizeof(char *));
                if (res == NULL)
                        return NULL;
@@ -1427,7 +1434,7 @@ static char ** complete_info_bss(int s, const char *str, int pos)
        switch (arg) {
        case 1:
                /* counter list */
-               count = sizeof(bss_infos) / sizeof(bss_infos[0]);
+               count = ARRAY_SIZE(bss_infos);
                res = os_calloc(count, sizeof(char *));
                if (res == NULL)
                        return NULL;
@@ -1446,6 +1453,119 @@ static char ** complete_info_bss(int s, const char *str, int pos)
 }
 
 
+static int cmd_get_tx_tid(int s, int argc, char *argv[])
+{
+       u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+       u8 buf[100], *end, *pos;
+       int rlen;
+       size_t len;
+
+       if (argc != 3) {
+               printf("get_tx_tid needs three arguments: "
+                      "BSSID, STA address, and TID\n");
+               return -1;
+       }
+
+       pos = buf;
+       end = buf + sizeof(buf);
+       WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_TX_TID);
+       pos += 4;
+
+       pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+       if (hwaddr_aton(argv[0], pos) < 0) {
+               printf("Invalid BSSID '%s'\n", argv[0]);
+               return -1;
+       }
+       pos += ETH_ALEN;
+
+       pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
+       if (hwaddr_aton(argv[1], pos) < 0) {
+               printf("Invalid STA address '%s'\n", argv[1]);
+               return -1;
+       }
+       pos += ETH_ALEN;
+
+       pos = attr_add_be32(pos, end, WLANTEST_ATTR_TID, atoi(argv[2]));
+
+       rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+       if (rlen < 0)
+               return -1;
+
+       pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
+       if (pos == NULL || len != 4)
+               return -1;
+       printf("%u\n", WPA_GET_BE32(pos));
+       return 0;
+}
+
+
+static int cmd_get_rx_tid(int s, int argc, char *argv[])
+{
+       u8 resp[WLANTEST_CTRL_MAX_RESP_LEN];
+       u8 buf[100], *end, *pos;
+       int rlen;
+       size_t len;
+
+       if (argc != 3) {
+               printf("get_tx_tid needs three arguments: "
+                      "BSSID, STA address, and TID\n");
+               return -1;
+       }
+
+       pos = buf;
+       end = buf + sizeof(buf);
+       WPA_PUT_BE32(pos, WLANTEST_CTRL_GET_RX_TID);
+       pos += 4;
+
+       pos = attr_hdr_add(pos, end, WLANTEST_ATTR_BSSID, ETH_ALEN);
+       if (hwaddr_aton(argv[0], pos) < 0) {
+               printf("Invalid BSSID '%s'\n", argv[0]);
+               return -1;
+       }
+       pos += ETH_ALEN;
+
+       pos = attr_hdr_add(pos, end, WLANTEST_ATTR_STA_ADDR, ETH_ALEN);
+       if (hwaddr_aton(argv[1], pos) < 0) {
+               printf("Invalid STA address '%s'\n", argv[1]);
+               return -1;
+       }
+       pos += ETH_ALEN;
+
+       pos = attr_add_be32(pos, end, WLANTEST_ATTR_TID, atoi(argv[2]));
+
+       rlen = cmd_send_and_recv(s, buf, pos - buf, resp, sizeof(resp));
+       if (rlen < 0)
+               return -1;
+
+       pos = attr_get(resp + 4, rlen - 4, WLANTEST_ATTR_COUNTER, &len);
+       if (pos == NULL || len != 4)
+               return -1;
+       printf("%u\n", WPA_GET_BE32(pos));
+       return 0;
+}
+
+
+static char ** complete_get_tid(int s, const char *str, int pos)
+{
+       int arg = get_cmd_arg_num(str, pos);
+       char **res = NULL;
+       u8 addr[ETH_ALEN];
+
+       switch (arg) {
+       case 1:
+               res = get_bssid_list(s);
+               break;
+       case 2:
+               if (hwaddr_aton(&str[get_prev_arg_pos(str, pos)], addr) < 0)
+                       break;
+               res = get_sta_list(s, addr, 0);
+               break;
+       }
+
+       return res;
+}
+
+
 struct wlantest_cli_cmd {
        const char *cmd;
        int (*handler)(int s, int argc, char *argv[]);
@@ -1496,6 +1616,13 @@ static const struct wlantest_cli_cmd wlantest_cli_commands[] = {
        { "get_bss_counter", cmd_get_bss_counter,
          "<counter> <BSSID> = get BSS counter value",
          complete_get_bss_counter },
+       { "relog", cmd_relog, "= re-open log-file (allow rolling logs)", NULL },
+       { "get_tx_tid", cmd_get_tx_tid,
+         "<BSSID> <STA> <TID> = get STA TX TID counter value",
+         complete_get_tid },
+       { "get_rx_tid", cmd_get_rx_tid,
+         "<BSSID> <STA> <TID> = get STA RX TID counter value",
+         complete_get_tid },
        { NULL, NULL, NULL, NULL }
 };
 
@@ -1606,11 +1733,9 @@ static void wlantest_cli_edit_eof_cb(void *ctx)
 static char ** wlantest_cli_cmd_list(void)
 {
        char **res;
-       int i, count;
+       int i;
 
-       count = sizeof(wlantest_cli_commands) /
-               sizeof(wlantest_cli_commands[0]);
-       res = os_calloc(count, sizeof(char *));
+       res = os_calloc(ARRAY_SIZE(wlantest_cli_commands), sizeof(char *));
        if (res == NULL)
                return NULL;
 
index 618cf8c..1af6838 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * wlantest control interface
- * Copyright (c) 2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2010-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -35,6 +35,9 @@ enum wlantest_ctrl_cmd {
        WLANTEST_CTRL_SEND,
        WLANTEST_CTRL_CLEAR_TDLS_COUNTERS,
        WLANTEST_CTRL_GET_TDLS_COUNTER,
+       WLANTEST_CTRL_RELOG,
+       WLANTEST_CTRL_GET_TX_TID,
+       WLANTEST_CTRL_GET_RX_TID,
 };
 
 enum wlantest_ctrl_attr {
@@ -55,6 +58,7 @@ enum wlantest_ctrl_attr {
        WLANTEST_ATTR_TDLS_COUNTER,
        WLANTEST_ATTR_STA2_ADDR,
        WLANTEST_ATTR_WEPKEY,
+       WLANTEST_ATTR_TID,
 };
 
 enum wlantest_bss_counter {
@@ -63,6 +67,7 @@ enum wlantest_bss_counter {
        WLANTEST_BSS_COUNTER_MISSING_BIP_MMIE,
        WLANTEST_BSS_COUNTER_BIP_DEAUTH,
        WLANTEST_BSS_COUNTER_BIP_DISASSOC,
+       WLANTEST_BSS_COUNTER_PROBE_RESPONSE,
        NUM_WLANTEST_BSS_COUNTER
 };
 
index 58f01a0..28b306b 100644 (file)
@@ -12,6 +12,7 @@
 
 #include "utils/common.h"
 #include "wlantest.h"
+#include "common/qca-vendor.h"
 
 
 int write_pcap_init(struct wlantest *wt, const char *fname)
@@ -68,8 +69,10 @@ void write_pcap_decrypted(struct wlantest *wt, const u8 *buf1, size_t len1,
        u8 rtap[] = {
                0x00 /* rev */,
                0x00 /* pad */,
-               0x08, 0x00, /* header len */
-               0x00, 0x00, 0x00, 0x00 /* present flags */
+               0x0e, 0x00, /* header len */
+               0x00, 0x00, 0x00, 0x40, /* present flags */
+               0x00, 0x13, 0x74, QCA_RADIOTAP_VID_WLANTEST,
+               0x00, 0x00
        };
        u8 *buf;
        size_t len;
index 6b6fc06..579582b 100644 (file)
@@ -8,14 +8,10 @@
 LOCAL_PATH := $(call my-dir)
 PKG_CONFIG ?= pkg-config
 
-WPA_BUILD_SUPPLICANT := false
 ifneq ($(BOARD_WPA_SUPPLICANT_DRIVER),)
-  WPA_BUILD_SUPPLICANT := true
   CONFIG_DRIVER_$(BOARD_WPA_SUPPLICANT_DRIVER) := y
 endif
 
-ifeq ($(WPA_BUILD_SUPPLICANT),true)
-
 include $(LOCAL_PATH)/android.config
 
 # To ignore possible wrong network configurations
@@ -26,6 +22,15 @@ L_CFLAGS += -DVERSION_STR_POSTFIX=\"-$(PLATFORM_VERSION)\"
 # Set Android log name
 L_CFLAGS += -DANDROID_LOG_NAME=\"wpa_supplicant\"
 
+# Disable unused parameter warnings
+L_CFLAGS += -Wno-unused-parameter
+
+# Set Android extended P2P functionality
+L_CFLAGS += -DANDROID_P2P
+ifeq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),)
+L_CFLAGS += -DANDROID_P2P_STUB
+endif
+
 # Disable roaming in wpa_supplicant
 ifdef CONFIG_NO_ROAMING
 L_CFLAGS += -DCONFIG_NO_ROAMING
@@ -40,12 +45,6 @@ ifeq ($(TARGET_ARCH),arm)
 L_CFLAGS += -mabi=aapcs-linux
 endif
 
-# To allow non-ASCII characters in SSID
-L_CFLAGS += -DWPA_UNICODE_SSID
-
-# OpenSSL is configured without engines on Android
-L_CFLAGS += -DOPENSSL_NO_ENGINE
-
 INCLUDES = $(LOCAL_PATH)
 INCLUDES += $(LOCAL_PATH)/src
 INCLUDES += $(LOCAL_PATH)/src/common
@@ -63,13 +62,14 @@ INCLUDES += $(LOCAL_PATH)/src/tls
 INCLUDES += $(LOCAL_PATH)/src/utils
 INCLUDES += $(LOCAL_PATH)/src/wps
 INCLUDES += external/openssl/include
-# frameworks/base/cmds/keystore is the old location and can be dropped at some
-# point
-INCLUDES += frameworks/base/cmds/keystore
-INCLUDES += system/security/keystore
+INCLUDES += system/security/keystore/include
 ifdef CONFIG_DRIVER_NL80211
+ifneq ($(wildcard external/libnl),)
+INCLUDES += external/libnl/include
+else
 INCLUDES += external/libnl-headers
 endif
+endif
 
 ifdef CONFIG_FIPS
 CONFIG_NO_RANDOM_POOL=
@@ -83,6 +83,7 @@ OBJS += eap_register.c
 OBJS += src/utils/common.c
 OBJS += src/utils/wpa_debug.c
 OBJS += src/utils/wpabuf.c
+OBJS += wmm_ac.c
 OBJS_p = wpa_passphrase.c
 OBJS_p += src/utils/common.c
 OBJS_p += src/utils/wpa_debug.c
@@ -134,6 +135,10 @@ ifdef CONFIG_ELOOP_POLL
 L_CFLAGS += -DCONFIG_ELOOP_POLL
 endif
 
+ifdef CONFIG_ELOOP_EPOLL
+L_CFLAGS += -DCONFIG_ELOOP_EPOLL
+endif
+
 ifdef CONFIG_EAPOL_TEST
 L_CFLAGS += -Werror -DEAPOL_TEST
 endif
@@ -178,6 +183,17 @@ ifdef CONFIG_NO_SCAN_PROCESSING
 L_CFLAGS += -DCONFIG_NO_SCAN_PROCESSING
 endif
 
+ifdef CONFIG_SUITEB
+L_CFLAGS += -DCONFIG_SUITEB
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_SUITEB192
+L_CFLAGS += -DCONFIG_SUITEB192
+NEED_SHA384=y
+endif
+
 ifdef CONFIG_IEEE80211W
 L_CFLAGS += -DCONFIG_IEEE80211W
 NEED_SHA256=y
@@ -187,9 +203,22 @@ endif
 ifdef CONFIG_IEEE80211R
 L_CFLAGS += -DCONFIG_IEEE80211R
 OBJS += src/rsn_supp/wpa_ft.c
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_MESH
 NEED_80211_COMMON=y
 NEED_SHA256=y
+NEED_AES_SIV=y
 NEED_AES_OMAC1=y
+NEED_AES_CTR=y
+CONFIG_SAE=y
+CONFIG_AP=y
+L_CFLAGS += -DCONFIG_MESH
+OBJS += mesh.c
+OBJS += mesh_mpm.c
+OBJS += mesh_rsn.c
 endif
 
 ifdef CONFIG_SAE
@@ -253,10 +282,10 @@ OBJS += src/p2p/p2p_invitation.c
 OBJS += src/p2p/p2p_dev_disc.c
 OBJS += src/p2p/p2p_group.c
 OBJS += src/ap/p2p_hostapd.c
+OBJS += src/utils/bitfield.c
 L_CFLAGS += -DCONFIG_P2P
 NEED_GAS=y
 NEED_OFFCHANNEL=y
-NEED_80211_COMMON=y
 CONFIG_WPS=y
 CONFIG_AP=y
 ifdef CONFIG_P2P_STRICT
@@ -273,6 +302,7 @@ ifdef CONFIG_HS20
 OBJS += hs20_supplicant.c
 L_CFLAGS += -DCONFIG_HS20
 CONFIG_INTERWORKING=y
+NEED_AES_OMAC1=y
 endif
 
 ifdef CONFIG_INTERWORKING
@@ -321,6 +351,12 @@ ifeq ($(CONFIG_L2_PACKET), freebsd)
 LIBS += -lpcap
 endif
 
+ifdef CONFIG_ERP
+L_CFLAGS += -DCONFIG_ERP
+NEED_SHA256=y
+NEED_HMAC_SHA256_KDF=y
+endif
+
 ifdef CONFIG_EAP_TLS
 # EAP-TLS
 ifeq ($(CONFIG_EAP_TLS), dyn)
@@ -335,6 +371,17 @@ TLS_FUNCS=y
 CONFIG_IEEE8021X_EAPOL=y
 endif
 
+ifdef CONFIG_EAP_UNAUTH_TLS
+# EAP-UNAUTH-TLS
+L_CFLAGS += -DEAP_UNAUTH_TLS
+ifndef CONFIG_EAP_UNAUTH_TLS
+OBJS += src/eap_peer/eap_tls.c
+OBJS_h += src/eap_server/eap_server_tls.c
+TLS_FUNCS=y
+endif
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
 ifdef CONFIG_EAP_PEAP
 # EAP-PEAP
 ifeq ($(CONFIG_EAP_PEAP), dyn)
@@ -488,6 +535,13 @@ CONFIG_EAP_SIM_COMMON=y
 NEED_AES_CBC=y
 endif
 
+ifdef CONFIG_EAP_PROXY
+L_CFLAGS += -DCONFIG_EAP_PROXY
+OBJS += src/eap_peer/eap_proxy_$(CONFIG_EAP_PROXY).c
+include $(LOCAL_PATH)/eap_proxy_$(CONFIG_EAP_PROXY).mk
+CONFIG_IEEE8021X_EAPOL=y
+endif
+
 ifdef CONFIG_EAP_AKA_PRIME
 # EAP-AKA'
 ifeq ($(CONFIG_EAP_AKA_PRIME), dyn)
@@ -574,11 +628,23 @@ CONFIG_IEEE8021X_EAPOL=y
 NEED_SHA256=y
 endif
 
-ifdef CONFIG_WPS
-ifdef CONFIG_WPS2
-L_CFLAGS += -DCONFIG_WPS2
+ifdef CONFIG_EAP_EKE
+# EAP-EKE
+ifeq ($(CONFIG_EAP_EKE), dyn)
+L_CFLAGS += -DEAP_EKE_DYNAMIC
+EAPDYN += src/eap_peer/eap_eke.so
+else
+L_CFLAGS += -DEAP_EKE
+OBJS += src/eap_peer/eap_eke.c src/eap_common/eap_eke_common.c
+OBJS_h += src/eap_server/eap_server_eke.c
+endif
+CONFIG_IEEE8021X_EAPOL=y
+NEED_DH_GROUPS=y
+NEED_DH_GROUPS_ALL=y
+NEED_SHA256=y
 endif
 
+ifdef CONFIG_WPS
 # EAP-WSC
 L_CFLAGS += -DCONFIG_WPS -DEAP_WSC
 OBJS += wps_supplicant.c
@@ -597,7 +663,6 @@ CONFIG_IEEE8021X_EAPOL=y
 NEED_DH_GROUPS=y
 NEED_SHA256=y
 NEED_BASE64=y
-NEED_80211_COMMON=y
 NEED_AES_CBC=y
 NEED_MODEXP=y
 
@@ -706,7 +771,6 @@ endif
 endif
 
 ifdef CONFIG_AP
-NEED_80211_COMMON=y
 NEED_EAP_COMMON=y
 NEED_RSN_AUTHENTICATOR=y
 L_CFLAGS += -DCONFIG_AP
@@ -730,16 +794,20 @@ OBJS += src/ap/ieee802_11_shared.c
 OBJS += src/ap/drv_callbacks.c
 OBJS += src/ap/ap_drv_ops.c
 OBJS += src/ap/beacon.c
+OBJS += src/ap/bss_load.c
 OBJS += src/ap/eap_user_db.c
 ifdef CONFIG_IEEE80211N
 OBJS += src/ap/ieee802_11_ht.c
+ifdef CONFIG_IEEE80211AC
+OBJS += src/ap/ieee802_11_vht.c
 endif
-ifdef CONFIG_CTRL_IFACE
-OBJS += src/ap/ctrl_iface_ap.c
 endif
 ifdef CONFIG_WNM
 OBJS += src/ap/wnm_ap.c
 endif
+ifdef CONFIG_CTRL_IFACE
+OBJS += src/ap/ctrl_iface_ap.c
+endif
 
 L_CFLAGS += -DEAP_SERVER -DEAP_SERVER_IDENTITY
 OBJS += src/eap_server/eap_server.c
@@ -748,6 +816,9 @@ OBJS += src/eap_server/eap_server_methods.c
 
 ifdef CONFIG_IEEE80211N
 L_CFLAGS += -DCONFIG_IEEE80211N
+ifdef CONFIG_IEEE80211AC
+L_CFLAGS += -DCONFIG_IEEE80211AC
+endif
 endif
 
 ifdef NEED_AP_MLME
@@ -755,6 +826,7 @@ OBJS += src/ap/wmm.c
 OBJS += src/ap/ap_list.c
 OBJS += src/ap/ieee802_11.c
 OBJS += src/ap/hw_features.c
+OBJS += src/ap/dfs.c
 L_CFLAGS += -DNEED_AP_MLME
 endif
 ifdef CONFIG_WPS
@@ -899,6 +971,10 @@ OBJS += src/crypto/fips_prf_openssl.c
 endif
 LIBS += -lcrypto
 LIBS_p += -lcrypto
+ifdef CONFIG_TLS_ADD_DL
+LIBS += -ldl
+LIBS_p += -ldl
+endif
 endif
 
 ifeq ($(CONFIG_TLS), gnutls)
@@ -909,7 +985,8 @@ endif
 OBJS += src/crypto/crypto_gnutls.c
 OBJS_p += src/crypto/crypto_gnutls.c
 ifdef NEED_FIPS186_2_PRF
-OBJS += src/crypto/fips_prf_gnutls.c
+OBJS += src/crypto/fips_prf_internal.c
+OBJS += src/crypto/sha1-internal.c
 endif
 LIBS += -lgcrypt
 LIBS_p += -lgcrypt
@@ -925,29 +1002,14 @@ endif
 OBJS += src/crypto/crypto_cryptoapi.c
 OBJS_p += src/crypto/crypto_cryptoapi.c
 ifdef NEED_FIPS186_2_PRF
-OBJS += src/crypto/fips_prf_cryptoapi.c
+OBJS += src/crypto/fips_prf_internal.c
+OBJS += src/crypto/sha1-internal.c
 endif
 CONFIG_INTERNAL_SHA256=y
 CONFIG_INTERNAL_RC4=y
 CONFIG_INTERNAL_DH_GROUP5=y
 endif
 
-ifeq ($(CONFIG_TLS), nss)
-ifdef TLS_FUNCS
-OBJS += src/crypto/tls_nss.c
-LIBS += -lssl3
-endif
-OBJS += src/crypto/crypto_nss.c
-OBJS_p += src/crypto/crypto_nss.c
-ifdef NEED_FIPS186_2_PRF
-OBJS += src/crypto/fips_prf_nss.c
-endif
-LIBS += -lnss3
-LIBS_p += -lnss3
-CONFIG_INTERNAL_MD4=y
-CONFIG_INTERNAL_DH_GROUP5=y
-endif
-
 ifeq ($(CONFIG_TLS), internal)
 ifndef CONFIG_CRYPTO
 CONFIG_CRYPTO=internal
@@ -1065,7 +1127,9 @@ ifdef CONFIG_INTERNAL_AES
 AESOBJS += src/crypto/aes-internal.c src/crypto/aes-internal-dec.c
 endif
 
+ifneq ($(CONFIG_TLS), openssl)
 AESOBJS += src/crypto/aes-unwrap.c
+endif
 ifdef NEED_AES_EAX
 AESOBJS += src/crypto/aes-eax.c
 NEED_AES_CTR=y
@@ -1079,15 +1143,17 @@ endif
 ifdef NEED_AES_OMAC1
 NEED_AES_ENC=y
 ifdef CONFIG_OPENSSL_CMAC
-CFLAGS += -DCONFIG_OPENSSL_CMAC
+L_CFLAGS += -DCONFIG_OPENSSL_CMAC
 else
 AESOBJS += src/crypto/aes-omac1.c
 endif
 endif
 ifdef NEED_AES_WRAP
 NEED_AES_ENC=y
+ifneq ($(CONFIG_TLS), openssl)
 AESOBJS += src/crypto/aes-wrap.c
 endif
+endif
 ifdef NEED_AES_CBC
 NEED_AES_ENC=y
 AESOBJS += src/crypto/aes-cbc.c
@@ -1097,6 +1163,9 @@ ifdef CONFIG_INTERNAL_AES
 AESOBJS += src/crypto/aes-internal-enc.c
 endif
 endif
+ifdef NEED_AES_SIV
+AESOBJS += src/crypto/aes-siv.c
+endif
 ifdef NEED_AES
 OBJS += $(AESOBJS)
 endif
@@ -1128,9 +1197,12 @@ SHA1OBJS += src/crypto/sha1-tlsprf.c
 endif
 endif
 
+MD5OBJS =
 ifndef CONFIG_FIPS
+ifneq ($(CONFIG_TLS), openssl)
 MD5OBJS += src/crypto/md5.c
 endif
+endif
 ifdef NEED_MD5
 ifdef CONFIG_INTERNAL_MD5
 MD5OBJS += src/crypto/md5-internal.c
@@ -1171,8 +1243,14 @@ endif
 ifdef NEED_TLS_PRF_SHA256
 SHA256OBJS += src/crypto/sha256-tlsprf.c
 endif
+ifdef NEED_HMAC_SHA256_KDF
+SHA256OBJS += src/crypto/sha256-kdf.c
+endif
 OBJS += $(SHA256OBJS)
 endif
+ifdef NEED_SHA384
+L_CFLAGS += -DCONFIG_SHA384
+endif
 
 ifdef NEED_DH_GROUPS
 OBJS += src/crypto/dh_groups.c
@@ -1315,14 +1393,12 @@ OBJS += src/utils/base64.c
 endif
 
 ifdef NEED_SME
-NEED_80211_COMMON=y
 OBJS += sme.c
 L_CFLAGS += -DCONFIG_SME
 endif
 
-ifdef NEED_80211_COMMON
 OBJS += src/common/ieee802_11_common.c
-endif
+OBJS += src/common/hw_features_common.c
 
 ifdef NEED_EAP_COMMON
 OBJS += src/eap_common/eap_common.c
@@ -1384,7 +1460,7 @@ NEED_AUTOSCAN=y
 endif
 
 ifdef CONFIG_AUTOSCAN_PERIODIC
-CFLAGS += -DCONFIG_AUTOSCAN_PERIODIC
+L_CFLAGS += -DCONFIG_AUTOSCAN_PERIODIC
 OBJS += autoscan_periodic.c
 NEED_AUTOSCAN=y
 endif
@@ -1447,26 +1523,6 @@ OBJS_priv += wpa_priv.c
 ifdef CONFIG_DRIVER_NL80211
 OBJS_priv += src/common/ieee802_11_common.c
 endif
-ifdef CONFIG_DRIVER_TEST
-OBJS_priv += $(SHA1OBJS)
-OBJS_priv += $(MD5OBJS)
-ifeq ($(CONFIG_TLS), openssl)
-OBJS_priv += src/crypto/crypto_openssl.c
-endif
-ifeq ($(CONFIG_TLS), gnutls)
-OBJS_priv += src/crypto/crypto_gnutls.c
-endif
-ifeq ($(CONFIG_TLS), nss)
-OBJS_priv += src/crypto/crypto_nss.c
-endif
-ifeq ($(CONFIG_TLS), internal)
-ifeq ($(CONFIG_CRYPTO), libtomcrypt)
-OBJS_priv += src/crypto/crypto_libtomcrypt.c
-else
-OBJS_priv += src/crypto/crypto_internal.c
-endif
-endif
-endif # CONFIG_DRIVER_TEST
 OBJS += src/l2_packet/l2_packet_privsep.c
 OBJS += src/drivers/driver_privsep.c
 EXTRA_progs += wpa_priv
@@ -1495,7 +1551,7 @@ endif
 include $(CLEAR_VARS)
 LOCAL_MODULE := wpa_cli
 LOCAL_MODULE_TAGS := debug
-LOCAL_SHARED_LIBRARIES := libc libcutils
+LOCAL_SHARED_LIBRARIES := libc libcutils liblog
 LOCAL_CFLAGS := $(L_CFLAGS)
 LOCAL_SRC_FILES := $(OBJS_c)
 LOCAL_C_INCLUDES := $(INCLUDES)
@@ -1510,19 +1566,21 @@ endif
 ifneq ($(BOARD_WPA_SUPPLICANT_PRIVATE_LIB),)
 LOCAL_STATIC_LIBRARIES += $(BOARD_WPA_SUPPLICANT_PRIVATE_LIB)
 endif
-LOCAL_SHARED_LIBRARIES := libc libcutils
-
+LOCAL_SHARED_LIBRARIES := libc libcutils liblog
 ifdef CONFIG_EAP_PROXY
-OBJS += src/eap_peer/eap_proxy_$(CONFIG_EAP_PROXY).c
-include $(LOCAL_PATH)/eap_proxy_$(CONFIG_EAP_PROXY).mk
+LOCAL_STATIC_LIBRARIES += $(LIB_STATIC_EAP_PROXY)
+LOCAL_SHARED_LIBRARIES += $(LIB_SHARED_EAP_PROXY)
 endif
-
 ifeq ($(CONFIG_TLS), openssl)
-LOCAL_SHARED_LIBRARIES += libcrypto libssl
+LOCAL_SHARED_LIBRARIES += libcrypto libssl libkeystore_binder
 endif
 ifdef CONFIG_DRIVER_NL80211
+ifneq ($(wildcard external/libnl),)
+LOCAL_SHARED_LIBRARIES += libnl
+else
 LOCAL_STATIC_LIBRARIES += libnl_2
 endif
+endif
 LOCAL_CFLAGS := $(L_CFLAGS)
 LOCAL_SRC_FILES := $(OBJS)
 LOCAL_C_INCLUDES := $(INCLUDES)
@@ -1547,7 +1605,6 @@ include $(BUILD_EXECUTABLE)
 #
 #include $(CLEAR_VARS)
 #LOCAL_MODULE := wpa_supplicant.conf
-#LOCAL_MODULE_TAGS := user
 #LOCAL_MODULE_CLASS := ETC
 #LOCAL_MODULE_PATH := $(local_target_dir)
 #LOCAL_SRC_FILES := $(LOCAL_MODULE)
@@ -1555,14 +1612,13 @@ include $(BUILD_EXECUTABLE)
 #
 ########################
 
-endif # ifeq ($(WPA_BUILD_SUPPLICANT),true)
-
 include $(CLEAR_VARS)
 LOCAL_MODULE = libwpa_client
 LOCAL_CFLAGS = $(L_CFLAGS)
 LOCAL_SRC_FILES = src/common/wpa_ctrl.c src/utils/os_$(CONFIG_OS).c
 LOCAL_C_INCLUDES = $(INCLUDES)
-LOCAL_SHARED_LIBRARIES := libcutils
+LOCAL_SHARED_LIBRARIES := libcutils liblog
 LOCAL_COPY_HEADERS_TO := libwpa_client
 LOCAL_COPY_HEADERS := src/common/wpa_ctrl.h
+LOCAL_COPY_HEADERS += src/common/qca-vendor.h
 include $(BUILD_SHARED_LIBRARY)
index 3f10e11..1ac79b4 100644 (file)
@@ -1,8 +1,376 @@
 ChangeLog for wpa_supplicant
 
-????-??-?? - v2.1
-       * added support for simulataneous authentication of equals (SAE) for
+2015-03-15 - v2.4
+       * allow OpenSSL cipher configuration to be set for internal EAP server
+         (openssl_ciphers parameter)
+       * fixed number of small issues based on hwsim test case failures and
+         static analyzer reports
+       * P2P:
+         - add new=<0/1> flag to P2P-DEVICE-FOUND events
+         - add passive channels in invitation response from P2P Client
+         - enable nl80211 P2P_DEVICE support by default
+         - fix regresssion in disallow_freq preventing search on social
+           channels
+         - fix regressions in P2P SD query processing
+         - try to re-invite with social operating channel if no common channels
+           in invitation
+         - allow cross connection on parent interface (this fixes number of
+           use cases with nl80211)
+         - add support for P2P services (P2PS)
+         - add p2p_go_ctwindow configuration parameter to allow GO CTWindow to
+           be configured
+       * increase postponing of EAPOL-Start by one second with AP/GO that
+         supports WPS 2.0 (this makes it less likely to trigger extra roundtrip
+         of identity frames)
+       * add support for PMKSA caching with SAE
+       * add support for control mesh BSS (IEEE 802.11s) operations
+       * fixed number of issues with D-Bus P2P commands
+       * fixed regression in ap_scan=2 special case for WPS
+       * fixed macsec_validate configuration
+       * add a workaround for incorrectly behaving APs that try to use
+         EAPOL-Key descriptor version 3 when the station supports PMF even if
+         PMF is not enabled on the AP
+       * allow TLS v1.1 and v1.2 to be negotiated by default; previous behavior
+         of disabling these can be configured to work around issues with broken
+         servers with phase1="tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1"
+       * add support for Suite B (128-bit and 192-bit level) key management and
+         cipher suites
+       * add WMM-AC support (WMM_AC_ADDTS/WMM_AC_DELTS)
+       * improved BSS Transition Management processing
+       * add support for neighbor report
+       * add support for link measurement
+       * fixed expiration of BSS entry with all-zeros BSSID
+       * add optional LAST_ID=x argument to LIST_NETWORK to allow all
+         configured networks to be listed even with huge number of network
+         profiles
+       * add support for EAP Re-Authentication Protocol (ERP)
+       * fixed EAP-IKEv2 fragmentation reassembly
+       * improved PKCS#11 configuration for OpenSSL
+       * set stdout to be line-buffered
+       * add TDLS channel switch configuration
+       * add support for MAC address randomization in scans with nl80211
+       * enable HT for IBSS if supported by the driver
+       * add BSSID black and white lists (bssid_blacklist, bssid_whitelist)
+       * add support for domain_suffix_match with GnuTLS
+       * add OCSP stapling client support with GnuTLS
+       * include peer certificate in EAP events even without a separate probe
+         operation; old behavior can be restored with cert_in_cb=0
+       * add peer ceritficate alt subject name to EAP events
+         (CTRL-EVENT-EAP-PEER-ALT)
+       * add domain_match network profile parameter (similar to
+         domain_suffix_match, but full match is required)
+       * enable AP/GO mode HT Tx STBC automatically based on driver support
+       * add ANQP-QUERY-DONE event to provide information on ANQP parsing
+         status
+       * allow passive scanning to be forced with passive_scan=1
+       * add a workaround for Linux packet socket behavior when interface is in
+         bridge
+       * increase 5 GHz band preference in BSS selection (estimate SNR, if info
+         not available from driver; estimate maximum throughput based on common
+         HT/VHT/specific TX rate support)
+       * add INTERWORKING_ADD_NETWORK ctrl_iface command; this can be used to
+         implement Interworking network selection behavior in upper layers
+         software components
+       * add optional reassoc_same_bss_optim=1 (disabled by default)
+         optimization to avoid unnecessary Authentication frame exchange
+       * extend TDLS frame padding workaround to cover all packets
+       * allow wpa_supplicant to recover nl80211 functionality if the cfg80211
+         module gets removed and reloaded without restarting wpa_supplicant
+       * allow hostapd DFS implementation to be used in wpa_supplicant AP mode
+
+2014-10-09 - v2.3
+       * fixed number of minor issues identified in static analyzer warnings
+       * fixed wfd_dev_info to be more careful and not read beyond the buffer
+         when parsing invalid information for P2P-DEVICE-FOUND
+       * extended P2P and GAS query operations to support drivers that have
+         maximum remain-on-channel time below 1000 ms (500 ms is the current
+         minimum supported value)
+       * added p2p_search_delay parameter to make the default p2p_find delay
+         configurable
+       * improved P2P operating channel selection for various multi-channel
+         concurrency cases
+       * fixed some TDLS failure cases to clean up driver state
+       * fixed dynamic interface addition cases with nl80211 to avoid adding
+         ifindex values to incorrect interface to skip foreign interface events
+         properly
+       * added TDLS workaround for some APs that may add extra data to the
+         end of a short frame
+       * fixed EAP-AKA' message parser with multiple AT_KDF attributes
+       * added configuration option (p2p_passphrase_len) to allow longer
+         passphrases to be generated for P2P groups
+       * fixed IBSS channel configuration in some corner cases
+       * improved HT/VHT/QoS parameter setup for TDLS
+       * modified D-Bus interface for P2P peers/groups
+       * started to use constant time comparison for various password and hash
+         values to reduce possibility of any externally measurable timing
+         differences
+       * extended explicit clearing of freed memory and expired keys to avoid
+         keeping private data in memory longer than necessary
+       * added optional scan_id parameter to the SCAN command to allow manual
+         scan requests for active scans for specific configured SSIDs
+       * fixed CTRL-EVENT-REGDOM-CHANGE event init parameter value
+       * added option to set Hotspot 2.0 Rel 2 update_identifier in network
+         configuration to support external configuration
+       * modified Android PNO functionality to send Probe Request frames only
+         for hidden SSIDs (based on scan_ssid=1)
+       * added generic mechanism for adding vendor elements into frames at
+         runtime (VENDOR_ELEM_ADD, VENDOR_ELEM_GET, VENDOR_ELEM_REMOVE)
+       * added fields to show unrecognized vendor elements in P2P_PEER
+       * removed EAP-TTLS/MSCHAPv2 interoperability workaround so that
+         MS-CHAP2-Success is required to be present regardless of
+         eap_workaround configuration
+       * modified EAP fast session resumption to allow results to be used only
+         with the same network block that generated them
+       * extended freq_list configuration to apply for sched_scan as well as
+         normal scan
+       * modified WPS to merge mixed-WPA/WPA2 credentials from a single session
+       * fixed nl80211/RTM_DELLINK processing when a P2P GO interface is
+         removed from a bridge
+       * fixed number of small P2P issues to make negotiations more robust in
+         corner cases
+       * added experimental support for using temporary, random local MAC
+         address (mac_addr and preassoc_mac_addr parameters); this is disabled
+         by default (i.e., previous behavior of using permanent address is
+         maintained if configuration is not changed)
+       * added D-Bus interface for setting/clearing WFD IEs
+       * fixed TDLS AID configuration for VHT
+       * modified -m<conf> configuration file to be used only for the P2P
+         non-netdev management device and do not load this for the default
+         station interface or load the station interface configuration for
+         the P2P management interface
+       * fixed external MAC address changes while wpa_supplicant is running
+       * started to enable HT (if supported by the driver) for IBSS
+       * fixed wpa_cli action script execution to use more robust mechanism
+         (CVE-2014-3686)
+
+2014-06-04 - v2.2
+       * added DFS indicator to get_capability freq
+       * added/fixed nl80211 functionality
+         - BSSID/frequency hint for driver-based BSS selection
+         - fix tearing down WDS STA interfaces
+         - support vendor specific driver command
+           (VENDOR <vendor id> <sub command id> [<hex formatted data>])
+         - GO interface teardown optimization
+         - allow beacon interval to be configured for IBSS
+         - add SHA256-based AKM suites to CONNECT/ASSOCIATE commands
+       * removed unused NFC_RX_HANDOVER_REQ and NFC_RX_HANDOVER_SEL control
+         interface commands (the more generic NFC_REPORT_HANDOVER is now used)
+       * fixed MSCHAP UTF-8 to UCS-2 conversion for three-byte encoding;
+         this fixes password with include UTF-8 characters that use
+         three-byte encoding EAP methods that use NtPasswordHash
+       * fixed couple of sequencies where radio work items could get stuck,
+         e.g., when rfkill blocking happens during scanning or when
+         scan-for-auth workaround is used
+       * P2P enhancements/fixes
+         - enable enable U-APSD on GO automatically if the driver indicates
+           support for this
+         - fixed some service discovery cases with broadcast queries not being
+           sent to all stations
+         - fixed Probe Request frame triggering invitation to trigger only a
+           single invitation instance even if multiple Probe Request frames are
+           received
+         - fixed a potential NULL pointer dereference crash when processing an
+           invalid Invitation Request frame
+         - add optional configuration file for the P2P_DEVICE parameters
+         - optimize scan for GO during persistent group invocation
+         - fix possible segmentation fault when PBC overlap is detected while
+           using a separate P2P group interface
+         - improve GO Negotiation robustness by allowing GO Negotiation
+           Confirmation to be retransmitted
+         - do use freed memory on device found event when P2P NFC
+       * added phase1 network parameter options for disabling TLS v1.1 and v1.2
+         to allow workarounds with misbehaving AAA servers
+         (tls_disable_tlsv1_1=1 and tls_disable_tlsv1_2=1)
+       * added support for OCSP stapling to validate AAA server certificate
+         during TLS exchange
+       * Interworking/Hotspot 2.0 enhancements
+         - prefer the last added network in Interworking connection to make the
+           behavior more consistent with likely user expectation
+         - roaming partner configuration (roaming_partner within a cred block)
+         - support Hotspot 2.0 Release 2
+           * "hs20_anqp_get <BSSID> 8" to request OSU Providers list
+           * "hs20_icon_request <BSSID> <icon filename>" to request icon files
+           * "fetch_osu" and "cancel_osu_fetch" to start/stop full OSU provider
+             search (all suitable APs in scan results)
+           * OSEN network for online signup connection
+           * min_{dl,ul}_bandwidth_{home,roaming} cred parameters
+           * max_bss_load cred parameter
+           * req_conn_capab cred parameter
+           * sp_priority cred parameter
+           * ocsp cred parameter
+           * slow down automatic connection attempts on EAP failure to meet
+             required behavior (no more than 10 retries within a 10-minute
+             interval)
+           * sample implementation of online signup client (both SPP and
+             OMA-DM protocols) (hs20/client/*)
+         - fixed GAS indication for additional comeback delay with status
+           code 95
+         - extend ANQP_GET to accept Hotspot 2.0 subtypes
+           ANQP_GET <addr> <info id>[,<info id>]...
+           [,hs20:<subtype>][...,hs20:<subtype>]
+         - add control interface events CRED-ADDED <id>,
+           CRED-MODIFIED <id> <field>, CRED-REMOVED <id>
+         - add "GET_CRED <id> <field>" command
+         - enable FT for the connection automatically if the AP advertises
+           support for this
+         - fix a case where auto_interworking=1 could end up stopping scanning
+       * fixed TDLS interoperability issues with supported operating class in
+         some deployed stations
+       * internal TLS implementation enhancements/fixes
+         - add SHA256-based cipher suites
+         - add DHE-RSA cipher suites
+         - fix X.509 validation of PKCS#1 signature to check for extra data
+       * fixed PTK derivation for CCMP-256 and GCMP-256
+       * added "reattach" command for fast reassociate-back-to-same-BSS
+       * allow PMF to be enabled for AP mode operation with the ieee80211w
+         parameter
+       * added "get_capability tdls" command
+       * added option to set config blobs through control interface with
+         "SET blob <name> <hexdump>"
+       * D-Bus interface extensions/fixes
+         - make p2p_no_group_iface configurable
+         - declare ServiceDiscoveryRequest method properly
+         - export peer's device address as a property
+         - make reassociate command behave like the control interface one,
+           i.e., to allow connection from disconnected state
+       * added optional "freq=<channel ranges>" parameter to SET pno
+       * added optional "freq=<channel ranges>" parameter to SELECT_NETWORK
+       * fixed OBSS scan result processing for 20/40 MHz co-ex report
+       * remove WPS 1.0 only support, i.e., WSC 2.0 support is now enabled
+         whenever CONFIG_WPS=y is set
+       * fixed regression in parsing of WNM Sleep Mode exit key data
+       * fixed potential segmentation fault and memory leaks in WNM neighbor
+         report processing
+       * EAP-pwd fixes
+         - fragmentation of PWD-Confirm-Resp
+         - fix memory leak when fragmentation is used
+         - fix possible segmentation fault on EAP method deinit if an invalid
+           group is negotiated
+       * added MACsec/IEEE Std 802.1X-2010 PAE implementation (currently
+         available only with the macsec_qca driver wrapper)
+       * fixed EAP-SIM counter-too-small message
+       * added 'dup_network <id_s> <id_d> <name>' command; this can be used to
+         clone the psk field without having toextract it from wpa_supplicant
+       * fixed GSM authentication on USIM
+       * added support for usin epoll in eloop (CONFIG_ELOOP_EPOLL=y)
+       * fixed some concurrent virtual interface cases with dedicated P2P
+         management interface to not catch events from removed interface (this
+         could result in the management interface getting disabled)
+       * fixed a memory leak in SAE random number generation
+       * fixed off-by-one bounds checking in printf_encode()
+         - this could result in some control interface ATTACH command cases
+           terminating wpa_supplicant
+       * fixed EAPOL-Key exchange when GCMP is used with SHA256-based AKM
+       * various bug fixes
+
+2014-02-04 - v2.1
+       * added support for simultaneous authentication of equals (SAE) for
          stronger password-based authentication with WPA2-Personal
+       * improved P2P negotiation and group formation robustness
+         - avoid unnecessary Dialog Token value changes during retries
+         - avoid more concurrent scanning cases during full group formation
+           sequence
+         - do not use potentially obsolete scan result data from driver
+           cache for peer discovery/updates
+         - avoid undesired re-starting of GO negotiation based on Probe
+           Request frames
+         - increase GO Negotiation and Invitation timeouts to address busy
+           environments and peers that take long time to react to messages,
+           e.g., due to power saving
+         - P2P Device interface type
+       * improved P2P channel selection (use more peer information and allow
+         more local options)
+       * added support for optional per-device PSK assignment by P2P GO
+         (wpa_cli p2p_set per_sta_psk <0/1>)
+       * added P2P_REMOVE_CLIENT for removing a client from P2P groups
+         (including persistent groups); this can be used to securely remove
+         a client from a group if per-device PSKs are used
+       * added more configuration flexibility for allowed P2P GO/client
+         channels (p2p_no_go_freq list and p2p_add_cli_chan=0/1)
+       * added nl80211 functionality
+         - VHT configuration for nl80211
+         - MFP (IEEE 802.11w) information for nl80211 command API
+         - support split wiphy dump
+         - FT (IEEE 802.11r) with driver-based SME
+         - use advertised number of supported concurrent channels
+         - QoS Mapping configuration
+       * improved TDLS negotiation robustness
+       * added more TDLS peer parameters to be configured to the driver
+       * optimized connection time by allowing recently received scan results
+         to be used instead of having to run through a new scan
+       * fixed ctrl_iface BSS command iteration with RANGE argument and no
+         exact matches; also fixed argument parsing for some cases with
+         multiple arguments
+       * added 'SCAN TYPE=ONLY' ctrl_iface command to request manual scan
+         without executing roaming/network re-selection on scan results
+       * added Session-Id derivation for EAP peer methods
+       * added fully automated regression testing with mac80211_hwsim
+       * changed configuration parser to reject invalid integer values
+       * allow AP/Enrollee to be specified with BSSID instead of UUID for
+         WPS ER operations
+       * disable network block temporarily on repeated connection failures
+       * changed the default driver interface from wext to nl80211 if both are
+         included in the build
+       * remove duplicate networks if WPS provisioning is run multiple times
+       * remove duplicate networks when Interworking network selection uses the
+         same network
+       * added global freq_list configuration to allow scan frequencies to be
+         limited for all cases instead of just for a specific network block
+       * added support for BSS Transition Management
+       * added option to use "IFNAME=<ifname> " prefix to use the global
+         control interface connection to perform per-interface commands;
+         similarly, allow global control interface to be used as a monitor
+         interface to receive events from all interfaces
+       * fixed OKC-based PMKSA cache entry clearing
+       * fixed TKIP group key configuration with FT
+       * added support for using OCSP stapling to validate server certificate
+         (ocsp=1 as optional and ocsp=2 as mandatory)
+       * added EAP-EKE peer
+       * added peer restart detection for IBSS RSN
+       * added domain_suffix_match (and domain_suffix_match2 for Phase 2
+         EAP-TLS) to specify additional constraint for the server certificate
+         domain name
+       * added support for external SIM/USIM processing in EAP-SIM, EAP-AKA,
+         and EAP-AKA' (CTRL-REQ-SIM and CTRL-RSP-SIM commands over control
+         interface)
+       * added global bgscan configuration option as a default for all network
+         blocks that do not specify their own bgscan parameters
+       * added D-Bus methods for TDLS
+       * added more control to scan requests
+         - "SCAN freq=<freq list>" can be used to specify which channels are
+           scanned (comma-separated frequency ranges in MHz)
+         - "SCAN passive=1" can be used to request a passive scan (no Probe
+           Request frames are sent)
+         - "SCAN use_id" can be used to request a scan id to be returned and
+           included in event messages related to this specific scan operation
+         - "SCAN only_new=1" can be used to request the driver/cfg80211 to
+           report only BSS entries that have been updated during this scan
+           round
+         - these optional arguments to the SCAN command can be combined with
+           each other
+       * modified behavior on externally triggered scans
+         - avoid concurrent operations requiring full control of the radio when
+           an externally triggered scan is detected
+         - do not use results for internal roaming decision
+       * added a new cred block parameter 'temporary' to allow credential
+         blocks to be stored separately even if wpa_supplicant configuration
+         file is used to maintain other network information
+       * added "radio work" framework to schedule exclusive radio operations
+         for off-channel functionality
+         - reduce issues with concurrent operations that try to control which
+           channel is used
+         - allow external programs to request exclusive radio control in a way
+           that avoids conflicts with wpa_supplicant
+       * added support for using Protected Dual of Public Action frames for
+         GAS/ANQP exchanges when associated with PMF
+       * added support for WPS+NFC updates and P2P+NFC
+         - improved protocol for WPS
+         - P2P group formation/join based on NFC connection handover
+         - new IPv4 address assignment for P2P groups (ip_addr_* configuration
+           parameters on the GO) to replace DHCP
+         - option to fetch and report alternative carrier records for external
+           NFC operations
+       * various bug fixes
 
 2013-01-12 - v2.0
        * removed Qt3-based wpa_gui (obsoleted by wpa_qui-qt4)
index 10e14cc..32629c0 100644 (file)
@@ -6,17 +6,25 @@ ifndef CFLAGS
 CFLAGS = -MMD -O2 -Wall -g
 endif
 
-export LIBDIR ?= /usr/local/lib/
-export BINDIR ?= /usr/local/sbin/
+export LIBDIR ?= /usr/lib
+export BINDIR ?= /usr/sbin
 PKG_CONFIG ?= pkg-config
 
-CFLAGS += -I../src
-CFLAGS += -I../src/utils
+CFLAGS += $(EXTRA_CFLAGS)
+CFLAGS += -I$(abspath ../src)
+CFLAGS += -I$(abspath ../src/utils)
+
 CFLAGS += -fPIE
 LDFLAGS += -pie
 
 -include .config
 
+ifdef CONFIG_TESTING_OPTIONS
+CFLAGS += -DCONFIG_TESTING_OPTIONS
+CONFIG_WPS_TESTING=y
+CONFIG_TDLS_TESTING=y
+endif
+
 BINALL=wpa_supplicant wpa_cli
 
 ifndef CONFIG_NO_WPA_PASSPHRASE
@@ -76,13 +84,19 @@ OBJS_p += ../src/utils/wpabuf.o
 OBJS_c = wpa_cli.o ../src/common/wpa_ctrl.o
 OBJS_c += ../src/utils/wpa_debug.o
 OBJS_c += ../src/utils/common.o
-
+OBJS += wmm_ac.o
 
 CFLAGS += -DTIZEN_EXT
 CFLAGS += -DTIZEN_EXT_P2P
-CFLAGS += -DTIZEN_EXT_ENROLLEE
-CFLAGS += -DTIZEN_EXT_BUSY_DEVICE
-CFLAGS += -DBCM_DRIVER_V115
+#ifdef CONFIG_BCM_DRIVER_V115
+#CFLAGS += -DBCM_DRIVER_V115
+#endif
+ifdef CONFIG_TIZEN_MOBILE
+CFLAGS += -DTIZEN_MOBILE
+endif
+ifdef CONFIG_TIZEN_WLAN_BOARD_SPRD
+CFLAGS += -DTIZEN_WLAN_BOARD_SPRD
+endif
 
 ifndef CONFIG_OS
 ifdef CONFIG_NATIVE_WINDOWS
@@ -106,13 +120,14 @@ OBJS += ../src/utils/trace.o
 OBJS_p += ../src/utils/trace.o
 OBJS_c += ../src/utils/trace.o
 OBJS_priv += ../src/utils/trace.o
+LIBCTRL += ../src/utils/trace.o
 LDFLAGS += -rdynamic
 CFLAGS += -funwind-tables
 ifdef CONFIG_WPA_TRACE_BFD
-CFLAGS += -DWPA_TRACE_BFD
-LIBS += -lbfd
-LIBS_p += -lbfd
-LIBS_c += -lbfd
+CFLAGS += -DPACKAGE="wpa_supplicant" -DWPA_TRACE_BFD
+LIBS += -lbfd -ldl -liberty -lz
+LIBS_p += -lbfd -ldl -liberty -lz
+LIBS_c += -lbfd -ldl -liberty -lz
 endif
 endif
 
@@ -122,15 +137,32 @@ endif
 OBJS += ../src/utils/$(CONFIG_ELOOP).o
 OBJS_c += ../src/utils/$(CONFIG_ELOOP).o
 
+ifeq ($(CONFIG_ELOOP), eloop)
+# Using glibc < 2.17 requires -lrt for clock_gettime()
+LIBS += -lrt
+LIBS_c += -lrt
+LIBS_p += -lrt
+endif
+
 ifdef CONFIG_ELOOP_POLL
 CFLAGS += -DCONFIG_ELOOP_POLL
 endif
 
+ifdef CONFIG_ELOOP_EPOLL
+CFLAGS += -DCONFIG_ELOOP_EPOLL
+endif
 
 ifdef CONFIG_EAPOL_TEST
 CFLAGS += -Werror -DEAPOL_TEST
 endif
 
+ifdef CONFIG_CODE_COVERAGE
+CFLAGS += -O0 -fprofile-arcs -ftest-coverage
+LIBS += -lgcov
+LIBS_c += -lgcov
+LIBS_p += -lgcov
+endif
+
 ifdef CONFIG_HT_OVERRIDES
 CFLAGS += -DCONFIG_HT_OVERRIDES
 endif
@@ -171,6 +203,17 @@ ifdef CONFIG_NO_SCAN_PROCESSING
 CFLAGS += -DCONFIG_NO_SCAN_PROCESSING
 endif
 
+ifdef CONFIG_SUITEB
+CFLAGS += -DCONFIG_SUITEB
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_SUITEB192
+CFLAGS += -DCONFIG_SUITEB192
+NEED_SHA384=y
+endif
+
 ifdef CONFIG_IEEE80211W
 CFLAGS += -DCONFIG_IEEE80211W
 NEED_SHA256=y
@@ -180,9 +223,22 @@ endif
 ifdef CONFIG_IEEE80211R
 CFLAGS += -DCONFIG_IEEE80211R
 OBJS += ../src/rsn_supp/wpa_ft.o
+NEED_SHA256=y
+NEED_AES_OMAC1=y
+endif
+
+ifdef CONFIG_MESH
 NEED_80211_COMMON=y
 NEED_SHA256=y
+NEED_AES_SIV=y
 NEED_AES_OMAC1=y
+NEED_AES_CTR=y
+CONFIG_SAE=y
+CONFIG_AP=y
+CFLAGS += -DCONFIG_MESH
+OBJS += mesh.o
+OBJS += mesh_mpm.o
+OBJS += mesh_rsn.o
 endif
 
 ifdef CONFIG_SAE
@@ -246,10 +302,10 @@ OBJS += ../src/p2p/p2p_invitation.o
 OBJS += ../src/p2p/p2p_dev_disc.o
 OBJS += ../src/p2p/p2p_group.o
 OBJS += ../src/ap/p2p_hostapd.o
+OBJS += ../src/utils/bitfield.o
 CFLAGS += -DCONFIG_P2P
 NEED_GAS=y
 NEED_OFFCHANNEL=y
-NEED_80211_COMMON=y
 CONFIG_WPS=y
 CONFIG_AP=y
 ifdef CONFIG_P2P_STRICT
@@ -266,6 +322,7 @@ ifdef CONFIG_HS20
 OBJS += hs20_supplicant.o
 CFLAGS += -DCONFIG_HS20
 CONFIG_INTERWORKING=y
+NEED_AES_OMAC1=y
 endif
 
 ifdef CONFIG_INTERWORKING
@@ -313,6 +370,12 @@ ifeq ($(CONFIG_L2_PACKET), freebsd)
 LIBS += -lpcap
 endif
 
+ifdef CONFIG_ERP
+CFLAGS += -DCONFIG_ERP
+NEED_SHA256=y
+NEED_HMAC_SHA256_KDF=y
+endif
+
 ifdef CONFIG_EAP_TLS
 # EAP-TLS
 ifeq ($(CONFIG_EAP_TLS), dyn)
@@ -494,7 +557,7 @@ endif
 ifdef CONFIG_EAP_PROXY
 CFLAGS += -DCONFIG_EAP_PROXY
 OBJS += ../src/eap_peer/eap_proxy_$(CONFIG_EAP_PROXY).o
-include eap_proxy_$(CONFIG_EAP_PROXY).mk
+include eap_proxy_$(CONFIG_EAP_PROXY).mak
 CONFIG_IEEE8021X_EAPOL=y
 endif
 
@@ -584,11 +647,23 @@ CONFIG_IEEE8021X_EAPOL=y
 NEED_SHA256=y
 endif
 
-ifdef CONFIG_WPS
-ifdef CONFIG_WPS2
-CFLAGS += -DCONFIG_WPS2
+ifdef CONFIG_EAP_EKE
+# EAP-EKE
+ifeq ($(CONFIG_EAP_EKE), dyn)
+CFLAGS += -DEAP_EKE_DYNAMIC
+EAPDYN += ../src/eap_peer/eap_eke.so
+else
+CFLAGS += -DEAP_EKE
+OBJS += ../src/eap_peer/eap_eke.o ../src/eap_common/eap_eke_common.o
+OBJS_h += ../src/eap_server/eap_server_eke.o
+endif
+CONFIG_IEEE8021X_EAPOL=y
+NEED_DH_GROUPS=y
+NEED_DH_GROUPS_ALL=y
+NEED_SHA256=y
 endif
 
+ifdef CONFIG_WPS
 # EAP-WSC
 CFLAGS += -DCONFIG_WPS -DEAP_WSC
 OBJS += wps_supplicant.o
@@ -607,7 +682,6 @@ CONFIG_IEEE8021X_EAPOL=y
 NEED_DH_GROUPS=y
 NEED_SHA256=y
 NEED_BASE64=y
-NEED_80211_COMMON=y
 NEED_AES_CBC=y
 NEED_MODEXP=y
 
@@ -715,8 +789,20 @@ LIBS += -ldl -rdynamic
 endif
 endif
 
+ifdef CONFIG_MACSEC
+CFLAGS += -DCONFIG_MACSEC
+NEED_AES_ENCBLOCK=y
+NEED_AES_UNWRAP=y
+NEED_AES_WRAP=y
+NEED_AES_OMAC1=y
+OBJS += wpas_kay.o
+OBJS += ../src/pae/ieee802_1x_cp.o
+OBJS += ../src/pae/ieee802_1x_kay.o
+OBJS += ../src/pae/ieee802_1x_key.o
+OBJS += ../src/pae/ieee802_1x_secy_ops.o
+endif
+
 ifdef CONFIG_AP
-NEED_80211_COMMON=y
 NEED_EAP_COMMON=y
 NEED_RSN_AUTHENTICATOR=y
 CFLAGS += -DCONFIG_AP
@@ -740,9 +826,13 @@ OBJS += ../src/ap/ieee802_11_shared.o
 OBJS += ../src/ap/drv_callbacks.o
 OBJS += ../src/ap/ap_drv_ops.o
 OBJS += ../src/ap/beacon.o
+OBJS += ../src/ap/bss_load.o
 OBJS += ../src/ap/eap_user_db.o
 ifdef CONFIG_IEEE80211N
 OBJS += ../src/ap/ieee802_11_ht.o
+ifdef CONFIG_IEEE80211AC
+OBJS += ../src/ap/ieee802_11_vht.o
+endif
 endif
 ifdef CONFIG_WNM
 OBJS += ../src/ap/wnm_ap.o
@@ -758,6 +848,9 @@ OBJS += ../src/eap_server/eap_server_methods.o
 
 ifdef CONFIG_IEEE80211N
 CFLAGS += -DCONFIG_IEEE80211N
+ifdef CONFIG_IEEE80211AC
+CFLAGS += -DCONFIG_IEEE80211AC
+endif
 endif
 
 ifdef NEED_AP_MLME
@@ -765,6 +858,7 @@ OBJS += ../src/ap/wmm.o
 OBJS += ../src/ap/ap_list.o
 OBJS += ../src/ap/ieee802_11.o
 OBJS += ../src/ap/hw_features.o
+OBJS += ../src/ap/dfs.o
 CFLAGS += -DNEED_AP_MLME
 endif
 ifdef CONFIG_WPS
@@ -923,7 +1017,8 @@ endif
 OBJS += ../src/crypto/crypto_gnutls.o
 OBJS_p += ../src/crypto/crypto_gnutls.o
 ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_gnutls.o
+OBJS += ../src/crypto/fips_prf_internal.o
+SHA1OBJS += ../src/crypto/sha1-internal.o
 endif
 LIBS += -lgcrypt
 LIBS_p += -lgcrypt
@@ -939,29 +1034,14 @@ endif
 OBJS += ../src/crypto/crypto_cryptoapi.o
 OBJS_p += ../src/crypto/crypto_cryptoapi.o
 ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_cryptoapi.o
+OBJS += ../src/crypto/fips_prf_internal.o
+SHA1OBJS += ../src/crypto/sha1-internal.o
 endif
 CONFIG_INTERNAL_SHA256=y
 CONFIG_INTERNAL_RC4=y
 CONFIG_INTERNAL_DH_GROUP5=y
 endif
 
-ifeq ($(CONFIG_TLS), nss)
-ifdef TLS_FUNCS
-OBJS += ../src/crypto/tls_nss.o
-LIBS += -lssl3
-endif
-OBJS += ../src/crypto/crypto_nss.o
-OBJS_p += ../src/crypto/crypto_nss.o
-ifdef NEED_FIPS186_2_PRF
-OBJS += ../src/crypto/fips_prf_nss.o
-endif
-LIBS += -lnss3
-LIBS_p += -lnss3
-CONFIG_INTERNAL_MD4=y
-CONFIG_INTERNAL_DH_GROUP5=y
-endif
-
 ifeq ($(CONFIG_TLS), internal)
 ifndef CONFIG_CRYPTO
 CONFIG_CRYPTO=internal
@@ -1079,7 +1159,9 @@ ifdef CONFIG_INTERNAL_AES
 AESOBJS += ../src/crypto/aes-internal.o ../src/crypto/aes-internal-dec.o
 endif
 
+ifneq ($(CONFIG_TLS), openssl)
 AESOBJS += ../src/crypto/aes-unwrap.o
+endif
 ifdef NEED_AES_EAX
 AESOBJS += ../src/crypto/aes-eax.o
 NEED_AES_CTR=y
@@ -1098,10 +1180,15 @@ else
 AESOBJS += ../src/crypto/aes-omac1.o
 endif
 endif
+ifdef NEED_AES_SIV
+AESOBJS += ../src/crypto/aes-siv.o
+endif
 ifdef NEED_AES_WRAP
 NEED_AES_ENC=y
+ifneq ($(CONFIG_TLS), openssl)
 AESOBJS += ../src/crypto/aes-wrap.o
 endif
+endif
 ifdef NEED_AES_CBC
 NEED_AES_ENC=y
 AESOBJS += ../src/crypto/aes-cbc.o
@@ -1142,8 +1229,10 @@ endif
 endif
 
 ifndef CONFIG_FIPS
+ifneq ($(CONFIG_TLS), openssl)
 MD5OBJS += ../src/crypto/md5.o
 endif
+endif
 ifdef NEED_MD5
 ifdef CONFIG_INTERNAL_MD5
 MD5OBJS += ../src/crypto/md5-internal.o
@@ -1184,8 +1273,14 @@ endif
 ifdef NEED_TLS_PRF_SHA256
 SHA256OBJS += ../src/crypto/sha256-tlsprf.o
 endif
+ifdef NEED_HMAC_SHA256_KDF
+OBJS += ../src/crypto/sha256-kdf.o
+endif
 OBJS += $(SHA256OBJS)
 endif
+ifdef NEED_SHA384
+CFLAGS += -DCONFIG_SHA384
+endif
 
 ifdef NEED_DH_GROUPS
 OBJS += ../src/crypto/dh_groups.o
@@ -1224,6 +1319,11 @@ endif
 ifeq ($(CONFIG_CTRL_IFACE), udp)
 CFLAGS += -DCONFIG_CTRL_IFACE_UDP
 endif
+ifeq ($(CONFIG_CTRL_IFACE), udp6)
+CONFIG_CTRL_IFACE=udp
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP_IPV6
+endif
 ifeq ($(CONFIG_CTRL_IFACE), named_pipe)
 CFLAGS += -DCONFIG_CTRL_IFACE_NAMED_PIPE
 endif
@@ -1232,6 +1332,12 @@ CONFIG_CTRL_IFACE=udp
 CFLAGS += -DCONFIG_CTRL_IFACE_UDP
 CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE
 endif
+ifeq ($(CONFIG_CTRL_IFACE), udp6-remote)
+CONFIG_CTRL_IFACE=udp
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP_REMOTE
+CFLAGS += -DCONFIG_CTRL_IFACE_UDP_IPV6
+endif
 OBJS += ctrl_iface.o ctrl_iface_$(CONFIG_CTRL_IFACE).o
 endif
 
@@ -1324,14 +1430,12 @@ OBJS += ../src/utils/base64.o
 endif
 
 ifdef NEED_SME
-NEED_80211_COMMON=y
 OBJS += sme.o
 CFLAGS += -DCONFIG_SME
 endif
 
-ifdef NEED_80211_COMMON
 OBJS += ../src/common/ieee802_11_common.o
-endif
+OBJS += ../src/common/hw_features_common.o
 
 ifdef NEED_EAP_COMMON
 OBJS += ../src/eap_common/eap_common.o
@@ -1429,6 +1533,20 @@ OBJS += offchannel.o
 CFLAGS += -DCONFIG_OFFCHANNEL
 endif
 
+ifdef CONFIG_MODULE_TESTS
+CFLAGS += -DCONFIG_MODULE_TESTS
+OBJS += wpas_module_tests.o
+OBJS += ../src/utils/utils_module_tests.o
+OBJS += ../src/common/common_module_tests.o
+OBJS += ../src/crypto/crypto_module_tests.o
+ifdef CONFIG_WPS
+OBJS += ../src/wps/wps_module_tests.o
+endif
+ifndef CONFIG_P2P
+OBJS += ../src/utils/bitfield.o
+endif
+endif
+
 OBJS += ../src/drivers/driver_common.o
 OBJS_priv += ../src/drivers/driver_common.o
 
@@ -1464,26 +1582,6 @@ OBJS_priv += wpa_priv.o
 ifdef CONFIG_DRIVER_NL80211
 OBJS_priv += ../src/common/ieee802_11_common.o
 endif
-ifdef CONFIG_DRIVER_TEST
-OBJS_priv += $(SHA1OBJS)
-OBJS_priv += $(MD5OBJS)
-ifeq ($(CONFIG_TLS), openssl)
-OBJS_priv += ../src/crypto/crypto_openssl.o
-endif
-ifeq ($(CONFIG_TLS), gnutls)
-OBJS_priv += ../src/crypto/crypto_gnutls.o
-endif
-ifeq ($(CONFIG_TLS), nss)
-OBJS_priv += ../src/crypto/crypto_nss.o
-endif
-ifeq ($(CONFIG_TLS), internal)
-ifeq ($(CONFIG_CRYPTO), libtomcrypt)
-OBJS_priv += ../src/crypto/crypto_libtomcrypt.o
-else
-OBJS_priv += ../src/crypto/crypto_internal.o
-endif
-endif
-endif # CONFIG_DRIVER_TEST
 OBJS += ../src/l2_packet/l2_packet_privsep.o
 OBJS += ../src/drivers/driver_privsep.o
 EXTRA_progs += wpa_priv
@@ -1513,6 +1611,10 @@ ifeq ($(V), 1)
 Q=
 E=true
 endif
+ifeq ($(QUIET), 1)
+Q=@
+E=true
+endif
 
 dynamic_eap_methods: $(EAPDYN)
 
@@ -1550,6 +1652,15 @@ wpa_cli: $(OBJS_c)
        $(Q)$(LDO) $(LDFLAGS) -o wpa_cli $(OBJS_c) $(LIBS_c)
        @$(E) "  LD " $@
 
+LIBCTRL += ../src/common/wpa_ctrl.o
+LIBCTRL += ../src/utils/os_$(CONFIG_OS).o
+LIBCTRL += ../src/utils/wpa_debug.o
+
+libwpa_ctrl.a: $(LIBCTRL)
+       $(Q)rm -f $@
+       $(Q)$(AR) crs $@ $?
+       @$(E) "  AR " $@
+
 link_test: $(OBJS) $(OBJS_h) tests/link_test.o
        $(Q)$(LDO) $(LDFLAGS) -o link_test $(OBJS) $(OBJS_h) tests/link_test.o $(LIBS)
        @$(E) "  LD " $@
@@ -1586,19 +1697,31 @@ eap_ikev2.so: ../src/eap_peer/eap_ikev2.c ../src/eap_peer/ikev2.c ../src/eap_com
        $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
                -Deap_peer_ikev2_register=eap_peer_method_dynamic_init
 
+eap_eke.so: ../src/eap_peer/eap_eke.c ../src/eap_common/eap_eke_common.c
+       $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $^ \
+               -Deap_peer_eke_register=eap_peer_method_dynamic_init
+
 %.so: %.c
        $(CC) $(LDFLAGS) -o $@ $(CFLAGS) -shared -rdynamic -fPIC $< \
                -D$(*F:eap_%=eap_peer_%)_register=eap_peer_method_dynamic_init
 
+ifdef CONFIG_CODE_COVERAGE
+%.o: %.c
+       @$(E) "  CC " $<
+       $(Q)cd $(dir $@); $(CC) -c -o $(notdir $@) $(CFLAGS) $(notdir $<)
+else
 %.o: %.c
        $(Q)$(CC) -c -o $@ $(CFLAGS) $<
        @$(E) "  CC " $<
+endif
 
 %.service: %.service.in
-       sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
+       $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
+       @$(E) "  sed" $<
 
 %@.service: %.service.arg.in
-       sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
+       $(Q)sed -e 's|\@BINDIR\@|$(BINDIR)|g' $< >$@
+       @$(E) "  sed" $<
 
 wpa_supplicant.exe: wpa_supplicant
        mv -f $< $@
@@ -1644,11 +1767,19 @@ FIPSLD=$(FIPSDIR)/bin/fipsld
 fips:
        $(MAKE) CC=$(FIPSLD) FIPSLD_CC="$(CC)"
 
+lcov-html: wpa_supplicant.gcda
+       lcov -c -d .. > lcov.info
+       genhtml lcov.info --output-directory lcov-html
+
 clean:
        $(MAKE) -C ../src clean
        $(MAKE) -C dbus clean
-       rm -f core *~ *.o *.d eap_*.so $(ALL) $(WINALL) eapol_test preauth_test
+       rm -f core *~ *.o *.d *.gcno *.gcda *.gcov
+       rm -f eap_*.so $(ALL) $(WINALL) eapol_test preauth_test
        rm -f wpa_priv
        rm -f nfc_pw_token
+       rm -f lcov.info
+       rm -rf lcov-html
+       rm -f libwpa_ctrl.a
 
 -include $(OBJS:%.o=%.d)
index 78df89e..f9c65d2 100644 (file)
@@ -1,7 +1,7 @@
 WPA Supplicant
 ==============
 
-Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi> and contributors
+Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors
 All Rights Reserved.
 
 This program is licensed under the BSD license (the one with
@@ -413,7 +413,7 @@ usage:
         [-G<group>] \
         -i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] [-p<driver_param>] \
         [-b<br_ifname> [-N -i<ifname> -c<conf> [-C<ctrl>] [-D<driver>] \
-        [-p<driver_param>] [-b<br_ifname>] ...]
+        [-p<driver_param>] [-b<br_ifname>] [-m<P2P Device config file>] ...
 
 options:
   -b = optional bridge interface name
@@ -438,6 +438,7 @@ options:
   -w = wait for interface to be added, if needed
   -W = wait for a control interface monitor before starting
   -N = start describing new interface
+  -m = Configuration file for the P2P Device
 
 drivers:
   nl80211 = Linux nl80211/cfg80211
@@ -949,3 +950,105 @@ can be started when an interface is added (hotplug/udev/etc. scripts).
 wpa_priv can control multiple interface with one process, but it is
 also possible to run multiple wpa_priv processes at the same time, if
 desired.
+
+
+Linux capabilities instead of privileged process
+------------------------------------------------
+
+wpa_supplicant performs operations that need special permissions, e.g.,
+to control the network connection. Traditionally this has been achieved
+by running wpa_supplicant as a privileged process with effective user id
+0 (root). Linux capabilities can be used to provide restricted set of
+capabilities to match the functions needed by wpa_supplicant. The
+minimum set of capabilities needed for the operations is CAP_NET_ADMIN
+and CAP_NET_RAW.
+
+setcap(8) can be used to set file capabilities. For example:
+
+sudo setcap cap_net_raw,cap_net_admin+ep wpa_supplicant
+
+Please note that this would give anyone being able to run that
+wpa_supplicant binary access to the additional capabilities. This can
+further be limited by file owner/group and mode bits. For example:
+
+sudo chown wpas wpa_supplicant
+sudo chmod 0100 wpa_supplicant
+
+This combination of setcap, chown, and chmod commands would allow wpas
+user to execute wpa_supplicant with additional network admin/raw
+capabilities.
+
+Common way style of creating a control interface socket in
+/var/run/wpa_supplicant could not be done by this user, but this
+directory could be created before starting the wpa_supplicant and set to
+suitable mode to allow wpa_supplicant to create sockets
+there. Alternatively, other directory or abstract socket namespace could
+be used for the control interface.
+
+
+External requests for radio control
+-----------------------------------
+
+External programs can request wpa_supplicant to not start offchannel
+operations during other tasks that may need exclusive control of the
+radio. The RADIO_WORK control interface command can be used for this.
+
+"RADIO_WORK add <name> [freq=<MHz>] [timeout=<seconds>]" command can be
+used to reserve a slot for radio access. If freq is specified, other
+radio work items on the same channel may be completed in
+parallel. Otherwise, all other radio work items are blocked during
+execution. Timeout is set to 10 seconds by default to avoid blocking
+wpa_supplicant operations for excessive time. If a longer (or shorter)
+safety timeout is needed, that can be specified with the optional
+timeout parameter. This command returns an identifier for the radio work
+item.
+
+Once the radio work item has been started, "EXT-RADIO-WORK-START <id>"
+event message is indicated that the external processing can start. Once
+the operation has been completed, "RADIO_WORK done <id>" is used to
+indicate that to wpa_supplicant. This allows other radio works to be
+performed. If this command is forgotten (e.g., due to the external
+program terminating), wpa_supplicant will time out the radio owrk item
+and send "EXT-RADIO-WORK-TIMEOUT <id>" event ot indicate that this has
+happened. "RADIO_WORK done <id>" can also be used to cancel items that
+have not yet been started.
+
+For example, in wpa_cli interactive mode:
+
+> radio_work add test
+1
+<3>EXT-RADIO-WORK-START 1
+> radio_work show
+ext:test@wlan0:0:1:2.487797
+> radio_work done 1
+OK
+> radio_work show
+
+
+> radio_work done 3
+OK
+> radio_work show
+ext:test freq=2412 timeout=30@wlan0:2412:1:28.583483
+<3>EXT-RADIO-WORK-TIMEOUT 2
+
+
+> radio_work add test2 freq=2412 timeout=60
+5
+<3>EXT-RADIO-WORK-START 5
+> radio_work add test3
+6
+> radio_work add test4
+7
+> radio_work show
+ext:test2 freq=2412 timeout=60@wlan0:2412:1:9.751844
+ext:test3@wlan0:0:0:5.071812
+ext:test4@wlan0:0:0:3.143870
+> radio_work done 6
+OK
+> radio_work show
+ext:test2 freq=2412 timeout=60@wlan0:2412:1:16.287869
+ext:test4@wlan0:0:0:9.679895
+> radio_work done 5
+OK
+<3>EXT-RADIO-WORK-START 7
+<3>EXT-RADIO-WORK-TIMEOUT 7
index 5669c55..161dc06 100644 (file)
@@ -109,6 +109,8 @@ Credentials can be pre-configured for automatic network selection:
 #
 # credential fields:
 #
+# temporary: Whether this credential is temporary and not to be saved
+#
 # priority: Priority group
 #      By default, all networks and credentials get the same priority group
 #      (0). This field can be used to give higher priority for credentials
@@ -166,9 +168,25 @@ Credentials can be pre-configured for automatic network selection:
 # milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN>
 #      format
 #
-# domain: Home service provider FQDN
+# domain_suffix_match: Constraint for server domain name
+#      If set, this FQDN is used as a suffix match requirement for the AAA
+#      server certificate in SubjectAltName dNSName element(s). If a
+#      matching dNSName is found, this constraint is met. If no dNSName
+#      values are present, this constraint is matched against SubjectName CN
+#      using same suffix match comparison. Suffix match here means that the
+#      host/domain name is compared one label at a time starting from the
+#      top-level domain and all the labels in @domain_suffix_match shall be
+#      included in the certificate. The certificate may include additional
+#      sub-level labels in addition to the required labels.
+#
+#      For example, domain_suffix_match=example.com would match
+#      test.example.com but would not match test-example.com.
+#
+# domain: Home service provider FQDN(s)
 #      This is used to compare against the Domain Name List to figure out
-#      whether the AP is operated by the Home SP.
+#      whether the AP is operated by the Home SP. Multiple domain entries can
+#      be used to configure alternative FQDNs that will be considered home
+#      networks.
 #
 # roaming_consortium: Roaming Consortium OI
 #      If roaming_consortium_len is non-zero, this field contains the
@@ -195,6 +213,65 @@ Credentials can be pre-configured for automatic network selection:
 #      matching with the network. Multiple entries can be used to specify more
 #      than one SSID.
 #
+# roaming_partner: Roaming partner information
+#      This optional field can be used to configure preferences between roaming
+#      partners. The field is a string in following format:
+#      <FQDN>,<0/1 exact match>,<priority>,<* or country code>
+#      (non-exact match means any subdomain matches the entry; priority is in
+#      0..255 range with 0 being the highest priority)
+#
+# update_identifier: PPS MO ID
+#      (Hotspot 2.0 PerProviderSubscription/UpdateIdentifier)
+#
+# provisioning_sp: FQDN of the SP that provisioned the credential
+#      This optional field can be used to keep track of the SP that provisioned
+#      the credential to find the PPS MO (./Wi-Fi/<provisioning_sp>).
+#
+# sp_priority: Credential priority within a provisioning SP
+#      This is the priority of the credential among all credentials
+#      provisionined by the same SP (i.e., for entries that have identical
+#      provisioning_sp value). The range of this priority is 0-255 with 0
+#      being the highest and 255 the lower priority.
+#
+# Minimum backhaul threshold (PPS/<X+>/Policy/MinBackhauldThreshold/*)
+#      These fields can be used to specify minimum download/upload backhaul
+#      bandwidth that is preferred for the credential. This constraint is
+#      ignored if the AP does not advertise WAN Metrics information or if the
+#      limit would prevent any connection. Values are in kilobits per second.
+# min_dl_bandwidth_home
+# min_ul_bandwidth_home
+# min_dl_bandwidth_roaming
+# min_ul_bandwidth_roaming
+#
+# max_bss_load: Maximum BSS Load Channel Utilization (1..255)
+#      (PPS/<X+>/Policy/MaximumBSSLoadValue)
+#      This value is used as the maximum channel utilization for network
+#      selection purposes for home networks. If the AP does not advertise
+#      BSS Load or if the limit would prevent any connection, this constraint
+#      will be ignored.
+#
+# req_conn_capab: Required connection capability
+#      (PPS/<X+>/Policy/RequiredProtoPortTuple)
+#      This value is used to configure set of required protocol/port pairs that
+#      a roaming network shall support (include explicitly in Connection
+#      Capability ANQP element). This constraint is ignored if the AP does not
+#      advertise Connection Capability or if this constraint would prevent any
+#      network connection. This policy is not used in home networks.
+#      Format: <protocol>[:<comma-separated list of ports]
+#      Multiple entries can be used to list multiple requirements.
+#      For example, number of common TCP protocols:
+#      req_conn_capab=6:22,80,443
+#      For example, IPSec/IKE:
+#      req_conn_capab=17:500
+#      req_conn_capab=50
+#
+# ocsp: Whether to use/require OCSP to check server certificate
+#      0 = do not use OCSP stapling (TLS certificate status extension)
+#      1 = try to use OCSP stapling, but not require response
+#      2 = require valid OCSP stapling response
+#
+# sim_num: Identifier for which SIM to use in multi-SIM devices
+#
 # for example:
 #
 #cred={
@@ -203,6 +280,7 @@ Credentials can be pre-configured for automatic network selection:
 #      password="password"
 #      ca_cert="/etc/wpa_supplicant/ca.pem"
 #      domain="example.com"
+#      domain_suffix_match="example.com"
 #}
 #
 #cred={
@@ -252,6 +330,8 @@ OK
 OK
 > set_cred 0 priority 1
 OK
+> set_cred 0 temporary 1
+OK
 
 Add a SIM credential using a simulated SIM/USIM card for testing:
 
@@ -267,6 +347,17 @@ OK
 Note: the return value of add_cred is used as the first argument to
 the following set_cred commands.
 
+Add a SIM credential using a external SIM/USIM processing:
+
+> set external_sim 1
+OK
+> add_cred
+1
+> set_cred 1 imsi "23456-0000000000"
+OK
+> set_cred 1 eap SIM
+OK
+
 
 Add a WPA2-Enterprise network:
 
index 8447a90..6a5b032 100644 (file)
@@ -72,7 +72,8 @@ over the main control interface.
 Device Discovery
 
 p2p_find [timeout in seconds] [type=<social|progressive>] \
-       [dev_id=<addr>] [delay=<search delay in ms>]
+       [dev_id=<addr>] [dev_type=<device type>] \
+       [delay=<search delay in ms>] [seek=<service name>] [freq=<MHz>]
 
 The default behavior is to run a single full scan in the beginning and
 then scan only social channels. type=social will scan only social
@@ -80,13 +81,37 @@ channels, i.e., it skips the initial full scan. type=progressive is
 like the default behavior, but it will scan through all the channels
 progressively one channel at the time in the Search state rounds. This
 will help in finding new groups or groups missed during the initial
-full scan.
+full scan. When the type parameter is not included (i.e., full scan), the
+optional freq parameter can be used to override the first scan to use only
+the specified channel after which only social channels are scanned.
 
 The optional dev_id option can be used to specify a single P2P peer to
 search for. The optional delay parameter can be used to request an extra
 delay to be used between search iterations (e.g., to free up radio
 resources for concurrent operations).
 
+The optional dev_type option can be used to specify a single device type
+(primary or secondary) to search for, e.g.,
+"p2p_find dev_type=1-0050F204-1".
+
+
+With one or more seek arguments, the command sends Probe Request frames
+for a P2PS service. For example,
+p2p_find 5 dev_id=11:22:33:44:55:66 seek=alt.example.chat seek=alt.example.video
+
+Parameters description:
+    Timeout - Optional ASCII base-10-encoded u16. If missing, request will not
+       time out and must be canceled manually
+    dev_id - Optional to request responses from a single known remote device
+    Service Name - Mandatory UTF-8 string for ASP seeks
+       Service name must match the remote service being advertised exactly
+       (no prefix matching).
+       Service name may be empty, in which case all ASP services will be
+       returned, and may be filtered with p2p_serv_disc_req settings, and
+       p2p_serv_asp_resp results.
+       Multiple service names may be requested, but if it exceeds internal
+       limit, it will automatically revert to requesting all ASP services.
+
 p2p_listen [timeout in seconds]
 
 Start Listen-only state (become discoverable without searching for
@@ -123,9 +148,9 @@ parameter can be used to request wpa_supplicant to automatically figure
 out whether the peer device is operating as a GO and if so, use
 join-a-group style PD instead of GO Negotiation style PD.
 
-p2p_connect <peer device address> <pbc|pin|PIN#> [display|keypad]
+p2p_connect <peer device address> <pbc|pin|PIN#|p2ps> [display|keypad|p2ps]
        [persistent|persistent=<network id>] [join|auth]
-       [go_intent=<0..15>] [freq=<in MHz>] [ht40] [provdisc]
+       [go_intent=<0..15>] [freq=<in MHz>] [ht40] [vht] [provdisc] [auto]
 
 Start P2P group formation with a discovered P2P peer. This includes
 optional group owner negotiation, group interface setup, provisioning,
@@ -166,7 +191,71 @@ used prior to starting GO Negotiation as a workaround with some deployed
 P2P implementations that require this to allow the user to accept the
 connection.
 
-p2p_group_add [persistent|persistent=<network id>] [freq=<freq in MHz>] [ht40]
+"auto" can be used to request wpa_supplicant to automatically figure
+out whether the peer device is operating as a GO and if so, use
+join-a-group operation rather than GO Negotiation.
+
+P2PS attribute changes to p2p_connect command:
+
+P2PS supports two WPS provisioning methods namely PIN method and P2PS default.
+The remaining paramters hold same role as in legacy P2P. In case of P2PS default
+config method "p2ps" keyword is added in p2p_connect command.
+
+For example:
+p2p_connect 02:0a:f5:85:11:00 12345670 p2ps persistent join
+       (WPS Method = P2PS default)
+
+p2p_connect 02:0a:f5:85:11:00 45629034 keypad persistent
+       (WPS Method = PIN)
+
+p2p_asp_provision <peer MAC address> <adv_id=peer adv id>
+       <adv_mac=peer MAC address> [role=2|4|1] <session=session id>
+       <session_mac=initiator mac address>
+       [info='service info'] <method=Default|keypad|Display>
+
+This command starts provision discovery with the P2PS enabled peer device.
+
+For example,
+p2p_asp_provision 00:11:22:33:44:55 adv_id=4d6fc7 adv_mac=00:55:44:33:22:11 role=1 session=12ab34 session_mac=00:11:22:33:44:55 info='name=john' method=1000
+
+Parameter description:
+    MAC address - Mandatory
+    adv_id - Mandatory remote Advertising ID of service connection is being
+       established for
+    adv_mac - Mandatory MAC address that owns/registered the service
+    role - Optional
+       2 (group client only) or 4 (group owner only)
+       if not present (or 1) role is negotiated by the two peers.
+    session - Mandatory Session ID of the first session to be established
+    session_mac - Mandatory MAC address that owns/initiated the session
+    method - Optional method to request for provisioning (1000 - P2PS Default,
+       100 - Keypad(PIN), 8 - Display(PIN))
+    info - Optional UTF-8 string. Hint for service to indicate possible usage
+       parameters - Escape single quote & backslash:
+       with a backslash 0x27 == ' == \', and 0x5c == \ == \\
+
+p2p_asp_provision_resp <peer mac address> <adv_id= local adv id>
+       <adv_mac=local MAC address> <role=1|2|4> <status=0>
+       <session=session id> <session_mac=peer MAC address>
+
+This command sends a provision discovery response from responder side.
+
+For example,
+p2p_asp_provision_resp 00:55:44:33:22:11 adv_id=4d6fc7 adv_mac=00:55:44:33:22:11 role=1 status=0 session=12ab34 session_mac=00:11:22:33:44:55
+
+Parameters definition:
+    MAC address - Mandatory
+    adv_id - Mandatory local Advertising ID of service connection is being
+       established for
+    adv_mac - Mandatory MAC address that owns/registered the service
+    role -  Optional 2 (group client only) or 4 (group owner only)
+       if not present (or 1) role is negotiated by the two peers.
+    status - Mandatory Acceptance/Rejection code of Provisioning
+    session - Mandatory Session ID of the first session to be established
+    session_mac - Mandatory MAC address that owns/initiated the session
+
+p2p_group_add [persistent|persistent=<network id>] [freq=<freq in MHz>]
+       [ht40] [vht]
 
 Set up a P2P group owner manually (i.e., without group owner
 negotiation with a specific peer). This is also known as autonomous
@@ -199,8 +288,80 @@ P2P group interface (if one was used) that is in the WPS provisioning
 step. If the WPS provisioning step has been completed, the group is not
 terminated.
 
+p2p_remove_client <peer's P2P Device Address|iface=<interface address>>
+
+This command can be used to remove the specified client from all groups
+(operating and persistent) from the local GO. Note that the peer device
+can rejoin the group if it is in possession of a valid key. See p2p_set
+per_sta_psk command below for more details on how the peer can be
+removed securely.
+
 Service Discovery
 
+p2p_service_add asp <auto accept> <adv id> <status 0/1> <Config Methods>
+       <Service name> [Service Information] [Response Info]
+
+This command can be used to search for a P2PS service which includes
+Play, Send, Display, and Print service. The parameters for this command
+are "asp" to identify the command as P2PS one, auto accept value,
+advertisement id which uniquely identifies the service requests, state
+of the service whether the service is available or not, config methods
+which can be either P2PS method or PIN method, service name followed by
+two optional parameters service information, and response info.
+
+For example,
+p2p_service_add asp 1 4d6fc7 0 1108 alt.example.chat svc_info='name=john' rsp_info='enter PIN 1234'
+
+Parameters definition:
+    asp - Mandatory for ASP service registration
+    auto accept - Mandatory ASCII hex-encoded boolean (0 == no auto-accept,
+       1 == auto-accept ANY role, 2 == auto-accept CLIENT role,
+       4 == auto-accept GO role)
+    Advertisement ID - Mandatory non-zero ASCII hex-encoded u32
+       (Must be unique/not yet exist in svc db)
+    State - Mandatory ASCII hex-encoded u8 (0 -- Svc not available,
+       1 -- Svc available, 2-0xff  Application defined)
+    Config Methods - Mandatory ASCII hex-encoded u16 (bitmask of WSC config
+       methods)
+    Service Name - Mandatory UTF-8 string
+    Service Information - Optional UTF-8 string
+       Escape single quote & backslash with a backslash:
+       0x27 == ' == \', and 0x5c == \ == \\
+    Session response information -  Optional (used only if auto accept is TRUE)
+       UTF-8 string
+       Escape single quote & backslash with a backslash:
+       0x27 == ' == \', and 0x5c == \ == \\
+
+p2p_service_rep asp <auto accept> <adv id> <status 0/1> <Config Methods>
+       <Service name> [Service Information] [Response Info]
+
+This command can be used to replace the existing service request
+attributes from the initiator side. The replacement is only allowed if
+the advertisement id issued in the command matches with any one entry in
+the list of existing SD queries. If advertisement id doesn't match the
+command returns a failure.
+
+For example,
+p2p_service_rep asp 1 4d6fc7 1 1108 alt.example.chat svc_info='name=john' rsp_info='enter PIN 1234'
+
+Parameters definition:
+    asp - Mandatory for ASP service registration
+    auto accept - Mandatory ASCII hex-encoded boolean (1 == true, 0 == false)
+    Advertisement ID - Mandatory non-zero ASCII hex-encoded u32
+       (Must already exist in svc db)
+    State - Mandatory ASCII hex-encoded u8 (can be used to indicate svc
+       available or not available for instance)
+    Config Methods - Mandatory ASCII hex-encoded u16 (bitmask of WSC config
+       methods)
+    Service Name - Mandatory UTF-8 string (Must match existing string in svc db)
+    Service Information - Optional UTF-8 string
+       Escape single quote & backslash with a backslash:
+       0x27 == ' == \', and 0x5c == \ == \\
+    Session response information -  Optional (used only if auto accept is TRUE)
+       UTF-8 string
+       Escape single quote & backslash with a backslash:
+       0x27 == ' == \', and 0x5c == \ == \\
+
 p2p_serv_disc_req
 
 Schedule a P2P service discovery request. The parameters for this
@@ -216,9 +377,8 @@ discovery protocols and requests this to be sent to all discovered
 peers (note: this can result in long response frames). The pending
 requests are sent during device discovery (see p2p_find).
 
-Only a single pending wildcard query is supported, but there can be
-multiple pending peer device specific queries (each will be sent in
-sequence whenever the peer is found).
+There can be multiple pending peer device specific queries (each will be
+sent in sequence whenever the peer is found).
 
 This command returns an identifier for the pending query (e.g.,
 "1f77628") that can be used to cancel the request. Directed requests
@@ -283,6 +443,27 @@ p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [sec-source] 2
 p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source+sink] 2,3,4,5
 p2p_serv_disc_req 00:00:00:00:00:00 wifi-display [source][pri-sink] 2,3,4,5
 
+p2p_serv_disc_req <Unicast|Broadcast mac address> asp <Transaction ID>
+       <Service Name> [Service Information]
+
+The command can be used for service discovery for P2PS enabled devices.
+
+For example: p2p_serv_disc_req 00:00:00:00:00:00 asp a1 alt.example 'john'
+
+Parameters definition:
+    MAC address - Mandatory Existing
+    asp - Mandatory for ASP queries
+    Transaction ID - Mandatory non-zero ASCII hex-encoded u8 for GAS
+    Service Name Prefix - Mandatory UTF-8 string.
+       Will match from beginning of remote Service Name
+    Service Information Substring - Optional UTF-8 string
+       If Service Information Substring is not included, all services matching
+       Service Name Prefix will be returned.
+       If Service Information Substring is included, both the Substring and the
+       Service Name Prefix must match for service to be returned.
+       If remote service has no Service Information, all Substring searches
+       will fail.
+
 p2p_serv_disc_cancel_req <query identifier>
 
 Cancel a pending P2P service discovery request. This command takes a
@@ -358,6 +539,11 @@ p2p_service_del upnp <version hex> <service>
 
 Remove a local UPnP service from internal SD query processing.
 
+p2p_service_del asp <adv id>
+
+Removes the local asp service from internal SD query list.
+For example: p2p_service_del asp 4d6fc7
+
 p2p_service_flush
 
 Remove all local services from internal SD query processing.
@@ -365,7 +551,8 @@ Remove all local services from internal SD query processing.
 Invitation
 
 p2p_invite [persistent=<network id>|group=<group ifname>] [peer=address]
-       [go_dev_addr=address] [freq=<freq in MHz>] [ht40] [pref=<MHz>]
+       [go_dev_addr=address] [freq=<freq in MHz>] [ht40] [vht]
+       [pref=<MHz>]
 
 Invite a peer to join a group (e.g., group=wlan1) or to reinvoke a
 persistent group (e.g., persistent=4). If the peer device is the GO of
@@ -408,9 +595,11 @@ p2p_presence_req [<duration> <interval>] [<duration> <interval>]
 Send a P2P Presence Request to the GO (this is only available when
 acting as a P2P client). If no duration/interval pairs are given, the
 request indicates that this client has no special needs for GO
-presence. the first parameter pair gives the preferred duration and
+presence. The first parameter pair gives the preferred duration and
 interval values in microseconds. If the second pair is included, that
-indicates which value would be acceptable.
+indicates which value would be acceptable. This command returns OK
+immediately and the response from the GO is indicated in a
+P2P-PRESENCE-RESPONSE event message.
 
 Parameters
 
@@ -456,6 +645,20 @@ Set postfix string to be added to the automatically generated P2P SSID
 (DIRECT-<two random characters>). For example, postfix of "-testing"
 could result in the SSID becoming DIRECT-ab-testing.
 
+p2p_set per_sta_psk <0/1>
+
+Disabled(default)/enables use of per-client PSK in the P2P groups. This
+can be used to request GO to assign a unique PSK for each client during
+WPS provisioning. When enabled, this allow clients to be removed from
+the group securily with p2p_remove_client command since that client's
+PSK is removed at the same time to prevent it from connecting back using
+the old PSK. When per-client PSK is not used, the client can still be
+disconnected, but it will be able to re-join the group since the PSK it
+learned previously is still valid. It should be noted that the default
+passphrase on the GO that is normally used to allow legacy stations to
+connect through manual configuration does not change here, so if that is
+shared, devices with knowledge of that passphrase can still connect.
+
 set <field> <value>
 
 Set global configuration parameters which may also affect P2P
@@ -524,6 +727,13 @@ set country <two character country code>
 
 Set country code (this is included in some P2P messages).
 
+set p2p_search_delay <delay>
+
+Set p2p_search_delay which adds extra delay in milliseconds between
+concurrent search iterations to make p2p_find friendlier to concurrent
+operations by avoiding it from taking 100% of radio resources. The
+default value is 500 ms.
+
 Status
 
 p2p_peers [discovered]
@@ -568,6 +778,63 @@ remove_network <network id>
 Remove a network entry from configuration. 
 
 
+P2PS Events/Responses:
+
+P2PS-PROV-START: This events gets triggered when provisioning is issued for
+either seeker or advertiser.
+
+For example,
+P2PS-PROV-START 00:55:44:33:22:11 adv_id=111 adv_mac=00:55:44:33:22:11 conncap=1 session=1234567 session_mac=00:11:22:33:44:55 info='xxxx'
+
+Parameters definition:
+    MAC address - always
+    adv_id - always ASCII hex-encoded u32
+    adv_mac - always MAC address that owns/registered the service
+    conncap - always mask of 0x01 (new), 0x02 (group client), 0x04 (group owner)
+       bits
+    session - always Session ID of the first session to be established
+    session_mac - always MAC address that owns/initiated the session
+    info - if available, UTF-8 string
+       Escaped single quote & backslash with a backslash:
+       \' == 0x27 == ', and \\ == 0x5c == \
+
+P2PS-PROV-DONE: When provisioning is completed then this event gets triggered.
+
+For example,
+P2PS-PROV-DONE 00:11:22:33:44:55 status=0 adv_id=111 adv_mac=00:55:44:33:22:11 conncap=1 session=1234567 session_mac=00:11:22:33:44:55 [dev_passwd_id=8 | go=p2p-wlan0-0 | join=11:22:33:44:55:66 | persist=0]
+
+Parameters definition:
+    MAC address - always main device address of peer. May be different from MAC
+       ultimately connected to.
+    status - always ascii hex-encoded u8 (0 == success, 12 == deferred success)
+    adv_id - always ascii hex-encoded u32
+    adv_mac - always MAC address that owns/registered the service
+    conncap - always One of: 1 (new), 2 (group client), 4 (group owner) bits
+    session - always Session ID of the first session to be established
+    session_mac - always MAC address that owns/initiated the session
+    dev_passwd_id - only if conncap value == 1 (New GO negotiation)
+       8 - "p2ps" password must be passed in p2p_connect command
+       1 - "display" password must be passed in p2p_connect command
+       5 - "keypad" password must be passed in p2p_connect command
+    join only - if conncap value == 2 (Client Only). Display password and "join"
+       must be passed in p2p_connect and address must be the MAC specified
+    go only - if conncap value == 4 (GO Only). Interface name must be set with a
+       password
+    persist - only if previous persistent group existed between peers and shall
+       be re-used. Group is restarted by sending "p2p_group_add persistent=0"
+       where value is taken from P2P-PROV-DONE
+
+Extended Events/Response
+
+P2P-DEVICE-FOUND 00:11:22:33:44:55 p2p_dev_addr=00:11:22:33:44:55 pri_dev_type=0-00000000-0 name='' config_methods=0x108 dev_capab=0x21 group_capab=0x0 adv_id=111 asp_svc=alt.example.chat
+
+Parameters definition:
+    adv_id - if ASP ASCII hex-encoded u32. If it is reporting the
+       "wildcard service", this value will be 0
+    asp_svc - if ASP this is the service string. If it is reporting the
+       "wildcard service", this value will be org.wi-fi.wfds
+
+
 wpa_cli action script
 ---------------------
 
index 3d07109..b884f67 100644 (file)
@@ -60,7 +60,6 @@ driver interface:
 
 CONFIG_DRIVER_NL80211=y
 CONFIG_WPS=y
-CONFIG_WPS2=y
 
 If you want to enable WPS external registrar (ER) functionality, you
 will also need to add following line:
@@ -366,11 +365,11 @@ the ER functionality has been started (wps_er_start), the NFC password
 token is used to enable enrollment of a new station (that was the source
 of the NFC password token).
 
-"nfc_get_handover_req <NDEF> <WPS>" command can be used to build the
-contents of a Handover Request Message for connection handover. The
-first argument selects the format of the output data and the second
-argument selects which type of connection handover is requested (WPS =
-Wi-Fi handover as specified in WSC 2.0).
+"nfc_get_handover_req <NDEF> <WPS-CR>" command can be used to build the
+WPS carrier record for a Handover Request Message for connection
+handover. The first argument selects the format of the output data and
+the second argument selects which type of connection handover is
+requested (WPS-CR = Wi-Fi handover as specified in WSC 2.0).
 
 "nfc_get_handover_sel <NDEF> <WPS> [UUID|BSSID]" command can be used to
 build the contents of a Handover Select Message for connection handover
@@ -382,17 +381,6 @@ UUID|BSSID argument is included, this is a request to build the handover
 message for the specified AP when wpa_supplicant is operating as a WPS
 ER.
 
-"nfc_rx_handover_req <hexdump of payload>" is used to indicate receipt
-of NFC connection handover request. The payload may include multiple
-carriers the the applicable ones are matched based on the media
-type. The reply data is contents for the Handover Select Message
-(hexdump).
-
-"nfc_rx_handover_sel <hexdump of payload>" is used to indicate receipt
-of NFC connection handover select. The payload may include multiple
-carriers the the applicable ones are matched based on the media
-type.
-
 "nfc_report_handover <INIT/RESP> WPS <carrier from handover request>
 <carrier from handover select>" can be used as an alternative way for
 reporting completed NFC connection handover. The first parameter
index 6686422..8d27bb2 100644 (file)
 # used to fix build issues on such systems (krb5.h not found).
 #CFLAGS += -I/usr/include/kerberos
 
-# Example configuration for various cross-compilation platforms
-
-#### sveasoft (e.g., for Linksys WRT54G) ######################################
-#CC=mipsel-uclibc-gcc
-#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc
-#CFLAGS += -Os
-#CPPFLAGS += -I../src/include -I../../src/router/openssl/include
-#LIBS += -L/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib -lssl
-###############################################################################
-
-#### openwrt (e.g., for Linksys WRT54G) #######################################
-#CC=mipsel-uclibc-gcc
-#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc
-#CFLAGS += -Os
-#CPPFLAGS=-I../src/include -I../openssl-0.9.7d/include \
-#      -I../WRT54GS/release/src/include
-#LIBS = -lssl
-###############################################################################
-
-
-# Driver interface for Host AP driver
-#CONFIG_DRIVER_HOSTAP=y
-
-# Driver interface for Agere driver
-#CONFIG_DRIVER_HERMES=y
-# Change include directories to match with the local setup
-#CFLAGS += -I../../hcf -I../../include -I../../include/hcf
-#CFLAGS += -I../../include/wireless
-
-# Driver interface for madwifi driver
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_MADWIFI=y
-# Set include directory to the madwifi source tree
-#CFLAGS += -I../../madwifi
-
-# Driver interface for ndiswrapper
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_NDISWRAPPER=y
-
-# Driver interface for Atmel driver
-#CONFIG_DRIVER_ATMEL=y
-
-# Driver interface for old Broadcom driver
-# Please note that the newer Broadcom driver ("hybrid Linux driver") supports
-# Linux wireless extensions and does not need (or even work) with the old
-# driver wrapper. Use CONFIG_DRIVER_WEXT=y with that driver.
-#CONFIG_DRIVER_BROADCOM=y
-# Example path for wlioctl.h; change to match your configuration
-#CFLAGS += -I/opt/WRT54GS/release/src/include
-
-# Driver interface for Intel ipw2100/2200 driver
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_IPW=y
-
-# Driver interface for Ralink driver
-#CONFIG_DRIVER_RALINK=y
-
 # Driver interface for generic Linux wireless extensions
 # Note: WEXT is deprecated in the current Linux kernel version and no new
 # functionality is added to it. nl80211-based interface is the new
@@ -112,9 +55,6 @@ CONFIG_LIBNL20=y
 # wpa_supplicant.
 # CONFIG_USE_NDISUIO=y
 
-# Driver interface for development testing
-#CONFIG_DRIVER_TEST=y
-
 # Driver interface for wired Ethernet drivers
 #CONFIG_DRIVER_WIRED=y
 
@@ -180,7 +120,7 @@ CONFIG_EAP_AKA=y
 
 # EAP-AKA' (enable CONFIG_PCSC, if EAP-AKA' is used).
 # This requires CONFIG_EAP_AKA to be enabled, too.
-#CONFIG_EAP_AKA_PRIME=y
+CONFIG_EAP_AKA_PRIME=y
 
 # Enable USIM simulator (Milenage) for EAP-AKA
 #CONFIG_USIM_SIMULATOR=y
@@ -198,8 +138,6 @@ CONFIG_EAP_AKA=y
 
 # Wi-Fi Protected Setup (WPS)
 CONFIG_WPS=y
-# Enable WSC 2.0 support
-CONFIG_WPS2=y
 # Enable WPS external registrar functionality
 CONFIG_WPS_ER=y
 # Disable credentials for an open network by default when acting as a WPS
@@ -296,7 +234,7 @@ CONFIG_BACKEND=file
 # main_none = Very basic example (development use only)
 #CONFIG_MAIN=main
 
-# Select wrapper for operatins system and C library specific functions
+# Select wrapper for operating system and C library specific functions
 # unix = UNIX/POSIX like systems (default)
 # win32 = Windows systems
 # none = Empty template
@@ -305,12 +243,14 @@ CONFIG_OS=unix
 # Select event loop implementation
 # eloop = select() loop (default)
 # eloop_win = Windows events and WaitForMultipleObject() loop
-# eloop_none = Empty template
 CONFIG_ELOOP=eloop
 
 # Should we use poll instead of select? Select is used by default.
 #CONFIG_ELOOP_POLL=y
 
+# Should we use epoll instead of select? Select is used by default.
+#CONFIG_ELOOP_EPOLL=y
+
 # Select layer 2 packet implementation
 # linux = Linux packet socket (default)
 # pcap = libpcap/libdnet/WinPcap
@@ -510,11 +450,17 @@ CONFIG_AP=y
 # more information on P2P operations.
 CONFIG_P2P=y
 
+# Enable TDLS support
 CONFIG_TDLS=y
 
+# Wi-Fi Direct
+# This can be used to enable Wi-Fi Direct extensions for P2P using an external
+# program to control the additional information exchanges in the messages.
+CONFIG_WIFI_DISPLAY=y
+
 # Autoscan
 # This can be used to enable automatic scan support in wpa_supplicant.
-# See wpa_supplicant.conf for more information on autoscan usage.
+# See wpa_supplicant.conf for more information on autoscan usage.
 #
 # Enabling directly a module will enable autoscan support.
 # For exponential module:
index bf84dc4..7ecf7a8 100644 (file)
@@ -14,6 +14,8 @@
 #include "utils/uuid.h"
 #include "common/ieee802_11_defs.h"
 #include "common/wpa_ctrl.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "crypto/dh_group5.h"
 #include "ap/hostapd.h"
 #include "ap/ap_config.h"
 #include "ap/ap_drv_ops.h"
@@ -24,6 +26,7 @@
 #include "ap/ieee802_1x.h"
 #include "ap/wps_hostapd.h"
 #include "ap/ctrl_iface_ap.h"
+#include "ap/dfs.h"
 #include "wps/wps.h"
 #include "common/ieee802_11_defs.h"
 #include "config_ssid.h"
@@ -41,30 +44,42 @@ static void wpas_wps_ap_pin_timeout(void *eloop_data, void *user_ctx);
 #endif /* CONFIG_WPS */
 
 
-static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
-                                 struct wpa_ssid *ssid,
-                                 struct hostapd_config *conf)
+#ifdef CONFIG_IEEE80211N
+static void wpas_conf_ap_vht(struct wpa_supplicant *wpa_s,
+                            struct hostapd_config *conf,
+                            struct hostapd_hw_modes *mode)
 {
-       struct hostapd_bss_config *bss = &conf->bss[0];
+#ifdef CONFIG_P2P
+       u8 center_chan = 0;
+       u8 channel = conf->channel;
 
-       conf->driver = wpa_s->driver;
+       if (!conf->secondary_channel)
+               goto no_vht;
 
-       os_strlcpy(bss->iface, wpa_s->ifname, sizeof(bss->iface));
+       center_chan = wpas_p2p_get_vht80_center(wpa_s, mode, channel);
+       if (!center_chan)
+               goto no_vht;
 
-       if (ssid->frequency == 0) {
-               /* default channel 11 */
-               conf->hw_mode = HOSTAPD_MODE_IEEE80211G;
-               conf->channel = 11;
-       } else {
-               conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency,
-                                                      &conf->channel);
-               if (conf->hw_mode == NUM_HOSTAPD_MODES) {
-                       wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: "
-                                  "%d MHz", ssid->frequency);
-                       return -1;
-               }
-       }
+       /* Use 80 MHz channel */
+       conf->vht_oper_chwidth = 1;
+       conf->vht_oper_centr_freq_seg0_idx = center_chan;
+       return;
 
+no_vht:
+       conf->vht_oper_centr_freq_seg0_idx =
+               channel + conf->secondary_channel * 2;
+#else /* CONFIG_P2P */
+       conf->vht_oper_centr_freq_seg0_idx =
+               conf->channel + conf->secondary_channel * 2;
+#endif /* CONFIG_P2P */
+}
+#endif /* CONFIG_IEEE80211N */
+
+
+void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
+                              struct wpa_ssid *ssid,
+                              struct hostapd_config *conf)
+{
        /* TODO: enable HT40 if driver supports it;
         * drop to 11b if driver does not support 11g */
 
@@ -118,10 +133,45 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
                                 HT_CAP_INFO_SHORT_GI20MHZ |
                                 HT_CAP_INFO_SHORT_GI40MHZ |
                                 HT_CAP_INFO_RX_STBC_MASK |
+                                HT_CAP_INFO_TX_STBC |
                                 HT_CAP_INFO_MAX_AMSDU_SIZE);
+
+                       if (mode->vht_capab && ssid->vht) {
+                               conf->ieee80211ac = 1;
+                               wpas_conf_ap_vht(wpa_s, conf, mode);
+                       }
                }
        }
 #endif /* CONFIG_IEEE80211N */
+}
+
+
+static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
+                                 struct wpa_ssid *ssid,
+                                 struct hostapd_config *conf)
+{
+       struct hostapd_bss_config *bss = conf->bss[0];
+
+       conf->driver = wpa_s->driver;
+
+       os_strlcpy(bss->iface, wpa_s->ifname, sizeof(bss->iface));
+
+       conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency,
+                                              &conf->channel);
+       if (conf->hw_mode == NUM_HOSTAPD_MODES) {
+               wpa_printf(MSG_ERROR, "Unsupported AP mode frequency: %d MHz",
+                          ssid->frequency);
+               return -1;
+       }
+
+       wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf);
+
+       if (ieee80211_is_dfs(ssid->frequency) && wpa_s->conf->country[0]) {
+               conf->ieee80211h = 1;
+               conf->ieee80211d = 1;
+               conf->country[0] = wpa_s->conf->country[0];
+               conf->country[1] = wpa_s->conf->country[1];
+       }
 
 #ifdef CONFIG_P2P
        if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G &&
@@ -153,6 +203,17 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
        }
 
        bss->isolate = !wpa_s->conf->p2p_intra_bss;
+       bss->force_per_enrollee_psk = wpa_s->global->p2p_per_sta_psk;
+
+       if (ssid->p2p_group) {
+               os_memcpy(bss->ip_addr_go, wpa_s->parent->conf->ip_addr_go, 4);
+               os_memcpy(bss->ip_addr_mask, wpa_s->parent->conf->ip_addr_mask,
+                         4);
+               os_memcpy(bss->ip_addr_start,
+                         wpa_s->parent->conf->ip_addr_start, 4);
+               os_memcpy(bss->ip_addr_end, wpa_s->parent->conf->ip_addr_end,
+                         4);
+       }
 #endif /* CONFIG_P2P */
 
        if (ssid->ssid_len == 0) {
@@ -173,7 +234,7 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
        bss->wpa_key_mgmt = ssid->key_mgmt;
        bss->wpa_pairwise = ssid->pairwise_cipher;
        if (ssid->psk_set) {
-               os_free(bss->ssid.wpa_psk);
+               bin_clear_free(bss->ssid.wpa_psk, sizeof(*bss->ssid.wpa_psk));
                bss->ssid.wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk));
                if (bss->ssid.wpa_psk == NULL)
                        return -1;
@@ -212,6 +273,17 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
        else if (wpa_s->conf->beacon_int)
                conf->beacon_int = wpa_s->conf->beacon_int;
 
+#ifdef CONFIG_P2P
+       if (wpa_s->conf->p2p_go_ctwindow > conf->beacon_int) {
+               wpa_printf(MSG_INFO,
+                          "CTWindow (%d) is bigger than beacon interval (%d) - avoid configuring it",
+                          wpa_s->conf->p2p_go_ctwindow, conf->beacon_int);
+               conf->p2p_go_ctwindow = 0;
+       } else {
+               conf->p2p_go_ctwindow = wpa_s->conf->p2p_go_ctwindow;
+       }
+#endif /* CONFIG_P2P */
+
        if ((bss->wpa & 2) && bss->rsn_pairwise == 0)
                bss->rsn_pairwise = bss->wpa_pairwise;
        bss->wpa_group = wpa_select_ap_group_cipher(bss->wpa, bss->wpa_pairwise,
@@ -248,7 +320,9 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
 
        if (bss->wpa_group_rekey < 86400 && (bss->wpa & 2) &&
            (bss->wpa_group == WPA_CIPHER_CCMP ||
-            bss->wpa_group == WPA_CIPHER_GCMP)) {
+            bss->wpa_group == WPA_CIPHER_GCMP ||
+            bss->wpa_group == WPA_CIPHER_CCMP_256 ||
+            bss->wpa_group == WPA_CIPHER_GCMP_256)) {
                /*
                 * Strong ciphers do not need frequent rekeying, so increase
                 * the default GTK rekeying period to 24 hours.
@@ -256,6 +330,11 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
                bss->wpa_group_rekey = 86400;
        }
 
+#ifdef CONFIG_IEEE80211W
+       if (ssid->ieee80211w != MGMT_FRAME_PROTECTION_DEFAULT)
+               bss->ieee80211w = ssid->ieee80211w;
+#endif /* CONFIG_IEEE80211W */
+
 #ifdef CONFIG_WPS
        /*
         * Enable WPS by default for open and WPA/WPA2-Personal network, but
@@ -265,12 +344,11 @@ static int wpa_supplicant_conf_ap(struct wpa_supplicant *wpa_s,
        if (bss->ssid.security_policy != SECURITY_WPA_PSK &&
            bss->ssid.security_policy != SECURITY_PLAINTEXT)
                goto no_wps;
-#ifdef CONFIG_WPS2
        if (bss->ssid.security_policy == SECURITY_WPA_PSK &&
-           (!(bss->rsn_pairwise & WPA_CIPHER_CCMP) || !(bss->wpa & 2)))
+           (!(bss->rsn_pairwise & (WPA_CIPHER_CCMP | WPA_CIPHER_GCMP)) ||
+            !(bss->wpa & 2)))
                goto no_wps; /* WPS2 does not allow WPA/TKIP-only
                              * configuration */
-#endif /* CONFIG_WPS2 */
        bss->eap_server = 1;
 
        if (!ssid->ignore_broadcast_ssid)
@@ -324,16 +402,16 @@ static void ap_public_action_rx(void *ctx, const u8 *buf, size_t len, int freq)
 #ifdef CONFIG_P2P
        struct wpa_supplicant *wpa_s = ctx;
        const struct ieee80211_mgmt *mgmt;
-       size_t hdr_len;
 
        mgmt = (const struct ieee80211_mgmt *) buf;
-       hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
-       if (hdr_len > len)
+       if (len < IEEE80211_HDRLEN + 1)
+               return;
+       if (mgmt->u.action.category != WLAN_ACTION_PUBLIC)
                return;
        wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
                           mgmt->u.action.category,
-                          &mgmt->u.action.u.vs_public_action.action,
-                          len - hdr_len, freq);
+                          buf + IEEE80211_HDRLEN + 1,
+                          len - IEEE80211_HDRLEN - 1, freq);
 #endif /* CONFIG_P2P */
 }
 
@@ -371,21 +449,32 @@ static void ap_sta_authorized_cb(void *ctx, const u8 *mac_addr,
 }
 
 
+#ifdef CONFIG_P2P
+static void ap_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *p2p_dev_addr,
+                         const u8 *psk, size_t psk_len)
+{
+
+       struct wpa_supplicant *wpa_s = ctx;
+       if (wpa_s->ap_iface == NULL || wpa_s->current_ssid == NULL)
+               return;
+       wpas_p2p_new_psk_cb(wpa_s, mac_addr, p2p_dev_addr, psk, psk_len);
+}
+#endif /* CONFIG_P2P */
+
+
 static int ap_vendor_action_rx(void *ctx, const u8 *buf, size_t len, int freq)
 {
 #ifdef CONFIG_P2P
        struct wpa_supplicant *wpa_s = ctx;
        const struct ieee80211_mgmt *mgmt;
-       size_t hdr_len;
 
        mgmt = (const struct ieee80211_mgmt *) buf;
-       hdr_len = (const u8 *) &mgmt->u.action.u.vs_public_action.action - buf;
-       if (hdr_len > len)
+       if (len < IEEE80211_HDRLEN + 1)
                return -1;
        wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
                           mgmt->u.action.category,
-                          &mgmt->u.action.u.vs_public_action.action,
-                          len - hdr_len, freq);
+                          buf + IEEE80211_HDRLEN + 1,
+                          len - IEEE80211_HDRLEN - 1, freq);
 #endif /* CONFIG_P2P */
        return 0;
 }
@@ -395,23 +484,17 @@ static int ap_probe_req_rx(void *ctx, const u8 *sa, const u8 *da,
                           const u8 *bssid, const u8 *ie, size_t ie_len,
                           int ssi_signal)
 {
-#ifdef CONFIG_P2P
        struct wpa_supplicant *wpa_s = ctx;
        return wpas_p2p_probe_req_rx(wpa_s, sa, da, bssid, ie, ie_len,
                                     ssi_signal);
-#else /* CONFIG_P2P */
-       return 0;
-#endif /* CONFIG_P2P */
 }
 
 
 static void ap_wps_reg_success_cb(void *ctx, const u8 *mac_addr,
                                  const u8 *uuid_e)
 {
-#ifdef CONFIG_P2P
        struct wpa_supplicant *wpa_s = ctx;
        wpas_p2p_wps_success(wpa_s, mac_addr, 1);
-#endif /* CONFIG_P2P */
 }
 
 
@@ -449,26 +532,24 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
        params.ssid = ssid->ssid;
        params.ssid_len = ssid->ssid_len;
        switch (ssid->mode) {
-       case WPAS_MODE_INFRA:
-               params.mode = IEEE80211_MODE_INFRA;
-               break;
-       case WPAS_MODE_IBSS:
-               params.mode = IEEE80211_MODE_IBSS;
-               break;
        case WPAS_MODE_AP:
        case WPAS_MODE_P2P_GO:
        case WPAS_MODE_P2P_GROUP_FORMATION:
                params.mode = IEEE80211_MODE_AP;
                break;
+       default:
+               return -1;
        }
-       params.freq = ssid->frequency;
+       if (ssid->frequency == 0)
+               ssid->frequency = 2462; /* default channel 11 */
+       params.freq.freq = ssid->frequency;
 
        params.wpa_proto = ssid->proto;
        if (ssid->key_mgmt & WPA_KEY_MGMT_PSK)
                wpa_s->key_mgmt = WPA_KEY_MGMT_PSK;
        else
                wpa_s->key_mgmt = WPA_KEY_MGMT_NONE;
-       params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt);
+       params.key_mgmt_suite = wpa_s->key_mgmt;
 
        wpa_s->pairwise_cipher = wpa_pick_pairwise_cipher(ssid->pairwise_cipher,
                                                          1);
@@ -477,8 +558,7 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
                           "cipher.");
                return -1;
        }
-       params.pairwise_suite =
-               wpa_cipher_to_suite_driver(wpa_s->pairwise_cipher);
+       params.pairwise_suite = wpa_s->pairwise_cipher;
        params.group_suite = params.pairwise_suite;
 
 #ifdef CONFIG_P2P
@@ -489,9 +569,14 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
 
        if (wpa_s->parent->set_ap_uapsd)
                params.uapsd = wpa_s->parent->ap_uapsd;
+       else if (params.p2p && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
+               params.uapsd = 1; /* mandatory for P2P GO */
        else
                params.uapsd = -1;
 
+       if (ieee80211_is_dfs(params.freq.freq))
+               params.freq.freq = 0; /* set channel after CAC */
+
        if (wpa_drv_associate(wpa_s, &params) < 0) {
                wpa_msg(wpa_s, MSG_INFO, "Failed to start AP functionality");
                return -1;
@@ -502,6 +587,7 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
                return -1;
        hapd_iface->owner = wpa_s;
        hapd_iface->drv_flags = wpa_s->drv_flags;
+       hapd_iface->smps_modes = wpa_s->drv_smps_modes;
        hapd_iface->probe_resp_offloads = wpa_s->probe_resp_offloads;
        hapd_iface->extended_capa = wpa_s->extended_capa;
        hapd_iface->extended_capa_mask = wpa_s->extended_capa_mask;
@@ -518,8 +604,8 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
                  sizeof(wpa_s->conf->wmm_ac_params));
 
        if (params.uapsd > 0) {
-               conf->bss->wmm_enabled = 1;
-               conf->bss->wmm_uapsd = 1;
+               conf->bss[0]->wmm_enabled = 1;
+               conf->bss[0]->wmm_uapsd = 1;
        }
 
        if (wpa_supplicant_conf_ap(wpa_s, ssid, conf)) {
@@ -530,9 +616,9 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
 
 #ifdef CONFIG_P2P
        if (ssid->mode == WPAS_MODE_P2P_GO)
-               conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER;
+               conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER;
        else if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
-               conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER |
+               conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER |
                        P2P_GROUP_FORMATION;
 #endif /* CONFIG_P2P */
 
@@ -547,7 +633,7 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
        for (i = 0; i < conf->num_bss; i++) {
                hapd_iface->bss[i] =
                        hostapd_alloc_bss_data(hapd_iface, conf,
-                                              &conf->bss[i]);
+                                              conf->bss[i]);
                if (hapd_iface->bss[i] == NULL) {
                        wpa_supplicant_ap_deinit(wpa_s);
                        return -1;
@@ -568,12 +654,18 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
                hapd_iface->bss[i]->sta_authorized_cb = ap_sta_authorized_cb;
                hapd_iface->bss[i]->sta_authorized_cb_ctx = wpa_s;
 #ifdef CONFIG_P2P
+               hapd_iface->bss[i]->new_psk_cb = ap_new_psk_cb;
+               hapd_iface->bss[i]->new_psk_cb_ctx = wpa_s;
                hapd_iface->bss[i]->p2p = wpa_s->global->p2p;
                hapd_iface->bss[i]->p2p_group = wpas_p2p_group_init(wpa_s,
                                                                    ssid);
 #endif /* CONFIG_P2P */
                hapd_iface->bss[i]->setup_complete_cb = wpas_ap_configured_cb;
                hapd_iface->bss[i]->setup_complete_cb_ctx = wpa_s;
+#ifdef CONFIG_TESTING_OPTIONS
+               hapd_iface->bss[i]->ext_eapol_frame_io =
+                       wpa_s->ext_eapol_frame_io;
+#endif /* CONFIG_TESTING_OPTIONS */
        }
 
        os_memcpy(hapd_iface->bss[0]->own_addr, wpa_s->own_addr, ETH_ALEN);
@@ -581,6 +673,7 @@ int wpa_supplicant_create_ap(struct wpa_supplicant *wpa_s,
        hapd_iface->bss[0]->drv_priv = wpa_s->drv_priv;
 
        wpa_s->current_ssid = ssid;
+       eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
        os_memcpy(wpa_s->bssid, wpa_s->own_addr, ETH_ALEN);
        wpa_s->assoc_freq = ssid->frequency;
 
@@ -604,16 +697,19 @@ void wpa_supplicant_ap_deinit(struct wpa_supplicant *wpa_s)
                return;
 
        wpa_s->current_ssid = NULL;
+       eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
        wpa_s->assoc_freq = 0;
-#ifdef CONFIG_P2P
-       if (wpa_s->ap_iface->bss)
-               wpa_s->ap_iface->bss[0]->p2p_group = NULL;
-       wpas_p2p_group_deinit(wpa_s);
-#endif /* CONFIG_P2P */
+       wpas_p2p_ap_deinit(wpa_s);
+       wpa_s->ap_iface->driver_ap_teardown =
+               !!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_AP_TEARDOWN_SUPPORT);
+
        hostapd_interface_deinit(wpa_s->ap_iface);
        hostapd_interface_free(wpa_s->ap_iface);
        wpa_s->ap_iface = NULL;
        wpa_drv_deinit_ap(wpa_s);
+       wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_DISCONNECTED "bssid=" MACSTR
+               " reason=%d locally_generated=1",
+               MAC2STR(wpa_s->own_addr), WLAN_REASON_DEAUTH_LEAVING);
 }
 
 
@@ -632,6 +728,8 @@ void ap_eapol_tx_status(void *ctx, const u8 *dst,
 {
 #ifdef NEED_AP_MLME
        struct wpa_supplicant *wpa_s = ctx;
+       if (!wpa_s->ap_iface)
+               return;
        hostapd_tx_status(wpa_s->ap_iface->bss[0], dst, data, len, ack);
 #endif /* NEED_AP_MLME */
 }
@@ -740,9 +838,14 @@ int wpa_supplicant_ap_wps_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
        if (pin == NULL) {
                unsigned int rpin = wps_generate_pin();
                ret_len = os_snprintf(buf, buflen, "%08d", rpin);
+               if (os_snprintf_error(buflen, ret_len))
+                       return -1;
                pin = buf;
-       } else
+       } else if (buf) {
                ret_len = os_snprintf(buf, buflen, "%s", pin);
+               if (os_snprintf_error(buflen, ret_len))
+                       return -1;
+       }
 
        ret = hostapd_wps_add_pin(wpa_s->ap_iface->bss[0], bssid, "any", pin,
                                  timeout);
@@ -832,7 +935,7 @@ int wpas_wps_ap_pin_set(struct wpa_supplicant *wpa_s, const char *pin,
                return -1;
        hapd = wpa_s->ap_iface->bss[0];
        ret = os_snprintf(pin_txt, sizeof(pin_txt), "%s", pin);
-       if (ret < 0 || ret >= (int) sizeof(pin_txt))
+       if (os_snprintf_error(sizeof(pin_txt), ret))
                return -1;
        os_free(hapd->conf->ap_pin);
        hapd->conf->ap_pin = os_strdup(pin_txt);
@@ -894,6 +997,19 @@ struct wpabuf * wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
        return hostapd_wps_nfc_hs_cr(hapd, ndef);
 }
 
+
+int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+                                   const struct wpabuf *req,
+                                   const struct wpabuf *sel)
+{
+       struct hostapd_data *hapd;
+
+       if (wpa_s->ap_iface == NULL)
+               return -1;
+       hapd = wpa_s->ap_iface->bss[0];
+       return hostapd_wps_nfc_report_handover(hapd, req, sel);
+}
+
 #endif /* CONFIG_WPS_NFC */
 
 #endif /* CONFIG_WPS */
@@ -904,30 +1020,45 @@ struct wpabuf * wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
 int ap_ctrl_iface_sta_first(struct wpa_supplicant *wpa_s,
                            char *buf, size_t buflen)
 {
-       if (wpa_s->ap_iface == NULL)
+       struct hostapd_data *hapd;
+
+       if (wpa_s->ap_iface)
+               hapd = wpa_s->ap_iface->bss[0];
+       else if (wpa_s->ifmsh)
+               hapd = wpa_s->ifmsh->bss[0];
+       else
                return -1;
-       return hostapd_ctrl_iface_sta_first(wpa_s->ap_iface->bss[0],
-                                           buf, buflen);
+       return hostapd_ctrl_iface_sta_first(hapd, buf, buflen);
 }
 
 
 int ap_ctrl_iface_sta(struct wpa_supplicant *wpa_s, const char *txtaddr,
                      char *buf, size_t buflen)
 {
-       if (wpa_s->ap_iface == NULL)
+       struct hostapd_data *hapd;
+
+       if (wpa_s->ap_iface)
+               hapd = wpa_s->ap_iface->bss[0];
+       else if (wpa_s->ifmsh)
+               hapd = wpa_s->ifmsh->bss[0];
+       else
                return -1;
-       return hostapd_ctrl_iface_sta(wpa_s->ap_iface->bss[0], txtaddr,
-                                     buf, buflen);
+       return hostapd_ctrl_iface_sta(hapd, txtaddr, buf, buflen);
 }
 
 
 int ap_ctrl_iface_sta_next(struct wpa_supplicant *wpa_s, const char *txtaddr,
                           char *buf, size_t buflen)
 {
-       if (wpa_s->ap_iface == NULL)
+       struct hostapd_data *hapd;
+
+       if (wpa_s->ap_iface)
+               hapd = wpa_s->ap_iface->bss[0];
+       else if (wpa_s->ifmsh)
+               hapd = wpa_s->ifmsh->bss[0];
+       else
                return -1;
-       return hostapd_ctrl_iface_sta_next(wpa_s->ap_iface->bss[0], txtaddr,
-                                          buf, buflen);
+       return hostapd_ctrl_iface_sta_next(hapd, txtaddr, buf, buflen);
 }
 
 
@@ -973,7 +1104,7 @@ int ap_ctrl_iface_wpa_get_status(struct wpa_supplicant *wpa_s, char *buf,
                          wpa_cipher_txt(conf->wpa_group),
                          wpa_key_mgmt_txt(conf->wpa_key_mgmt,
                                           conf->wpa));
-       if (ret < 0 || ret >= end - pos)
+       if (os_snprintf_error(end - pos, ret))
                return pos - buf;
        pos += ret;
        return pos - buf;
@@ -995,9 +1126,9 @@ int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s)
 
 #ifdef CONFIG_P2P
        if (ssid->mode == WPAS_MODE_P2P_GO)
-               iface->conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER;
+               iface->conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER;
        else if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
-               iface->conf->bss[0].p2p = P2P_ENABLED | P2P_GROUP_OWNER |
+               iface->conf->bss[0]->p2p = P2P_ENABLED | P2P_GROUP_OWNER |
                        P2P_GROUP_FORMATION;
 #endif /* CONFIG_P2P */
 
@@ -1011,14 +1142,40 @@ int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s)
 }
 
 
+int ap_switch_channel(struct wpa_supplicant *wpa_s,
+                     struct csa_settings *settings)
+{
+#ifdef NEED_AP_MLME
+       if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
+               return -1;
+
+       return hostapd_switch_channel(wpa_s->ap_iface->bss[0], settings);
+#else /* NEED_AP_MLME */
+       return -1;
+#endif /* NEED_AP_MLME */
+}
+
+
+int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *pos)
+{
+       struct csa_settings settings;
+       int ret = hostapd_parse_csa_settings(pos, &settings);
+
+       if (ret)
+               return ret;
+
+       return ap_switch_channel(wpa_s, &settings);
+}
+
+
 void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
-                      int offset)
+                      int offset, int width, int cf1, int cf2)
 {
        if (!wpa_s->ap_iface)
                return;
 
        wpa_s->assoc_freq = freq;
-       hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset);
+       hostapd_event_ch_switch(wpa_s->ap_iface->bss[0], freq, ht, offset, width, cf1, cf1);
 }
 
 
@@ -1061,3 +1218,122 @@ int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
 
        return 0;
 }
+
+
+#ifdef CONFIG_WPS_NFC
+int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id,
+                          const struct wpabuf *pw, const u8 *pubkey_hash)
+{
+       struct hostapd_data *hapd;
+       struct wps_context *wps;
+
+       if (!wpa_s->ap_iface)
+               return -1;
+       hapd = wpa_s->ap_iface->bss[0];
+       wps = hapd->wps;
+
+       if (wpa_s->parent->conf->wps_nfc_dh_pubkey == NULL ||
+           wpa_s->parent->conf->wps_nfc_dh_privkey == NULL) {
+               wpa_printf(MSG_DEBUG, "P2P: No NFC DH key known");
+               return -1;
+       }
+
+       dh5_free(wps->dh_ctx);
+       wpabuf_free(wps->dh_pubkey);
+       wpabuf_free(wps->dh_privkey);
+       wps->dh_privkey = wpabuf_dup(
+               wpa_s->parent->conf->wps_nfc_dh_privkey);
+       wps->dh_pubkey = wpabuf_dup(
+               wpa_s->parent->conf->wps_nfc_dh_pubkey);
+       if (wps->dh_privkey == NULL || wps->dh_pubkey == NULL) {
+               wps->dh_ctx = NULL;
+               wpabuf_free(wps->dh_pubkey);
+               wps->dh_pubkey = NULL;
+               wpabuf_free(wps->dh_privkey);
+               wps->dh_privkey = NULL;
+               return -1;
+       }
+       wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey);
+       if (wps->dh_ctx == NULL)
+               return -1;
+
+       return wps_registrar_add_nfc_pw_token(hapd->wps->registrar, pubkey_hash,
+                                             pw_id,
+                                             pw ? wpabuf_head(pw) : NULL,
+                                             pw ? wpabuf_len(pw) : 0, 1);
+}
+#endif /* CONFIG_WPS_NFC */
+
+
+int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s)
+{
+       struct hostapd_data *hapd;
+
+       if (!wpa_s->ap_iface)
+               return -1;
+       hapd = wpa_s->ap_iface->bss[0];
+       return hostapd_ctrl_iface_stop_ap(hapd);
+}
+
+
+#ifdef NEED_AP_MLME
+void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s,
+                                  struct dfs_event *radar)
+{
+       if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
+               return;
+       wpa_printf(MSG_DEBUG, "DFS radar detected on %d MHz", radar->freq);
+       hostapd_dfs_radar_detected(wpa_s->ap_iface, radar->freq,
+                                  radar->ht_enabled, radar->chan_offset,
+                                  radar->chan_width,
+                                  radar->cf1, radar->cf2);
+}
+
+
+void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s,
+                               struct dfs_event *radar)
+{
+       if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
+               return;
+       wpa_printf(MSG_DEBUG, "DFS CAC started on %d MHz", radar->freq);
+       hostapd_dfs_start_cac(wpa_s->ap_iface, radar->freq,
+                             radar->ht_enabled, radar->chan_offset,
+                             radar->chan_width, radar->cf1, radar->cf2);
+}
+
+
+void wpas_event_dfs_cac_finished(struct wpa_supplicant *wpa_s,
+                                struct dfs_event *radar)
+{
+       if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
+               return;
+       wpa_printf(MSG_DEBUG, "DFS CAC finished on %d MHz", radar->freq);
+       hostapd_dfs_complete_cac(wpa_s->ap_iface, 1, radar->freq,
+                                radar->ht_enabled, radar->chan_offset,
+                                radar->chan_width, radar->cf1, radar->cf2);
+}
+
+
+void wpas_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s,
+                               struct dfs_event *radar)
+{
+       if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
+               return;
+       wpa_printf(MSG_DEBUG, "DFS CAC aborted on %d MHz", radar->freq);
+       hostapd_dfs_complete_cac(wpa_s->ap_iface, 0, radar->freq,
+                                radar->ht_enabled, radar->chan_offset,
+                                radar->chan_width, radar->cf1, radar->cf2);
+}
+
+
+void wpas_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s,
+                                    struct dfs_event *radar)
+{
+       if (!wpa_s->ap_iface || !wpa_s->ap_iface->bss[0])
+               return;
+       wpa_printf(MSG_DEBUG, "DFS NOP finished on %d MHz", radar->freq);
+       hostapd_dfs_nop_finished(wpa_s->ap_iface, radar->freq,
+                                radar->ht_enabled, radar->chan_offset,
+                                radar->chan_width, radar->cf1, radar->cf2);
+}
+#endif /* NEED_AP_MLME */
index fd4c25a..3f4151d 100644 (file)
@@ -50,11 +50,47 @@ int wpa_supplicant_ap_update_beacon(struct wpa_supplicant *wpa_s);
 int wpa_supplicant_ap_mac_addr_filter(struct wpa_supplicant *wpa_s,
                                      const u8 *addr);
 void wpa_supplicant_ap_pwd_auth_fail(struct wpa_supplicant *wpa_s);
+int ap_switch_channel(struct wpa_supplicant *wpa_s,
+                     struct csa_settings *settings);
+int ap_ctrl_iface_chanswitch(struct wpa_supplicant *wpa_s, const char *txtaddr);
 void wpas_ap_ch_switch(struct wpa_supplicant *wpa_s, int freq, int ht,
-                      int offset);
+                      int offset, int width, int cf1, int cf2);
 struct wpabuf * wpas_ap_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
                                             int ndef);
+#ifdef CONFIG_AP
 struct wpabuf * wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
                                             int ndef);
+#else /* CONFIG_AP */
+static inline struct wpabuf *
+wpas_ap_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+                            int ndef)
+{
+       return NULL;
+}
+#endif /* CONFIG_AP */
+
+int wpas_ap_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+                                   const struct wpabuf *req,
+                                   const struct wpabuf *sel);
+int wpas_ap_wps_add_nfc_pw(struct wpa_supplicant *wpa_s, u16 pw_id,
+                          const struct wpabuf *pw, const u8 *pubkey_hash);
+
+struct hostapd_config;
+void wpa_supplicant_conf_ap_ht(struct wpa_supplicant *wpa_s,
+                              struct wpa_ssid *ssid,
+                              struct hostapd_config *conf);
+
+int wpas_ap_stop_ap(struct wpa_supplicant *wpa_s);
+
+void wpas_event_dfs_radar_detected(struct wpa_supplicant *wpa_s,
+                                  struct dfs_event *radar);
+void wpas_event_dfs_cac_started(struct wpa_supplicant *wpa_s,
+                               struct dfs_event *radar);
+void wpas_event_dfs_cac_finished(struct wpa_supplicant *wpa_s,
+                                struct dfs_event *radar);
+void wpas_event_dfs_cac_aborted(struct wpa_supplicant *wpa_s,
+                               struct dfs_event *radar);
+void wpas_event_dfs_cac_nop_finished(struct wpa_supplicant *wpa_s,
+                                    struct dfs_event *radar);
 
 #endif /* AP_H */
index 9a9bd52..f74cdbf 100644 (file)
@@ -31,9 +31,9 @@ static const struct bgscan_ops * bgscan_modules[] = {
 };
 
 
-int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+               const char *name)
 {
-       const char *name = ssid->bgscan;
        const char *params;
        size_t nlen;
        int i;
@@ -41,7 +41,7 @@ int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
 
        bgscan_deinit(wpa_s);
        if (name == NULL)
-               return 0;
+               return -1;
 
        params = os_strchr(name, ':');
        if (params == NULL) {
index e9d15fc..9131e4e 100644 (file)
@@ -29,7 +29,8 @@ struct bgscan_ops {
 
 #ifdef CONFIG_BGSCAN
 
-int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+int bgscan_init(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+               const char *name);
 void bgscan_deinit(struct wpa_supplicant *wpa_s);
 int bgscan_notify_scan(struct wpa_supplicant *wpa_s,
                       struct wpa_scan_results *scan_res);
@@ -41,7 +42,7 @@ void bgscan_notify_signal_change(struct wpa_supplicant *wpa_s, int above,
 #else /* CONFIG_BGSCAN */
 
 static inline int bgscan_init(struct wpa_supplicant *wpa_s,
-                             struct wpa_ssid *ssid)
+                             struct wpa_ssid *ssid, const char name)
 {
        return 0;
 }
index 07d31e4..a320cc4 100644 (file)
@@ -34,7 +34,7 @@ struct bgscan_learn_data {
        int signal_threshold;
        int short_interval; /* use if signal < threshold */
        int long_interval; /* use if signal > threshold */
-       struct os_time last_bgscan;
+       struct os_reltime last_bgscan;
        char *fname;
        struct dl_list bss;
        int *supp_freqs;
@@ -240,17 +240,14 @@ static int * bgscan_learn_get_probe_freq(struct bgscan_learn_data *data,
        if (data->supp_freqs == NULL)
                return freqs;
 
-       idx = data->probe_idx + 1;
-       while (idx != data->probe_idx) {
-               if (data->supp_freqs[idx] == 0) {
-                       if (data->probe_idx == 0)
-                               break;
-                       idx = 0;
-               }
+       idx = data->probe_idx;
+       do {
                if (!in_array(freqs, data->supp_freqs[idx])) {
                        wpa_printf(MSG_DEBUG, "bgscan learn: Probe new freq "
                                   "%u", data->supp_freqs[idx]);
-                       data->probe_idx = idx;
+                       data->probe_idx = idx + 1;
+                       if (data->supp_freqs[data->probe_idx] == 0)
+                               data->probe_idx = 0;
                        n = os_realloc_array(freqs, count + 2, sizeof(int));
                        if (n == NULL)
                                return freqs;
@@ -262,7 +259,9 @@ static int * bgscan_learn_get_probe_freq(struct bgscan_learn_data *data,
                }
 
                idx++;
-       }
+               if (data->supp_freqs[idx] == 0)
+                       idx = 0;
+       } while (idx != data->probe_idx);
 
        return freqs;
 }
@@ -295,7 +294,7 @@ static void bgscan_learn_timeout(void *eloop_ctx, void *timeout_ctx)
                        int ret;
                        ret = os_snprintf(pos, msg + sizeof(msg) - pos, " %d",
                                          freqs[i]);
-                       if (ret < 0 || ret >= msg + sizeof(msg) - pos)
+                       if (os_snprintf_error(msg + sizeof(msg) - pos, ret))
                                break;
                        pos += ret;
                }
@@ -311,7 +310,7 @@ static void bgscan_learn_timeout(void *eloop_ctx, void *timeout_ctx)
                eloop_register_timeout(data->scan_interval, 0,
                                       bgscan_learn_timeout, data, NULL);
        } else
-               os_get_time(&data->last_bgscan);
+               os_get_reltime(&data->last_bgscan);
        os_free(freqs);
 }
 
@@ -363,6 +362,9 @@ static int * bgscan_learn_get_supp_freqs(struct wpa_supplicant *wpa_s)
                for (j = 0; j < modes[i].num_channels; j++) {
                        if (modes[i].channels[j].flag & HOSTAPD_CHAN_DISABLED)
                                continue;
+                       /* some hw modes (e.g. 11b & 11g) contain same freqs */
+                       if (in_array(freqs, modes[i].channels[j].freq))
+                               continue;
                        n = os_realloc_array(freqs, count + 2, sizeof(int));
                        if (n == NULL)
                                continue;
@@ -419,6 +421,14 @@ static void * bgscan_learn_init(struct wpa_supplicant *wpa_s,
 
        data->supp_freqs = bgscan_learn_get_supp_freqs(wpa_s);
        data->scan_interval = data->short_interval;
+       if (data->signal_threshold) {
+               /* Poll for signal info to set initial scan interval */
+               struct wpa_signal_info siginfo;
+               if (wpa_drv_signal_poll(wpa_s, &siginfo) == 0 &&
+                   siginfo.current_signal >= data->signal_threshold)
+                       data->scan_interval = data->long_interval;
+       }
+
        eloop_register_timeout(data->scan_interval, 0, bgscan_learn_timeout,
                               data, NULL);
 
@@ -428,7 +438,7 @@ static void * bgscan_learn_init(struct wpa_supplicant *wpa_s,
         * us skip an immediate new scan in cases where the current signal
         * level is below the bgscan threshold.
         */
-       os_get_time(&data->last_bgscan);
+       os_get_reltime(&data->last_bgscan);
 
        return data;
 }
@@ -555,7 +565,7 @@ static void bgscan_learn_notify_signal_change(void *priv, int above,
 {
        struct bgscan_learn_data *data = priv;
        int scan = 0;
-       struct os_time now;
+       struct os_reltime now;
 
        if (data->short_interval == data->long_interval ||
            data->signal_threshold == 0)
@@ -569,7 +579,7 @@ static void bgscan_learn_notify_signal_change(void *priv, int above,
                wpa_printf(MSG_DEBUG, "bgscan learn: Start using short bgscan "
                           "interval");
                data->scan_interval = data->short_interval;
-               os_get_time(&now);
+               os_get_reltime(&now);
                if (now.sec > data->last_bgscan.sec + 1)
                        scan = 1;
        } else if (data->scan_interval == data->short_interval && above) {
@@ -584,7 +594,7 @@ static void bgscan_learn_notify_signal_change(void *priv, int above,
                 * Signal dropped further 4 dB. Request a new scan if we have
                 * not yet scanned in a while.
                 */
-               os_get_time(&now);
+               os_get_reltime(&now);
                if (now.sec > data->last_bgscan.sec + 10)
                        scan = 1;
        }
index 479f703..a467cc5 100644 (file)
@@ -26,7 +26,7 @@ struct bgscan_simple_data {
        int max_short_scans; /* maximum times we short-scan before back-off */
        int short_interval; /* use if signal < threshold */
        int long_interval; /* use if signal > threshold */
-       struct os_time last_bgscan;
+       struct os_reltime last_bgscan;
 };
 
 
@@ -75,7 +75,7 @@ static void bgscan_simple_timeout(void *eloop_ctx, void *timeout_ctx)
                         */
                        data->short_scan_count--;
                }
-               os_get_time(&data->last_bgscan);
+               os_get_reltime(&data->last_bgscan);
        }
 }
 
@@ -159,7 +159,7 @@ static void * bgscan_simple_init(struct wpa_supplicant *wpa_s,
         * us skip an immediate new scan in cases where the current signal
         * level is below the bgscan threshold.
         */
-       os_get_time(&data->last_bgscan);
+       os_get_reltime(&data->last_bgscan);
 
        return data;
 }
@@ -211,7 +211,7 @@ static void bgscan_simple_notify_signal_change(void *priv, int above,
 {
        struct bgscan_simple_data *data = priv;
        int scan = 0;
-       struct os_time now;
+       struct os_reltime now;
 
        if (data->short_interval == data->long_interval ||
            data->signal_threshold == 0)
@@ -225,7 +225,7 @@ static void bgscan_simple_notify_signal_change(void *priv, int above,
                wpa_printf(MSG_DEBUG, "bgscan simple: Start using short "
                           "bgscan interval");
                data->scan_interval = data->short_interval;
-               os_get_time(&now);
+               os_get_reltime(&now);
                if (now.sec > data->last_bgscan.sec + 1 &&
                    data->short_scan_count <= data->max_short_scans)
                        /*
@@ -259,7 +259,7 @@ static void bgscan_simple_notify_signal_change(void *priv, int above,
                 * Signal dropped further 4 dB. Request a new scan if we have
                 * not yet scanned in a while.
                 */
-               os_get_time(&now);
+               os_get_reltime(&now);
                if (now.sec > data->last_bgscan.sec + 10)
                        scan = 1;
        }
old mode 100644 (file)
new mode 100755 (executable)
index 0e1576b..3e015a1
@@ -1,6 +1,6 @@
 /*
  * BSS table
- * Copyright (c) 2009-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -85,6 +85,7 @@ static struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp)
 
 #define ANQP_DUP(f) if (anqp->f) n->f = wpabuf_dup(anqp->f)
 #ifdef CONFIG_INTERWORKING
+       ANQP_DUP(capability_list);
        ANQP_DUP(venue_name);
        ANQP_DUP(network_auth_type);
        ANQP_DUP(roaming_consortium);
@@ -94,10 +95,12 @@ static struct wpa_bss_anqp * wpa_bss_anqp_clone(struct wpa_bss_anqp *anqp)
        ANQP_DUP(domain_name);
 #endif /* CONFIG_INTERWORKING */
 #ifdef CONFIG_HS20
+       ANQP_DUP(hs20_capability_list);
        ANQP_DUP(hs20_operator_friendly_name);
        ANQP_DUP(hs20_wan_metrics);
        ANQP_DUP(hs20_connection_capability);
        ANQP_DUP(hs20_operating_class);
+       ANQP_DUP(hs20_osu_providers_list);
 #endif /* CONFIG_HS20 */
 #undef ANQP_DUP
 
@@ -153,6 +156,7 @@ static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp)
        }
 
 #ifdef CONFIG_INTERWORKING
+       wpabuf_free(anqp->capability_list);
        wpabuf_free(anqp->venue_name);
        wpabuf_free(anqp->network_auth_type);
        wpabuf_free(anqp->roaming_consortium);
@@ -162,16 +166,43 @@ static void wpa_bss_anqp_free(struct wpa_bss_anqp *anqp)
        wpabuf_free(anqp->domain_name);
 #endif /* CONFIG_INTERWORKING */
 #ifdef CONFIG_HS20
+       wpabuf_free(anqp->hs20_capability_list);
        wpabuf_free(anqp->hs20_operator_friendly_name);
        wpabuf_free(anqp->hs20_wan_metrics);
        wpabuf_free(anqp->hs20_connection_capability);
        wpabuf_free(anqp->hs20_operating_class);
+       wpabuf_free(anqp->hs20_osu_providers_list);
 #endif /* CONFIG_HS20 */
 
        os_free(anqp);
 }
 
 
+static void wpa_bss_update_pending_connect(struct wpa_supplicant *wpa_s,
+                                          struct wpa_bss *old_bss,
+                                          struct wpa_bss *new_bss)
+{
+       struct wpa_radio_work *work;
+       struct wpa_connect_work *cwork;
+
+       work = radio_work_pending(wpa_s, "sme-connect");
+       if (!work)
+               work = radio_work_pending(wpa_s, "connect");
+       if (!work)
+               return;
+
+       cwork = work->ctx;
+       if (cwork->bss != old_bss)
+               return;
+
+       wpa_printf(MSG_DEBUG,
+                  "Update BSS pointer for the pending connect radio work");
+       cwork->bss = new_bss;
+       if (!new_bss)
+               cwork->bss_removed = 1;
+}
+
+
 static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
                           const char *reason)
 {
@@ -188,6 +219,7 @@ static void wpa_bss_remove(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
                        }
                }
        }
+       wpa_bss_update_pending_connect(wpa_s, bss, NULL);
        dl_list_del(&bss->list);
        dl_list_del(&bss->list_id);
        wpa_s->num_bss--;
@@ -224,9 +256,9 @@ struct wpa_bss * wpa_bss_get(struct wpa_supplicant *wpa_s, const u8 *bssid,
 }
 
 
-static void calculate_update_time(const struct os_time *fetch_time,
+static void calculate_update_time(const struct os_reltime *fetch_time,
                                  unsigned int age_ms,
-                                 struct os_time *update_time)
+                                 struct os_reltime *update_time)
 {
        os_time_t usec;
 
@@ -243,7 +275,7 @@ static void calculate_update_time(const struct os_time *fetch_time,
 
 
 static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src,
-                            struct os_time *fetch_time)
+                            struct os_reltime *fetch_time)
 {
        dst->flags = src->flags;
        os_memcpy(dst->bssid, src->bssid, ETH_ALEN);
@@ -254,6 +286,8 @@ static void wpa_bss_copy_res(struct wpa_bss *dst, struct wpa_scan_res *src,
        dst->noise = src->noise;
        dst->level = src->level;
        dst->tsf = src->tsf;
+       dst->est_throughput = src->est_throughput;
+       dst->snr = src->snr;
 
        calculate_update_time(fetch_time, src->age, &dst->last_update);
 }
@@ -278,8 +312,9 @@ static int wpa_bss_known(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 static int wpa_bss_in_use(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
        return bss == wpa_s->current_bss ||
-               os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 ||
-               os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0;
+               (!is_zero_ether_addr(bss->bssid) &&
+                (os_memcmp(bss->bssid, wpa_s->bssid, ETH_ALEN) == 0 ||
+                 os_memcmp(bss->bssid, wpa_s->pending_bssid, ETH_ALEN) == 0));
 }
 
 
@@ -326,7 +361,7 @@ static int wpa_bss_remove_oldest(struct wpa_supplicant *wpa_s)
 static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s,
                                    const u8 *ssid, size_t ssid_len,
                                    struct wpa_scan_res *res,
-                                   struct os_time *fetch_time)
+                                   struct os_reltime *fetch_time)
 {
        struct wpa_bss *bss;
 
@@ -343,6 +378,37 @@ static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s,
        os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
        wpa_bss_set_hessid(bss);
 
+       if (wpa_s->num_bss + 1 > wpa_s->conf->bss_max_count &&
+           wpa_bss_remove_oldest(wpa_s) != 0) {
+               wpa_printf(MSG_ERROR, "Increasing the MAX BSS count to %d "
+                          "because all BSSes are in use. We should normally "
+                          "not get here!", (int) wpa_s->num_bss + 1);
+               wpa_s->conf->bss_max_count = wpa_s->num_bss + 1;
+       }
+
+#if defined TIZEN_EXT
+       if (wpa_s->conf->auto_interworking &&
+               wpa_s->conf->interworking &&
+               wpa_s->conf->cred) {
+
+               /*
+                * Passpoint : enable the disabled network when the network is found again.
+                */
+               struct wpa_ssid *ssid_temp;
+
+               if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) {
+                       for (ssid_temp = wpa_s->conf->ssid; ssid_temp; ssid_temp = ssid_temp->next) {
+                               if (ssid_temp->ssid == NULL || ssid_temp->ssid_len == 0)
+                                       continue;
+
+                               if (ssid_temp->ssid_len == bss->ssid_len &&
+                                       os_memcmp(ssid_temp->ssid, bss->ssid, ssid_temp->ssid_len) == 0)
+                                       wpa_supplicant_enable_network(wpa_s, ssid_temp);
+                       }
+               }
+       }
+#endif
+
        dl_list_add_tail(&wpa_s->bss, &bss->list);
        dl_list_add_tail(&wpa_s->bss_id, &bss->list_id);
        wpa_s->num_bss++;
@@ -350,13 +416,6 @@ static struct wpa_bss * wpa_bss_add(struct wpa_supplicant *wpa_s,
                " SSID '%s'",
                bss->id, MAC2STR(bss->bssid), wpa_ssid_txt(ssid, ssid_len));
        wpas_notify_bss_added(wpa_s, bss->bssid, bss->id);
-       if (wpa_s->num_bss > wpa_s->conf->bss_max_count &&
-           wpa_bss_remove_oldest(wpa_s) != 0) {
-               wpa_printf(MSG_ERROR, "Increasing the MAX BSS count to %d "
-                          "because all BSSes are in use. We should normally "
-                          "not get here!", (int) wpa_s->num_bss);
-               wpa_s->conf->bss_max_count = wpa_s->num_bss;
-       }
        return bss;
 }
 
@@ -486,12 +545,14 @@ static void notify_bss_changes(struct wpa_supplicant *wpa_s, u32 changes,
 
        if (changes & WPA_BSS_RATES_CHANGED_FLAG)
                wpas_notify_bss_rates_changed(wpa_s, bss->id);
+
+       wpas_notify_bss_seen(wpa_s, bss->id);
 }
 
 
 static struct wpa_bss *
 wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
-              struct wpa_scan_res *res, struct os_time *fetch_time)
+              struct wpa_scan_res *res, struct os_reltime *fetch_time)
 {
        u32 changes;
 
@@ -501,6 +562,22 @@ wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
        wpa_bss_copy_res(bss, res, fetch_time);
        /* Move the entry to the end of the list */
        dl_list_del(&bss->list);
+#ifdef CONFIG_P2P
+       if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
+           !wpa_scan_get_vendor_ie(res, P2P_IE_VENDOR_TYPE)) {
+               /*
+                * This can happen when non-P2P station interface runs a scan
+                * without P2P IE in the Probe Request frame. P2P GO would reply
+                * to that with a Probe Response that does not include P2P IE.
+                * Do not update the IEs in this BSS entry to avoid such loss of
+                * information that may be needed for P2P operations to
+                * determine group information.
+                */
+               wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Do not update scan IEs for "
+                       MACSTR " since that would remove P2P IE information",
+                       MAC2STR(bss->bssid));
+       } else
+#endif /* CONFIG_P2P */
        if (bss->ie_len + bss->beacon_ie_len >=
            res->ie_len + res->beacon_ie_len) {
                os_memcpy(bss + 1, res + 1, res->ie_len + res->beacon_ie_len);
@@ -522,6 +599,7 @@ wpa_bss_update(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
                        }
                        if (wpa_s->current_bss == bss)
                                wpa_s->current_bss = nbss;
+                       wpa_bss_update_pending_connect(wpa_s, bss, nbss);
                        bss = nbss;
                        os_memcpy(bss + 1, res + 1,
                                  res->ie_len + res->beacon_ie_len);
@@ -570,17 +648,18 @@ void wpa_bss_update_start(struct wpa_supplicant *wpa_s)
  */
 void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
                             struct wpa_scan_res *res,
-                            struct os_time *fetch_time)
+                            struct os_reltime *fetch_time)
 {
-       const u8 *ssid, *p2p;
+       const u8 *ssid, *p2p, *mesh;
        struct wpa_bss *bss;
 
        if (wpa_s->conf->ignore_old_scan_res) {
-               struct os_time update;
+               struct os_reltime update;
                calculate_update_time(fetch_time, res->age, &update);
-               if (os_time_before(&update, &wpa_s->scan_trigger_time)) {
-                       struct os_time age;
-                       os_time_sub(&wpa_s->scan_trigger_time, &update, &age);
+               if (os_reltime_before(&update, &wpa_s->scan_trigger_time)) {
+                       struct os_reltime age;
+                       os_reltime_sub(&wpa_s->scan_trigger_time, &update,
+                                      &age);
                        wpa_dbg(wpa_s, MSG_DEBUG, "BSS: Ignore driver BSS "
                                "table entry that is %u.%06u seconds older "
                                "than our scan trigger",
@@ -621,11 +700,26 @@ void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
 
        /* TODO: add option for ignoring BSSes we are not interested in
         * (to save memory) */
+
+       mesh = wpa_scan_get_ie(res, WLAN_EID_MESH_ID);
+       if (mesh && mesh[1] <= 32)
+               ssid = mesh;
+
        bss = wpa_bss_get(wpa_s, res->bssid, ssid + 2, ssid[1]);
        if (bss == NULL)
                bss = wpa_bss_add(wpa_s, ssid + 2, ssid[1], res, fetch_time);
-       else
+       else {
                bss = wpa_bss_update(wpa_s, bss, res, fetch_time);
+               if (wpa_s->last_scan_res) {
+                       unsigned int i;
+                       for (i = 0; i < wpa_s->last_scan_res_used; i++) {
+                               if (bss == wpa_s->last_scan_res[i]) {
+                                       /* Already in the list */
+                                       return;
+                               }
+                       }
+               }
+       }
 
        if (bss == NULL)
                return;
@@ -644,7 +738,8 @@ void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
                wpa_s->last_scan_res_size = siz;
        }
 
-       wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss;
+       if (wpa_s->last_scan_res)
+               wpa_s->last_scan_res[wpa_s->last_scan_res_used++] = bss;
 }
 
 
@@ -704,26 +799,10 @@ void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
 {
        struct wpa_bss *bss, *n;
 
-       wpa_s->last_scan_full = 0;
-       os_get_time(&wpa_s->last_scan);
+       os_get_reltime(&wpa_s->last_scan);
        if (!new_scan)
                return; /* do not expire entries without new scan */
 
-       if (info && !info->aborted && !info->freqs) {
-               size_t i;
-               if (info->num_ssids == 0) {
-                       wpa_s->last_scan_full = 1;
-               } else {
-                       for (i = 0; i < info->num_ssids; i++) {
-                               if (info->ssids[i].ssid == NULL ||
-                                   info->ssids[i].ssid_len == 0) {
-                                       wpa_s->last_scan_full = 1;
-                                       break;
-                               }
-                       }
-               }
-       }
-
        dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
                if (wpa_bss_in_use(wpa_s, bss))
                        continue;
@@ -737,10 +816,8 @@ void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
                }
        }
 
-       wpa_printf(MSG_DEBUG, "BSS: last_scan_res_used=%u/%u "
-                  "last_scan_full=%d",
-                  wpa_s->last_scan_res_used, wpa_s->last_scan_res_size,
-                  wpa_s->last_scan_full);
+       wpa_printf(MSG_DEBUG, "BSS: last_scan_res_used=%u/%u",
+                  wpa_s->last_scan_res_used, wpa_s->last_scan_res_size);
 }
 
 
@@ -754,19 +831,19 @@ void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
 void wpa_bss_flush_by_age(struct wpa_supplicant *wpa_s, int age)
 {
        struct wpa_bss *bss, *n;
-       struct os_time t;
+       struct os_reltime t;
 
        if (dl_list_empty(&wpa_s->bss))
                return;
 
-       os_get_time(&t);
+       os_get_reltime(&t);
        t.sec -= age;
 
        dl_list_for_each_safe(bss, n, &wpa_s->bss, struct wpa_bss, list) {
                if (wpa_bss_in_use(wpa_s, bss))
                        continue;
 
-               if (os_time_before(&bss->last_update, &t)) {
+               if (os_reltime_before(&bss->last_update, &t)) {
                        wpa_bss_remove(wpa_s, bss, __func__);
                } else
                        break;
@@ -810,6 +887,8 @@ void wpa_bss_flush(struct wpa_supplicant *wpa_s)
 {
        struct wpa_bss *bss, *n;
 
+       wpa_s->clear_driver_scan_cache = 1;
+
        if (wpa_s->bss.next == NULL)
                return; /* BSS table not yet initialized */
 
@@ -873,7 +952,7 @@ struct wpa_bss * wpa_bss_get_bssid_latest(struct wpa_supplicant *wpa_s,
                if (os_memcmp(bss->bssid, bssid, ETH_ALEN) != 0)
                        continue;
                if (found == NULL ||
-                   os_time_before(&found->last_update, &bss->last_update))
+                   os_reltime_before(&found->last_update, &bss->last_update))
                        found = bss;
        }
        return found;
@@ -1001,6 +1080,43 @@ const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type)
 
 
 /**
+ * wpa_bss_get_vendor_ie_beacon - Fetch a vendor information from a BSS entry
+ * @bss: BSS table entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the BSS
+ * entry.
+ *
+ * This function is like wpa_bss_get_vendor_ie(), but uses IE buffer only
+ * from Beacon frames instead of either Beacon or Probe Response frames.
+ */
+const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss,
+                                       u32 vendor_type)
+{
+       const u8 *end, *pos;
+
+       if (bss->beacon_ie_len == 0)
+               return NULL;
+
+       pos = (const u8 *) (bss + 1);
+       pos += bss->ie_len;
+       end = pos + bss->beacon_ie_len;
+
+       while (pos + 1 < end) {
+               if (pos + 2 + pos[1] > end)
+                       break;
+               if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+                   vendor_type == WPA_GET_BE32(&pos[2]))
+                       return pos;
+               pos += 2 + pos[1];
+       }
+
+       return NULL;
+}
+
+
+/**
  * wpa_bss_get_vendor_ie_multi - Fetch vendor IE data from a BSS entry
  * @bss: BSS table entry
  * @vendor_type: Vendor type (four octets starting the IE payload)
index 2b41948..634aa3c 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * BSS table
- * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -26,6 +26,7 @@ struct wpa_bss_anqp {
        /** Number of BSS entries referring to this ANQP data instance */
        unsigned int users;
 #ifdef CONFIG_INTERWORKING
+       struct wpabuf *capability_list;
        struct wpabuf *venue_name;
        struct wpabuf *network_auth_type;
        struct wpabuf *roaming_consortium;
@@ -35,10 +36,12 @@ struct wpa_bss_anqp {
        struct wpabuf *domain_name;
 #endif /* CONFIG_INTERWORKING */
 #ifdef CONFIG_HS20
+       struct wpabuf *hs20_capability_list;
        struct wpabuf *hs20_operator_friendly_name;
        struct wpabuf *hs20_wan_metrics;
        struct wpabuf *hs20_connection_capability;
        struct wpabuf *hs20_operating_class;
+       struct wpabuf *hs20_osu_providers_list;
 #endif /* CONFIG_HS20 */
 };
 
@@ -84,7 +87,11 @@ struct wpa_bss {
        /** Timestamp of last Beacon/Probe Response frame */
        u64 tsf;
        /** Time of the last update (i.e., Beacon or Probe Response RX) */
-       struct os_time last_update;
+       struct os_reltime last_update;
+       /** Estimated throughput in kbps */
+       unsigned int est_throughput;
+       /** Signal-to-noise ratio in dB */
+       int snr;
        /** ANQP data */
        struct wpa_bss_anqp *anqp;
        /** Length of the following IE field in octets (from Probe Response) */
@@ -98,7 +105,7 @@ struct wpa_bss {
 void wpa_bss_update_start(struct wpa_supplicant *wpa_s);
 void wpa_bss_update_scan_res(struct wpa_supplicant *wpa_s,
                             struct wpa_scan_res *res,
-                            struct os_time *fetch_time);
+                            struct os_reltime *fetch_time);
 void wpa_bss_update_end(struct wpa_supplicant *wpa_s, struct scan_info *info,
                        int new_scan);
 int wpa_bss_init(struct wpa_supplicant *wpa_s);
@@ -118,6 +125,8 @@ struct wpa_bss * wpa_bss_get_id_range(struct wpa_supplicant *wpa_s,
                                      unsigned int idf, unsigned int idl);
 const u8 * wpa_bss_get_ie(const struct wpa_bss *bss, u8 ie);
 const u8 * wpa_bss_get_vendor_ie(const struct wpa_bss *bss, u32 vendor_type);
+const u8 * wpa_bss_get_vendor_ie_beacon(const struct wpa_bss *bss,
+                                       u32 vendor_type);
 struct wpabuf * wpa_bss_get_vendor_ie_multi(const struct wpa_bss *bss,
                                            u32 vendor_type);
 struct wpabuf * wpa_bss_get_vendor_ie_multi_beacon(const struct wpa_bss *bss,
@@ -127,4 +136,15 @@ int wpa_bss_get_bit_rates(const struct wpa_bss *bss, u8 **rates);
 struct wpa_bss_anqp * wpa_bss_anqp_alloc(void);
 int wpa_bss_anqp_unshare_alloc(struct wpa_bss *bss);
 
+static inline int bss_is_dmg(const struct wpa_bss *bss)
+{
+       return bss->freq > 45000;
+}
+
+static inline void wpa_bss_update_level(struct wpa_bss *bss, int new_level)
+{
+       if (bss != NULL && new_level < 0)
+               bss->level = new_level;
+}
+
 #endif /* BSS_H */
index a35be51..8e6cd20 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / Configuration parser and common functions
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +10,7 @@
 
 #include "common.h"
 #include "utils/uuid.h"
+#include "utils/ip_addr.h"
 #include "crypto/sha1.h"
 #include "rsn_supp/wpa.h"
 #include "eap_peer/eap.h"
@@ -224,7 +225,7 @@ static char * wpa_config_write_int(const struct parse_data *data,
        if (value == NULL)
                return NULL;
        res = os_snprintf(value, 20, "%d", *src);
-       if (res < 0 || res >= 20) {
+       if (os_snprintf_error(20, res)) {
                os_free(value);
                return NULL;
        }
@@ -234,6 +235,99 @@ static char * wpa_config_write_int(const struct parse_data *data,
 #endif /* NO_CONFIG_WRITE */
 
 
+static int wpa_config_parse_addr_list(const struct parse_data *data,
+                                     int line, const char *value,
+                                     u8 **list, size_t *num, char *name,
+                                     u8 abort_on_error, u8 masked)
+{
+       const char *pos;
+       u8 *buf, *n, addr[2 * ETH_ALEN];
+       size_t count;
+
+       buf = NULL;
+       count = 0;
+
+       pos = value;
+       while (pos && *pos) {
+               while (*pos == ' ')
+                       pos++;
+
+               if (hwaddr_masked_aton(pos, addr, &addr[ETH_ALEN], masked)) {
+                       if (abort_on_error || count == 0) {
+                               wpa_printf(MSG_ERROR,
+                                          "Line %d: Invalid %s address '%s'",
+                                          line, name, value);
+                               os_free(buf);
+                               return -1;
+                       }
+                       /* continue anyway since this could have been from a
+                        * truncated configuration file line */
+                       wpa_printf(MSG_INFO,
+                                  "Line %d: Ignore likely truncated %s address '%s'",
+                                  line, name, pos);
+               } else {
+                       n = os_realloc_array(buf, count + 1, 2 * ETH_ALEN);
+                       if (n == NULL) {
+                               os_free(buf);
+                               return -1;
+                       }
+                       buf = n;
+                       os_memmove(buf + 2 * ETH_ALEN, buf,
+                                  count * 2 * ETH_ALEN);
+                       os_memcpy(buf, addr, 2 * ETH_ALEN);
+                       count++;
+                       wpa_printf(MSG_MSGDUMP,
+                                  "%s: addr=" MACSTR " mask=" MACSTR,
+                                  name, MAC2STR(addr),
+                                  MAC2STR(&addr[ETH_ALEN]));
+               }
+
+               pos = os_strchr(pos, ' ');
+       }
+
+       os_free(*list);
+       *list = buf;
+       *num = count;
+
+       return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_addr_list(const struct parse_data *data,
+                                        const u8 *list, size_t num, char *name)
+{
+       char *value, *end, *pos;
+       int res;
+       size_t i;
+
+       if (list == NULL || num == 0)
+               return NULL;
+
+       value = os_malloc(2 * 20 * num);
+       if (value == NULL)
+               return NULL;
+       pos = value;
+       end = value + 2 * 20 * num;
+
+       for (i = num; i > 0; i--) {
+               const u8 *a = list + (i - 1) * 2 * ETH_ALEN;
+               const u8 *m = a + ETH_ALEN;
+
+               if (i < num)
+                       *pos++ = ' ';
+               res = hwaddr_mask_txt(pos, end - pos, a, m);
+               if (res < 0) {
+                       os_free(value);
+                       return NULL;
+               }
+               pos += res;
+       }
+
+       return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
 static int wpa_config_parse_bssid(const struct parse_data *data,
                                  struct wpa_ssid *ssid, int line,
                                  const char *value)
@@ -269,7 +363,7 @@ static char * wpa_config_write_bssid(const struct parse_data *data,
        if (value == NULL)
                return NULL;
        res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->bssid));
-       if (res < 0 || res >= 20) {
+       if (os_snprintf_error(20, res)) {
                os_free(value);
                return NULL;
        }
@@ -279,13 +373,57 @@ static char * wpa_config_write_bssid(const struct parse_data *data,
 #endif /* NO_CONFIG_WRITE */
 
 
+static int wpa_config_parse_bssid_blacklist(const struct parse_data *data,
+                                           struct wpa_ssid *ssid, int line,
+                                           const char *value)
+{
+       return wpa_config_parse_addr_list(data, line, value,
+                                         &ssid->bssid_blacklist,
+                                         &ssid->num_bssid_blacklist,
+                                         "bssid_blacklist", 1, 1);
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_bssid_blacklist(const struct parse_data *data,
+                                              struct wpa_ssid *ssid)
+{
+       return wpa_config_write_addr_list(data, ssid->bssid_blacklist,
+                                         ssid->num_bssid_blacklist,
+                                         "bssid_blacklist");
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_bssid_whitelist(const struct parse_data *data,
+                                           struct wpa_ssid *ssid, int line,
+                                           const char *value)
+{
+       return wpa_config_parse_addr_list(data, line, value,
+                                         &ssid->bssid_whitelist,
+                                         &ssid->num_bssid_whitelist,
+                                         "bssid_whitelist", 1, 1);
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_bssid_whitelist(const struct parse_data *data,
+                                              struct wpa_ssid *ssid)
+{
+       return wpa_config_write_addr_list(data, ssid->bssid_whitelist,
+                                         ssid->num_bssid_whitelist,
+                                         "bssid_whitelist");
+}
+#endif /* NO_CONFIG_WRITE */
+
+
 static int wpa_config_parse_psk(const struct parse_data *data,
                                struct wpa_ssid *ssid, int line,
                                const char *value)
 {
 #ifdef CONFIG_EXT_PASSWORD
        if (os_strncmp(value, "ext:", 4) == 0) {
-               os_free(ssid->passphrase);
+               str_clear_free(ssid->passphrase);
                ssid->passphrase = NULL;
                ssid->psk_set = 0;
                os_free(ssid->ext_psk);
@@ -321,7 +459,7 @@ static int wpa_config_parse_psk(const struct parse_data *data,
                    os_memcmp(ssid->passphrase, value, len) == 0)
                        return 0;
                ssid->psk_set = 0;
-               os_free(ssid->passphrase);
+               str_clear_free(ssid->passphrase);
                ssid->passphrase = dup_binstr(value, len);
                if (ssid->passphrase == NULL)
                        return -1;
@@ -340,7 +478,7 @@ static int wpa_config_parse_psk(const struct parse_data *data,
                return -1;
        }
 
-       os_free(ssid->passphrase);
+       str_clear_free(ssid->passphrase);
        ssid->passphrase = NULL;
 
        ssid->psk_set = 1;
@@ -357,9 +495,15 @@ static char * wpa_config_write_psk(const struct parse_data *data,
        if (ssid->ext_psk) {
                size_t len = 4 + os_strlen(ssid->ext_psk) + 1;
                char *buf = os_malloc(len);
+               int res;
+
                if (buf == NULL)
                        return NULL;
-               os_snprintf(buf, len, "ext:%s", ssid->ext_psk);
+               res = os_snprintf(buf, len, "ext:%s", ssid->ext_psk);
+               if (os_snprintf_error(len, res)) {
+                       os_free(buf);
+                       buf = NULL;
+               }
                return buf;
        }
 #endif /* CONFIG_EXT_PASSWORD */
@@ -404,6 +548,8 @@ static int wpa_config_parse_proto(const struct parse_data *data,
                else if (os_strcmp(start, "RSN") == 0 ||
                         os_strcmp(start, "WPA2") == 0)
                        val |= WPA_PROTO_RSN;
+               else if (os_strcmp(start, "OSEN") == 0)
+                       val |= WPA_PROTO_OSEN;
                else {
                        wpa_printf(MSG_ERROR, "Line %d: invalid proto '%s'",
                                   line, start);
@@ -432,28 +578,41 @@ static int wpa_config_parse_proto(const struct parse_data *data,
 static char * wpa_config_write_proto(const struct parse_data *data,
                                     struct wpa_ssid *ssid)
 {
-       int first = 1, ret;
+       int ret;
        char *buf, *pos, *end;
 
-       pos = buf = os_zalloc(10);
+       pos = buf = os_zalloc(20);
        if (buf == NULL)
                return NULL;
-       end = buf + 10;
+       end = buf + 20;
 
        if (ssid->proto & WPA_PROTO_WPA) {
-               ret = os_snprintf(pos, end - pos, "%sWPA", first ? "" : " ");
-               if (ret < 0 || ret >= end - pos)
+               ret = os_snprintf(pos, end - pos, "%sWPA",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret))
                        return buf;
                pos += ret;
-               first = 0;
        }
 
        if (ssid->proto & WPA_PROTO_RSN) {
-               ret = os_snprintf(pos, end - pos, "%sRSN", first ? "" : " ");
-               if (ret < 0 || ret >= end - pos)
+               ret = os_snprintf(pos, end - pos, "%sRSN",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret))
+                       return buf;
+               pos += ret;
+       }
+
+       if (ssid->proto & WPA_PROTO_OSEN) {
+               ret = os_snprintf(pos, end - pos, "%sOSEN",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret))
                        return buf;
                pos += ret;
-               first = 0;
+       }
+
+       if (pos == buf) {
+               os_free(buf);
+               buf = NULL;
        }
 
        return buf;
@@ -515,6 +674,18 @@ static int wpa_config_parse_key_mgmt(const struct parse_data *data,
                else if (os_strcmp(start, "FT-SAE") == 0)
                        val |= WPA_KEY_MGMT_FT_SAE;
 #endif /* CONFIG_SAE */
+#ifdef CONFIG_HS20
+               else if (os_strcmp(start, "OSEN") == 0)
+                       val |= WPA_KEY_MGMT_OSEN;
+#endif /* CONFIG_HS20 */
+#ifdef CONFIG_SUITEB
+               else if (os_strcmp(start, "WPA-EAP-SUITE-B") == 0)
+                       val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B;
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SUITEB192
+               else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0)
+                       val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+#endif /* CONFIG_SUITEB192 */
                else {
                        wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
                                   line, start);
@@ -546,15 +717,15 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data,
        char *buf, *pos, *end;
        int ret;
 
-       pos = buf = os_zalloc(50);
+       pos = buf = os_zalloc(100);
        if (buf == NULL)
                return NULL;
-       end = buf + 50;
+       end = buf + 100;
 
        if (ssid->key_mgmt & WPA_KEY_MGMT_PSK) {
                ret = os_snprintf(pos, end - pos, "%sWPA-PSK",
                                  pos == buf ? "" : " ");
-               if (ret < 0 || ret >= end - pos) {
+               if (os_snprintf_error(end - pos, ret)) {
                        end[-1] = '\0';
                        return buf;
                }
@@ -564,7 +735,7 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data,
        if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
                ret = os_snprintf(pos, end - pos, "%sWPA-EAP",
                                  pos == buf ? "" : " ");
-               if (ret < 0 || ret >= end - pos) {
+               if (os_snprintf_error(end - pos, ret)) {
                        end[-1] = '\0';
                        return buf;
                }
@@ -574,7 +745,7 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data,
        if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
                ret = os_snprintf(pos, end - pos, "%sIEEE8021X",
                                  pos == buf ? "" : " ");
-               if (ret < 0 || ret >= end - pos) {
+               if (os_snprintf_error(end - pos, ret)) {
                        end[-1] = '\0';
                        return buf;
                }
@@ -584,7 +755,7 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data,
        if (ssid->key_mgmt & WPA_KEY_MGMT_NONE) {
                ret = os_snprintf(pos, end - pos, "%sNONE",
                                  pos == buf ? "" : " ");
-               if (ret < 0 || ret >= end - pos) {
+               if (os_snprintf_error(end - pos, ret)) {
                        end[-1] = '\0';
                        return buf;
                }
@@ -594,7 +765,7 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data,
        if (ssid->key_mgmt & WPA_KEY_MGMT_WPA_NONE) {
                ret = os_snprintf(pos, end - pos, "%sWPA-NONE",
                                  pos == buf ? "" : " ");
-               if (ret < 0 || ret >= end - pos) {
+               if (os_snprintf_error(end - pos, ret)) {
                        end[-1] = '\0';
                        return buf;
                }
@@ -602,31 +773,124 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data,
        }
 
 #ifdef CONFIG_IEEE80211R
-       if (ssid->key_mgmt & WPA_KEY_MGMT_FT_PSK)
-               pos += os_snprintf(pos, end - pos, "%sFT-PSK",
-                                  pos == buf ? "" : " ");
+       if (ssid->key_mgmt & WPA_KEY_MGMT_FT_PSK) {
+               ret = os_snprintf(pos, end - pos, "%sFT-PSK",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret)) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
 
-       if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X)
-               pos += os_snprintf(pos, end - pos, "%sFT-EAP",
-                                  pos == buf ? "" : " ");
+       if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
+               ret = os_snprintf(pos, end - pos, "%sFT-EAP",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret)) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
 #endif /* CONFIG_IEEE80211R */
 
 #ifdef CONFIG_IEEE80211W
-       if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256)
-               pos += os_snprintf(pos, end - pos, "%sWPA-PSK-SHA256",
-                                  pos == buf ? "" : " ");
+       if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
+               ret = os_snprintf(pos, end - pos, "%sWPA-PSK-SHA256",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret)) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
 
-       if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
-               pos += os_snprintf(pos, end - pos, "%sWPA-EAP-SHA256",
-                                  pos == buf ? "" : " ");
+       if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
+               ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SHA256",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret)) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
 #endif /* CONFIG_IEEE80211W */
 
 #ifdef CONFIG_WPS
-       if (ssid->key_mgmt & WPA_KEY_MGMT_WPS)
-               pos += os_snprintf(pos, end - pos, "%sWPS",
-                                  pos == buf ? "" : " ");
+       if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
+               ret = os_snprintf(pos, end - pos, "%sWPS",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret)) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
 #endif /* CONFIG_WPS */
 
+#ifdef CONFIG_SAE
+       if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
+               ret = os_snprintf(pos, end - pos, "%sSAE",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret)) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+
+       if (ssid->key_mgmt & WPA_KEY_MGMT_FT_SAE) {
+               ret = os_snprintf(pos, end - pos, "%sFT-SAE",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret)) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+#endif /* CONFIG_SAE */
+
+#ifdef CONFIG_HS20
+       if (ssid->key_mgmt & WPA_KEY_MGMT_OSEN) {
+               ret = os_snprintf(pos, end - pos, "%sOSEN",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret)) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+#endif /* CONFIG_HS20 */
+
+#ifdef CONFIG_SUITEB
+       if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+               ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SUITE-B",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret)) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+#endif /* CONFIG_SUITEB */
+
+#ifdef CONFIG_SUITEB192
+       if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+               ret = os_snprintf(pos, end - pos, "%sWPA-EAP-SUITE-B-192",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret)) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+#endif /* CONFIG_SUITEB192 */
+
+       if (pos == buf) {
+               os_free(buf);
+               buf = NULL;
+       }
+
        return buf;
 }
 #endif /* NO_CONFIG_WRITE */
@@ -791,7 +1055,7 @@ static char * wpa_config_write_auth_alg(const struct parse_data *data,
        if (ssid->auth_alg & WPA_AUTH_ALG_OPEN) {
                ret = os_snprintf(pos, end - pos, "%sOPEN",
                                  pos == buf ? "" : " ");
-               if (ret < 0 || ret >= end - pos) {
+               if (os_snprintf_error(end - pos, ret)) {
                        end[-1] = '\0';
                        return buf;
                }
@@ -801,7 +1065,7 @@ static char * wpa_config_write_auth_alg(const struct parse_data *data,
        if (ssid->auth_alg & WPA_AUTH_ALG_SHARED) {
                ret = os_snprintf(pos, end - pos, "%sSHARED",
                                  pos == buf ? "" : " ");
-               if (ret < 0 || ret >= end - pos) {
+               if (os_snprintf_error(end - pos, ret)) {
                        end[-1] = '\0';
                        return buf;
                }
@@ -811,13 +1075,18 @@ static char * wpa_config_write_auth_alg(const struct parse_data *data,
        if (ssid->auth_alg & WPA_AUTH_ALG_LEAP) {
                ret = os_snprintf(pos, end - pos, "%sLEAP",
                                  pos == buf ? "" : " ");
-               if (ret < 0 || ret >= end - pos) {
+               if (os_snprintf_error(end - pos, ret)) {
                        end[-1] = '\0';
                        return buf;
                }
                pos += ret;
        }
 
+       if (pos == buf) {
+               os_free(buf);
+               buf = NULL;
+       }
+
        return buf;
 }
 #endif /* NO_CONFIG_WRITE */
@@ -873,6 +1142,10 @@ static int wpa_config_parse_scan_freq(const struct parse_data *data,
        freqs = wpa_config_parse_int_array(value);
        if (freqs == NULL)
                return -1;
+       if (freqs[0] == 0) {
+               os_free(freqs);
+               freqs = NULL;
+       }
        os_free(ssid->scan_freq);
        ssid->scan_freq = freqs;
 
@@ -889,6 +1162,10 @@ static int wpa_config_parse_freq_list(const struct parse_data *data,
        freqs = wpa_config_parse_int_array(value);
        if (freqs == NULL)
                return -1;
+       if (freqs[0] == 0) {
+               os_free(freqs);
+               freqs = NULL;
+       }
        os_free(ssid->freq_list);
        ssid->freq_list = freqs;
 
@@ -919,7 +1196,7 @@ static char * wpa_config_write_freqs(const struct parse_data *data,
        for (i = 0; freqs[i]; i++) {
                ret = os_snprintf(pos, end - pos, "%s%u",
                                  i == 0 ? "" : " ", freqs[i]);
-               if (ret < 0 || ret >= end - pos) {
+               if (os_snprintf_error(end - pos, ret)) {
                        end[-1] = '\0';
                        return buf;
                }
@@ -1042,7 +1319,7 @@ static char * wpa_config_write_eap(const struct parse_data *data,
                if (name) {
                        ret = os_snprintf(pos, end - pos, "%s%s",
                                          pos == buf ? "" : " ", name);
-                       if (ret < 0 || ret >= end - pos)
+                       if (os_snprintf_error(end - pos, ret))
                                break;
                        pos += ret;
                }
@@ -1062,7 +1339,7 @@ static int wpa_config_parse_password(const struct parse_data *data,
 
        if (os_strcmp(value, "NULL") == 0) {
                wpa_printf(MSG_DEBUG, "Unset configuration string 'password'");
-               os_free(ssid->eap.password);
+               bin_clear_free(ssid->eap.password, ssid->eap.password_len);
                ssid->eap.password = NULL;
                ssid->eap.password_len = 0;
                return 0;
@@ -1073,7 +1350,7 @@ static int wpa_config_parse_password(const struct parse_data *data,
                char *name = os_strdup(value + 4);
                if (name == NULL)
                        return -1;
-               os_free(ssid->eap.password);
+               bin_clear_free(ssid->eap.password, ssid->eap.password_len);
                ssid->eap.password = (u8 *) name;
                ssid->eap.password_len = os_strlen(name);
                ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
@@ -1095,7 +1372,7 @@ static int wpa_config_parse_password(const struct parse_data *data,
                wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
                                      (u8 *) tmp, res_len);
 
-               os_free(ssid->eap.password);
+               bin_clear_free(ssid->eap.password, ssid->eap.password_len);
                ssid->eap.password = (u8 *) tmp;
                ssid->eap.password_len = res_len;
                ssid->eap.flags &= ~EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
@@ -1124,7 +1401,7 @@ static int wpa_config_parse_password(const struct parse_data *data,
 
        wpa_hexdump_key(MSG_MSGDUMP, data->name, hash, 16);
 
-       os_free(ssid->eap.password);
+       bin_clear_free(ssid->eap.password, ssid->eap.password_len);
        ssid->eap.password = hash;
        ssid->eap.password_len = 16;
        ssid->eap.flags |= EAP_CONFIG_FLAGS_PASSWORD_NTHASH;
@@ -1194,9 +1471,9 @@ static int wpa_config_parse_wep_key(u8 *key, size_t *len, int line,
                           line, (unsigned int) *len);
        }
        os_memcpy(key, buf, *len);
-       os_free(buf);
+       str_clear_free(buf);
        res = os_snprintf(title, sizeof(title), "wep_key%d", idx);
-       if (res >= 0 && (size_t) res < sizeof(title))
+       if (!os_snprintf_error(sizeof(title), res))
                wpa_hexdump_key(MSG_MSGDUMP, title, key, *len);
        return 0;
 }
@@ -1283,112 +1560,183 @@ static char * wpa_config_write_wep_key3(const struct parse_data *data,
 
 #ifdef CONFIG_P2P
 
-static int wpa_config_parse_p2p_client_list(const struct parse_data *data,
+static int wpa_config_parse_go_p2p_dev_addr(const struct parse_data *data,
                                            struct wpa_ssid *ssid, int line,
                                            const char *value)
 {
-       const char *pos;
-       u8 *buf, *n, addr[ETH_ALEN];
-       size_t count;
-
-       buf = NULL;
-       count = 0;
-
-       pos = value;
-       while (pos && *pos) {
-               while (*pos == ' ')
-                       pos++;
-
-               if (hwaddr_aton(pos, addr)) {
-                       if (count == 0) {
-                               wpa_printf(MSG_ERROR, "Line %d: Invalid "
-                                          "p2p_client_list address '%s'.",
-                                          line, value);
-                               os_free(buf);
-                               return -1;
-                       }
-                       /* continue anyway since this could have been from a
-                        * truncated configuration file line */
-                       wpa_printf(MSG_INFO, "Line %d: Ignore likely "
-                                  "truncated p2p_client_list address '%s'",
-                                  line, pos);
-               } else {
-                       n = os_realloc_array(buf, count + 1, ETH_ALEN);
-                       if (n == NULL) {
-                               os_free(buf);
-                               return -1;
-                       }
-                       buf = n;
-                       os_memmove(buf + ETH_ALEN, buf, count * ETH_ALEN);
-                       os_memcpy(buf, addr, ETH_ALEN);
-                       count++;
-                       wpa_hexdump(MSG_MSGDUMP, "p2p_client_list",
-                                   addr, ETH_ALEN);
-               }
-
-               pos = os_strchr(pos, ' ');
+       if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 ||
+           os_strcmp(value, "any") == 0) {
+               os_memset(ssid->go_p2p_dev_addr, 0, ETH_ALEN);
+               wpa_printf(MSG_MSGDUMP, "GO P2P Device Address any");
+               return 0;
        }
-
-       os_free(ssid->p2p_client_list);
-       ssid->p2p_client_list = buf;
-       ssid->num_p2p_clients = count;
-
+       if (hwaddr_aton(value, ssid->go_p2p_dev_addr)) {
+               wpa_printf(MSG_ERROR, "Line %d: Invalid GO P2P Device Address '%s'.",
+                          line, value);
+               return -1;
+       }
+       ssid->bssid_set = 1;
+       wpa_printf(MSG_MSGDUMP, "GO P2P Device Address " MACSTR,
+                  MAC2STR(ssid->go_p2p_dev_addr));
        return 0;
 }
 
 
 #ifndef NO_CONFIG_WRITE
-static char * wpa_config_write_p2p_client_list(const struct parse_data *data,
+static char * wpa_config_write_go_p2p_dev_addr(const struct parse_data *data,
                                               struct wpa_ssid *ssid)
 {
-       char *value, *end, *pos;
+       char *value;
        int res;
-       size_t i;
 
-       if (ssid->p2p_client_list == NULL || ssid->num_p2p_clients == 0)
+       if (is_zero_ether_addr(ssid->go_p2p_dev_addr))
                return NULL;
 
-       value = os_malloc(20 * ssid->num_p2p_clients);
+       value = os_malloc(20);
        if (value == NULL)
                return NULL;
-       pos = value;
-       end = value + 20 * ssid->num_p2p_clients;
-
-       for (i = ssid->num_p2p_clients; i > 0; i--) {
-               res = os_snprintf(pos, end - pos, MACSTR " ",
-                                 MAC2STR(ssid->p2p_client_list +
-                                         (i - 1) * ETH_ALEN));
-               if (res < 0 || res >= end - pos) {
-                       os_free(value);
-                       return NULL;
-               }
-               pos += res;
+       res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->go_p2p_dev_addr));
+       if (os_snprintf_error(20, res)) {
+               os_free(value);
+               return NULL;
        }
-
-       if (pos > value)
-               pos[-1] = '\0';
-
+       value[20 - 1] = '\0';
        return value;
 }
 #endif /* NO_CONFIG_WRITE */
 
-#endif /* CONFIG_P2P */
 
-/* Helper macros for network block parser */
+static int wpa_config_parse_p2p_client_list(const struct parse_data *data,
+                                           struct wpa_ssid *ssid, int line,
+                                           const char *value)
+{
+       return wpa_config_parse_addr_list(data, line, value,
+                                         &ssid->p2p_client_list,
+                                         &ssid->num_p2p_clients,
+                                         "p2p_client_list", 0, 0);
+}
 
-#ifdef OFFSET
-#undef OFFSET
-#endif /* OFFSET */
-/* OFFSET: Get offset of a variable within the wpa_ssid structure */
-#define OFFSET(v) ((void *) &((struct wpa_ssid *) 0)->v)
 
-/* STR: Define a string variable for an ASCII string; f = field name */
-#ifdef NO_CONFIG_WRITE
-#define _STR(f) #f, wpa_config_parse_str, OFFSET(f)
-#define _STRe(f) #f, wpa_config_parse_str, OFFSET(eap.f)
-#else /* NO_CONFIG_WRITE */
-#define _STR(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(f)
-#define _STRe(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(eap.f)
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_p2p_client_list(const struct parse_data *data,
+                                              struct wpa_ssid *ssid)
+{
+       return wpa_config_write_addr_list(data, ssid->p2p_client_list,
+                                         ssid->num_p2p_clients,
+                                         "p2p_client_list");
+}
+#endif /* NO_CONFIG_WRITE */
+
+
+static int wpa_config_parse_psk_list(const struct parse_data *data,
+                                    struct wpa_ssid *ssid, int line,
+                                    const char *value)
+{
+       struct psk_list_entry *p;
+       const char *pos;
+
+       p = os_zalloc(sizeof(*p));
+       if (p == NULL)
+               return -1;
+
+       pos = value;
+       if (os_strncmp(pos, "P2P-", 4) == 0) {
+               p->p2p = 1;
+               pos += 4;
+       }
+
+       if (hwaddr_aton(pos, p->addr)) {
+               wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list address '%s'",
+                          line, pos);
+               os_free(p);
+               return -1;
+       }
+       pos += 17;
+       if (*pos != '-') {
+               wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list '%s'",
+                          line, pos);
+               os_free(p);
+               return -1;
+       }
+       pos++;
+
+       if (hexstr2bin(pos, p->psk, PMK_LEN) || pos[PMK_LEN * 2] != '\0') {
+               wpa_printf(MSG_ERROR, "Line %d: Invalid psk_list PSK '%s'",
+                          line, pos);
+               os_free(p);
+               return -1;
+       }
+
+       dl_list_add(&ssid->psk_list, &p->list);
+
+       return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_psk_list(const struct parse_data *data,
+                                       struct wpa_ssid *ssid)
+{
+       return NULL;
+}
+#endif /* NO_CONFIG_WRITE */
+
+#endif /* CONFIG_P2P */
+
+
+#ifdef CONFIG_MESH
+
+static int wpa_config_parse_mesh_basic_rates(const struct parse_data *data,
+                                            struct wpa_ssid *ssid, int line,
+                                            const char *value)
+{
+       int *rates = wpa_config_parse_int_array(value);
+
+       if (rates == NULL) {
+               wpa_printf(MSG_ERROR, "Line %d: Invalid mesh_basic_rates '%s'",
+                          line, value);
+               return -1;
+       }
+       if (rates[0] == 0) {
+               os_free(rates);
+               rates = NULL;
+       }
+
+       os_free(ssid->mesh_basic_rates);
+       ssid->mesh_basic_rates = rates;
+
+       return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+
+static char * wpa_config_write_mesh_basic_rates(const struct parse_data *data,
+                                               struct wpa_ssid *ssid)
+{
+       return wpa_config_write_freqs(data, ssid->mesh_basic_rates);
+}
+
+#endif /* NO_CONFIG_WRITE */
+
+#endif /* CONFIG_MESH */
+
+
+/* Helper macros for network block parser */
+
+#ifdef OFFSET
+#undef OFFSET
+#endif /* OFFSET */
+/* OFFSET: Get offset of a variable within the wpa_ssid structure */
+#define OFFSET(v) ((void *) &((struct wpa_ssid *) 0)->v)
+
+/* STR: Define a string variable for an ASCII string; f = field name */
+#ifdef NO_CONFIG_WRITE
+#define _STR(f) #f, wpa_config_parse_str, OFFSET(f)
+#define _STRe(f) #f, wpa_config_parse_str, OFFSET(eap.f)
+#else /* NO_CONFIG_WRITE */
+#define _STR(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(f)
+#define _STRe(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(eap.f)
 #endif /* NO_CONFIG_WRITE */
 #define STR(f) _STR(f), NULL, NULL, NULL, 0
 #define STRe(f) _STRe(f), NULL, NULL, NULL, 0
@@ -1465,6 +1813,8 @@ static const struct parse_data ssid_fields[] = {
        { STR_RANGE(ssid, 0, MAX_SSID_LEN) },
        { INT_RANGE(scan_ssid, 0, 1) },
        { FUNC(bssid) },
+       { FUNC(bssid_blacklist) },
+       { FUNC(bssid_whitelist) },
        { FUNC_KEY(psk) },
        { FUNC(proto) },
        { FUNC(key_mgmt) },
@@ -1487,6 +1837,8 @@ static const struct parse_data ssid_fields[] = {
        { STRe(dh_file) },
        { STRe(subject_match) },
        { STRe(altsubject_match) },
+       { STRe(domain_suffix_match) },
+       { STRe(domain_match) },
        { STRe(ca_cert2) },
        { STRe(ca_path2) },
        { STRe(client_cert2) },
@@ -1495,6 +1847,8 @@ static const struct parse_data ssid_fields[] = {
        { STRe(dh_file2) },
        { STRe(subject_match2) },
        { STRe(altsubject_match2) },
+       { STRe(domain_suffix_match2) },
+       { STRe(domain_match2) },
        { STRe(phase1) },
        { STRe(phase2) },
        { STRe(pcsc) },
@@ -1511,6 +1865,9 @@ static const struct parse_data ssid_fields[] = {
        { INTe(engine) },
        { INTe(engine2) },
        { INT(eapol_flags) },
+       { INTe(sim_num) },
+       { STRe(openssl_ciphers) },
+       { INTe(erp) },
 #endif /* IEEE8021X_EAPOL */
        { FUNC_KEY(wep_key0) },
        { FUNC_KEY(wep_key1) },
@@ -1524,7 +1881,12 @@ static const struct parse_data ssid_fields[] = {
        { INTe(fragment_size) },
        { INTe(ocsp) },
 #endif /* IEEE8021X_EAPOL */
+#ifdef CONFIG_MESH
+       { INT_RANGE(mode, 0, 5) },
+       { INT_RANGE(no_auto_peer, 0, 1) },
+#else /* CONFIG_MESH */
        { INT_RANGE(mode, 0, 4) },
+#endif /* CONFIG_MESH */
        { INT_RANGE(proactive_key_caching, 0, 1) },
        { INT_RANGE(disabled, 0, 2) },
        { STR(id_str) },
@@ -1534,16 +1896,28 @@ static const struct parse_data ssid_fields[] = {
        { INT_RANGE(peerkey, 0, 1) },
        { INT_RANGE(mixed_cell, 0, 1) },
        { INT_RANGE(frequency, 0, 65000) },
+       { INT_RANGE(fixed_freq, 0, 1) },
+#ifdef CONFIG_MESH
+       { FUNC(mesh_basic_rates) },
+       { INT(dot11MeshMaxRetries) },
+       { INT(dot11MeshRetryTimeout) },
+       { INT(dot11MeshConfirmTimeout) },
+       { INT(dot11MeshHoldingTimeout) },
+#endif /* CONFIG_MESH */
        { INT(wpa_ptk_rekey) },
        { STR(bgscan) },
        { INT_RANGE(ignore_broadcast_ssid, 0, 2) },
 #ifdef CONFIG_P2P
+       { FUNC(go_p2p_dev_addr) },
        { FUNC(p2p_client_list) },
+       { FUNC(psk_list) },
 #endif /* CONFIG_P2P */
 #ifdef CONFIG_HT_OVERRIDES
        { INT_RANGE(disable_ht, 0, 1) },
        { INT_RANGE(disable_ht40, -1, 1) },
        { INT_RANGE(disable_sgi, 0, 1) },
+       { INT_RANGE(disable_ldpc, 0, 1) },
+       { INT_RANGE(ht40_intolerant, 0, 1) },
        { INT_RANGE(disable_max_amsdu, -1, 1) },
        { INT_RANGE(ampdu_factor, -1, 3) },
        { INT_RANGE(ampdu_density, -1, 7) },
@@ -1573,6 +1947,13 @@ static const struct parse_data ssid_fields[] = {
        { INT(ap_max_inactivity) },
        { INT(dtim_period) },
        { INT(beacon_int) },
+#ifdef CONFIG_MACSEC
+       { INT_RANGE(macsec_policy, 0, 1) },
+#endif /* CONFIG_MACSEC */
+#ifdef CONFIG_HS20
+       { INT(update_identifier) },
+#endif /* CONFIG_HS20 */
+       { INT_RANGE(mac_addr, 0, 2) },
 };
 
 #undef OFFSET
@@ -1591,7 +1972,7 @@ static const struct parse_data ssid_fields[] = {
 #undef _FUNC
 #undef FUNC
 #undef FUNC_KEY
-#define NUM_SSID_FIELDS (sizeof(ssid_fields) / sizeof(ssid_fields[0]))
+#define NUM_SSID_FIELDS ARRAY_SIZE(ssid_fields)
 
 
 /**
@@ -1682,29 +2063,33 @@ int wpa_config_update_prio_list(struct wpa_config *config)
 static void eap_peer_config_free(struct eap_peer_config *eap)
 {
        os_free(eap->eap_methods);
-       os_free(eap->identity);
+       bin_clear_free(eap->identity, eap->identity_len);
        os_free(eap->anonymous_identity);
-       os_free(eap->password);
+       bin_clear_free(eap->password, eap->password_len);
        os_free(eap->ca_cert);
        os_free(eap->ca_path);
        os_free(eap->client_cert);
        os_free(eap->private_key);
-       os_free(eap->private_key_passwd);
+       str_clear_free(eap->private_key_passwd);
        os_free(eap->dh_file);
        os_free(eap->subject_match);
        os_free(eap->altsubject_match);
+       os_free(eap->domain_suffix_match);
+       os_free(eap->domain_match);
        os_free(eap->ca_cert2);
        os_free(eap->ca_path2);
        os_free(eap->client_cert2);
        os_free(eap->private_key2);
-       os_free(eap->private_key2_passwd);
+       str_clear_free(eap->private_key2_passwd);
        os_free(eap->dh_file2);
        os_free(eap->subject_match2);
        os_free(eap->altsubject_match2);
+       os_free(eap->domain_suffix_match2);
+       os_free(eap->domain_match2);
        os_free(eap->phase1);
        os_free(eap->phase2);
        os_free(eap->pcsc);
-       os_free(eap->pin);
+       str_clear_free(eap->pin);
        os_free(eap->engine_id);
        os_free(eap->key_id);
        os_free(eap->cert_id);
@@ -1712,12 +2097,14 @@ static void eap_peer_config_free(struct eap_peer_config *eap)
        os_free(eap->key2_id);
        os_free(eap->cert2_id);
        os_free(eap->ca_cert2_id);
-       os_free(eap->pin2);
+       str_clear_free(eap->pin2);
        os_free(eap->engine2_id);
        os_free(eap->otp);
        os_free(eap->pending_req_otp);
        os_free(eap->pac_file);
-       os_free(eap->new_password);
+       bin_clear_free(eap->new_password, eap->new_password_len);
+       str_clear_free(eap->external_sim_resp);
+       os_free(eap->openssl_ciphers);
 }
 #endif /* IEEE8021X_EAPOL */
 
@@ -1731,8 +2118,10 @@ static void eap_peer_config_free(struct eap_peer_config *eap)
  */
 void wpa_config_free_ssid(struct wpa_ssid *ssid)
 {
+       struct psk_list_entry *psk;
+
        os_free(ssid->ssid);
-       os_free(ssid->passphrase);
+       str_clear_free(ssid->passphrase);
        os_free(ssid->ext_psk);
 #ifdef IEEE8021X_EAPOL
        eap_peer_config_free(&ssid->eap);
@@ -1742,33 +2131,70 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid)
        os_free(ssid->freq_list);
        os_free(ssid->bgscan);
        os_free(ssid->p2p_client_list);
+       os_free(ssid->bssid_blacklist);
+       os_free(ssid->bssid_whitelist);
 #ifdef CONFIG_HT_OVERRIDES
        os_free(ssid->ht_mcs);
 #endif /* CONFIG_HT_OVERRIDES */
-       os_free(ssid);
+#ifdef CONFIG_MESH
+       os_free(ssid->mesh_basic_rates);
+#endif /* CONFIG_MESH */
+       while ((psk = dl_list_first(&ssid->psk_list, struct psk_list_entry,
+                                   list))) {
+               dl_list_del(&psk->list);
+               bin_clear_free(psk, sizeof(*psk));
+       }
+       bin_clear_free(ssid, sizeof(*ssid));
 }
 
 
 void wpa_config_free_cred(struct wpa_cred *cred)
 {
+       size_t i;
+
        os_free(cred->realm);
-       os_free(cred->username);
-       os_free(cred->password);
+       str_clear_free(cred->username);
+       str_clear_free(cred->password);
        os_free(cred->ca_cert);
        os_free(cred->client_cert);
        os_free(cred->private_key);
-       os_free(cred->private_key_passwd);
+       str_clear_free(cred->private_key_passwd);
        os_free(cred->imsi);
-       os_free(cred->milenage);
+       str_clear_free(cred->milenage);
+       for (i = 0; i < cred->num_domain; i++)
+               os_free(cred->domain[i]);
        os_free(cred->domain);
+       os_free(cred->domain_suffix_match);
        os_free(cred->eap_method);
        os_free(cred->phase1);
        os_free(cred->phase2);
        os_free(cred->excluded_ssid);
+       os_free(cred->roaming_partner);
+       os_free(cred->provisioning_sp);
+       for (i = 0; i < cred->num_req_conn_capab; i++)
+               os_free(cred->req_conn_capab_port[i]);
+       os_free(cred->req_conn_capab_port);
+       os_free(cred->req_conn_capab_proto);
        os_free(cred);
 }
 
 
+void wpa_config_flush_blobs(struct wpa_config *config)
+{
+#ifndef CONFIG_NO_CONFIG_BLOBS
+       struct wpa_config_blob *blob, *prev;
+
+       blob = config->blobs;
+       config->blobs = NULL;
+       while (blob) {
+               prev = blob;
+               blob = blob->next;
+               wpa_config_free_blob(prev);
+       }
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+}
+
+
 /**
  * wpa_config_free - Free configuration data
  * @config: Configuration data from wpa_config_read()
@@ -1778,11 +2204,9 @@ void wpa_config_free_cred(struct wpa_cred *cred)
  */
 void wpa_config_free(struct wpa_config *config)
 {
-#ifndef CONFIG_NO_CONFIG_BLOBS
-       struct wpa_config_blob *blob, *prevblob;
-#endif /* CONFIG_NO_CONFIG_BLOBS */
        struct wpa_ssid *ssid, *prev = NULL;
        struct wpa_cred *cred, *cprev;
+       int i;
 
        ssid = config->ssid;
        while (ssid) {
@@ -1798,24 +2222,19 @@ void wpa_config_free(struct wpa_config *config)
                wpa_config_free_cred(cprev);
        }
 
-#ifndef CONFIG_NO_CONFIG_BLOBS
-       blob = config->blobs;
-       prevblob = NULL;
-       while (blob) {
-               prevblob = blob;
-               blob = blob->next;
-               wpa_config_free_blob(prevblob);
-       }
-#endif /* CONFIG_NO_CONFIG_BLOBS */
+       wpa_config_flush_blobs(config);
 
        wpabuf_free(config->wps_vendor_ext_m1);
+       for (i = 0; i < MAX_WPS_VENDOR_EXT; i++)
+               wpabuf_free(config->wps_vendor_ext[i]);
        os_free(config->ctrl_interface);
        os_free(config->ctrl_interface_group);
        os_free(config->opensc_engine_path);
        os_free(config->pkcs11_engine_path);
        os_free(config->pkcs11_module_path);
+       os_free(config->openssl_ciphers);
        os_free(config->pcsc_reader);
-       os_free(config->pcsc_pin);
+       str_clear_free(config->pcsc_pin);
        os_free(config->driver_param);
        os_free(config->device_name);
        os_free(config->manufacturer);
@@ -1826,6 +2245,7 @@ void wpa_config_free(struct wpa_config *config)
        os_free(config->p2p_ssid_postfix);
        os_free(config->pssid);
        os_free(config->p2p_pref_chan);
+       os_free(config->p2p_no_go_freq.range);
        os_free(config->autoscan);
        os_free(config->freq_list);
        wpabuf_free(config->wps_nfc_dh_pubkey);
@@ -1834,6 +2254,9 @@ void wpa_config_free(struct wpa_config *config)
        os_free(config->ext_password_backend);
        os_free(config->sae_groups);
        wpabuf_free(config->ap_vendor_elements);
+       os_free(config->osu_dir);
+       os_free(config->bgscan);
+       os_free(config->wowlan_triggers);
        os_free(config);
 }
 
@@ -1908,6 +2331,7 @@ struct wpa_ssid * wpa_config_add_network(struct wpa_config *config)
        if (ssid == NULL)
                return NULL;
        ssid->id = id;
+       dl_list_init(&ssid->psk_list);
        if (last)
                last->next = ssid;
        else
@@ -1966,11 +2390,19 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
        ssid->eapol_flags = DEFAULT_EAPOL_FLAGS;
        ssid->eap_workaround = DEFAULT_EAP_WORKAROUND;
        ssid->eap.fragment_size = DEFAULT_FRAGMENT_SIZE;
+       ssid->eap.sim_num = DEFAULT_USER_SELECTED_SIM;
 #endif /* IEEE8021X_EAPOL */
+#ifdef CONFIG_MESH
+       ssid->dot11MeshMaxRetries = DEFAULT_MESH_MAX_RETRIES;
+       ssid->dot11MeshRetryTimeout = DEFAULT_MESH_RETRY_TIMEOUT;
+       ssid->dot11MeshConfirmTimeout = DEFAULT_MESH_CONFIRM_TIMEOUT;
+       ssid->dot11MeshHoldingTimeout = DEFAULT_MESH_HOLDING_TIMEOUT;
+#endif /* CONFIG_MESH */
 #ifdef CONFIG_HT_OVERRIDES
        ssid->disable_ht = DEFAULT_DISABLE_HT;
        ssid->disable_ht40 = DEFAULT_DISABLE_HT40;
        ssid->disable_sgi = DEFAULT_DISABLE_SGI;
+       ssid->disable_ldpc = DEFAULT_DISABLE_LDPC;
        ssid->disable_max_amsdu = DEFAULT_DISABLE_MAX_AMSDU;
        ssid->ampdu_factor = DEFAULT_AMPDU_FACTOR;
        ssid->ampdu_density = DEFAULT_AMPDU_DENSITY;
@@ -1997,6 +2429,7 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
 #ifdef CONFIG_IEEE80211W
        ssid->ieee80211w = MGMT_FRAME_PROTECTION_DEFAULT;
 #endif /* CONFIG_IEEE80211W */
+       ssid->mac_addr = -1;
 }
 
 
@@ -2193,7 +2626,7 @@ char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var)
                                        wpa_printf(MSG_DEBUG, "Do not allow "
                                                   "key_data field to be "
                                                   "exposed");
-                                       os_free(res);
+                                       str_clear_free(res);
                                        return os_strdup("*");
                                }
 
@@ -2228,17 +2661,93 @@ void wpa_config_update_psk(struct wpa_ssid *ssid)
 }
 
 
+static int wpa_config_set_cred_req_conn_capab(struct wpa_cred *cred,
+                                             const char *value)
+{
+       u8 *proto;
+       int **port;
+       int *ports, *nports;
+       const char *pos;
+       unsigned int num_ports;
+
+       proto = os_realloc_array(cred->req_conn_capab_proto,
+                                cred->num_req_conn_capab + 1, sizeof(u8));
+       if (proto == NULL)
+               return -1;
+       cred->req_conn_capab_proto = proto;
+
+       port = os_realloc_array(cred->req_conn_capab_port,
+                               cred->num_req_conn_capab + 1, sizeof(int *));
+       if (port == NULL)
+               return -1;
+       cred->req_conn_capab_port = port;
+
+       proto[cred->num_req_conn_capab] = atoi(value);
+
+       pos = os_strchr(value, ':');
+       if (pos == NULL) {
+               port[cred->num_req_conn_capab] = NULL;
+               cred->num_req_conn_capab++;
+               return 0;
+       }
+       pos++;
+
+       ports = NULL;
+       num_ports = 0;
+
+       while (*pos) {
+               nports = os_realloc_array(ports, num_ports + 1, sizeof(int));
+               if (nports == NULL) {
+                       os_free(ports);
+                       return -1;
+               }
+               ports = nports;
+               ports[num_ports++] = atoi(pos);
+
+               pos = os_strchr(pos, ',');
+               if (pos == NULL)
+                       break;
+               pos++;
+       }
+
+       nports = os_realloc_array(ports, num_ports + 1, sizeof(int));
+       if (nports == NULL) {
+               os_free(ports);
+               return -1;
+       }
+       ports = nports;
+       ports[num_ports] = -1;
+
+       port[cred->num_req_conn_capab] = ports;
+       cred->num_req_conn_capab++;
+       return 0;
+}
+
+
 int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
                        const char *value, int line)
 {
        char *val;
        size_t len;
 
+       if (os_strcmp(var, "temporary") == 0) {
+               cred->temporary = atoi(value);
+               return 0;
+       }
+
        if (os_strcmp(var, "priority") == 0) {
                cred->priority = atoi(value);
                return 0;
        }
 
+       if (os_strcmp(var, "sp_priority") == 0) {
+               int prio = atoi(value);
+               if (prio < 0 || prio > 255)
+                       return -1;
+               cred->sp_priority = prio;
+               return 0;
+       }
+
        if (os_strcmp(var, "pcsc") == 0) {
                cred->pcsc = atoi(value);
                return 0;
@@ -2263,12 +2772,55 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
 
        if (os_strcmp(var, "password") == 0 &&
            os_strncmp(value, "ext:", 4) == 0) {
-               os_free(cred->password);
+               str_clear_free(cred->password);
                cred->password = os_strdup(value);
                cred->ext_password = 1;
                return 0;
        }
 
+       if (os_strcmp(var, "update_identifier") == 0) {
+               cred->update_identifier = atoi(value);
+               return 0;
+       }
+
+       if (os_strcmp(var, "min_dl_bandwidth_home") == 0) {
+               cred->min_dl_bandwidth_home = atoi(value);
+               return 0;
+       }
+
+       if (os_strcmp(var, "min_ul_bandwidth_home") == 0) {
+               cred->min_ul_bandwidth_home = atoi(value);
+               return 0;
+       }
+
+       if (os_strcmp(var, "min_dl_bandwidth_roaming") == 0) {
+               cred->min_dl_bandwidth_roaming = atoi(value);
+               return 0;
+       }
+
+       if (os_strcmp(var, "min_ul_bandwidth_roaming") == 0) {
+               cred->min_ul_bandwidth_roaming = atoi(value);
+               return 0;
+       }
+
+       if (os_strcmp(var, "max_bss_load") == 0) {
+               cred->max_bss_load = atoi(value);
+               return 0;
+       }
+
+       if (os_strcmp(var, "req_conn_capab") == 0)
+               return wpa_config_set_cred_req_conn_capab(cred, value);
+
+       if (os_strcmp(var, "ocsp") == 0) {
+               cred->ocsp = atoi(value);
+               return 0;
+       }
+
+       if (os_strcmp(var, "sim_num") == 0) {
+               cred->sim_num = atoi(value);
+               return 0;
+       }
+
        val = wpa_config_parse_string(value, &len);
        if (val == NULL) {
                wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string "
@@ -2283,13 +2835,13 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
        }
 
        if (os_strcmp(var, "username") == 0) {
-               os_free(cred->username);
+               str_clear_free(cred->username);
                cred->username = val;
                return 0;
        }
 
        if (os_strcmp(var, "password") == 0) {
-               os_free(cred->password);
+               str_clear_free(cred->password);
                cred->password = val;
                cred->ext_password = 0;
                return 0;
@@ -2314,7 +2866,7 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
        }
 
        if (os_strcmp(var, "private_key_passwd") == 0) {
-               os_free(cred->private_key_passwd);
+               str_clear_free(cred->private_key_passwd);
                cred->private_key_passwd = val;
                return 0;
        }
@@ -2326,14 +2878,28 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
        }
 
        if (os_strcmp(var, "milenage") == 0) {
-               os_free(cred->milenage);
+               str_clear_free(cred->milenage);
                cred->milenage = val;
                return 0;
        }
 
+       if (os_strcmp(var, "domain_suffix_match") == 0) {
+               os_free(cred->domain_suffix_match);
+               cred->domain_suffix_match = val;
+               return 0;
+       }
+
        if (os_strcmp(var, "domain") == 0) {
-               os_free(cred->domain);
-               cred->domain = val;
+               char **new_domain;
+               new_domain = os_realloc_array(cred->domain,
+                                             cred->num_domain + 1,
+                                             sizeof(char *));
+               if (new_domain == NULL) {
+                       os_free(val);
+                       return -1;
+               }
+               new_domain[cred->num_domain++] = val;
+               cred->domain = new_domain;
                return 0;
        }
 
@@ -2363,6 +2929,21 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
                return 0;
        }
 
+       if (os_strcmp(var, "required_roaming_consortium") == 0) {
+               if (len < 3 || len > sizeof(cred->required_roaming_consortium))
+               {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid "
+                                  "required_roaming_consortium length %d "
+                                  "(3..15 expected)", line, (int) len);
+                       os_free(val);
+                       return -1;
+               }
+               os_memcpy(cred->required_roaming_consortium, val, len);
+               cred->required_roaming_consortium_len = len;
+               os_free(val);
+               return 0;
+       }
+
        if (os_strcmp(var, "excluded_ssid") == 0) {
                struct excluded_ssid *e;
 
@@ -2391,6 +2972,69 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
                return 0;
        }
 
+       if (os_strcmp(var, "roaming_partner") == 0) {
+               struct roaming_partner *p;
+               char *pos;
+
+               p = os_realloc_array(cred->roaming_partner,
+                                    cred->num_roaming_partner + 1,
+                                    sizeof(struct roaming_partner));
+               if (p == NULL) {
+                       os_free(val);
+                       return -1;
+               }
+               cred->roaming_partner = p;
+
+               p = &cred->roaming_partner[cred->num_roaming_partner];
+
+               pos = os_strchr(val, ',');
+               if (pos == NULL) {
+                       os_free(val);
+                       return -1;
+               }
+               *pos++ = '\0';
+               if (pos - val - 1 >= (int) sizeof(p->fqdn)) {
+                       os_free(val);
+                       return -1;
+               }
+               os_memcpy(p->fqdn, val, pos - val);
+
+               p->exact_match = atoi(pos);
+
+               pos = os_strchr(pos, ',');
+               if (pos == NULL) {
+                       os_free(val);
+                       return -1;
+               }
+               *pos++ = '\0';
+
+               p->priority = atoi(pos);
+
+               pos = os_strchr(pos, ',');
+               if (pos == NULL) {
+                       os_free(val);
+                       return -1;
+               }
+               *pos++ = '\0';
+
+               if (os_strlen(pos) >= sizeof(p->country)) {
+                       os_free(val);
+                       return -1;
+               }
+               os_memcpy(p->country, pos, os_strlen(pos) + 1);
+
+               cred->num_roaming_partner++;
+               os_free(val);
+
+               return 0;
+       }
+
+       if (os_strcmp(var, "provisioning_sp") == 0) {
+               os_free(cred->provisioning_sp);
+               cred->provisioning_sp = val;
+               return 0;
+       }
+
        if (line) {
                wpa_printf(MSG_ERROR, "Line %d: unknown cred field '%s'.",
                           line, var);
@@ -2402,6 +3046,281 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
 }
 
 
+static char * alloc_int_str(int val)
+{
+       const unsigned int bufsize = 20;
+       char *buf;
+       int res;
+
+       buf = os_malloc(bufsize);
+       if (buf == NULL)
+               return NULL;
+       res = os_snprintf(buf, bufsize, "%d", val);
+       if (os_snprintf_error(bufsize, res)) {
+               os_free(buf);
+               buf = NULL;
+       }
+       return buf;
+}
+
+
+static char * alloc_strdup(const char *str)
+{
+       if (str == NULL)
+               return NULL;
+       return os_strdup(str);
+}
+
+
+char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var)
+{
+       if (os_strcmp(var, "temporary") == 0)
+               return alloc_int_str(cred->temporary);
+
+       if (os_strcmp(var, "priority") == 0)
+               return alloc_int_str(cred->priority);
+
+       if (os_strcmp(var, "sp_priority") == 0)
+               return alloc_int_str(cred->sp_priority);
+
+       if (os_strcmp(var, "pcsc") == 0)
+               return alloc_int_str(cred->pcsc);
+
+       if (os_strcmp(var, "eap") == 0) {
+               if (!cred->eap_method)
+                       return NULL;
+               return alloc_strdup(eap_get_name(cred->eap_method[0].vendor,
+                                                cred->eap_method[0].method));
+       }
+
+       if (os_strcmp(var, "update_identifier") == 0)
+               return alloc_int_str(cred->update_identifier);
+
+       if (os_strcmp(var, "min_dl_bandwidth_home") == 0)
+               return alloc_int_str(cred->min_dl_bandwidth_home);
+
+       if (os_strcmp(var, "min_ul_bandwidth_home") == 0)
+               return alloc_int_str(cred->min_ul_bandwidth_home);
+
+       if (os_strcmp(var, "min_dl_bandwidth_roaming") == 0)
+               return alloc_int_str(cred->min_dl_bandwidth_roaming);
+
+       if (os_strcmp(var, "min_ul_bandwidth_roaming") == 0)
+               return alloc_int_str(cred->min_ul_bandwidth_roaming);
+
+       if (os_strcmp(var, "max_bss_load") == 0)
+               return alloc_int_str(cred->max_bss_load);
+
+       if (os_strcmp(var, "req_conn_capab") == 0) {
+               unsigned int i;
+               char *buf, *end, *pos;
+               int ret;
+
+               if (!cred->num_req_conn_capab)
+                       return NULL;
+
+               buf = os_malloc(4000);
+               if (buf == NULL)
+                       return NULL;
+               pos = buf;
+               end = pos + 4000;
+               for (i = 0; i < cred->num_req_conn_capab; i++) {
+                       int *ports;
+
+                       ret = os_snprintf(pos, end - pos, "%s%u",
+                                         i > 0 ? "\n" : "",
+                                         cred->req_conn_capab_proto[i]);
+                       if (os_snprintf_error(end - pos, ret))
+                               return buf;
+                       pos += ret;
+
+                       ports = cred->req_conn_capab_port[i];
+                       if (ports) {
+                               int j;
+                               for (j = 0; ports[j] != -1; j++) {
+                                       ret = os_snprintf(pos, end - pos,
+                                                         "%s%d",
+                                                         j > 0 ? "," : ":",
+                                                         ports[j]);
+                                       if (os_snprintf_error(end - pos, ret))
+                                               return buf;
+                                       pos += ret;
+                               }
+                       }
+               }
+
+               return buf;
+       }
+
+       if (os_strcmp(var, "ocsp") == 0)
+               return alloc_int_str(cred->ocsp);
+
+       if (os_strcmp(var, "realm") == 0)
+               return alloc_strdup(cred->realm);
+
+       if (os_strcmp(var, "username") == 0)
+               return alloc_strdup(cred->username);
+
+       if (os_strcmp(var, "password") == 0) {
+               if (!cred->password)
+                       return NULL;
+               return alloc_strdup("*");
+       }
+
+       if (os_strcmp(var, "ca_cert") == 0)
+               return alloc_strdup(cred->ca_cert);
+
+       if (os_strcmp(var, "client_cert") == 0)
+               return alloc_strdup(cred->client_cert);
+
+       if (os_strcmp(var, "private_key") == 0)
+               return alloc_strdup(cred->private_key);
+
+       if (os_strcmp(var, "private_key_passwd") == 0) {
+               if (!cred->private_key_passwd)
+                       return NULL;
+               return alloc_strdup("*");
+       }
+
+       if (os_strcmp(var, "imsi") == 0)
+               return alloc_strdup(cred->imsi);
+
+       if (os_strcmp(var, "milenage") == 0) {
+               if (!(cred->milenage))
+                       return NULL;
+               return alloc_strdup("*");
+       }
+
+       if (os_strcmp(var, "domain_suffix_match") == 0)
+               return alloc_strdup(cred->domain_suffix_match);
+
+       if (os_strcmp(var, "domain") == 0) {
+               unsigned int i;
+               char *buf, *end, *pos;
+               int ret;
+
+               if (!cred->num_domain)
+                       return NULL;
+
+               buf = os_malloc(4000);
+               if (buf == NULL)
+                       return NULL;
+               pos = buf;
+               end = pos + 4000;
+
+               for (i = 0; i < cred->num_domain; i++) {
+                       ret = os_snprintf(pos, end - pos, "%s%s",
+                                         i > 0 ? "\n" : "", cred->domain[i]);
+                       if (os_snprintf_error(end - pos, ret))
+                               return buf;
+                       pos += ret;
+               }
+
+               return buf;
+       }
+
+       if (os_strcmp(var, "phase1") == 0)
+               return alloc_strdup(cred->phase1);
+
+       if (os_strcmp(var, "phase2") == 0)
+               return alloc_strdup(cred->phase2);
+
+       if (os_strcmp(var, "roaming_consortium") == 0) {
+               size_t buflen;
+               char *buf;
+
+               if (!cred->roaming_consortium_len)
+                       return NULL;
+               buflen = cred->roaming_consortium_len * 2 + 1;
+               buf = os_malloc(buflen);
+               if (buf == NULL)
+                       return NULL;
+               wpa_snprintf_hex(buf, buflen, cred->roaming_consortium,
+                                cred->roaming_consortium_len);
+               return buf;
+       }
+
+       if (os_strcmp(var, "required_roaming_consortium") == 0) {
+               size_t buflen;
+               char *buf;
+
+               if (!cred->required_roaming_consortium_len)
+                       return NULL;
+               buflen = cred->required_roaming_consortium_len * 2 + 1;
+               buf = os_malloc(buflen);
+               if (buf == NULL)
+                       return NULL;
+               wpa_snprintf_hex(buf, buflen, cred->required_roaming_consortium,
+                                cred->required_roaming_consortium_len);
+               return buf;
+       }
+
+       if (os_strcmp(var, "excluded_ssid") == 0) {
+               unsigned int i;
+               char *buf, *end, *pos;
+
+               if (!cred->num_excluded_ssid)
+                       return NULL;
+
+               buf = os_malloc(4000);
+               if (buf == NULL)
+                       return NULL;
+               pos = buf;
+               end = pos + 4000;
+
+               for (i = 0; i < cred->num_excluded_ssid; i++) {
+                       struct excluded_ssid *e;
+                       int ret;
+
+                       e = &cred->excluded_ssid[i];
+                       ret = os_snprintf(pos, end - pos, "%s%s",
+                                         i > 0 ? "\n" : "",
+                                         wpa_ssid_txt(e->ssid, e->ssid_len));
+                       if (os_snprintf_error(end - pos, ret))
+                               return buf;
+                       pos += ret;
+               }
+
+               return buf;
+       }
+
+       if (os_strcmp(var, "roaming_partner") == 0) {
+               unsigned int i;
+               char *buf, *end, *pos;
+
+               if (!cred->num_roaming_partner)
+                       return NULL;
+
+               buf = os_malloc(4000);
+               if (buf == NULL)
+                       return NULL;
+               pos = buf;
+               end = pos + 4000;
+
+               for (i = 0; i < cred->num_roaming_partner; i++) {
+                       struct roaming_partner *p;
+                       int ret;
+
+                       p = &cred->roaming_partner[i];
+                       ret = os_snprintf(pos, end - pos, "%s%s,%d,%u,%s",
+                                         i > 0 ? "\n" : "",
+                                         p->fqdn, p->exact_match, p->priority,
+                                         p->country);
+                       if (os_snprintf_error(end - pos, ret))
+                               return buf;
+                       pos += ret;
+               }
+
+               return buf;
+       }
+
+       if (os_strcmp(var, "provisioning_sp") == 0)
+               return alloc_strdup(cred->provisioning_sp);
+
+       return NULL;
+}
+
+
 struct wpa_cred * wpa_config_get_cred(struct wpa_config *config, int id)
 {
        struct wpa_cred *cred;
@@ -2436,6 +3355,7 @@ struct wpa_cred * wpa_config_add_cred(struct wpa_config *config)
        if (cred == NULL)
                return NULL;
        cred->id = id;
+       cred->sim_num = DEFAULT_USER_SELECTED_SIM;
        if (last)
                last->next = cred;
        else
@@ -2516,7 +3436,7 @@ void wpa_config_free_blob(struct wpa_config_blob *blob)
 {
        if (blob) {
                os_free(blob->name);
-               os_free(blob->data);
+               bin_clear_free(blob->data, blob->len);
                os_free(blob);
        }
 }
@@ -2576,10 +3496,15 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
                return NULL;
        config->eapol_version = DEFAULT_EAPOL_VERSION;
        config->ap_scan = DEFAULT_AP_SCAN;
+       config->user_mpm = DEFAULT_USER_MPM;
+       config->max_peer_links = DEFAULT_MAX_PEER_LINKS;
+       config->mesh_max_inactivity = DEFAULT_MESH_MAX_INACTIVITY;
        config->fast_reauth = DEFAULT_FAST_REAUTH;
        config->p2p_go_intent = DEFAULT_P2P_GO_INTENT;
        config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS;
        config->p2p_go_max_inactivity = DEFAULT_P2P_GO_MAX_INACTIVITY;
+       config->p2p_optimize_listen_chan = DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN;
+       config->p2p_go_ctwindow = DEFAULT_P2P_GO_CTWINDOW;
        config->bss_max_count = DEFAULT_BSS_MAX_COUNT;
        config->bss_expiration_age = DEFAULT_BSS_EXPIRATION_AGE;
        config->bss_expiration_scan_count = DEFAULT_BSS_EXPIRATION_SCAN_COUNT;
@@ -2590,6 +3515,10 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
        config->wmm_ac_params[1] = ac_bk;
        config->wmm_ac_params[2] = ac_vi;
        config->wmm_ac_params[3] = ac_vo;
+       config->p2p_search_delay = DEFAULT_P2P_SEARCH_DELAY;
+       config->rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME;
+       config->key_mgmt_offload = DEFAULT_KEY_MGMT_OFFLOAD;
+       config->cert_in_cb = DEFAULT_CERT_IN_CB;
 
        if (ctrl_interface)
                config->ctrl_interface = os_strdup(ctrl_interface);
@@ -2629,6 +3558,8 @@ struct global_parse_data {
        char *name;
        int (*parser)(const struct global_parse_data *data,
                      struct wpa_config *config, int line, const char *value);
+       int (*get)(const char *name, struct wpa_config *config, long offset,
+                  char *buf, size_t buflen, int pretty_print);
        void *param1, *param2, *param3;
        unsigned int changed_flag;
 };
@@ -2707,6 +3638,27 @@ static int wpa_global_config_parse_str(const struct global_parse_data *data,
 }
 
 
+static int wpa_config_process_bgscan(const struct global_parse_data *data,
+                                    struct wpa_config *config, int line,
+                                    const char *pos)
+{
+       size_t len;
+       char *tmp;
+       int res;
+
+       tmp = wpa_config_parse_string(pos, &len);
+       if (tmp == NULL) {
+               wpa_printf(MSG_ERROR, "Line %d: failed to parse %s",
+                          line, data->name);
+               return -1;
+       }
+
+       res = wpa_global_config_parse_str(data, config, line, tmp);
+       os_free(tmp);
+       return res;
+}
+
+
 static int wpa_global_config_parse_bin(const struct global_parse_data *data,
                                       struct wpa_config *config, int line,
                                       const char *pos)
@@ -2745,12 +3697,39 @@ static int wpa_config_process_freq_list(const struct global_parse_data *data,
        freqs = wpa_config_parse_int_array(value);
        if (freqs == NULL)
                return -1;
+       if (freqs[0] == 0) {
+               os_free(freqs);
+               freqs = NULL;
+       }
        os_free(config->freq_list);
        config->freq_list = freqs;
        return 0;
 }
 
 
+#ifdef CONFIG_P2P
+static int wpa_global_config_parse_ipv4(const struct global_parse_data *data,
+                                       struct wpa_config *config, int line,
+                                       const char *pos)
+{
+       u32 *dst;
+       struct hostapd_ip_addr addr;
+
+       if (hostapd_parse_ip_addr(pos, &addr) < 0)
+               return -1;
+       if (addr.af != AF_INET)
+               return -1;
+
+       dst = (u32 *) (((u8 *) config) + (long) data->param1);
+       os_memcpy(dst, &addr.u.v4.s_addr, 4);
+       wpa_printf(MSG_DEBUG, "%s = 0x%x", data->name,
+                  WPA_GET_BE32((u8 *) dst));
+
+       return 0;
+}
+#endif /* CONFIG_P2P */
+
+
 static int wpa_config_process_country(const struct global_parse_data *data,
                                      struct wpa_config *config, int line,
                                      const char *pos)
@@ -2935,6 +3914,26 @@ fail:
        wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_pref_chan list", line);
        return -1;
 }
+
+
+static int wpa_config_process_p2p_no_go_freq(
+       const struct global_parse_data *data,
+       struct wpa_config *config, int line, const char *pos)
+{
+       int ret;
+
+       ret = freq_range_list_parse(&config->p2p_no_go_freq, pos);
+       if (ret < 0) {
+               wpa_printf(MSG_ERROR, "Line %d: Invalid p2p_no_go_freq", line);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "P2P: p2p_no_go_freq with %u items",
+                  config->p2p_no_go_freq.num);
+
+       return 0;
+}
+
 #endif /* CONFIG_P2P */
 
 
@@ -3007,36 +4006,96 @@ static int wpa_config_process_ap_vendor_elements(
 }
 
 
+#ifdef CONFIG_CTRL_IFACE
+static int wpa_config_process_no_ctrl_interface(
+       const struct global_parse_data *data,
+       struct wpa_config *config, int line, const char *pos)
+{
+       wpa_printf(MSG_DEBUG, "no_ctrl_interface -> ctrl_interface=NULL");
+       os_free(config->ctrl_interface);
+       config->ctrl_interface = NULL;
+       return 0;
+}
+#endif /* CONFIG_CTRL_IFACE */
+
+
+static int wpa_config_get_int(const char *name, struct wpa_config *config,
+                             long offset, char *buf, size_t buflen,
+                             int pretty_print)
+{
+       int *val = (int *) (((u8 *) config) + (long) offset);
+
+       if (pretty_print)
+               return os_snprintf(buf, buflen, "%s=%d\n", name, *val);
+       return os_snprintf(buf, buflen, "%d", *val);
+}
+
+
+static int wpa_config_get_str(const char *name, struct wpa_config *config,
+                             long offset, char *buf, size_t buflen,
+                             int pretty_print)
+{
+       char **val = (char **) (((u8 *) config) + (long) offset);
+       int res;
+
+       if (pretty_print)
+               res = os_snprintf(buf, buflen, "%s=%s\n", name,
+                                 *val ? *val : "null");
+       else if (!*val)
+               return -1;
+       else
+               res = os_snprintf(buf, buflen, "%s", *val);
+       if (os_snprintf_error(buflen, res))
+               res = -1;
+
+       return res;
+}
+
+
 #ifdef OFFSET
 #undef OFFSET
 #endif /* OFFSET */
 /* OFFSET: Get offset of a variable within the wpa_config structure */
 #define OFFSET(v) ((void *) &((struct wpa_config *) 0)->v)
 
-#define FUNC(f) #f, wpa_config_process_ ## f, OFFSET(f), NULL, NULL
-#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL
-#define _INT(f) #f, wpa_global_config_parse_int, OFFSET(f)
+#define FUNC(f) #f, wpa_config_process_ ## f, NULL, OFFSET(f), NULL, NULL
+#define FUNC_NO_VAR(f) #f, wpa_config_process_ ## f, NULL, NULL, NULL, NULL
+#define _INT(f) #f, wpa_global_config_parse_int, wpa_config_get_int, OFFSET(f)
 #define INT(f) _INT(f), NULL, NULL
 #define INT_RANGE(f, min, max) _INT(f), (void *) min, (void *) max
-#define _STR(f) #f, wpa_global_config_parse_str, OFFSET(f)
+#define _STR(f) #f, wpa_global_config_parse_str, wpa_config_get_str, OFFSET(f)
 #define STR(f) _STR(f), NULL, NULL
 #define STR_RANGE(f, min, max) _STR(f), (void *) min, (void *) max
-#define BIN(f) #f, wpa_global_config_parse_bin, OFFSET(f), NULL, NULL
+#define BIN(f) #f, wpa_global_config_parse_bin, NULL, OFFSET(f), NULL, NULL
+#define IPV4(f) #f, wpa_global_config_parse_ipv4, NULL, OFFSET(f), NULL, NULL
 
 static const struct global_parse_data global_fields[] = {
 #ifdef CONFIG_CTRL_IFACE
        { STR(ctrl_interface), 0 },
+       { FUNC_NO_VAR(no_ctrl_interface), 0 },
        { STR(ctrl_interface_group), 0 } /* deprecated */,
 #endif /* CONFIG_CTRL_IFACE */
+#ifdef CONFIG_MACSEC
+       { INT_RANGE(eapol_version, 1, 3), 0 },
+#else /* CONFIG_MACSEC */
        { INT_RANGE(eapol_version, 1, 2), 0 },
+#endif /* CONFIG_MACSEC */
        { INT(ap_scan), 0 },
+       { FUNC(bgscan), 0 },
+#ifdef CONFIG_MESH
+       { INT(user_mpm), 0 },
+       { INT_RANGE(max_peer_links, 0, 255), 0 },
+       { INT(mesh_max_inactivity), 0 },
+#endif /* CONFIG_MESH */
        { INT(disable_scan_offload), 0 },
        { INT(fast_reauth), 0 },
        { STR(opensc_engine_path), 0 },
        { STR(pkcs11_engine_path), 0 },
        { STR(pkcs11_module_path), 0 },
+       { STR(openssl_ciphers), 0 },
        { STR(pcsc_reader), 0 },
        { STR(pcsc_pin), 0 },
+       { INT(external_sim), 0 },
        { STR(driver_param), 0 },
        { INT(dot11RSNAConfigPMKLifetime), 0 },
        { INT(dot11RSNAConfigPMKReauthThreshold), 0 },
@@ -3062,18 +4121,29 @@ static const struct global_parse_data global_fields[] = {
        { FUNC(sec_device_type), CFG_CHANGED_SEC_DEVICE_TYPE },
        { INT(p2p_listen_reg_class), 0 },
        { INT(p2p_listen_channel), 0 },
-       { INT(p2p_oper_reg_class), 0 },
-       { INT(p2p_oper_channel), 0 },
+       { INT(p2p_oper_reg_class), CFG_CHANGED_P2P_OPER_CHANNEL },
+       { INT(p2p_oper_channel), CFG_CHANGED_P2P_OPER_CHANNEL },
        { INT_RANGE(p2p_go_intent, 0, 15), 0 },
        { STR(p2p_ssid_postfix), CFG_CHANGED_P2P_SSID_POSTFIX },
        { INT_RANGE(persistent_reconnect, 0, 1), 0 },
        { INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS },
        { INT(p2p_group_idle), 0 },
+       { INT_RANGE(p2p_passphrase_len, 8, 63),
+         CFG_CHANGED_P2P_PASSPHRASE_LEN },
        { FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN },
+       { FUNC(p2p_no_go_freq), CFG_CHANGED_P2P_PREF_CHAN },
+       { INT_RANGE(p2p_add_cli_chan, 0, 1), 0 },
+       { INT_RANGE(p2p_optimize_listen_chan, 0, 1), 0 },
        { INT(p2p_go_ht40), 0 },
+       { INT(p2p_go_vht), 0 },
        { INT(p2p_disabled), 0 },
+       { INT_RANGE(p2p_go_ctwindow, 0, 127), 0 },
        { INT(p2p_no_group_iface), 0 },
        { INT_RANGE(p2p_ignore_shared_freq, 0, 1), 0 },
+       { IPV4(ip_addr_go), 0 },
+       { IPV4(ip_addr_mask), 0 },
+       { IPV4(ip_addr_start), 0 },
+       { IPV4(ip_addr_end), 0 },
 #endif /* CONFIG_P2P */
        { FUNC(country), CFG_CHANGED_COUNTRY },
        { INT(bss_max_count), 0 },
@@ -3109,6 +4179,16 @@ static const struct global_parse_data global_fields[] = {
        { FUNC(freq_list), 0 },
        { INT(scan_cur_freq), 0 },
        { INT(sched_scan_interval), 0 },
+       { INT(tdls_external_control), 0},
+       { STR(osu_dir), 0 },
+       { STR(wowlan_triggers), 0 },
+       { INT(p2p_search_delay), 0},
+       { INT(mac_addr), 0 },
+       { INT(rand_addr_lifetime), 0 },
+       { INT(preassoc_mac_addr), 0 },
+       { INT(key_mgmt_offload), 0},
+       { INT(passive_scan), 0 },
+       { INT(reassoc_same_bss_optim), 0 },
 };
 
 #undef FUNC
@@ -3119,7 +4199,52 @@ static const struct global_parse_data global_fields[] = {
 #undef STR
 #undef STR_RANGE
 #undef BIN
-#define NUM_GLOBAL_FIELDS (sizeof(global_fields) / sizeof(global_fields[0]))
+#undef IPV4
+#define NUM_GLOBAL_FIELDS ARRAY_SIZE(global_fields)
+
+
+int wpa_config_dump_values(struct wpa_config *config, char *buf, size_t buflen)
+{
+       int result = 0;
+       size_t i;
+
+       for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
+               const struct global_parse_data *field = &global_fields[i];
+               int tmp;
+
+               if (!field->get)
+                       continue;
+
+               tmp = field->get(field->name, config, (long) field->param1,
+                                buf, buflen, 1);
+               if (tmp < 0)
+                       return -1;
+               buf += tmp;
+               buflen -= tmp;
+               result += tmp;
+       }
+       return result;
+}
+
+
+int wpa_config_get_value(const char *name, struct wpa_config *config,
+                        char *buf, size_t buflen)
+{
+       size_t i;
+
+       for (i = 0; i < NUM_GLOBAL_FIELDS; i++) {
+               const struct global_parse_data *field = &global_fields[i];
+
+               if (os_strcmp(name, field->name) != 0)
+                       continue;
+               if (!field->get)
+                       break;
+               return field->get(name, config, (long) field->param1,
+                                 buf, buflen, 0);
+       }
+
+       return -1;
+}
 
 
 int wpa_config_process_global(struct wpa_config *config, char *pos, int line)
index 1748cf3..34b754e 100644 (file)
 #else /* CONFIG_NO_SCAN_PROCESSING */
 #define DEFAULT_AP_SCAN 1
 #endif /* CONFIG_NO_SCAN_PROCESSING */
+#define DEFAULT_USER_MPM 1
+#define DEFAULT_MAX_PEER_LINKS 99
+#define DEFAULT_MESH_MAX_INACTIVITY 300
 #define DEFAULT_FAST_REAUTH 1
 #define DEFAULT_P2P_GO_INTENT 7
 #define DEFAULT_P2P_INTRA_BSS 1
 #define DEFAULT_P2P_GO_MAX_INACTIVITY (5 * 60)
+#define DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN 0
 #define DEFAULT_BSS_MAX_COUNT 200
 #define DEFAULT_BSS_EXPIRATION_AGE 180
 #define DEFAULT_BSS_EXPIRATION_SCAN_COUNT 2
 #define DEFAULT_MAX_NUM_STA 128
 #define DEFAULT_ACCESS_NETWORK_TYPE 15
 #define DEFAULT_SCAN_CUR_FREQ 0
+#define DEFAULT_P2P_SEARCH_DELAY 500
+#define DEFAULT_RAND_ADDR_LIFETIME 60
+#define DEFAULT_KEY_MGMT_OFFLOAD 1
+#define DEFAULT_CERT_IN_CB 1
+#define DEFAULT_P2P_GO_CTWINDOW 0
 
 #include "config_ssid.h"
 #include "wps/wps.h"
@@ -52,6 +61,11 @@ struct wpa_cred {
        int id;
 
        /**
+        * temporary - Whether this credential is temporary and not to be saved
+        */
+       int temporary;
+
+       /**
         * priority - Priority group
         *
         * By default, all networks and credentials get the same priority group
@@ -150,12 +164,37 @@ struct wpa_cred {
        char *milenage;
 
        /**
-        * domain - Home service provider FQDN
+        * domain_suffix_match - Constraint for server domain name
+        *
+        * If set, this FQDN is used as a suffix match requirement for the AAA
+        * server certificate in SubjectAltName dNSName element(s). If a
+        * matching dNSName is found, this constraint is met. If no dNSName
+        * values are present, this constraint is matched against SubjectName CN
+        * using same suffix match comparison. Suffix match here means that the
+        * host/domain name is compared one label at a time starting from the
+        * top-level domain and all the labels in @domain_suffix_match shall be
+        * included in the certificate. The certificate may include additional
+        * sub-level labels in addition to the required labels.
+        *
+        * For example, domain_suffix_match=example.com would match
+        * test.example.com but would not match test-example.com.
+        */
+       char *domain_suffix_match;
+
+       /**
+        * domain - Home service provider FQDN(s)
         *
         * This is used to compare against the Domain Name List to figure out
-        * whether the AP is operated by the Home SP.
+        * whether the AP is operated by the Home SP. Multiple domain entries
+        * can be used to configure alternative FQDNs that will be considered
+        * home networks.
         */
-       char *domain;
+       char **domain;
+
+       /**
+        * num_domain - Number of FQDNs in the domain array
+        */
+       size_t num_domain;
 
        /**
         * roaming_consortium - Roaming Consortium OI
@@ -175,6 +214,9 @@ struct wpa_cred {
         */
        size_t roaming_consortium_len;
 
+       u8 required_roaming_consortium[15];
+       size_t required_roaming_consortium_len;
+
        /**
         * eap_method - EAP method to use
         *
@@ -203,6 +245,66 @@ struct wpa_cred {
                size_t ssid_len;
        } *excluded_ssid;
        size_t num_excluded_ssid;
+
+       struct roaming_partner {
+               char fqdn[128];
+               int exact_match;
+               u8 priority;
+               char country[3];
+       } *roaming_partner;
+       size_t num_roaming_partner;
+
+       int update_identifier;
+
+       /**
+        * provisioning_sp - FQDN of the SP that provisioned the credential
+        */
+       char *provisioning_sp;
+
+       /**
+        * sp_priority - Credential priority within a provisioning SP
+        *
+        * This is the priority of the credential among all credentials
+        * provisionined by the same SP (i.e., for entries that have identical
+        * provisioning_sp value). The range of this priority is 0-255 with 0
+        * being the highest and 255 the lower priority.
+        */
+       int sp_priority;
+
+       unsigned int min_dl_bandwidth_home;
+       unsigned int min_ul_bandwidth_home;
+       unsigned int min_dl_bandwidth_roaming;
+       unsigned int min_ul_bandwidth_roaming;
+
+       /**
+        * max_bss_load - Maximum BSS Load Channel Utilization (1..255)
+        * This value is used as the maximum channel utilization for network
+        * selection purposes for home networks. If the AP does not advertise
+        * BSS Load or if the limit would prevent any connection, this
+        * constraint will be ignored.
+        */
+       unsigned int max_bss_load;
+
+       unsigned int num_req_conn_capab;
+       u8 *req_conn_capab_proto;
+       int **req_conn_capab_port;
+
+       /**
+        * ocsp - Whether to use/require OCSP to check server certificate
+        *
+        * 0 = do not use OCSP stapling (TLS certificate status extension)
+        * 1 = try to use OCSP stapling, but not require response
+        * 2 = require valid OCSP stapling response
+        */
+       int ocsp;
+
+       /**
+        * sim_num - User selected SIM identifier
+        *
+        * This variable is used for identifying which SIM is used if the system
+        * has more than one.
+        */
+       int sim_num;
 };
 
 
@@ -222,6 +324,7 @@ struct wpa_cred {
 #define CFG_CHANGED_P2P_PREF_CHAN BIT(13)
 #define CFG_CHANGED_EXT_PW_BACKEND BIT(14)
 #define CFG_CHANGED_NFC_PASSWORD_TOKEN BIT(15)
+#define CFG_CHANGED_P2P_PASSPHRASE_LEN BIT(16)
 
 /**
  * struct wpa_config - wpa_supplicant configuration data
@@ -301,6 +404,18 @@ struct wpa_config {
        int ap_scan;
 
        /**
+        * bgscan - Background scan and roaming parameters or %NULL if none
+        *
+        * This is an optional set of parameters for background scanning and
+        * roaming within a network (ESS). For more detailed information see
+        * ssid block documentation.
+        *
+        * The variable defines default bgscan behavior for all BSS station
+        * networks except for those which have their own bgscan configuration.
+        */
+        char *bgscan;
+
+       /**
         * disable_scan_offload - Disable automatic offloading of scan requests
         *
         * By default, %wpa_supplicant tries to offload scanning if the driver
@@ -408,6 +523,15 @@ struct wpa_config {
        char *pkcs11_module_path;
 
        /**
+        * openssl_ciphers - OpenSSL cipher string
+        *
+        * This is an OpenSSL specific configuration option for configuring the
+        * default ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the
+        * default.
+        */
+       char *openssl_ciphers;
+
+       /**
         * pcsc_reader - PC/SC reader name prefix
         *
         * If not %NULL, PC/SC reader with a name that matches this prefix is
@@ -425,6 +549,11 @@ struct wpa_config {
        char *pcsc_pin;
 
        /**
+        * external_sim - Use external processing for SIM/USIM operations
+        */
+       int external_sim;
+
+       /**
         * driver_param - Driver interface parameters
         *
         * This text string is passed to the selected driver interface with the
@@ -572,7 +701,10 @@ struct wpa_config {
        int p2p_intra_bss;
        unsigned int num_p2p_pref_chan;
        struct p2p_channel *p2p_pref_chan;
+       struct wpa_freq_range_list p2p_no_go_freq;
+       int p2p_add_cli_chan;
        int p2p_ignore_shared_freq;
+       int p2p_optimize_listen_chan;
 
        struct wpabuf *wps_vendor_ext_m1;
 
@@ -601,6 +733,14 @@ struct wpa_config {
        int p2p_group_idle;
 
        /**
+        * p2p_passphrase_len - Passphrase length (8..63) for P2P GO
+        *
+        * This parameter controls the length of the random passphrase that is
+        * generated at the GO.
+        */
+       unsigned int p2p_passphrase_len;
+
+       /**
         * bss_max_count - Maximum number of BSS entries to keep in memory
         */
        unsigned int bss_max_count;
@@ -793,6 +933,24 @@ struct wpa_config {
        int p2p_go_ht40;
 
        /**
+        * p2p_go_vht - Default mode for VHT enable when operating as GO
+        *
+        * This will take effect for p2p_group_add, p2p_connect, and p2p_invite.
+        * Note that regulatory constraints and driver capabilities are
+        * consulted anyway, so setting it to 1 can't do real harm.
+        * By default: 0 (disabled)
+        */
+       int p2p_go_vht;
+
+       /**
+        * p2p_go_ctwindow - CTWindow to use when operating as GO
+        *
+        * By default: 0 (no CTWindow). Values 0-127 can be used to indicate
+        * the length of the CTWindow in TUs.
+        */
+       int p2p_go_ctwindow;
+
+       /**
         * p2p_disabled - Whether P2P operations are disabled for this interface
         */
        int p2p_disabled;
@@ -876,6 +1034,135 @@ struct wpa_config {
         * sched_scan_interval -  schedule scan interval
         */
        unsigned int sched_scan_interval;
+
+       /**
+        * tdls_external_control - External control for TDLS setup requests
+        *
+        * Enable TDLS mode where external programs are given the control
+        * to specify the TDLS link to get established to the driver. The
+        * driver requests the TDLS setup to the supplicant only for the
+        * specified TDLS peers.
+        */
+       int tdls_external_control;
+
+       u8 ip_addr_go[4];
+       u8 ip_addr_mask[4];
+       u8 ip_addr_start[4];
+       u8 ip_addr_end[4];
+
+       /**
+        * osu_dir - OSU provider information directory
+        *
+        * If set, allow FETCH_OSU control interface command to be used to fetch
+        * OSU provider information into all APs and store the results in this
+        * directory.
+        */
+       char *osu_dir;
+
+       /**
+        * wowlan_triggers - Wake-on-WLAN triggers
+        *
+        * If set, these wowlan triggers will be configured.
+        */
+       char *wowlan_triggers;
+
+       /**
+        * p2p_search_delay - Extra delay between concurrent search iterations
+        *
+        * Add extra delay (in milliseconds) between search iterations when
+        * there is a concurrent operation to make p2p_find friendlier to
+        * concurrent operations by avoiding it from taking 100% of radio
+        * resources.
+        */
+       unsigned int p2p_search_delay;
+
+       /**
+        * mac_addr - MAC address policy default
+        *
+        * 0 = use permanent MAC address
+        * 1 = use random MAC address for each ESS connection
+        * 2 = like 1, but maintain OUI (with local admin bit set)
+        *
+        * By default, permanent MAC address is used unless policy is changed by
+        * the per-network mac_addr parameter. Global mac_addr=1 can be used to
+        * change this default behavior.
+        */
+       int mac_addr;
+
+       /**
+        * rand_addr_lifetime - Lifetime of random MAC address in seconds
+        */
+       unsigned int rand_addr_lifetime;
+
+       /**
+        * preassoc_mac_addr - Pre-association MAC address policy
+        *
+        * 0 = use permanent MAC address
+        * 1 = use random MAC address
+        * 2 = like 1, but maintain OUI (with local admin bit set)
+        */
+       int preassoc_mac_addr;
+
+       /**
+        * key_mgmt_offload - Use key management offload
+        *
+        * Key management offload should be used if the device supports it.
+        * Key management offload is the capability of a device operating as
+        * a station to do the exchange necessary to establish temporal keys
+        * during initial RSN connection, after roaming, or during a PTK
+        * rekeying operation.
+        */
+       int key_mgmt_offload;
+
+       /**
+        * user_mpm - MPM residency
+        *
+        * 0: MPM lives in driver.
+        * 1: wpa_supplicant handles peering and station allocation.
+        *
+        * If AMPE or SAE is enabled, the MPM is always in userspace.
+        */
+       int user_mpm;
+
+       /**
+        * max_peer_links - Maximum number of peer links
+        *
+        * Maximum number of mesh peering currently maintained by the STA.
+        */
+       int max_peer_links;
+
+       /**
+        * cert_in_cb - Whether to include a peer certificate dump in events
+        *
+        * This controls whether peer certificates for authentication server and
+        * its certificate chain are included in EAP peer certificate events.
+        */
+       int cert_in_cb;
+
+       /**
+        * mesh_max_inactivity - Timeout in seconds to detect STA inactivity
+        *
+        * This timeout value is used in mesh STA to clean up inactive stations.
+        * By default: 300 seconds.
+        */
+       int mesh_max_inactivity;
+
+       /**
+        * passive_scan - Whether to force passive scan for network connection
+        *
+        * This parameter can be used to force only passive scanning to be used
+        * for network connection cases. It should be noted that this will slow
+        * down scan operations and reduce likelihood of finding the AP. In
+        * addition, some use cases will override this due to functional
+        * requirements, e.g., for finding an AP that uses hidden SSID
+        * (scan_ssid=1) or P2P device discovery.
+        */
+       int passive_scan;
+
+       /**
+        * reassoc_same_bss_optim - Whether to optimize reassoc-to-same-BSS
+        */
+       int reassoc_same_bss_optim;
 };
 
 
@@ -894,6 +1181,11 @@ int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value,
                   int line);
 int wpa_config_set_quoted(struct wpa_ssid *ssid, const char *var,
                          const char *value);
+int wpa_config_dump_values(struct wpa_config *config, char *buf,
+                          size_t buflen);
+int wpa_config_get_value(const char *name, struct wpa_config *config,
+                        char *buf, size_t buflen);
+
 char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys);
 char * wpa_config_get(struct wpa_ssid *ssid, const char *var);
 char * wpa_config_get_no_key(struct wpa_ssid *ssid, const char *var);
@@ -907,6 +1199,7 @@ void wpa_config_set_blob(struct wpa_config *config,
                         struct wpa_config_blob *blob);
 void wpa_config_free_blob(struct wpa_config_blob *blob);
 int wpa_config_remove_blob(struct wpa_config *config, const char *name);
+void wpa_config_flush_blobs(struct wpa_config *config);
 
 struct wpa_cred * wpa_config_get_cred(struct wpa_config *config, int id);
 struct wpa_cred * wpa_config_add_cred(struct wpa_config *config);
@@ -914,6 +1207,7 @@ int wpa_config_remove_cred(struct wpa_config *config, int id);
 void wpa_config_free_cred(struct wpa_cred *cred);
 int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
                        const char *value, int line);
+char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var);
 
 struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
                                           const char *driver_param);
index d03de0b..3d3a6e4 100644 (file)
@@ -11,6 +11,9 @@
  */
 
 #include "includes.h"
+#ifdef ANDROID
+#include <sys/stat.h>
+#endif /* ANDROID */
 
 #include "common.h"
 #include "config.h"
@@ -143,6 +146,15 @@ static int wpa_config_validate_network(struct wpa_ssid *ssid, int line)
                ssid->group_cipher &= ~WPA_CIPHER_CCMP;
        }
 
+       if (ssid->mode == WPAS_MODE_MESH &&
+           (ssid->key_mgmt != WPA_KEY_MGMT_NONE &&
+           ssid->key_mgmt != WPA_KEY_MGMT_SAE)) {
+               wpa_printf(MSG_ERROR,
+                          "Line %d: key_mgmt for mesh network should be open or SAE",
+                          line);
+               errors++;
+       }
+
        return errors;
 }
 
@@ -158,6 +170,7 @@ static struct wpa_ssid * wpa_config_read_network(FILE *f, int *line, int id)
        ssid = os_zalloc(sizeof(*ssid));
        if (ssid == NULL)
                return NULL;
+       dl_list_init(&ssid->psk_list);
        ssid->id = id;
 
        wpa_config_set_network_defaults(ssid);
@@ -218,6 +231,7 @@ static struct wpa_cred * wpa_config_read_cred(FILE *f, int *line, int id)
        if (cred == NULL)
                return NULL;
        cred->id = id;
+       cred->sim_num = DEFAULT_USER_SELECTED_SIM;
 
        while (wpa_config_get_line(buf, sizeof(buf), f, line, &pos)) {
                if (os_strcmp(pos, "}") == 0) {
@@ -350,8 +364,8 @@ struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp)
        FILE *f;
        char buf[512], *pos;
        int errors = 0, line = 0;
-       struct wpa_ssid *ssid, *tail = NULL, *head = NULL;
-       struct wpa_cred *cred, *cred_tail = NULL, *cred_head = NULL;
+       struct wpa_ssid *ssid, *tail, *head;
+       struct wpa_cred *cred, *cred_tail, *cred_head;
        struct wpa_config *config;
        int id = 0;
        int cred_id = 0;
@@ -367,8 +381,12 @@ struct wpa_config * wpa_config_read(const char *name, struct wpa_config *cfgp)
                           "structure");
                return NULL;
        }
-       head = config->ssid;
-       cred_head = config->cred;
+       tail = head = config->ssid;
+       while (tail && tail->next)
+               tail = tail->next;
+       cred_tail = cred_head = config->cred;
+       while (cred_tail && cred_tail->next)
+               cred_tail = cred_tail->next;
 
        wpa_printf(MSG_DEBUG, "Reading configuration file '%s'", name);
        f = fopen(name, "r");
@@ -593,7 +611,7 @@ static void write_wep_key(FILE *f, int idx, struct wpa_ssid *ssid)
        int res;
 
        res = os_snprintf(field, sizeof(field), "wep_key%d", idx);
-       if (res < 0 || (size_t) res >= sizeof(field))
+       if (os_snprintf_error(sizeof(field), res))
                return;
        value = wpa_config_get(ssid, field);
        if (value) {
@@ -604,6 +622,16 @@ static void write_wep_key(FILE *f, int idx, struct wpa_ssid *ssid)
 
 
 #ifdef CONFIG_P2P
+
+static void write_go_p2p_dev_addr(FILE *f, struct wpa_ssid *ssid)
+{
+       char *value = wpa_config_get(ssid, "go_p2p_dev_addr");
+       if (value == NULL)
+               return;
+       fprintf(f, "\tgo_p2p_dev_addr=%s\n", value);
+       os_free(value);
+}
+
 static void write_p2p_client_list(FILE *f, struct wpa_ssid *ssid)
 {
        char *value = wpa_config_get(ssid, "p2p_client_list");
@@ -612,6 +640,20 @@ static void write_p2p_client_list(FILE *f, struct wpa_ssid *ssid)
        fprintf(f, "\tp2p_client_list=%s\n", value);
        os_free(value);
 }
+
+
+static void write_psk_list(FILE *f, struct wpa_ssid *ssid)
+{
+       struct psk_list_entry *psk;
+       char hex[32 * 2 + 1];
+
+       dl_list_for_each(psk, &ssid->psk_list, struct psk_list_entry, list) {
+               wpa_snprintf_hex(hex, sizeof(hex), psk->psk, sizeof(psk->psk));
+               fprintf(f, "\tpsk_list=%s" MACSTR "-%s\n",
+                       psk->p2p ? "P2P-" : "", MAC2STR(psk->addr), hex);
+       }
+}
+
 #endif /* CONFIG_P2P */
 
 
@@ -628,6 +670,8 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
        STR(ssid);
        INT(scan_ssid);
        write_bssid(f, ssid);
+       write_str(f, "bssid_blacklist", ssid);
+       write_str(f, "bssid_whitelist", ssid);
        write_psk(f, ssid);
        write_proto(f, ssid);
        write_key_mgmt(f, ssid);
@@ -637,6 +681,7 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
        write_auth_alg(f, ssid);
        STR(bgscan);
        STR(autoscan);
+       STR(scan_freq);
 #ifdef IEEE8021X_EAPOL
        write_eap(f, ssid);
        STR(identity);
@@ -650,6 +695,8 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
        STR(dh_file);
        STR(subject_match);
        STR(altsubject_match);
+       STR(domain_suffix_match);
+       STR(domain_match);
        STR(ca_cert2);
        STR(ca_path2);
        STR(client_cert2);
@@ -658,6 +705,8 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
        STR(dh_file2);
        STR(subject_match2);
        STR(altsubject_match2);
+       STR(domain_suffix_match2);
+       STR(domain_match2);
        STR(phase1);
        STR(phase2);
        STR(pcsc);
@@ -674,6 +723,8 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
        INTe(engine);
        INTe(engine2);
        INT_DEF(eapol_flags, DEFAULT_EAPOL_FLAGS);
+       STR(openssl_ciphers);
+       INTe(erp);
 #endif /* IEEE8021X_EAPOL */
        for (i = 0; i < 4; i++)
                write_wep_key(f, i, ssid);
@@ -683,22 +734,78 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
        INT_DEF(eap_workaround, DEFAULT_EAP_WORKAROUND);
        STR(pac_file);
        INT_DEFe(fragment_size, DEFAULT_FRAGMENT_SIZE);
+       INTe(ocsp);
+       INT_DEFe(sim_num, DEFAULT_USER_SELECTED_SIM);
 #endif /* IEEE8021X_EAPOL */
        INT(mode);
+       INT(no_auto_peer);
        INT(frequency);
+       INT(fixed_freq);
        write_int(f, "proactive_key_caching", ssid->proactive_key_caching, -1);
        INT(disabled);
        INT(peerkey);
+       INT(mixed_cell);
 #ifdef CONFIG_IEEE80211W
        write_int(f, "ieee80211w", ssid->ieee80211w,
                  MGMT_FRAME_PROTECTION_DEFAULT);
 #endif /* CONFIG_IEEE80211W */
        STR(id_str);
 #ifdef CONFIG_P2P
+       write_go_p2p_dev_addr(f, ssid);
        write_p2p_client_list(f, ssid);
+       write_psk_list(f, ssid);
 #endif /* CONFIG_P2P */
+       INT(ap_max_inactivity);
        INT(dtim_period);
        INT(beacon_int);
+#ifdef CONFIG_MACSEC
+       INT(macsec_policy);
+#endif /* CONFIG_MACSEC */
+#ifdef CONFIG_HS20
+       INT(update_identifier);
+#endif /* CONFIG_HS20 */
+       write_int(f, "mac_addr", ssid->mac_addr, -1);
+#ifdef CONFIG_MESH
+       STR(mesh_basic_rates);
+       INT_DEF(dot11MeshMaxRetries, DEFAULT_MESH_MAX_RETRIES);
+       INT_DEF(dot11MeshRetryTimeout, DEFAULT_MESH_RETRY_TIMEOUT);
+       INT_DEF(dot11MeshConfirmTimeout, DEFAULT_MESH_CONFIRM_TIMEOUT);
+       INT_DEF(dot11MeshHoldingTimeout, DEFAULT_MESH_HOLDING_TIMEOUT);
+#endif /* CONFIG_MESH */
+       INT(wpa_ptk_rekey);
+       INT(ignore_broadcast_ssid);
+#ifdef CONFIG_HT_OVERRIDES
+       INT_DEF(disable_ht, DEFAULT_DISABLE_HT);
+       INT_DEF(disable_ht40, DEFAULT_DISABLE_HT40);
+       INT_DEF(disable_sgi, DEFAULT_DISABLE_SGI);
+       INT_DEF(disable_ldpc, DEFAULT_DISABLE_LDPC);
+       INT(ht40_intolerant);
+       INT_DEF(disable_max_amsdu, DEFAULT_DISABLE_MAX_AMSDU);
+       INT_DEF(ampdu_factor, DEFAULT_AMPDU_FACTOR);
+       INT_DEF(ampdu_density, DEFAULT_AMPDU_DENSITY);
+       STR(ht_mcs);
+#endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+       INT(disable_vht);
+       INT(vht_capa);
+       INT(vht_capa_mask);
+       INT_DEF(vht_rx_mcs_nss_1, -1);
+       INT_DEF(vht_rx_mcs_nss_2, -1);
+       INT_DEF(vht_rx_mcs_nss_3, -1);
+       INT_DEF(vht_rx_mcs_nss_4, -1);
+       INT_DEF(vht_rx_mcs_nss_5, -1);
+       INT_DEF(vht_rx_mcs_nss_6, -1);
+       INT_DEF(vht_rx_mcs_nss_7, -1);
+       INT_DEF(vht_rx_mcs_nss_8, -1);
+       INT_DEF(vht_tx_mcs_nss_1, -1);
+       INT_DEF(vht_tx_mcs_nss_2, -1);
+       INT_DEF(vht_tx_mcs_nss_3, -1);
+       INT_DEF(vht_tx_mcs_nss_4, -1);
+       INT_DEF(vht_tx_mcs_nss_5, -1);
+       INT_DEF(vht_tx_mcs_nss_6, -1);
+       INT_DEF(vht_tx_mcs_nss_7, -1);
+       INT_DEF(vht_tx_mcs_nss_8, -1);
+#endif /* CONFIG_VHT_OVERRIDES */
 
 #undef STR
 #undef INT
@@ -708,6 +815,8 @@ static void wpa_config_write_network(FILE *f, struct wpa_ssid *ssid)
 
 static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred)
 {
+       size_t i;
+
        if (cred->priority)
                fprintf(f, "\tpriority=%d\n", cred->priority);
        if (cred->pcsc)
@@ -733,10 +842,12 @@ static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred)
                fprintf(f, "\timsi=\"%s\"\n", cred->imsi);
        if (cred->milenage)
                fprintf(f, "\tmilenage=\"%s\"\n", cred->milenage);
-       if (cred->domain)
-               fprintf(f, "\tdomain=\"%s\"\n", cred->domain);
+       for (i = 0; i < cred->num_domain; i++)
+               fprintf(f, "\tdomain=\"%s\"\n", cred->domain[i]);
+       if (cred->domain_suffix_match)
+               fprintf(f, "\tdomain_suffix_match=\"%s\"\n",
+                       cred->domain_suffix_match);
        if (cred->roaming_consortium_len) {
-               size_t i;
                fprintf(f, "\troaming_consortium=");
                for (i = 0; i < cred->roaming_consortium_len; i++)
                        fprintf(f, "%02x", cred->roaming_consortium[i]);
@@ -746,14 +857,15 @@ static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred)
                const char *name;
                name = eap_get_name(cred->eap_method[0].vendor,
                                    cred->eap_method[0].method);
-               fprintf(f, "\teap=%s\n", name);
+               if (name)
+                       fprintf(f, "\teap=%s\n", name);
        }
        if (cred->phase1)
                fprintf(f, "\tphase1=\"%s\"\n", cred->phase1);
        if (cred->phase2)
                fprintf(f, "\tphase2=\"%s\"\n", cred->phase2);
        if (cred->excluded_ssid) {
-               size_t i, j;
+               size_t j;
                for (i = 0; i < cred->num_excluded_ssid; i++) {
                        struct excluded_ssid *e = &cred->excluded_ssid[i];
                        fprintf(f, "\texcluded_ssid=");
@@ -762,6 +874,70 @@ static void wpa_config_write_cred(FILE *f, struct wpa_cred *cred)
                        fprintf(f, "\n");
                }
        }
+       if (cred->roaming_partner) {
+               for (i = 0; i < cred->num_roaming_partner; i++) {
+                       struct roaming_partner *p = &cred->roaming_partner[i];
+                       fprintf(f, "\troaming_partner=\"%s,%d,%u,%s\"\n",
+                               p->fqdn, p->exact_match, p->priority,
+                               p->country);
+               }
+       }
+       if (cred->update_identifier)
+               fprintf(f, "\tupdate_identifier=%d\n", cred->update_identifier);
+
+       if (cred->provisioning_sp)
+               fprintf(f, "\tprovisioning_sp=\"%s\"\n", cred->provisioning_sp);
+       if (cred->sp_priority)
+               fprintf(f, "\tsp_priority=%d\n", cred->sp_priority);
+
+       if (cred->min_dl_bandwidth_home)
+               fprintf(f, "\tmin_dl_bandwidth_home=%u\n",
+                       cred->min_dl_bandwidth_home);
+       if (cred->min_ul_bandwidth_home)
+               fprintf(f, "\tmin_ul_bandwidth_home=%u\n",
+                       cred->min_ul_bandwidth_home);
+       if (cred->min_dl_bandwidth_roaming)
+               fprintf(f, "\tmin_dl_bandwidth_roaming=%u\n",
+                       cred->min_dl_bandwidth_roaming);
+       if (cred->min_ul_bandwidth_roaming)
+               fprintf(f, "\tmin_ul_bandwidth_roaming=%u\n",
+                       cred->min_ul_bandwidth_roaming);
+
+       if (cred->max_bss_load)
+               fprintf(f, "\tmax_bss_load=%u\n",
+                       cred->max_bss_load);
+
+       if (cred->ocsp)
+               fprintf(f, "\tocsp=%d\n", cred->ocsp);
+
+       if (cred->num_req_conn_capab) {
+               for (i = 0; i < cred->num_req_conn_capab; i++) {
+                       int *ports;
+
+                       fprintf(f, "\treq_conn_capab=%u",
+                               cred->req_conn_capab_proto[i]);
+                       ports = cred->req_conn_capab_port[i];
+                       if (ports) {
+                               int j;
+                               for (j = 0; ports[j] != -1; j++) {
+                                       fprintf(f, "%s%d", j > 0 ? "," : ":",
+                                               ports[j]);
+                               }
+                       }
+                       fprintf(f, "\n");
+               }
+       }
+
+       if (cred->required_roaming_consortium_len) {
+               fprintf(f, "\trequired_roaming_consortium=");
+               for (i = 0; i < cred->required_roaming_consortium_len; i++)
+                       fprintf(f, "%02x",
+                               cred->required_roaming_consortium[i]);
+               fprintf(f, "\n");
+       }
+
+       if (cred->sim_num != DEFAULT_USER_SELECTED_SIM)
+               fprintf(f, "\tsim_num=%d\n", cred->sim_num);
 }
 
 
@@ -825,6 +1001,8 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
        if (config->pkcs11_module_path)
                fprintf(f, "pkcs11_module_path=%s\n",
                        config->pkcs11_module_path);
+       if (config->openssl_ciphers)
+               fprintf(f, "openssl_ciphers=%s\n", config->openssl_ciphers);
        if (config->pcsc_reader)
                fprintf(f, "pcsc_reader=%s\n", config->pcsc_reader);
        if (config->pcsc_pin)
@@ -907,6 +1085,9 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
                fprintf(f, "p2p_intra_bss=%u\n", config->p2p_intra_bss);
        if (config->p2p_group_idle)
                fprintf(f, "p2p_group_idle=%u\n", config->p2p_group_idle);
+       if (config->p2p_passphrase_len)
+               fprintf(f, "p2p_passphrase_len=%u\n",
+                       config->p2p_passphrase_len);
        if (config->p2p_pref_chan) {
                unsigned int i;
                fprintf(f, "p2p_pref_chan=");
@@ -917,8 +1098,25 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
                }
                fprintf(f, "\n");
        }
+       if (config->p2p_no_go_freq.num) {
+               char *val = freq_range_list_str(&config->p2p_no_go_freq);
+               if (val) {
+                       fprintf(f, "p2p_no_go_freq=%s\n", val);
+                       os_free(val);
+               }
+       }
+       if (config->p2p_add_cli_chan)
+               fprintf(f, "p2p_add_cli_chan=%d\n", config->p2p_add_cli_chan);
+       if (config->p2p_optimize_listen_chan !=
+           DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN)
+               fprintf(f, "p2p_optimize_listen_chan=%d\n",
+                       config->p2p_optimize_listen_chan);
        if (config->p2p_go_ht40)
                fprintf(f, "p2p_go_ht40=%u\n", config->p2p_go_ht40);
+       if (config->p2p_go_vht)
+               fprintf(f, "p2p_go_vht=%u\n", config->p2p_go_vht);
+       if (config->p2p_go_ctwindow != DEFAULT_P2P_GO_CTWINDOW)
+               fprintf(f, "p2p_go_ctwindow=%u\n", config->p2p_go_ctwindow);
        if (config->p2p_disabled)
                fprintf(f, "p2p_disabled=%u\n", config->p2p_disabled);
        if (config->p2p_no_group_iface)
@@ -1031,6 +1229,57 @@ static void wpa_config_write_global(FILE *f, struct wpa_config *config)
        if (config->sched_scan_interval)
                fprintf(f, "sched_scan_interval=%u\n",
                        config->sched_scan_interval);
+
+       if (config->external_sim)
+               fprintf(f, "external_sim=%d\n", config->external_sim);
+
+       if (config->tdls_external_control)
+               fprintf(f, "tdls_external_control=%d\n",
+                       config->tdls_external_control);
+
+       if (config->wowlan_triggers)
+               fprintf(f, "wowlan_triggers=%s\n",
+                       config->wowlan_triggers);
+
+       if (config->bgscan)
+               fprintf(f, "bgscan=\"%s\"\n", config->bgscan);
+
+       if (config->p2p_search_delay != DEFAULT_P2P_SEARCH_DELAY)
+               fprintf(f, "p2p_search_delay=%u\n",
+                       config->p2p_search_delay);
+
+       if (config->mac_addr)
+               fprintf(f, "mac_addr=%d\n", config->mac_addr);
+
+       if (config->rand_addr_lifetime != DEFAULT_RAND_ADDR_LIFETIME)
+               fprintf(f, "rand_addr_lifetime=%u\n",
+                       config->rand_addr_lifetime);
+
+       if (config->preassoc_mac_addr)
+               fprintf(f, "preassoc_mac_addr=%d\n", config->preassoc_mac_addr);
+
+       if (config->key_mgmt_offload != DEFAULT_KEY_MGMT_OFFLOAD)
+               fprintf(f, "key_mgmt_offload=%u\n", config->key_mgmt_offload);
+
+       if (config->user_mpm != DEFAULT_USER_MPM)
+               fprintf(f, "user_mpm=%d\n", config->user_mpm);
+
+       if (config->max_peer_links != DEFAULT_MAX_PEER_LINKS)
+               fprintf(f, "max_peer_links=%d\n", config->max_peer_links);
+
+       if (config->cert_in_cb != DEFAULT_CERT_IN_CB)
+               fprintf(f, "cert_in_cb=%d\n", config->cert_in_cb);
+
+       if (config->mesh_max_inactivity != DEFAULT_MESH_MAX_INACTIVITY)
+               fprintf(f, "mesh_max_inactivity=%d\n",
+                       config->mesh_max_inactivity);
+
+       if (config->passive_scan)
+               fprintf(f, "passive_scan=%d\n", config->passive_scan);
+
+       if (config->reassoc_same_bss_optim)
+               fprintf(f, "reassoc_same_bss_optim=%d\n",
+                       config->reassoc_same_bss_optim);
 }
 
 #endif /* CONFIG_NO_CONFIG_WRITE */
@@ -1046,18 +1295,29 @@ int wpa_config_write(const char *name, struct wpa_config *config)
        struct wpa_config_blob *blob;
 #endif /* CONFIG_NO_CONFIG_BLOBS */
        int ret = 0;
+       const char *orig_name = name;
+       int tmp_len = os_strlen(name) + 5; /* allow space for .tmp suffix */
+       char *tmp_name = os_malloc(tmp_len);
+
+       if (tmp_name) {
+               os_snprintf(tmp_name, tmp_len, "%s.tmp", name);
+               name = tmp_name;
+       }
 
        wpa_printf(MSG_DEBUG, "Writing configuration file '%s'", name);
 
        f = fopen(name, "w");
        if (f == NULL) {
                wpa_printf(MSG_DEBUG, "Failed to open '%s' for writing", name);
+               os_free(tmp_name);
                return -1;
        }
 
        wpa_config_write_global(f, config);
 
        for (cred = config->cred; cred; cred = cred->next) {
+               if (cred->temporary)
+                       continue;
                fprintf(f, "\ncred={\n");
                wpa_config_write_cred(f, cred);
                fprintf(f, "}\n");
@@ -1084,8 +1344,21 @@ int wpa_config_write(const char *name, struct wpa_config *config)
 
        fclose(f);
 
+       if (tmp_name) {
+               int chmod_ret = 0;
+
+#ifdef ANDROID
+               chmod_ret = chmod(tmp_name,
+                                 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+#endif /* ANDROID */
+               if (chmod_ret != 0 || rename(tmp_name, orig_name) != 0)
+                       ret = -1;
+
+               os_free(tmp_name);
+       }
+
        wpa_printf(MSG_DEBUG, "Configuration file '%s' written %ssuccessfully",
-                  name, ret ? "un" : "");
+                  orig_name, ret ? "un" : "");
        return ret;
 #else /* CONFIG_NO_CONFIG_WRITE */
        return -1;
index 1340dce..7c826cf 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / Network configuration structures
- * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -10,6 +10,7 @@
 #define CONFIG_SSID_H
 
 #include "common/defs.h"
+#include "utils/list.h"
 #include "eap_peer/eap_config.h"
 
 #define MAX_SSID_LEN 32
 #define DEFAULT_FRAGMENT_SIZE 1398
 
 #define DEFAULT_BG_SCAN_PERIOD -1
+#define DEFAULT_MESH_MAX_RETRIES 2
+#define DEFAULT_MESH_RETRY_TIMEOUT 40
+#define DEFAULT_MESH_CONFIRM_TIMEOUT 40
+#define DEFAULT_MESH_HOLDING_TIMEOUT 40
 #define DEFAULT_DISABLE_HT 0
 #define DEFAULT_DISABLE_HT40 0
 #define DEFAULT_DISABLE_SGI 0
+#define DEFAULT_DISABLE_LDPC 0
 #define DEFAULT_DISABLE_MAX_AMSDU -1 /* no change */
 #define DEFAULT_AMPDU_FACTOR -1 /* no change */
 #define DEFAULT_AMPDU_DENSITY -1 /* no change */
+#define DEFAULT_USER_SELECTED_SIM 1
+
+struct psk_list_entry {
+       struct dl_list list;
+       u8 addr[ETH_ALEN];
+       u8 psk[32];
+       u8 p2p;
+};
 
 /**
  * struct wpa_ssid - Network configuration data
@@ -118,11 +132,28 @@ struct wpa_ssid {
        u8 bssid[ETH_ALEN];
 
        /**
+        * bssid_blacklist - List of inacceptable BSSIDs
+        */
+       u8 *bssid_blacklist;
+       size_t num_bssid_blacklist;
+
+       /**
+        * bssid_blacklist - List of acceptable BSSIDs
+        */
+       u8 *bssid_whitelist;
+       size_t num_bssid_whitelist;
+
+       /**
         * bssid_set - Whether BSSID is configured for this network
         */
        int bssid_set;
 
        /**
+        * go_p2p_dev_addr - GO's P2P Device Address or all zeros if not set
+        */
+       u8 go_p2p_dev_addr[ETH_ALEN];
+
+       /**
         * psk - WPA pre-shared key (256 bits)
         */
        u8 psk[32];
@@ -302,12 +333,15 @@ struct wpa_ssid {
         * 4 = P2P Group Formation (used internally; not in configuration
         * files)
         *
-        * Note: IBSS can only be used with key_mgmt NONE (plaintext and
-        * static WEP) and key_mgmt=WPA-NONE (fixed group key TKIP/CCMP). In
-        * addition, ap_scan has to be set to 2 for IBSS. WPA-None requires
-        * following network block options: proto=WPA, key_mgmt=WPA-NONE,
-        * pairwise=NONE, group=TKIP (or CCMP, but not both), and psk must also
-        * be set (either directly or using ASCII passphrase).
+        * 5 = Mesh
+        *
+        * Note: IBSS can only be used with key_mgmt NONE (plaintext and static
+        * WEP) and WPA-PSK (with proto=RSN). In addition, key_mgmt=WPA-NONE
+        * (fixed group key TKIP/CCMP) is available for backwards compatibility,
+        * but its use is deprecated. WPA-None requires following network block
+        * options: proto=WPA, key_mgmt=WPA-NONE, pairwise=NONE, group=TKIP (or
+        * CCMP, but not both), and psk must also be set (either directly or
+        * using ASCII passphrase).
         */
        enum wpas_mode {
                WPAS_MODE_INFRA = 0,
@@ -315,6 +349,7 @@ struct wpa_ssid {
                WPAS_MODE_AP = 2,
                WPAS_MODE_P2P_GO = 3,
                WPAS_MODE_P2P_GROUP_FORMATION = 4,
+               WPAS_MODE_MESH = 5,
        } mode;
 
        /**
@@ -384,8 +419,29 @@ struct wpa_ssid {
         */
        int frequency;
 
+       /**
+        * fixed_freq - Use fixed frequency for IBSS
+        */
+       int fixed_freq;
+
+       /**
+        * mesh_basic_rates - BSS Basic rate set for mesh network
+        *
+        */
+       int *mesh_basic_rates;
+
+       /**
+        * Mesh network plink parameters
+        */
+       int dot11MeshMaxRetries;
+       int dot11MeshRetryTimeout; /* msec */
+       int dot11MeshConfirmTimeout; /* msec */
+       int dot11MeshHoldingTimeout; /* msec */
+
        int ht40;
 
+       int vht;
+
        /**
         * wpa_ptk_rekey - Maximum lifetime for PTK in seconds
         *
@@ -456,6 +512,11 @@ struct wpa_ssid {
 #endif /* P2P_MAX_STORED_CLIENTS */
 
        /**
+        * psk_list - Per-client PSKs (struct psk_list_entry)
+        */
+       struct dl_list psk_list;
+
+       /**
         * p2p_group - Network generated as a P2P group (used internally)
         */
        int p2p_group;
@@ -504,6 +565,19 @@ struct wpa_ssid {
        int disable_sgi;
 
        /**
+        * disable_ldpc - Disable LDPC for this network
+        *
+        * By default, use it if it is available, but this can be configured
+        * to 1 to have it disabled.
+        */
+       int disable_ldpc;
+
+       /**
+        * ht40_intolerant - Indicate 40 MHz intolerant for this network
+        */
+       int ht40_intolerant;
+
+       /**
         * disable_max_amsdu - Disable MAX A-MSDU
         *
         * A-MDSU will be 3839 bytes when disabled, or 7935
@@ -590,7 +664,7 @@ struct wpa_ssid {
        /**
         * disabled_until - Network block disabled until this time if non-zero
         */
-       struct os_time disabled_until;
+       struct os_reltime disabled_until;
 
        /**
         * parent_cred - Pointer to parent wpa_cred entry
@@ -600,6 +674,44 @@ struct wpa_ssid {
         * dereferences since it may not be updated in all cases.
         */
        void *parent_cred;
+
+#ifdef CONFIG_MACSEC
+       /**
+        * macsec_policy - Determines the policy for MACsec secure session
+        *
+        * 0: MACsec not in use (default)
+        * 1: MACsec enabled - Should secure, accept key server's advice to
+        *    determine whether to use a secure session or not.
+        */
+       int macsec_policy;
+#endif /* CONFIG_MACSEC */
+
+#ifdef CONFIG_HS20
+       int update_identifier;
+#endif /* CONFIG_HS20 */
+
+       unsigned int wps_run;
+
+       /**
+        * mac_addr - MAC address policy
+        *
+        * 0 = use permanent MAC address
+        * 1 = use random MAC address for each ESS connection
+        * 2 = like 1, but maintain OUI (with local admin bit set)
+        *
+        * Internally, special value -1 is used to indicate that the parameter
+        * was not specified in the configuration (i.e., default behavior is
+        * followed).
+        */
+       int mac_addr;
+
+       /**
+        * no_auto_peer - Do not automatically peer with compatible mesh peers
+        *
+        * When unset, the reception of a beacon from a another mesh peer in
+        * this MBSS will trigger a peering attempt.
+        */
+       int no_auto_peer;
 };
 
 #endif /* CONFIG_SSID_H */
index 3cf3a91..199f04f 100644 (file)
@@ -302,6 +302,7 @@ static struct wpa_ssid * wpa_config_read_network(HKEY hk, const TCHAR *netw,
                RegCloseKey(nhk);
                return NULL;
        }
+       dl_list_init(&ssid->psk_list);
        ssid->id = id;
 
        wpa_config_set_network_defaults(ssid);
@@ -622,6 +623,9 @@ static int wpa_config_write_global(struct wpa_config *config, HKEY hk)
        wpa_config_write_reg_dword(hk, TEXT("okc"), config->okc, 0);
        wpa_config_write_reg_dword(hk, TEXT("pmf"), config->pmf, 0);
 
+       wpa_config_write_reg_dword(hk, TEXT("external_sim"),
+                                  config->external_sim, 0);
+
        return 0;
 }
 
@@ -926,6 +930,9 @@ static int wpa_config_write_network(HKEY hk, struct wpa_ssid *ssid, int id)
                  MGMT_FRAME_PROTECTION_DEFAULT);
 #endif /* CONFIG_IEEE80211W */
        STR(id_str);
+#ifdef CONFIG_HS20
+       INT(update_identifier);
+#endif /* CONFIG_HS20 */
 
 #undef STR
 #undef INT
old mode 100644 (file)
new mode 100755 (executable)
index ab5feee..cc85012
@@ -1,19 +1,26 @@
 /*
  * WPA Supplicant / Control interface (shared code for all backends)
- * Copyright (c) 2004-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
  */
 
 #include "utils/includes.h"
+#ifdef CONFIG_TESTING_OPTIONS
+#include <net/ethernet.h>
+#include <netinet/ip.h>
+#endif /* CONFIG_TESTING_OPTIONS */
 
 #include "utils/common.h"
 #include "utils/eloop.h"
+#include "utils/uuid.h"
 #include "common/version.h"
 #include "common/ieee802_11_defs.h"
 #include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
+#include "crypto/tls.h"
+#include "ap/hostapd.h"
 #include "eap_peer/eap.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "rsn_supp/wpa.h"
 #include "blacklist.h"
 #include "autoscan.h"
 #include "wnm_sta.h"
-
-extern struct wpa_driver_ops *wpa_drivers[];
+#include "offchannel.h"
+#include "drivers/driver.h"
+#include "mesh.h"
 
 static int wpa_supplicant_global_iface_list(struct wpa_global *global,
                                            char *buf, int len);
 static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global,
                                                  char *buf, int len);
-
-
-static int pno_start(struct wpa_supplicant *wpa_s)
-{
-       int ret;
-       size_t i, num_ssid;
-       struct wpa_ssid *ssid;
-       struct wpa_driver_scan_params params;
-
-       if (wpa_s->pno)
-               return 0;
-
-       if (wpa_s->wpa_state == WPA_SCANNING) {
-               wpa_supplicant_cancel_sched_scan(wpa_s);
-               wpa_supplicant_cancel_scan(wpa_s);
-       }
-
-       os_memset(&params, 0, sizeof(params));
-
-       num_ssid = 0;
-       ssid = wpa_s->conf->ssid;
-       while (ssid) {
-               if (!wpas_network_disabled(wpa_s, ssid))
-                       num_ssid++;
-               ssid = ssid->next;
-       }
-       if (num_ssid > WPAS_MAX_SCAN_SSIDS) {
-               wpa_printf(MSG_DEBUG, "PNO: Use only the first %u SSIDs from "
-                          "%u", WPAS_MAX_SCAN_SSIDS, (unsigned int) num_ssid);
-               num_ssid = WPAS_MAX_SCAN_SSIDS;
-       }
-
-       if (num_ssid == 0) {
-               wpa_printf(MSG_DEBUG, "PNO: No configured SSIDs");
-               return -1;
-       }
-
-       params.filter_ssids = os_malloc(sizeof(struct wpa_driver_scan_filter) *
-                                       num_ssid);
-       if (params.filter_ssids == NULL)
-               return -1;
-       i = 0;
-       ssid = wpa_s->conf->ssid;
-       while (ssid) {
-               if (!wpas_network_disabled(wpa_s, ssid)) {
-                       params.ssids[i].ssid = ssid->ssid;
-                       params.ssids[i].ssid_len = ssid->ssid_len;
-                       params.num_ssids++;
-                       os_memcpy(params.filter_ssids[i].ssid, ssid->ssid,
-                                 ssid->ssid_len);
-                       params.filter_ssids[i].ssid_len = ssid->ssid_len;
-                       params.num_filter_ssids++;
-                       i++;
-                       if (i == num_ssid)
-                               break;
-               }
-               ssid = ssid->next;
-       }
-
-       if (wpa_s->conf->filter_rssi)
-               params.filter_rssi = wpa_s->conf->filter_rssi;
-
-       ret = wpa_drv_sched_scan(wpa_s, &params, 10 * 1000);
-       os_free(params.filter_ssids);
-       if (ret == 0)
-               wpa_s->pno = 1;
-       return ret;
-}
-
-
-static int pno_stop(struct wpa_supplicant *wpa_s)
-{
-       int ret = 0;
-
-       if (wpa_s->pno) {
-               wpa_s->pno = 0;
-               ret = wpa_drv_stop_sched_scan(wpa_s);
-       }
-
-       if (wpa_s->wpa_state == WPA_SCANNING)
-               wpa_supplicant_req_scan(wpa_s, 0, 0);
-
-       return ret;
-}
-
+static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s,
+                                       char *val);
 
 static int set_bssid_filter(struct wpa_supplicant *wpa_s, char *val)
 {
@@ -178,7 +103,7 @@ static int set_disallow_aps(struct wpa_supplicant *wpa_s, char *val)
        struct wpa_ssid *c;
 
        /*
-        * disallow_list ::= <ssid_spec> | <bssid_spec> | <disallow_list> | “”
+        * disallow_list ::= <ssid_spec> | <bssid_spec> | <disallow_list> | ""
         * SSID_SPEC ::= ssid <SSID_HEX>
         * BSSID_SPEC ::= bssid <BSSID_HEX>
         */
@@ -284,6 +209,7 @@ static int set_disallow_aps(struct wpa_supplicant *wpa_s, char *val)
        wpa_s->sme.prev_bssid_set = 0;
 #endif /* CONFIG_SME */
        wpa_s->reassociate = 1;
+       wpa_s->own_disconnect_req = 1;
        wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
        wpa_supplicant_req_scan(wpa_s, 0, 0);
 
@@ -291,6 +217,72 @@ static int set_disallow_aps(struct wpa_supplicant *wpa_s, char *val)
 }
 
 
+#ifndef CONFIG_NO_CONFIG_BLOBS
+static int wpas_ctrl_set_blob(struct wpa_supplicant *wpa_s, char *pos)
+{
+       char *name = pos;
+       struct wpa_config_blob *blob;
+       size_t len;
+
+       pos = os_strchr(pos, ' ');
+       if (pos == NULL)
+               return -1;
+       *pos++ = '\0';
+       len = os_strlen(pos);
+       if (len & 1)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "CTRL: Set blob '%s'", name);
+       blob = os_zalloc(sizeof(*blob));
+       if (blob == NULL)
+               return -1;
+       blob->name = os_strdup(name);
+       blob->data = os_malloc(len / 2);
+       if (blob->name == NULL || blob->data == NULL) {
+               wpa_config_free_blob(blob);
+               return -1;
+       }
+
+       if (hexstr2bin(pos, blob->data, len / 2) < 0) {
+               wpa_printf(MSG_DEBUG, "CTRL: Invalid blob hex data");
+               wpa_config_free_blob(blob);
+               return -1;
+       }
+       blob->len = len / 2;
+
+       wpa_config_set_blob(wpa_s->conf, blob);
+
+       return 0;
+}
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+
+
+static int wpas_ctrl_pno(struct wpa_supplicant *wpa_s, char *cmd)
+{
+       char *params;
+       char *pos;
+       int *freqs = NULL;
+       int ret;
+
+       if (atoi(cmd)) {
+               params = os_strchr(cmd, ' ');
+               os_free(wpa_s->manual_sched_scan_freqs);
+               if (params) {
+                       params++;
+                       pos = os_strstr(params, "freq=");
+                       if (pos)
+                               freqs = freq_range_to_channel_list(wpa_s,
+                                                                  pos + 5);
+               }
+               wpa_s->manual_sched_scan_freqs = freqs;
+               ret = wpas_start_pno(wpa_s);
+       } else {
+               ret = wpas_stop_pno(wpa_s);
+       }
+       return ret;
+}
+
+
 static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
                                         char *cmd)
 {
@@ -348,17 +340,21 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
                wps_testing_dummy_cred = atoi(value);
                wpa_printf(MSG_DEBUG, "WPS: Testing - dummy_cred=%d",
                           wps_testing_dummy_cred);
+       } else if (os_strcasecmp(cmd, "wps_corrupt_pkhash") == 0) {
+               wps_corrupt_pkhash = atoi(value);
+               wpa_printf(MSG_DEBUG, "WPS: Testing - wps_corrupt_pkhash=%d",
+                          wps_corrupt_pkhash);
 #endif /* CONFIG_WPS_TESTING */
        } else if (os_strcasecmp(cmd, "ampdu") == 0) {
                if (wpa_drv_ampdu(wpa_s, atoi(value)) < 0)
                        ret = -1;
+#ifdef CONFIG_TDLS
 #ifdef CONFIG_TDLS_TESTING
        } else if (os_strcasecmp(cmd, "tdls_testing") == 0) {
                extern unsigned int tdls_testing;
                tdls_testing = strtol(value, NULL, 0);
                wpa_printf(MSG_DEBUG, "TDLS: tdls_testing=0x%x", tdls_testing);
 #endif /* CONFIG_TDLS_TESTING */
-#ifdef CONFIG_TDLS
        } else if (os_strcasecmp(cmd, "tdls_disabled") == 0) {
                int disabled = atoi(value);
                wpa_printf(MSG_DEBUG, "TDLS: tdls_disabled=%d", disabled);
@@ -370,10 +366,7 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
                wpa_tdls_enable(wpa_s->wpa, !disabled);
 #endif /* CONFIG_TDLS */
        } else if (os_strcasecmp(cmd, "pno") == 0) {
-               if (atoi(value))
-                       ret = pno_start(wpa_s);
-               else
-                       ret = pno_stop(wpa_s);
+               ret = wpas_ctrl_pno(wpa_s, value);
        } else if (os_strcasecmp(cmd, "radio_disabled") == 0) {
                int disabled = atoi(value);
                if (wpa_drv_radio_disable(wpa_s, disabled) < 0)
@@ -420,7 +413,11 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
                ret = wpa_drv_set_p2p_powersave(wpa_s, atoi(value), -1, -1);
 #ifdef CONFIG_WIFI_DISPLAY
        } else if (os_strcasecmp(cmd, "wifi_display") == 0) {
-               wifi_display_enable(wpa_s->global, !!atoi(value));
+               int enabled = !!atoi(value);
+               if (enabled && !wpa_s->global->p2p)
+                       ret = -1;
+               else
+                       wifi_display_enable(wpa_s->global, enabled);
 #endif /* CONFIG_WIFI_DISPLAY */
        } else if (os_strcasecmp(cmd, "bssid_filter") == 0) {
                ret = set_bssid_filter(wpa_s, value);
@@ -428,6 +425,35 @@ static int wpa_supplicant_ctrl_iface_set(struct wpa_supplicant *wpa_s,
                ret = set_disallow_aps(wpa_s, value);
        } else if (os_strcasecmp(cmd, "no_keep_alive") == 0) {
                wpa_s->no_keep_alive = !!atoi(value);
+#ifdef CONFIG_TESTING_OPTIONS
+       } else if (os_strcasecmp(cmd, "ext_mgmt_frame_handling") == 0) {
+               wpa_s->ext_mgmt_frame_handling = !!atoi(value);
+       } else if (os_strcasecmp(cmd, "ext_eapol_frame_io") == 0) {
+               wpa_s->ext_eapol_frame_io = !!atoi(value);
+#ifdef CONFIG_AP
+               if (wpa_s->ap_iface) {
+                       wpa_s->ap_iface->bss[0]->ext_eapol_frame_io =
+                               wpa_s->ext_eapol_frame_io;
+               }
+#endif /* CONFIG_AP */
+       } else if (os_strcasecmp(cmd, "extra_roc_dur") == 0) {
+               wpa_s->extra_roc_dur = atoi(value);
+       } else if (os_strcasecmp(cmd, "test_failure") == 0) {
+               wpa_s->test_failure = atoi(value);
+#endif /* CONFIG_TESTING_OPTIONS */
+#ifndef CONFIG_NO_CONFIG_BLOBS
+       } else if (os_strcmp(cmd, "blob") == 0) {
+               ret = wpas_ctrl_set_blob(wpa_s, value);
+#endif /* CONFIG_NO_CONFIG_BLOBS */
+       } else if (os_strcasecmp(cmd, "setband") == 0) {
+               if (os_strcmp(value, "AUTO") == 0)
+                       wpa_s->setband = WPA_SETBAND_AUTO;
+               else if (os_strcmp(value, "5G") == 0)
+                       wpa_s->setband = WPA_SETBAND_5G;
+               else if (os_strcmp(value, "2G") == 0)
+                       wpa_s->setband = WPA_SETBAND_2G;
+               else
+                       ret = -1;
        } else {
                value[-1] = '=';
                ret = wpa_config_process_global(wpa_s->conf, cmd, -1);
@@ -455,11 +481,13 @@ static int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s,
                                          wpa_s->conf->country[1]);
 #ifdef CONFIG_WIFI_DISPLAY
        } else if (os_strcasecmp(cmd, "wifi_display") == 0) {
-               res = os_snprintf(buf, buflen, "%d",
-                                 wpa_s->global->wifi_display);
-               if (res < 0 || (unsigned int) res >= buflen)
-                       return -1;
-               return res;
+               int enabled;
+               if (wpa_s->global->p2p == NULL ||
+                   wpa_s->global->p2p_disabled)
+                       enabled = 0;
+               else
+                       enabled = wpa_s->global->wifi_display;
+               res = os_snprintf(buf, buflen, "%d", enabled);
 #endif /* CONFIG_WIFI_DISPLAY */
 #ifdef CONFIG_TESTING_GET_GTK
        } else if (os_strcmp(cmd, "gtk") == 0) {
@@ -469,9 +497,13 @@ static int wpa_supplicant_ctrl_iface_get(struct wpa_supplicant *wpa_s,
                                       wpa_s->last_gtk_len);
                return res;
 #endif /* CONFIG_TESTING_GET_GTK */
+       } else if (os_strcmp(cmd, "tls_library") == 0) {
+               res = tls_get_library_version(buf, buflen);
+       } else {
+               res = wpa_config_get_value(cmd, wpa_s->conf, buf, buflen);
        }
 
-       if (res < 0 || (unsigned int) res >= buflen)
+       if (os_snprintf_error(buflen, res))
                return -1;
        return res;
 }
@@ -562,6 +594,10 @@ static int wpa_supplicant_ctrl_iface_tdls_setup(
        wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_SETUP " MACSTR,
                   MAC2STR(peer));
 
+       if ((wpa_s->conf->tdls_external_control) &&
+           wpa_tdls_is_external_setup(wpa_s->wpa))
+               return wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer);
+
        wpa_tdls_remove(wpa_s->wpa, peer);
 
        if (wpa_tdls_is_external_setup(wpa_s->wpa))
@@ -577,6 +613,14 @@ static int wpa_supplicant_ctrl_iface_tdls_teardown(
        struct wpa_supplicant *wpa_s, char *addr)
 {
        u8 peer[ETH_ALEN];
+       int ret;
+
+       if (os_strcmp(addr, "*") == 0) {
+               /* remove everyone */
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN *");
+               wpa_tdls_teardown_peers(wpa_s->wpa);
+               return 0;
+       }
 
        if (hwaddr_aton(addr, peer)) {
                wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN: invalid "
@@ -587,13 +631,187 @@ static int wpa_supplicant_ctrl_iface_tdls_teardown(
        wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_TEARDOWN " MACSTR,
                   MAC2STR(peer));
 
-       return wpa_tdls_teardown_link(wpa_s->wpa, peer,
-                                     WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+       if ((wpa_s->conf->tdls_external_control) &&
+           wpa_tdls_is_external_setup(wpa_s->wpa))
+               return wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer);
+
+       if (wpa_tdls_is_external_setup(wpa_s->wpa))
+               ret = wpa_tdls_teardown_link(
+                       wpa_s->wpa, peer,
+                       WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+       else
+               ret = wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer);
+
+       return ret;
+}
+
+
+static int ctrl_iface_get_capability_tdls(
+       struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
+{
+       int ret;
+
+       ret = os_snprintf(buf, buflen, "%s\n",
+                         wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_SUPPORT ?
+                         (wpa_s->drv_flags &
+                          WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP ?
+                          "EXTERNAL" : "INTERNAL") : "UNSUPPORTED");
+       if (os_snprintf_error(buflen, ret))
+               return -1;
+       return ret;
+}
+
+
+static int wpa_supplicant_ctrl_iface_tdls_chan_switch(
+       struct wpa_supplicant *wpa_s, char *cmd)
+{
+       u8 peer[ETH_ALEN];
+       struct hostapd_freq_params freq_params;
+       u8 oper_class;
+       char *pos, *end;
+
+       if (!wpa_tdls_is_external_setup(wpa_s->wpa)) {
+               wpa_printf(MSG_INFO,
+                          "tdls_chanswitch: Only supported with external setup");
+               return -1;
+       }
+
+       os_memset(&freq_params, 0, sizeof(freq_params));
+
+       pos = os_strchr(cmd, ' ');
+       if (pos == NULL)
+               return -1;
+       *pos++ = '\0';
+
+       oper_class = strtol(pos, &end, 10);
+       if (pos == end) {
+               wpa_printf(MSG_INFO,
+                          "tdls_chanswitch: Invalid op class provided");
+               return -1;
+       }
+
+       pos = end;
+       freq_params.freq = atoi(pos);
+       if (freq_params.freq == 0) {
+               wpa_printf(MSG_INFO, "tdls_chanswitch: Invalid freq provided");
+               return -1;
+       }
+
+#define SET_FREQ_SETTING(str) \
+       do { \
+               const char *pos2 = os_strstr(pos, " " #str "="); \
+               if (pos2) { \
+                       pos2 += sizeof(" " #str "=") - 1; \
+                       freq_params.str = atoi(pos2); \
+               } \
+       } while (0)
+
+       SET_FREQ_SETTING(center_freq1);
+       SET_FREQ_SETTING(center_freq2);
+       SET_FREQ_SETTING(bandwidth);
+       SET_FREQ_SETTING(sec_channel_offset);
+#undef SET_FREQ_SETTING
+
+       freq_params.ht_enabled = !!os_strstr(pos, " ht");
+       freq_params.vht_enabled = !!os_strstr(pos, " vht");
+
+       if (hwaddr_aton(cmd, peer)) {
+               wpa_printf(MSG_DEBUG,
+                          "CTRL_IFACE TDLS_CHAN_SWITCH: Invalid address '%s'",
+                          cmd);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CHAN_SWITCH " MACSTR
+                  " OP CLASS %d FREQ %d CENTER1 %d CENTER2 %d BW %d SEC_OFFSET %d%s%s",
+                  MAC2STR(peer), oper_class, freq_params.freq,
+                  freq_params.center_freq1, freq_params.center_freq2,
+                  freq_params.bandwidth, freq_params.sec_channel_offset,
+                  freq_params.ht_enabled ? " HT" : "",
+                  freq_params.vht_enabled ? " VHT" : "");
+
+       return wpa_tdls_enable_chan_switch(wpa_s->wpa, peer, oper_class,
+                                          &freq_params);
+}
+
+
+static int wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch(
+       struct wpa_supplicant *wpa_s, char *cmd)
+{
+       u8 peer[ETH_ALEN];
+
+       if (!wpa_tdls_is_external_setup(wpa_s->wpa)) {
+               wpa_printf(MSG_INFO,
+                          "tdls_chanswitch: Only supported with external setup");
+               return -1;
+       }
+
+       if (hwaddr_aton(cmd, peer)) {
+               wpa_printf(MSG_DEBUG,
+                          "CTRL_IFACE TDLS_CANCEL_CHAN_SWITCH: Invalid address '%s'",
+                          cmd);
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE TDLS_CANCEL_CHAN_SWITCH " MACSTR,
+                  MAC2STR(peer));
+
+       return wpa_tdls_disable_chan_switch(wpa_s->wpa, peer);
 }
 
 #endif /* CONFIG_TDLS */
 
 
+static int wmm_ac_ctrl_addts(struct wpa_supplicant *wpa_s, char *cmd)
+{
+       char *token, *context = NULL;
+       struct wmm_ac_ts_setup_params params = {
+               .tsid = 0xff,
+               .direction = 0xff,
+       };
+
+       while ((token = str_token(cmd, " ", &context))) {
+               if (sscanf(token, "tsid=%i", &params.tsid) == 1 ||
+                   sscanf(token, "up=%i", &params.user_priority) == 1 ||
+                   sscanf(token, "nominal_msdu_size=%i",
+                          &params.nominal_msdu_size) == 1 ||
+                   sscanf(token, "mean_data_rate=%i",
+                          &params.mean_data_rate) == 1 ||
+                   sscanf(token, "min_phy_rate=%i",
+                          &params.minimum_phy_rate) == 1 ||
+                   sscanf(token, "sba=%i",
+                          &params.surplus_bandwidth_allowance) == 1)
+                       continue;
+
+               if (os_strcasecmp(token, "downlink") == 0) {
+                       params.direction = WMM_TSPEC_DIRECTION_DOWNLINK;
+               } else if (os_strcasecmp(token, "uplink") == 0) {
+                       params.direction = WMM_TSPEC_DIRECTION_UPLINK;
+               } else if (os_strcasecmp(token, "bidi") == 0) {
+                       params.direction = WMM_TSPEC_DIRECTION_BI_DIRECTIONAL;
+               } else if (os_strcasecmp(token, "fixed_nominal_msdu") == 0) {
+                       params.fixed_nominal_msdu = 1;
+               } else {
+                       wpa_printf(MSG_DEBUG,
+                                  "CTRL: Invalid WMM_AC_ADDTS parameter: '%s'",
+                                  token);
+                       return -1;
+               }
+
+       }
+
+       return wpas_wmm_ac_addts(wpa_s, &params);
+}
+
+
+static int wmm_ac_ctrl_delts(struct wpa_supplicant *wpa_s, char *cmd)
+{
+       u8 tsid = atoi(cmd);
+
+       return wpas_wmm_ac_delts(wpa_s, tsid);
+}
+
+
 #ifdef CONFIG_IEEE80211R
 static int wpa_supplicant_ctrl_iface_ft_ds(
        struct wpa_supplicant *wpa_s, char *addr)
@@ -707,7 +925,7 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s,
                if (ret < 0)
                        return -1;
                ret = os_snprintf(buf, buflen, "%s", pin);
-               if (ret < 0 || (size_t) ret >= buflen)
+               if (os_snprintf_error(buflen, ret))
                        return -1;
                return ret;
        }
@@ -719,7 +937,7 @@ static int wpa_supplicant_ctrl_iface_wps_pin(struct wpa_supplicant *wpa_s,
 done:
        /* Return the generated PIN */
        ret = os_snprintf(buf, buflen, "%08d", ret);
-       if (ret < 0 || (size_t) ret >= buflen)
+       if (os_snprintf_error(buflen, ret))
                return -1;
        return ret;
 }
@@ -756,14 +974,14 @@ static int wpa_supplicant_ctrl_iface_wps_check_pin(
                if (!wps_pin_valid(pin_val)) {
                        wpa_printf(MSG_DEBUG, "WPS: Invalid checksum digit");
                        ret = os_snprintf(buf, buflen, "FAIL-CHECKSUM\n");
-                       if (ret < 0 || (size_t) ret >= buflen)
+                       if (os_snprintf_error(buflen, ret))
                                return -1;
                        return ret;
                }
        }
 
        ret = os_snprintf(buf, buflen, "%s", pin);
-       if (ret < 0 || (size_t) ret >= buflen)
+       if (os_snprintf_error(buflen, ret))
                return -1;
 
        return ret;
@@ -782,7 +1000,8 @@ static int wpa_supplicant_ctrl_iface_wps_nfc(struct wpa_supplicant *wpa_s,
        else if (hwaddr_aton(cmd, bssid))
                return -1;
 
-       return wpas_wps_start_nfc(wpa_s, _bssid);
+       return wpas_wps_start_nfc(wpa_s, NULL, _bssid, NULL, 0, 0, NULL, NULL,
+                                 0, 0);
 }
 
 
@@ -854,6 +1073,15 @@ static int wpa_supplicant_ctrl_iface_wps_nfc_tag_read(
        size_t len;
        struct wpabuf *buf;
        int ret;
+       char *freq;
+       int forced_freq = 0;
+
+       freq = strstr(pos, " freq=");
+       if (freq) {
+               *freq = '\0';
+               freq += 6;
+               forced_freq = atoi(freq);
+       }
 
        len = os_strlen(pos);
        if (len & 0x01)
@@ -868,7 +1096,7 @@ static int wpa_supplicant_ctrl_iface_wps_nfc_tag_read(
                return -1;
        }
 
-       ret = wpas_wps_nfc_tag_read(wpa_s, buf);
+       ret = wpas_wps_nfc_tag_read(wpa_s, buf, forced_freq);
        wpabuf_free(buf);
 
        return ret;
@@ -877,12 +1105,12 @@ static int wpa_supplicant_ctrl_iface_wps_nfc_tag_read(
 
 static int wpas_ctrl_nfc_get_handover_req_wps(struct wpa_supplicant *wpa_s,
                                              char *reply, size_t max_len,
-                                             int cr)
+                                             int ndef)
 {
        struct wpabuf *buf;
        int res;
 
-       buf = wpas_wps_nfc_handover_req(wpa_s, cr);
+       buf = wpas_wps_nfc_handover_req(wpa_s, ndef);
        if (buf == NULL)
                return -1;
 
@@ -897,24 +1125,64 @@ static int wpas_ctrl_nfc_get_handover_req_wps(struct wpa_supplicant *wpa_s,
 }
 
 
+#ifdef CONFIG_P2P
+static int wpas_ctrl_nfc_get_handover_req_p2p(struct wpa_supplicant *wpa_s,
+                                             char *reply, size_t max_len,
+                                             int ndef)
+{
+       struct wpabuf *buf;
+       int res;
+
+       buf = wpas_p2p_nfc_handover_req(wpa_s, ndef);
+       if (buf == NULL) {
+               wpa_printf(MSG_DEBUG, "P2P: Could not generate NFC handover request");
+               return -1;
+       }
+
+       res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+                                        wpabuf_len(buf));
+       reply[res++] = '\n';
+       reply[res] = '\0';
+
+       wpabuf_free(buf);
+
+       return res;
+}
+#endif /* CONFIG_P2P */
+
+
 static int wpas_ctrl_nfc_get_handover_req(struct wpa_supplicant *wpa_s,
                                          char *cmd, char *reply,
                                          size_t max_len)
 {
        char *pos;
+       int ndef;
 
        pos = os_strchr(cmd, ' ');
        if (pos == NULL)
                return -1;
        *pos++ = '\0';
 
-       if (os_strcmp(cmd, "NDEF") != 0)
+       if (os_strcmp(cmd, "WPS") == 0)
+               ndef = 0;
+       else if (os_strcmp(cmd, "NDEF") == 0)
+               ndef = 1;
+       else
                return -1;
 
        if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) {
+               if (!ndef)
+                       return -1;
                return wpas_ctrl_nfc_get_handover_req_wps(
-                       wpa_s, reply, max_len, os_strcmp(pos, "WPS-CR") == 0);
+                       wpa_s, reply, max_len, ndef);
+       }
+
+#ifdef CONFIG_P2P
+       if (os_strcmp(pos, "P2P-CR") == 0) {
+               return wpas_ctrl_nfc_get_handover_req_p2p(
+                       wpa_s, reply, max_len, ndef);
        }
+#endif /* CONFIG_P2P */
 
        return -1;
 }
@@ -942,6 +1210,30 @@ static int wpas_ctrl_nfc_get_handover_sel_wps(struct wpa_supplicant *wpa_s,
 }
 
 
+#ifdef CONFIG_P2P
+static int wpas_ctrl_nfc_get_handover_sel_p2p(struct wpa_supplicant *wpa_s,
+                                             char *reply, size_t max_len,
+                                             int ndef, int tag)
+{
+       struct wpabuf *buf;
+       int res;
+
+       buf = wpas_p2p_nfc_handover_sel(wpa_s, ndef, tag);
+       if (buf == NULL)
+               return -1;
+
+       res = wpa_snprintf_hex_uppercase(reply, max_len, wpabuf_head(buf),
+                                        wpabuf_len(buf));
+       reply[res++] = '\n';
+       reply[res] = '\0';
+
+       wpabuf_free(buf);
+
+       return res;
+}
+#endif /* CONFIG_P2P */
+
+
 static int wpas_ctrl_nfc_get_handover_sel(struct wpa_supplicant *wpa_s,
                                          char *cmd, char *reply,
                                          size_t max_len)
@@ -965,110 +1257,92 @@ static int wpas_ctrl_nfc_get_handover_sel(struct wpa_supplicant *wpa_s,
        if (pos2)
                *pos2++ = '\0';
        if (os_strcmp(pos, "WPS") == 0 || os_strcmp(pos, "WPS-CR") == 0) {
+               if (!ndef)
+                       return -1;
                return wpas_ctrl_nfc_get_handover_sel_wps(
                        wpa_s, reply, max_len, ndef,
                        os_strcmp(pos, "WPS-CR") == 0, pos2);
        }
 
+#ifdef CONFIG_P2P
+       if (os_strcmp(pos, "P2P-CR") == 0) {
+               return wpas_ctrl_nfc_get_handover_sel_p2p(
+                       wpa_s, reply, max_len, ndef, 0);
+       }
+
+       if (os_strcmp(pos, "P2P-CR-TAG") == 0) {
+               return wpas_ctrl_nfc_get_handover_sel_p2p(
+                       wpa_s, reply, max_len, ndef, 1);
+       }
+#endif /* CONFIG_P2P */
+
        return -1;
 }
 
 
-static int wpas_ctrl_nfc_rx_handover_req(struct wpa_supplicant *wpa_s,
-                                        char *cmd, char *reply,
-                                        size_t max_len)
+static int wpas_ctrl_nfc_report_handover(struct wpa_supplicant *wpa_s,
+                                        char *cmd)
 {
        size_t len;
-       struct wpabuf *buf;
+       struct wpabuf *req, *sel;
        int ret;
+       char *pos, *role, *type, *pos2;
+#ifdef CONFIG_P2P
+       char *freq;
+       int forced_freq = 0;
 
-       len = os_strlen(cmd);
-       if (len & 0x01)
-               return -1;
-       len /= 2;
-
-       buf = wpabuf_alloc(len);
-       if (buf == NULL)
-               return -1;
-       if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) {
-               wpabuf_free(buf);
-               return -1;
-       }
-
-       ret = wpas_wps_nfc_rx_handover_req(wpa_s, buf);
-       wpabuf_free(buf);
-
-       return ret;
-}
-
-
-static int wpas_ctrl_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
-                                        char *cmd)
-{
-       size_t len;
-       struct wpabuf *buf;
-       int ret;
-
-       len = os_strlen(cmd);
-       if (len & 0x01)
-               return -1;
-       len /= 2;
-
-       buf = wpabuf_alloc(len);
-       if (buf == NULL)
-               return -1;
-       if (hexstr2bin(cmd, wpabuf_put(buf, len), len) < 0) {
-               wpabuf_free(buf);
-               return -1;
-       }
-
-       ret = wpas_wps_nfc_rx_handover_sel(wpa_s, buf);
-       wpabuf_free(buf);
-
-       return ret;
-}
-
-
-static int wpas_ctrl_nfc_report_handover(struct wpa_supplicant *wpa_s,
-                                        char *cmd)
-{
-       size_t len;
-       struct wpabuf *req, *sel;
-       int ret;
-       char *pos, *role, *type, *pos2;
+       freq = strstr(cmd, " freq=");
+       if (freq) {
+               *freq = '\0';
+               freq += 6;
+               forced_freq = atoi(freq);
+       }
+#endif /* CONFIG_P2P */
 
        role = cmd;
        pos = os_strchr(role, ' ');
-       if (pos == NULL)
+       if (pos == NULL) {
+               wpa_printf(MSG_DEBUG, "NFC: Missing type in handover report");
                return -1;
+       }
        *pos++ = '\0';
 
        type = pos;
        pos = os_strchr(type, ' ');
-       if (pos == NULL)
+       if (pos == NULL) {
+               wpa_printf(MSG_DEBUG, "NFC: Missing request message in handover report");
                return -1;
+       }
        *pos++ = '\0';
 
        pos2 = os_strchr(pos, ' ');
-       if (pos2 == NULL)
+       if (pos2 == NULL) {
+               wpa_printf(MSG_DEBUG, "NFC: Missing select message in handover report");
                return -1;
+       }
        *pos2++ = '\0';
 
        len = os_strlen(pos);
-       if (len & 0x01)
+       if (len & 0x01) {
+               wpa_printf(MSG_DEBUG, "NFC: Invalid request message length in handover report");
                return -1;
+       }
        len /= 2;
 
        req = wpabuf_alloc(len);
-       if (req == NULL)
+       if (req == NULL) {
+               wpa_printf(MSG_DEBUG, "NFC: Failed to allocate memory for request message");
                return -1;
+       }
        if (hexstr2bin(pos, wpabuf_put(req, len), len) < 0) {
+               wpa_printf(MSG_DEBUG, "NFC: Invalid request message hexdump in handover report");
                wpabuf_free(req);
                return -1;
        }
 
        len = os_strlen(pos2);
        if (len & 0x01) {
+               wpa_printf(MSG_DEBUG, "NFC: Invalid select message length in handover report");
                wpabuf_free(req);
                return -1;
        }
@@ -1076,17 +1350,38 @@ static int wpas_ctrl_nfc_report_handover(struct wpa_supplicant *wpa_s,
 
        sel = wpabuf_alloc(len);
        if (sel == NULL) {
+               wpa_printf(MSG_DEBUG, "NFC: Failed to allocate memory for select message");
                wpabuf_free(req);
                return -1;
        }
        if (hexstr2bin(pos2, wpabuf_put(sel, len), len) < 0) {
+               wpa_printf(MSG_DEBUG, "NFC: Invalid select message hexdump in handover report");
                wpabuf_free(req);
                wpabuf_free(sel);
                return -1;
        }
 
+       wpa_printf(MSG_DEBUG, "NFC: Connection handover reported - role=%s type=%s req_len=%d sel_len=%d",
+                  role, type, (int) wpabuf_len(req), (int) wpabuf_len(sel));
+
        if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "WPS") == 0) {
                ret = wpas_wps_nfc_report_handover(wpa_s, req, sel);
+#ifdef CONFIG_AP
+       } else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "WPS") == 0)
+       {
+               ret = wpas_ap_wps_nfc_report_handover(wpa_s, req, sel);
+               if (ret < 0)
+                       ret = wpas_er_wps_nfc_report_handover(wpa_s, req, sel);
+#endif /* CONFIG_AP */
+#ifdef CONFIG_P2P
+       } else if (os_strcmp(role, "INIT") == 0 && os_strcmp(type, "P2P") == 0)
+       {
+               ret = wpas_p2p_nfc_report_handover(wpa_s, 1, req, sel, 0);
+       } else if (os_strcmp(role, "RESP") == 0 && os_strcmp(type, "P2P") == 0)
+       {
+               ret = wpas_p2p_nfc_report_handover(wpa_s, 0, req, sel,
+                                                  forced_freq);
+#endif /* CONFIG_P2P */
        } else {
                wpa_printf(MSG_DEBUG, "NFC: Unsupported connection handover "
                           "reported: role=%s type=%s", role, type);
@@ -1095,6 +1390,9 @@ static int wpas_ctrl_nfc_report_handover(struct wpa_supplicant *wpa_s,
        wpabuf_free(req);
        wpabuf_free(sel);
 
+       if (ret)
+               wpa_printf(MSG_DEBUG, "NFC: Failed to process reported handover messages");
+
        return ret;
 }
 
@@ -1403,7 +1701,14 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
 {
        char *pos, *end, tmp[30];
        int res, verbose, wps, ret;
+#ifdef CONFIG_HS20
+       const u8 *hs20;
+#endif /* CONFIG_HS20 */
+       const u8 *sess_id;
+       size_t sess_id_len;
 
+       if (os_strcmp(params, "-DRIVER") == 0)
+               return wpa_drv_status(wpa_s, buf, buflen);
        verbose = os_strcmp(params, "-VERBOSE") == 0;
        wps = os_strcmp(params, "-WPS") == 0;
        pos = buf;
@@ -1412,7 +1717,12 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
                struct wpa_ssid *ssid = wpa_s->current_ssid;
                ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n",
                                  MAC2STR(wpa_s->bssid));
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+               ret = os_snprintf(pos, end - pos, "freq=%u\n",
+                                 wpa_s->assoc_freq);
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
                if (ssid) {
@@ -1430,7 +1740,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
                        ret = os_snprintf(pos, end - pos, "ssid=%s\nid=%d\n",
                                          wpa_ssid_txt(_ssid, ssid_len),
                                          ssid->id);
-                       if (ret < 0 || ret >= end - pos)
+                       if (os_snprintf_error(end - pos, ret))
                                return pos - buf;
                        pos += ret;
 
@@ -1441,7 +1751,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
                                ret = os_snprintf(pos, end - pos,
                                                  "passphrase=%s\n",
                                                  ssid->passphrase);
-                               if (ret < 0 || ret >= end - pos)
+                               if (os_snprintf_error(end - pos, ret))
                                        return pos - buf;
                                pos += ret;
                        }
@@ -1449,7 +1759,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
                                ret = os_snprintf(pos, end - pos,
                                                  "id_str=%s\n",
                                                  ssid->id_str);
-                               if (ret < 0 || ret >= end - pos)
+                               if (os_snprintf_error(end - pos, ret))
                                        return pos - buf;
                                pos += ret;
                        }
@@ -1480,7 +1790,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
                                ret = 0;
                                break;
                        }
-                       if (ret < 0 || ret >= end - pos)
+                       if (os_snprintf_error(end - pos, ret))
                                return pos - buf;
                        pos += ret;
                }
@@ -1496,24 +1806,27 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
        }
 #ifdef CONFIG_SAE
        if (wpa_s->wpa_state >= WPA_ASSOCIATED &&
-           wpa_s->sme.sae.state == SAE_ACCEPTED && !wpa_s->ap_iface) {
+#ifdef CONFIG_AP
+           !wpa_s->ap_iface &&
+#endif /* CONFIG_AP */
+           wpa_s->sme.sae.state == SAE_ACCEPTED) {
                ret = os_snprintf(pos, end - pos, "sae_group=%d\n",
                                  wpa_s->sme.sae.group);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
 #endif /* CONFIG_SAE */
        ret = os_snprintf(pos, end - pos, "wpa_state=%s\n",
                          wpa_supplicant_state_txt(wpa_s->wpa_state));
-       if (ret < 0 || ret >= end - pos)
+       if (os_snprintf_error(end - pos, ret))
                return pos - buf;
        pos += ret;
 
        if (wpa_s->l2 &&
            l2_packet_get_ip_addr(wpa_s->l2, tmp, sizeof(tmp)) >= 0) {
                ret = os_snprintf(pos, end - pos, "ip_address=%s\n", tmp);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
@@ -1522,7 +1835,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
        if (wpa_s->global->p2p) {
                ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR
                                  "\n", MAC2STR(wpa_s->global->p2p_dev_addr));
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
@@ -1530,17 +1843,23 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
 
        ret = os_snprintf(pos, end - pos, "address=" MACSTR "\n",
                          MAC2STR(wpa_s->own_addr));
-       if (ret < 0 || ret >= end - pos)
+       if (os_snprintf_error(end - pos, ret))
                return pos - buf;
        pos += ret;
 
 #ifdef CONFIG_HS20
        if (wpa_s->current_bss &&
-           wpa_bss_get_vendor_ie(wpa_s->current_bss, HS20_IE_VENDOR_TYPE) &&
+           (hs20 = wpa_bss_get_vendor_ie(wpa_s->current_bss,
+                                         HS20_IE_VENDOR_TYPE)) &&
            wpa_s->wpa_proto == WPA_PROTO_RSN &&
            wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt)) {
-               ret = os_snprintf(pos, end - pos, "hs20=1\n");
-               if (ret < 0 || ret >= end - pos)
+               int release = 1;
+               if (hs20[1] >= 5) {
+                       u8 rel_num = (hs20[6] & 0xf0) >> 4;
+                       release = rel_num + 1;
+               }
+               ret = os_snprintf(pos, end - pos, "hs20=%d\n", release);
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
@@ -1550,17 +1869,43 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
                char *type;
 
                for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+                       size_t i;
+
                        if (wpa_s->current_ssid->parent_cred != cred)
                                continue;
-                       if (!cred->domain)
-                               continue;
 
+                       if (cred->provisioning_sp) {
+                               ret = os_snprintf(pos, end - pos,
+                                                 "provisioning_sp=%s\n",
+                                                 cred->provisioning_sp);
+                               if (os_snprintf_error(end - pos, ret))
+                                       return pos - buf;
+                               pos += ret;
+                       }
+
+                       if (!cred->domain)
+                               goto no_domain;
+
+                       i = 0;
+                       if (wpa_s->current_bss && wpa_s->current_bss->anqp) {
+                               struct wpabuf *names =
+                                       wpa_s->current_bss->anqp->domain_name;
+                               for (i = 0; names && i < cred->num_domain; i++)
+                               {
+                                       if (domain_name_list_contains(
+                                                   names, cred->domain[i], 1))
+                                               break;
+                               }
+                               if (i == cred->num_domain)
+                                       i = 0; /* show first entry by default */
+                       }
                        ret = os_snprintf(pos, end - pos, "home_sp=%s\n",
-                                         cred->domain);
-                       if (ret < 0 || ret >= end - pos)
+                                         cred->domain[i]);
+                       if (os_snprintf_error(end - pos, ret))
                                return pos - buf;
                        pos += ret;
 
+               no_domain:
                        if (wpa_s->current_bss == NULL ||
                            wpa_s->current_bss->anqp == NULL)
                                res = -1;
@@ -1576,7 +1921,7 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
                                type = "unknown";
 
                        ret = os_snprintf(pos, end - pos, "sp_type=%s\n", type);
-                       if (ret < 0 || ret >= end - pos)
+                       if (os_snprintf_error(end - pos, ret))
                                return pos - buf;
                        pos += ret;
 
@@ -1593,10 +1938,66 @@ static int wpa_supplicant_ctrl_iface_status(struct wpa_supplicant *wpa_s,
                        pos += res;
        }
 
+       sess_id = eapol_sm_get_session_id(wpa_s->eapol, &sess_id_len);
+       if (sess_id) {
+               char *start = pos;
+
+               ret = os_snprintf(pos, end - pos, "eap_session_id=");
+               if (os_snprintf_error(end - pos, ret))
+                       return start - buf;
+               pos += ret;
+               ret = wpa_snprintf_hex(pos, end - pos, sess_id, sess_id_len);
+               if (ret <= 0)
+                       return start - buf;
+               pos += ret;
+               ret = os_snprintf(pos, end - pos, "\n");
+               if (os_snprintf_error(end - pos, ret))
+                       return start - buf;
+               pos += ret;
+       }
+
        res = rsn_preauth_get_status(wpa_s->wpa, pos, end - pos, verbose);
        if (res >= 0)
                pos += res;
 
+#ifdef CONFIG_WPS
+       {
+               char uuid_str[100];
+               uuid_bin2str(wpa_s->wps->uuid, uuid_str, sizeof(uuid_str));
+               ret = os_snprintf(pos, end - pos, "uuid=%s\n", uuid_str);
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+#endif /* CONFIG_WPS */
+
+#ifdef ANDROID
+       /*
+        * Allow using the STATUS command with default behavior, say for debug,
+        * i.e., don't generate a "fake" CONNECTION and SUPPLICANT_STATE_CHANGE
+        * events with STATUS-NO_EVENTS.
+        */
+       if (os_strcmp(params, "-NO_EVENTS")) {
+               wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_STATE_CHANGE
+                            "id=%d state=%d BSSID=" MACSTR " SSID=%s",
+                            wpa_s->current_ssid ? wpa_s->current_ssid->id : -1,
+                            wpa_s->wpa_state,
+                            MAC2STR(wpa_s->bssid),
+                            wpa_s->current_ssid && wpa_s->current_ssid->ssid ?
+                            wpa_ssid_txt(wpa_s->current_ssid->ssid,
+                                         wpa_s->current_ssid->ssid_len) : "");
+               if (wpa_s->wpa_state == WPA_COMPLETED) {
+                       struct wpa_ssid *ssid = wpa_s->current_ssid;
+                       wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED
+                                    "- connection to " MACSTR
+                                    " completed %s [id=%d id_str=%s]",
+                                    MAC2STR(wpa_s->bssid), "(auth)",
+                                    ssid ? ssid->id : -1,
+                                    ssid && ssid->id_str ? ssid->id_str : "");
+               }
+       }
+#endif /* ANDROID */
+
        return pos - buf;
 }
 
@@ -1652,7 +2053,7 @@ static int wpa_supplicant_ctrl_iface_blacklist(struct wpa_supplicant *wpa_s,
                while (e) {
                        ret = os_snprintf(pos, end - pos, MACSTR "\n",
                                          MAC2STR(e->bssid));
-                       if (ret < 0 || ret >= end - pos)
+                       if (os_snprintf_error(end - pos, ret))
                                return pos - buf;
                        pos += ret;
                        e = e->next;
@@ -1678,19 +2079,16 @@ static int wpa_supplicant_ctrl_iface_blacklist(struct wpa_supplicant *wpa_s,
         * skipped when processing scan results.
         */
        ret = wpa_blacklist_add(wpa_s, bssid);
-       if (ret != 0)
+       if (ret < 0)
                return -1;
        ret = wpa_blacklist_add(wpa_s, bssid);
-       if (ret != 0)
+       if (ret < 0)
                return -1;
        os_memcpy(buf, "OK\n", 3);
        return 3;
 }
 
 
-extern int wpa_debug_level;
-extern int wpa_debug_timestamp;
-
 static const char * debug_level_str(int level)
 {
        switch (level) {
@@ -1737,10 +2135,6 @@ static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s,
        char *pos, *end, *stamp;
        int ret;
 
-       if (cmd == NULL) {
-               return -1;
-       }
-
        /* cmd: "LOG_LEVEL [<level>]" */
        if (*cmd == '\0') {
                pos = buf;
@@ -1749,7 +2143,7 @@ static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s,
                                  "Timestamp: %d\n",
                                  debug_level_str(wpa_debug_level),
                                  wpa_debug_timestamp);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        ret = 0;
 
                return ret;
@@ -1782,9 +2176,9 @@ static int wpa_supplicant_ctrl_iface_log_level(struct wpa_supplicant *wpa_s,
 
 
 static int wpa_supplicant_ctrl_iface_list_networks(
-       struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
+       struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
 {
-       char *pos, *end;
+       char *pos, *end, *prev;
        struct wpa_ssid *ssid;
        int ret;
 
@@ -1792,17 +2186,29 @@ static int wpa_supplicant_ctrl_iface_list_networks(
        end = buf + buflen;
        ret = os_snprintf(pos, end - pos,
                          "network id / ssid / bssid / flags\n");
-       if (ret < 0 || ret >= end - pos)
+       if (os_snprintf_error(end - pos, ret))
                return pos - buf;
        pos += ret;
 
        ssid = wpa_s->conf->ssid;
+
+       /* skip over ssids until we find next one */
+       if (cmd != NULL && os_strncmp(cmd, "LAST_ID=", 8) == 0) {
+               int last_id = atoi(cmd + 8);
+               if (last_id != -1) {
+                       while (ssid != NULL && ssid->id <= last_id) {
+                               ssid = ssid->next;
+                       }
+               }
+       }
+
        while (ssid) {
+               prev = pos;
                ret = os_snprintf(pos, end - pos, "%d\t%s",
                                  ssid->id,
                                  wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
-               if (ret < 0 || ret >= end - pos)
-                       return pos - buf;
+               if (os_snprintf_error(end - pos, ret))
+                       return prev - buf;
                pos += ret;
                if (ssid->bssid_set) {
                        ret = os_snprintf(pos, end - pos, "\t" MACSTR,
@@ -1810,8 +2216,8 @@ static int wpa_supplicant_ctrl_iface_list_networks(
                } else {
                        ret = os_snprintf(pos, end - pos, "\tany");
                }
-               if (ret < 0 || ret >= end - pos)
-                       return pos - buf;
+               if (os_snprintf_error(end - pos, ret))
+                       return prev - buf;
                pos += ret;
                ret = os_snprintf(pos, end - pos, "\t%s%s%s%s",
                                  ssid == wpa_s->current_ssid ?
@@ -1821,12 +2227,12 @@ static int wpa_supplicant_ctrl_iface_list_networks(
                                  "[TEMP-DISABLED]" : "",
                                  ssid->disabled == 2 ? "[P2P-PERSISTENT]" :
                                  "");
-               if (ret < 0 || ret >= end - pos)
-                       return pos - buf;
+               if (os_snprintf_error(end - pos, ret))
+                       return prev - buf;
                pos += ret;
                ret = os_snprintf(pos, end - pos, "\n");
-               if (ret < 0 || ret >= end - pos)
-                       return pos - buf;
+               if (os_snprintf_error(end - pos, ret))
+                       return prev - buf;
                pos += ret;
 
                ssid = ssid->next;
@@ -1840,7 +2246,7 @@ static char * wpa_supplicant_cipher_txt(char *pos, char *end, int cipher)
 {
        int ret;
        ret = os_snprintf(pos, end - pos, "-");
-       if (ret < 0 || ret >= end - pos)
+       if (os_snprintf_error(end - pos, ret))
                return pos;
        pos += ret;
        ret = wpa_write_ciphers(pos, end, cipher, "+");
@@ -1855,91 +2261,122 @@ static char * wpa_supplicant_ie_txt(char *pos, char *end, const char *proto,
                                    const u8 *ie, size_t ie_len)
 {
        struct wpa_ie_data data;
-       int first, ret;
+       char *start;
+       int ret;
 
        ret = os_snprintf(pos, end - pos, "[%s-", proto);
-       if (ret < 0 || ret >= end - pos)
+       if (os_snprintf_error(end - pos, ret))
                return pos;
        pos += ret;
 
        if (wpa_parse_wpa_ie(ie, ie_len, &data) < 0) {
                ret = os_snprintf(pos, end - pos, "?]");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos;
                pos += ret;
                return pos;
        }
 
-       first = 1;
+       start = pos;
        if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) {
-               ret = os_snprintf(pos, end - pos, "%sEAP", first ? "" : "+");
-               if (ret < 0 || ret >= end - pos)
+               ret = os_snprintf(pos, end - pos, "%sEAP",
+                                 pos == start ? "" : "+");
+               if (os_snprintf_error(end - pos, ret))
                        return pos;
                pos += ret;
-               first = 0;
        }
        if (data.key_mgmt & WPA_KEY_MGMT_PSK) {
-               ret = os_snprintf(pos, end - pos, "%sPSK", first ? "" : "+");
-               if (ret < 0 || ret >= end - pos)
+               ret = os_snprintf(pos, end - pos, "%sPSK",
+                                 pos == start ? "" : "+");
+               if (os_snprintf_error(end - pos, ret))
                        return pos;
                pos += ret;
-               first = 0;
        }
        if (data.key_mgmt & WPA_KEY_MGMT_WPA_NONE) {
-               ret = os_snprintf(pos, end - pos, "%sNone", first ? "" : "+");
-               if (ret < 0 || ret >= end - pos)
+               ret = os_snprintf(pos, end - pos, "%sNone",
+                                 pos == start ? "" : "+");
+               if (os_snprintf_error(end - pos, ret))
+                       return pos;
+               pos += ret;
+       }
+       if (data.key_mgmt & WPA_KEY_MGMT_SAE) {
+               ret = os_snprintf(pos, end - pos, "%sSAE",
+                                 pos == start ? "" : "+");
+               if (os_snprintf_error(end - pos, ret))
                        return pos;
                pos += ret;
-               first = 0;
        }
 #ifdef CONFIG_IEEE80211R
        if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) {
                ret = os_snprintf(pos, end - pos, "%sFT/EAP",
-                                 first ? "" : "+");
-               if (ret < 0 || ret >= end - pos)
+                                 pos == start ? "" : "+");
+               if (os_snprintf_error(end - pos, ret))
                        return pos;
                pos += ret;
-               first = 0;
        }
        if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) {
                ret = os_snprintf(pos, end - pos, "%sFT/PSK",
-                                 first ? "" : "+");
-               if (ret < 0 || ret >= end - pos)
+                                 pos == start ? "" : "+");
+               if (os_snprintf_error(end - pos, ret))
+                       return pos;
+               pos += ret;
+       }
+       if (data.key_mgmt & WPA_KEY_MGMT_FT_SAE) {
+               ret = os_snprintf(pos, end - pos, "%sFT/SAE",
+                                 pos == start ? "" : "+");
+               if (os_snprintf_error(end - pos, ret))
                        return pos;
                pos += ret;
-               first = 0;
        }
 #endif /* CONFIG_IEEE80211R */
 #ifdef CONFIG_IEEE80211W
        if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) {
                ret = os_snprintf(pos, end - pos, "%sEAP-SHA256",
-                                 first ? "" : "+");
-               if (ret < 0 || ret >= end - pos)
+                                 pos == start ? "" : "+");
+               if (os_snprintf_error(end - pos, ret))
                        return pos;
                pos += ret;
-               first = 0;
        }
        if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
                ret = os_snprintf(pos, end - pos, "%sPSK-SHA256",
-                                 first ? "" : "+");
-               if (ret < 0 || ret >= end - pos)
+                                 pos == start ? "" : "+");
+               if (os_snprintf_error(end - pos, ret))
                        return pos;
                pos += ret;
-               first = 0;
        }
 #endif /* CONFIG_IEEE80211W */
 
+#ifdef CONFIG_SUITEB
+       if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+               ret = os_snprintf(pos, end - pos, "%sEAP-SUITE-B",
+                                 pos == start ? "" : "+");
+               if (os_snprintf_error(end - pos, ret))
+                       return pos;
+               pos += ret;
+       }
+#endif /* CONFIG_SUITEB */
+
+#ifdef CONFIG_SUITEB192
+       if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+               ret = os_snprintf(pos, end - pos, "%sEAP-SUITE-B-192",
+                                 pos == start ? "" : "+");
+               if (os_snprintf_error(end - pos, ret))
+                       return pos;
+               pos += ret;
+       }
+#endif /* CONFIG_SUITEB192 */
+
        pos = wpa_supplicant_cipher_txt(pos, end, data.pairwise_cipher);
 
        if (data.capabilities & WPA_CAPABILITY_PREAUTH) {
                ret = os_snprintf(pos, end - pos, "-preauth");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos;
                pos += ret;
        }
 
        ret = os_snprintf(pos, end - pos, "]");
-       if (ret < 0 || ret >= end - pos)
+       if (os_snprintf_error(end - pos, ret))
                return pos;
        pos += ret;
 
@@ -1959,17 +2396,15 @@ static char * wpa_supplicant_wps_ie_txt_buf(struct wpa_supplicant *wpa_s,
                return pos;
        if (wps_is_selected_pbc_registrar(wps_ie))
                txt = "[WPS-PBC]";
-#ifdef CONFIG_WPS2
        else if (wps_is_addr_authorized(wps_ie, wpa_s->own_addr, 0))
                txt = "[WPS-AUTH]";
-#endif /* CONFIG_WPS2 */
        else if (wps_is_selected_pin_registrar(wps_ie))
                txt = "[WPS-PIN]";
        else
                txt = "[WPS]";
 
        ret = os_snprintf(pos, end - pos, "%s", txt);
-       if (ret >= 0 && ret < end - pos)
+       if (!os_snprintf_error(end - pos, ret))
                pos += ret;
        wpabuf_free(wps_ie);
        return pos;
@@ -1998,9 +2433,12 @@ static int wpa_supplicant_ctrl_iface_scan_result(
 {
        char *pos, *end;
        int ret;
-       const u8 *ie, *ie2, *p2p;
+       const u8 *ie, *ie2, *p2p, *mesh;
 
+       mesh = wpa_bss_get_ie(bss, WLAN_EID_MESH_ID);
        p2p = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
+       if (!p2p)
+               p2p = wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE);
        if (p2p && bss->ssid_len == P2P_WILDCARD_SSID_LEN &&
            os_memcmp(bss->ssid, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN) ==
            0)
@@ -2011,44 +2449,78 @@ static int wpa_supplicant_ctrl_iface_scan_result(
 
        ret = os_snprintf(pos, end - pos, MACSTR "\t%d\t%d\t",
                          MAC2STR(bss->bssid), bss->freq, bss->level);
-       if (ret < 0 || ret >= end - pos)
+       if (os_snprintf_error(end - pos, ret))
                return -1;
        pos += ret;
        ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
        if (ie)
                pos = wpa_supplicant_ie_txt(pos, end, "WPA", ie, 2 + ie[1]);
        ie2 = wpa_bss_get_ie(bss, WLAN_EID_RSN);
-       if (ie2)
-               pos = wpa_supplicant_ie_txt(pos, end, "WPA2", ie2, 2 + ie2[1]);
+       if (ie2) {
+               pos = wpa_supplicant_ie_txt(pos, end, mesh ? "RSN" : "WPA2",
+                                           ie2, 2 + ie2[1]);
+       }
        pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
        if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) {
                ret = os_snprintf(pos, end - pos, "[WEP]");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return -1;
                pos += ret;
        }
-       if (bss->caps & IEEE80211_CAP_IBSS) {
-               ret = os_snprintf(pos, end - pos, "[IBSS]");
-               if (ret < 0 || ret >= end - pos)
+       if (mesh) {
+               ret = os_snprintf(pos, end - pos, "[MESH]");
+               if (os_snprintf_error(end - pos, ret))
                        return -1;
                pos += ret;
        }
-       if (bss->caps & IEEE80211_CAP_ESS) {
-               ret = os_snprintf(pos, end - pos, "[ESS]");
-               if (ret < 0 || ret >= end - pos)
+       if (bss_is_dmg(bss)) {
+               const char *s;
+               ret = os_snprintf(pos, end - pos, "[DMG]");
+               if (os_snprintf_error(end - pos, ret))
+                       return -1;
+               pos += ret;
+               switch (bss->caps & IEEE80211_CAP_DMG_MASK) {
+               case IEEE80211_CAP_DMG_IBSS:
+                       s = "[IBSS]";
+                       break;
+               case IEEE80211_CAP_DMG_AP:
+                       s = "[ESS]";
+                       break;
+               case IEEE80211_CAP_DMG_PBSS:
+                       s = "[PBSS]";
+                       break;
+               default:
+                       s = "";
+                       break;
+               }
+               ret = os_snprintf(pos, end - pos, "%s", s);
+               if (os_snprintf_error(end - pos, ret))
                        return -1;
                pos += ret;
+       } else {
+               if (bss->caps & IEEE80211_CAP_IBSS) {
+                       ret = os_snprintf(pos, end - pos, "[IBSS]");
+                       if (os_snprintf_error(end - pos, ret))
+                               return -1;
+                       pos += ret;
+               }
+               if (bss->caps & IEEE80211_CAP_ESS) {
+                       ret = os_snprintf(pos, end - pos, "[ESS]");
+                       if (os_snprintf_error(end - pos, ret))
+                               return -1;
+                       pos += ret;
+               }
        }
        if (p2p) {
                ret = os_snprintf(pos, end - pos, "[P2P]");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return -1;
                pos += ret;
        }
 #ifdef CONFIG_HS20
        if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE) && ie2) {
                ret = os_snprintf(pos, end - pos, "[HS20]");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return -1;
                pos += ret;
        }
@@ -2056,12 +2528,12 @@ static int wpa_supplicant_ctrl_iface_scan_result(
 
        ret = os_snprintf(pos, end - pos, "\t%s",
                          wpa_ssid_txt(bss->ssid, bss->ssid_len));
-       if (ret < 0 || ret >= end - pos)
+       if (os_snprintf_error(end - pos, ret))
                return -1;
        pos += ret;
 
        ret = os_snprintf(pos, end - pos, "\n");
-       if (ret < 0 || ret >= end - pos)
+       if (os_snprintf_error(end - pos, ret))
                return -1;
        pos += ret;
 
@@ -2080,7 +2552,7 @@ static int wpa_supplicant_ctrl_iface_scan_results(
        end = buf + buflen;
        ret = os_snprintf(pos, end - pos, "bssid / frequency / signal level / "
                          "flags / ssid\n");
-       if (ret < 0 || ret >= end - pos)
+       if (os_snprintf_error(end - pos, ret))
                return pos - buf;
        pos += ret;
 
@@ -2096,14 +2568,125 @@ static int wpa_supplicant_ctrl_iface_scan_results(
 }
 
 
+#ifdef CONFIG_MESH
+
+static int wpa_supplicant_ctrl_iface_mesh_interface_add(
+       struct wpa_supplicant *wpa_s, char *cmd, char *reply, size_t max_len)
+{
+       char *pos, ifname[IFNAMSIZ + 1];
+
+       ifname[0] = '\0';
+
+       pos = os_strstr(cmd, "ifname=");
+       if (pos) {
+               pos += 7;
+               os_strlcpy(ifname, pos, sizeof(ifname));
+       }
+
+       if (wpas_mesh_add_interface(wpa_s, ifname, sizeof(ifname)) < 0)
+               return -1;
+
+       os_strlcpy(reply, ifname, max_len);
+       return os_strlen(ifname);
+}
+
+
+static int wpa_supplicant_ctrl_iface_mesh_group_add(
+       struct wpa_supplicant *wpa_s, char *cmd)
+{
+       int id;
+       struct wpa_ssid *ssid;
+
+       id = atoi(cmd);
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE: MESH_GROUP_ADD id=%d", id);
+
+       ssid = wpa_config_get_network(wpa_s->conf, id);
+       if (ssid == NULL) {
+               wpa_printf(MSG_DEBUG,
+                          "CTRL_IFACE: Could not find network id=%d", id);
+               return -1;
+       }
+       if (ssid->mode != WPAS_MODE_MESH) {
+               wpa_printf(MSG_DEBUG,
+                          "CTRL_IFACE: Cannot use MESH_GROUP_ADD on a non mesh network");
+               return -1;
+       }
+       if (ssid->key_mgmt != WPA_KEY_MGMT_NONE &&
+           ssid->key_mgmt != WPA_KEY_MGMT_SAE) {
+               wpa_printf(MSG_ERROR,
+                          "CTRL_IFACE: key_mgmt for mesh network should be open or SAE");
+               return -1;
+       }
+
+       /*
+        * TODO: If necessary write our own group_add function,
+        * for now we can reuse select_network
+        */
+       wpa_supplicant_select_network(wpa_s, ssid);
+
+       return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_mesh_group_remove(
+       struct wpa_supplicant *wpa_s, char *cmd)
+{
+       struct wpa_supplicant *orig;
+       struct wpa_global *global;
+       int found = 0;
+
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s", cmd);
+
+       global = wpa_s->global;
+       orig = wpa_s;
+
+       for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               if (os_strcmp(wpa_s->ifname, cmd) == 0) {
+                       found = 1;
+                       break;
+               }
+       }
+       if (!found) {
+               wpa_printf(MSG_ERROR,
+                          "CTRL_IFACE: MESH_GROUP_REMOVE ifname=%s not found",
+                          cmd);
+               return -1;
+       }
+       if (wpa_s->mesh_if_created && wpa_s == orig) {
+               wpa_printf(MSG_ERROR,
+                          "CTRL_IFACE: MESH_GROUP_REMOVE can't remove itself");
+               return -1;
+       }
+
+       wpa_s->reassociate = 0;
+       wpa_s->disconnected = 1;
+       wpa_supplicant_cancel_sched_scan(wpa_s);
+       wpa_supplicant_cancel_scan(wpa_s);
+
+       /*
+        * TODO: If necessary write our own group_remove function,
+        * for now we can reuse deauthenticate
+        */
+       wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+
+       if (wpa_s->mesh_if_created)
+               wpa_supplicant_remove_iface(global, wpa_s, 0);
+
+       return 0;
+}
+
+#endif /* CONFIG_MESH */
+
+
 static int wpa_supplicant_ctrl_iface_select_network(
        struct wpa_supplicant *wpa_s, char *cmd)
 {
        int id;
        struct wpa_ssid *ssid;
+       char *pos;
 
        /* cmd: "<network id>" or "any" */
-       if (os_strcmp(cmd, "any") == 0) {
+       if (os_strncmp(cmd, "any", 3) == 0) {
                wpa_printf(MSG_DEBUG, "CTRL_IFACE: SELECT_NETWORK any");
                ssid = NULL;
        } else {
@@ -2123,6 +2706,16 @@ static int wpa_supplicant_ctrl_iface_select_network(
                }
        }
 
+       pos = os_strstr(cmd, " freq=");
+       if (pos) {
+               int *freqs = freq_range_to_channel_list(wpa_s, pos + 6);
+               if (freqs) {
+                       wpa_s->scan_req = MANUAL_SCAN_REQ;
+                       os_free(wpa_s->manual_scan_freqs);
+                       wpa_s->manual_scan_freqs = freqs;
+               }
+       }
+
        wpa_supplicant_select_network(wpa_s, ssid);
 
        return 0;
@@ -2217,7 +2810,7 @@ static int wpa_supplicant_ctrl_iface_add_network(
        wpa_config_set_network_defaults(ssid);
 
        ret = os_snprintf(buf, buflen, "%d\n", ssid->id);
-       if (ret < 0 || (size_t) ret >= buflen)
+       if (os_snprintf_error(buflen, ret))
                return -1;
        return ret;
 }
@@ -2243,6 +2836,7 @@ static int wpa_supplicant_ctrl_iface_remove_network(
 #endif /* CONFIG_SME */
                        wpa_sm_set_config(wpa_s->wpa, NULL);
                        eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+                       wpa_s->own_disconnect_req = 1;
                        wpa_supplicant_deauthenticate(
                                wpa_s, WLAN_REASON_DEAUTH_LEAVING);
                }
@@ -2251,6 +2845,8 @@ static int wpa_supplicant_ctrl_iface_remove_network(
                        struct wpa_ssid *remove_ssid = ssid;
                        id = ssid->id;
                        ssid = ssid->next;
+                       if (wpa_s->last_ssid == remove_ssid)
+                               wpa_s->last_ssid = NULL;
                        wpas_notify_network_removed(wpa_s, remove_ssid);
                        wpa_config_remove_network(wpa_s->conf, id);
                }
@@ -2269,6 +2865,9 @@ static int wpa_supplicant_ctrl_iface_remove_network(
                return -1;
        }
 
+       if (wpa_s->last_ssid == ssid)
+               wpa_s->last_ssid = NULL;
+
        if (ssid == wpa_s->current_ssid || wpa_s->current_ssid == NULL) {
 #ifdef CONFIG_SME
                wpa_s->sme.prev_bssid_set = 0;
@@ -2284,6 +2883,7 @@ static int wpa_supplicant_ctrl_iface_remove_network(
                wpa_sm_set_config(wpa_s->wpa, NULL);
                eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
 
+               wpa_s->own_disconnect_req = 1;
                wpa_supplicant_deauthenticate(wpa_s,
                                              WLAN_REASON_DEAUTH_LEAVING);
        }
@@ -2307,12 +2907,46 @@ static int wpa_supplicant_ctrl_iface_remove_network(
 }
 
 
-static int wpa_supplicant_ctrl_iface_set_network(
-       struct wpa_supplicant *wpa_s, char *cmd)
+static int wpa_supplicant_ctrl_iface_update_network(
+       struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
+       char *name, char *value)
 {
-       int id;
-       struct wpa_ssid *ssid;
+       if (wpa_config_set(ssid, name, value, 0) < 0) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set network "
+                          "variable '%s'", name);
+               return -1;
+       }
+
+       if (os_strcmp(name, "bssid") != 0 &&
+           os_strcmp(name, "priority") != 0)
+               wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
+
+       if (wpa_s->current_ssid == ssid || wpa_s->current_ssid == NULL) {
+               /*
+                * Invalidate the EAP session cache if anything in the current
+                * or previously used configuration changes.
+                */
+               eapol_sm_invalidate_cached_session(wpa_s->eapol);
+       }
+
+       if ((os_strcmp(name, "psk") == 0 &&
+            value[0] == '"' && ssid->ssid_len) ||
+           (os_strcmp(name, "ssid") == 0 && ssid->passphrase))
+               wpa_config_update_psk(ssid);
+       else if (os_strcmp(name, "priority") == 0)
+               wpa_config_update_prio_list(wpa_s->conf);
+
+       return 0;
+}
+
+
+static int wpa_supplicant_ctrl_iface_set_network(
+       struct wpa_supplicant *wpa_s, char *cmd)
+{
+       int id, ret, prev_bssid_set, prev_disabled;
+       struct wpa_ssid *ssid;
        char *name, *value;
+       u8 prev_bssid[ETH_ALEN];
 
        /* cmd: "<network id> <variable name> <value>" */
        name = os_strchr(cmd, ' ');
@@ -2338,32 +2972,21 @@ static int wpa_supplicant_ctrl_iface_set_network(
                return -1;
        }
 
-       if (wpa_config_set(ssid, name, value, 0) < 0) {
-               wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to set network "
-                          "variable '%s'", name);
-               return -1;
-       }
-
-       if (os_strcmp(name, "bssid") != 0 &&
-           os_strcmp(name, "priority") != 0)
-               wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
-
-       if (wpa_s->current_ssid == ssid || wpa_s->current_ssid == NULL) {
-               /*
-                * Invalidate the EAP session cache if anything in the current
-                * or previously used configuration changes.
-                */
-               eapol_sm_invalidate_cached_session(wpa_s->eapol);
-       }
+       prev_bssid_set = ssid->bssid_set;
+       prev_disabled = ssid->disabled;
+       os_memcpy(prev_bssid, ssid->bssid, ETH_ALEN);
+       ret = wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid, name,
+                                                      value);
+       if (ret == 0 &&
+           (ssid->bssid_set != prev_bssid_set ||
+            os_memcmp(ssid->bssid, prev_bssid, ETH_ALEN) != 0))
+               wpas_notify_network_bssid_set_changed(wpa_s, ssid);
 
-       if ((os_strcmp(name, "psk") == 0 &&
-            value[0] == '"' && ssid->ssid_len) ||
-           (os_strcmp(name, "ssid") == 0 && ssid->passphrase))
-               wpa_config_update_psk(ssid);
-       else if (os_strcmp(name, "priority") == 0)
-               wpa_config_update_prio_list(wpa_s->conf);
+       if (prev_disabled != ssid->disabled &&
+           (prev_disabled == 2 || ssid->disabled == 2))
+               wpas_notify_network_type_changed(wpa_s, ssid);
 
-       return 0;
+       return ret;
 }
 
 
@@ -2411,6 +3034,59 @@ static int wpa_supplicant_ctrl_iface_get_network(
 }
 
 
+static int wpa_supplicant_ctrl_iface_dup_network(
+       struct wpa_supplicant *wpa_s, char *cmd)
+{
+       struct wpa_ssid *ssid_s, *ssid_d;
+       char *name, *id, *value;
+       int id_s, id_d, ret;
+
+       /* cmd: "<src network id> <dst network id> <variable name>" */
+       id = os_strchr(cmd, ' ');
+       if (id == NULL)
+               return -1;
+       *id++ = '\0';
+
+       name = os_strchr(id, ' ');
+       if (name == NULL)
+               return -1;
+       *name++ = '\0';
+
+       id_s = atoi(cmd);
+       id_d = atoi(id);
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE: DUP_NETWORK id=%d -> %d name='%s'",
+                  id_s, id_d, name);
+
+       ssid_s = wpa_config_get_network(wpa_s->conf, id_s);
+       if (ssid_s == NULL) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
+                          "network id=%d", id_s);
+               return -1;
+       }
+
+       ssid_d = wpa_config_get_network(wpa_s->conf, id_d);
+       if (ssid_d == NULL) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find "
+                          "network id=%d", id_d);
+               return -1;
+       }
+
+       value = wpa_config_get(ssid_s, name);
+       if (value == NULL) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get network "
+                          "variable '%s'", name);
+               return -1;
+       }
+
+       ret = wpa_supplicant_ctrl_iface_update_network(wpa_s, ssid_d, name,
+                                                      value);
+
+       os_free(value);
+
+       return ret;
+}
+
+
 static int wpa_supplicant_ctrl_iface_list_creds(struct wpa_supplicant *wpa_s,
                                                char *buf, size_t buflen)
 {
@@ -2422,7 +3098,7 @@ static int wpa_supplicant_ctrl_iface_list_creds(struct wpa_supplicant *wpa_s,
        end = buf + buflen;
        ret = os_snprintf(pos, end - pos,
                          "cred id / realm / username / domain / imsi\n");
-       if (ret < 0 || ret >= end - pos)
+       if (os_snprintf_error(end - pos, ret))
                return pos - buf;
        pos += ret;
 
@@ -2431,9 +3107,9 @@ static int wpa_supplicant_ctrl_iface_list_creds(struct wpa_supplicant *wpa_s,
                ret = os_snprintf(pos, end - pos, "%d\t%s\t%s\t%s\t%s\n",
                                  cred->id, cred->realm ? cred->realm : "",
                                  cred->username ? cred->username : "",
-                                 cred->domain ? cred->domain : "",
+                                 cred->domain ? cred->domain[0] : "",
                                  cred->imsi ? cred->imsi : "");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
 
@@ -2456,8 +3132,10 @@ static int wpa_supplicant_ctrl_iface_add_cred(struct wpa_supplicant *wpa_s,
        if (cred == NULL)
                return -1;
 
+       wpa_msg(wpa_s, MSG_INFO, CRED_ADDED "%d", cred->id);
+
        ret = os_snprintf(buf, buflen, "%d\n", cred->id);
-       if (ret < 0 || (size_t) ret >= buflen)
+       if (os_snprintf_error(buflen, ret))
                return -1;
        return ret;
 }
@@ -2468,19 +3146,32 @@ static int wpas_ctrl_remove_cred(struct wpa_supplicant *wpa_s,
 {
        struct wpa_ssid *ssid;
        char str[20];
+       int id;
+
+       if (cred == NULL) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred");
+               return -1;
+       }
 
-       if (cred == NULL || wpa_config_remove_cred(wpa_s->conf, cred->id) < 0) {
+       id = cred->id;
+       if (wpa_config_remove_cred(wpa_s->conf, id) < 0) {
                wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred");
                return -1;
        }
 
+       wpa_msg(wpa_s, MSG_INFO, CRED_REMOVED "%d", id);
+
        /* Remove any network entry created based on the removed credential */
        ssid = wpa_s->conf->ssid;
        while (ssid) {
                if (ssid->parent_cred == cred) {
+                       int res;
+
                        wpa_printf(MSG_DEBUG, "Remove network id %d since it "
                                   "used the removed credential", ssid->id);
-                       os_snprintf(str, sizeof(str), "%d", ssid->id);
+                       res = os_snprintf(str, sizeof(str), "%d", ssid->id);
+                       if (os_snprintf_error(sizeof(str), res))
+                               str[sizeof(str) - 1] = '\0';
                        ssid = ssid->next;
                        wpa_supplicant_ctrl_iface_remove_network(wpa_s, str);
                } else
@@ -2497,7 +3188,8 @@ static int wpa_supplicant_ctrl_iface_remove_cred(struct wpa_supplicant *wpa_s,
        int id;
        struct wpa_cred *cred, *prev;
 
-       /* cmd: "<cred id>", "all", or "sp_fqdn=<FQDN>" */
+       /* cmd: "<cred id>", "all", "sp_fqdn=<FQDN>", or
+        * "provisioning_sp=<FQDN> */
        if (os_strcmp(cmd, "all") == 0) {
                wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED all");
                cred = wpa_s->conf->cred;
@@ -2516,8 +3208,29 @@ static int wpa_supplicant_ctrl_iface_remove_cred(struct wpa_supplicant *wpa_s,
                while (cred) {
                        prev = cred;
                        cred = cred->next;
-                       if (prev->domain &&
-                           os_strcmp(prev->domain, cmd + 8) == 0)
+                       if (prev->domain) {
+                               size_t i;
+                               for (i = 0; i < prev->num_domain; i++) {
+                                       if (os_strcmp(prev->domain[i], cmd + 8)
+                                           != 0)
+                                               continue;
+                                       wpas_ctrl_remove_cred(wpa_s, prev);
+                                       break;
+                               }
+                       }
+               }
+               return 0;
+       }
+
+       if (os_strncmp(cmd, "provisioning_sp=", 16) == 0) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: REMOVE_CRED provisioning SP FQDN '%s'",
+                          cmd + 16);
+               cred = wpa_s->conf->cred;
+               while (cred) {
+                       prev = cred;
+                       cred = cred->next;
+                       if (prev->provisioning_sp &&
+                           os_strcmp(prev->provisioning_sp, cmd + 16) == 0)
                                wpas_ctrl_remove_cred(wpa_s, prev);
                }
                return 0;
@@ -2568,10 +3281,57 @@ static int wpa_supplicant_ctrl_iface_set_cred(struct wpa_supplicant *wpa_s,
                return -1;
        }
 
+       wpa_msg(wpa_s, MSG_INFO, CRED_MODIFIED "%d %s", cred->id, name);
+
        return 0;
 }
 
 
+static int wpa_supplicant_ctrl_iface_get_cred(struct wpa_supplicant *wpa_s,
+                                             char *cmd, char *buf,
+                                             size_t buflen)
+{
+       int id;
+       size_t res;
+       struct wpa_cred *cred;
+       char *name, *value;
+
+       /* cmd: "<cred id> <variable name>" */
+       name = os_strchr(cmd, ' ');
+       if (name == NULL)
+               return -1;
+       *name++ = '\0';
+
+       id = atoi(cmd);
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE: GET_CRED id=%d name='%s'",
+                  id, name);
+
+       cred = wpa_config_get_cred(wpa_s->conf, id);
+       if (cred == NULL) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: Could not find cred id=%d",
+                          id);
+               return -1;
+       }
+
+       value = wpa_config_get_cred_no_key(cred, name);
+       if (value == NULL) {
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE: Failed to get cred variable '%s'",
+                          name);
+               return -1;
+       }
+
+       res = os_strlcpy(buf, value, buflen);
+       if (res >= buflen) {
+               os_free(value);
+               return -1;
+       }
+
+       os_free(value);
+
+       return res;
+}
+
+
 #ifndef CONFIG_NO_CONFIG_WRITE
 static int wpa_supplicant_ctrl_iface_save_config(struct wpa_supplicant *wpa_s)
 {
@@ -2597,13 +3357,39 @@ static int wpa_supplicant_ctrl_iface_save_config(struct wpa_supplicant *wpa_s)
 #endif /* CONFIG_NO_CONFIG_WRITE */
 
 
+struct cipher_info {
+       unsigned int capa;
+       const char *name;
+       int group_only;
+};
+
+static const struct cipher_info ciphers[] = {
+       { WPA_DRIVER_CAPA_ENC_CCMP_256, "CCMP-256", 0 },
+       { WPA_DRIVER_CAPA_ENC_GCMP_256, "GCMP-256", 0 },
+       { WPA_DRIVER_CAPA_ENC_CCMP, "CCMP", 0 },
+       { WPA_DRIVER_CAPA_ENC_GCMP, "GCMP", 0 },
+       { WPA_DRIVER_CAPA_ENC_TKIP, "TKIP", 0 },
+       { WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE, "NONE", 0 },
+       { WPA_DRIVER_CAPA_ENC_WEP104, "WEP104", 1 },
+       { WPA_DRIVER_CAPA_ENC_WEP40, "WEP40", 1 }
+};
+
+static const struct cipher_info ciphers_group_mgmt[] = {
+       { WPA_DRIVER_CAPA_ENC_BIP, "AES-128-CMAC", 1 },
+       { WPA_DRIVER_CAPA_ENC_BIP_GMAC_128, "BIP-GMAC-128", 1 },
+       { WPA_DRIVER_CAPA_ENC_BIP_GMAC_256, "BIP-GMAC-256", 1 },
+       { WPA_DRIVER_CAPA_ENC_BIP_CMAC_256, "BIP-CMAC-256", 1 },
+};
+
+
 static int ctrl_iface_get_capability_pairwise(int res, char *strict,
                                              struct wpa_driver_capa *capa,
                                              char *buf, size_t buflen)
 {
-       int ret, first = 1;
+       int ret;
        char *pos, *end;
        size_t len;
+       unsigned int i;
 
        pos = buf;
        end = pos + buflen;
@@ -2617,36 +3403,15 @@ static int ctrl_iface_get_capability_pairwise(int res, char *strict,
                return len;
        }
 
-       if (capa->enc & WPA_DRIVER_CAPA_ENC_CCMP) {
-               ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : " ");
-               if (ret < 0 || ret >= end - pos)
-                       return pos - buf;
-               pos += ret;
-               first = 0;
-       }
-
-       if (capa->enc & WPA_DRIVER_CAPA_ENC_GCMP) {
-               ret = os_snprintf(pos, end - pos, "%sGCMP", first ? "" : " ");
-               if (ret < 0 || ret >= end - pos)
-                       return pos - buf;
-               pos += ret;
-               first = 0;
-       }
-
-       if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) {
-               ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " ");
-               if (ret < 0 || ret >= end - pos)
-                       return pos - buf;
-               pos += ret;
-               first = 0;
-       }
-
-       if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
-               ret = os_snprintf(pos, end - pos, "%sNONE", first ? "" : " ");
-               if (ret < 0 || ret >= end - pos)
-                       return pos - buf;
-               pos += ret;
-               first = 0;
+       for (i = 0; i < ARRAY_SIZE(ciphers); i++) {
+               if (!ciphers[i].group_only && capa->enc & ciphers[i].capa) {
+                       ret = os_snprintf(pos, end - pos, "%s%s",
+                                         pos == buf ? "" : " ",
+                                         ciphers[i].name);
+                       if (os_snprintf_error(end - pos, ret))
+                               return pos - buf;
+                       pos += ret;
+               }
        }
 
        return pos - buf;
@@ -2657,9 +3422,10 @@ static int ctrl_iface_get_capability_group(int res, char *strict,
                                           struct wpa_driver_capa *capa,
                                           char *buf, size_t buflen)
 {
-       int ret, first = 1;
+       int ret;
        char *pos, *end;
        size_t len;
+       unsigned int i;
 
        pos = buf;
        end = pos + buflen;
@@ -2673,45 +3439,44 @@ static int ctrl_iface_get_capability_group(int res, char *strict,
                return len;
        }
 
-       if (capa->enc & WPA_DRIVER_CAPA_ENC_CCMP) {
-               ret = os_snprintf(pos, end - pos, "%sCCMP", first ? "" : " ");
-               if (ret < 0 || ret >= end - pos)
-                       return pos - buf;
-               pos += ret;
-               first = 0;
+       for (i = 0; i < ARRAY_SIZE(ciphers); i++) {
+               if (capa->enc & ciphers[i].capa) {
+                       ret = os_snprintf(pos, end - pos, "%s%s",
+                                         pos == buf ? "" : " ",
+                                         ciphers[i].name);
+                       if (os_snprintf_error(end - pos, ret))
+                               return pos - buf;
+                       pos += ret;
+               }
        }
 
-       if (capa->enc & WPA_DRIVER_CAPA_ENC_GCMP) {
-               ret = os_snprintf(pos, end - pos, "%sGCMP", first ? "" : " ");
-               if (ret < 0 || ret >= end - pos)
-                       return pos - buf;
-               pos += ret;
-               first = 0;
-       }
+       return pos - buf;
+}
 
-       if (capa->enc & WPA_DRIVER_CAPA_ENC_TKIP) {
-               ret = os_snprintf(pos, end - pos, "%sTKIP", first ? "" : " ");
-               if (ret < 0 || ret >= end - pos)
-                       return pos - buf;
-               pos += ret;
-               first = 0;
-       }
 
-       if (capa->enc & WPA_DRIVER_CAPA_ENC_WEP104) {
-               ret = os_snprintf(pos, end - pos, "%sWEP104",
-                                 first ? "" : " ");
-               if (ret < 0 || ret >= end - pos)
-                       return pos - buf;
-               pos += ret;
-               first = 0;
-       }
+static int ctrl_iface_get_capability_group_mgmt(int res, char *strict,
+                                               struct wpa_driver_capa *capa,
+                                               char *buf, size_t buflen)
+{
+       int ret;
+       char *pos, *end;
+       unsigned int i;
 
-       if (capa->enc & WPA_DRIVER_CAPA_ENC_WEP40) {
-               ret = os_snprintf(pos, end - pos, "%sWEP40", first ? "" : " ");
-               if (ret < 0 || ret >= end - pos)
-                       return pos - buf;
-               pos += ret;
-               first = 0;
+       pos = buf;
+       end = pos + buflen;
+
+       if (res < 0)
+               return 0;
+
+       for (i = 0; i < ARRAY_SIZE(ciphers_group_mgmt); i++) {
+               if (capa->enc & ciphers_group_mgmt[i].capa) {
+                       ret = os_snprintf(pos, end - pos, "%s%s",
+                                         pos == buf ? "" : " ",
+                                         ciphers_group_mgmt[i].name);
+                       if (os_snprintf_error(end - pos, ret))
+                               return pos - buf;
+                       pos += ret;
+               }
        }
 
        return pos - buf;
@@ -2740,14 +3505,14 @@ static int ctrl_iface_get_capability_key_mgmt(int res, char *strict,
        }
 
        ret = os_snprintf(pos, end - pos, "NONE IEEE8021X");
-       if (ret < 0 || ret >= end - pos)
+       if (os_snprintf_error(end - pos, ret))
                return pos - buf;
        pos += ret;
 
        if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
                              WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
                ret = os_snprintf(pos, end - pos, " WPA-EAP");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
@@ -2755,18 +3520,35 @@ static int ctrl_iface_get_capability_key_mgmt(int res, char *strict,
        if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
                              WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
                ret = os_snprintf(pos, end - pos, " WPA-PSK");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
 
        if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
                ret = os_snprintf(pos, end - pos, " WPA-NONE");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
 
+#ifdef CONFIG_SUITEB
+       if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B) {
+               ret = os_snprintf(pos, end - pos, " WPA-EAP-SUITE-B");
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SUITEB192
+       if (capa->key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192) {
+               ret = os_snprintf(pos, end - pos, " WPA-EAP-SUITE-B-192");
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+#endif /* CONFIG_SUITEB192 */
+
        return pos - buf;
 }
 
@@ -2775,7 +3557,7 @@ static int ctrl_iface_get_capability_proto(int res, char *strict,
                                           struct wpa_driver_capa *capa,
                                           char *buf, size_t buflen)
 {
-       int ret, first = 1;
+       int ret;
        char *pos, *end;
        size_t len;
 
@@ -2793,31 +3575,32 @@ static int ctrl_iface_get_capability_proto(int res, char *strict,
 
        if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
                              WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
-               ret = os_snprintf(pos, end - pos, "%sRSN", first ? "" : " ");
-               if (ret < 0 || ret >= end - pos)
+               ret = os_snprintf(pos, end - pos, "%sRSN",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
-               first = 0;
        }
 
        if (capa->key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
                              WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) {
-               ret = os_snprintf(pos, end - pos, "%sWPA", first ? "" : " ");
-               if (ret < 0 || ret >= end - pos)
+               ret = os_snprintf(pos, end - pos, "%sWPA",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
-               first = 0;
        }
 
        return pos - buf;
 }
 
 
-static int ctrl_iface_get_capability_auth_alg(int res, char *strict,
+static int ctrl_iface_get_capability_auth_alg(struct wpa_supplicant *wpa_s,
+                                             int res, char *strict,
                                              struct wpa_driver_capa *capa,
                                              char *buf, size_t buflen)
 {
-       int ret, first = 1;
+       int ret;
        char *pos, *end;
        size_t len;
 
@@ -2834,29 +3617,38 @@ static int ctrl_iface_get_capability_auth_alg(int res, char *strict,
        }
 
        if (capa->auth & (WPA_DRIVER_AUTH_OPEN)) {
-               ret = os_snprintf(pos, end - pos, "%sOPEN", first ? "" : " ");
-               if (ret < 0 || ret >= end - pos)
+               ret = os_snprintf(pos, end - pos, "%sOPEN",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
-               first = 0;
        }
 
        if (capa->auth & (WPA_DRIVER_AUTH_SHARED)) {
                ret = os_snprintf(pos, end - pos, "%sSHARED",
-                                 first ? "" : " ");
-               if (ret < 0 || ret >= end - pos)
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
-               first = 0;
        }
 
        if (capa->auth & (WPA_DRIVER_AUTH_LEAP)) {
-               ret = os_snprintf(pos, end - pos, "%sLEAP", first ? "" : " ");
-               if (ret < 0 || ret >= end - pos)
+               ret = os_snprintf(pos, end - pos, "%sLEAP",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+
+#ifdef CONFIG_SAE
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) {
+               ret = os_snprintf(pos, end - pos, "%sSAE",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
-               first = 0;
        }
+#endif /* CONFIG_SAE */
 
        return pos - buf;
 }
@@ -2866,7 +3658,7 @@ static int ctrl_iface_get_capability_modes(int res, char *strict,
                                           struct wpa_driver_capa *capa,
                                           char *buf, size_t buflen)
 {
-       int ret, first = 1;
+       int ret;
        char *pos, *end;
        size_t len;
 
@@ -2883,20 +3675,30 @@ static int ctrl_iface_get_capability_modes(int res, char *strict,
        }
 
        if (capa->flags & WPA_DRIVER_FLAGS_IBSS) {
-               ret = os_snprintf(pos, end - pos, "%sIBSS", first ? "" : " ");
-               if (ret < 0 || ret >= end - pos)
+               ret = os_snprintf(pos, end - pos, "%sIBSS",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
-               first = 0;
        }
 
        if (capa->flags & WPA_DRIVER_FLAGS_AP) {
-               ret = os_snprintf(pos, end - pos, "%sAP", first ? "" : " ");
-               if (ret < 0 || ret >= end - pos)
+               ret = os_snprintf(pos, end - pos, "%sAP",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+
+#ifdef CONFIG_MESH
+       if (capa->flags & WPA_DRIVER_FLAGS_MESH) {
+               ret = os_snprintf(pos, end - pos, "%sMESH",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
-               first = 0;
        }
+#endif /* CONFIG_MESH */
 
        return pos - buf;
 }
@@ -2930,7 +3732,7 @@ static int ctrl_iface_get_capability_channels(struct wpa_supplicant *wpa_s,
                        continue;
                }
                ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:", hmode);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
                chnl = wpa_s->hw.modes[j].channels;
@@ -2938,12 +3740,12 @@ static int ctrl_iface_get_capability_channels(struct wpa_supplicant *wpa_s,
                        if (chnl[i].flag & HOSTAPD_CHAN_DISABLED)
                                continue;
                        ret = os_snprintf(pos, end - pos, " %d", chnl[i].chan);
-                       if (ret < 0 || ret >= end - pos)
+                       if (os_snprintf_error(end - pos, ret))
                                return pos - buf;
                        pos += ret;
                }
                ret = os_snprintf(pos, end - pos, "\n");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
@@ -2981,23 +3783,26 @@ static int ctrl_iface_get_capability_freq(struct wpa_supplicant *wpa_s,
                }
                ret = os_snprintf(pos, end - pos, "Mode[%s] Channels:\n",
                                  hmode);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
                chnl = wpa_s->hw.modes[j].channels;
                for (i = 0; i < wpa_s->hw.modes[j].num_channels; i++) {
                        if (chnl[i].flag & HOSTAPD_CHAN_DISABLED)
                                continue;
-                       ret = os_snprintf(pos, end - pos, " %d = %d MHz%s\n",
+                       ret = os_snprintf(pos, end - pos, " %d = %d MHz%s%s\n",
                                          chnl[i].chan, chnl[i].freq,
-                                         chnl[i].flag & HOSTAPD_CHAN_NO_IBSS ?
-                                         " (NO_IBSS)" : "");
-                       if (ret < 0 || ret >= end - pos)
+                                         chnl[i].flag & HOSTAPD_CHAN_NO_IR ?
+                                         " (NO_IR)" : "",
+                                         chnl[i].flag & HOSTAPD_CHAN_RADAR ?
+                                         " (DFS)" : "");
+
+                       if (os_snprintf_error(end - pos, ret))
                                return pos - buf;
                        pos += ret;
                }
                ret = os_snprintf(pos, end - pos, "\n");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return pos - buf;
                pos += ret;
        }
@@ -3044,6 +3849,10 @@ static int wpa_supplicant_ctrl_iface_get_capability(
                return ctrl_iface_get_capability_group(res, strict, &capa,
                                                       buf, buflen);
 
+       if (os_strcmp(field, "group_mgmt") == 0)
+               return ctrl_iface_get_capability_group_mgmt(res, strict, &capa,
+                                                           buf, buflen);
+
        if (os_strcmp(field, "key_mgmt") == 0)
                return ctrl_iface_get_capability_key_mgmt(res, strict, &capa,
                                                          buf, buflen);
@@ -3053,8 +3862,8 @@ static int wpa_supplicant_ctrl_iface_get_capability(
                                                       buf, buflen);
 
        if (os_strcmp(field, "auth_alg") == 0)
-               return ctrl_iface_get_capability_auth_alg(res, strict, &capa,
-                                                         buf, buflen);
+               return ctrl_iface_get_capability_auth_alg(wpa_s, res, strict,
+                                                         &capa, buf, buflen);
 
        if (os_strcmp(field, "modes") == 0)
                return ctrl_iface_get_capability_modes(res, strict, &capa,
@@ -3066,11 +3875,25 @@ static int wpa_supplicant_ctrl_iface_get_capability(
        if (os_strcmp(field, "freq") == 0)
                return ctrl_iface_get_capability_freq(wpa_s, buf, buflen);
 
-       wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
-                  field);
+#ifdef CONFIG_TDLS
+       if (os_strcmp(field, "tdls") == 0)
+               return ctrl_iface_get_capability_tdls(wpa_s, buf, buflen);
+#endif /* CONFIG_TDLS */
 
-       return -1;
-}
+#ifdef CONFIG_ERP
+       if (os_strcmp(field, "erp") == 0) {
+               res = os_snprintf(buf, buflen, "ERP");
+               if (os_snprintf_error(buflen, res))
+                       return -1;
+               return res;
+       }
+#endif /* CONFIG_EPR */
+
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown GET_CAPABILITY field '%s'",
+                  field);
+
+       return -1;
+}
 
 
 #ifdef CONFIG_INTERWORKING
@@ -3086,20 +3909,20 @@ static char * anqp_add_hex(char *pos, char *end, const char *title,
                return start;
 
        ret = os_snprintf(pos, end - pos, "%s=", title);
-       if (ret < 0 || ret >= end - pos)
+       if (os_snprintf_error(end - pos, ret))
                return start;
        pos += ret;
 
        d = wpabuf_head_u8(data);
        for (i = 0; i < wpabuf_len(data); i++) {
                ret = os_snprintf(pos, end - pos, "%02x", *d++);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return start;
                pos += ret;
        }
 
        ret = os_snprintf(pos, end - pos, "\n");
-       if (ret < 0 || ret >= end - pos)
+       if (os_snprintf_error(end - pos, ret))
                return start;
        pos += ret;
 
@@ -3121,7 +3944,7 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 
        if (mask & WPA_BSS_MASK_ID) {
                ret = os_snprintf(pos, end - pos, "id=%u\n", bss->id);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return 0;
                pos += ret;
        }
@@ -3129,14 +3952,14 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
        if (mask & WPA_BSS_MASK_BSSID) {
                ret = os_snprintf(pos, end - pos, "bssid=" MACSTR "\n",
                                  MAC2STR(bss->bssid));
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return 0;
                pos += ret;
        }
 
        if (mask & WPA_BSS_MASK_FREQ) {
                ret = os_snprintf(pos, end - pos, "freq=%d\n", bss->freq);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return 0;
                pos += ret;
        }
@@ -3144,7 +3967,7 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
        if (mask & WPA_BSS_MASK_BEACON_INT) {
                ret = os_snprintf(pos, end - pos, "beacon_int=%d\n",
                                  bss->beacon_int);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return 0;
                pos += ret;
        }
@@ -3152,28 +3975,28 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
        if (mask & WPA_BSS_MASK_CAPABILITIES) {
                ret = os_snprintf(pos, end - pos, "capabilities=0x%04x\n",
                                  bss->caps);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return 0;
                pos += ret;
        }
 
        if (mask & WPA_BSS_MASK_QUAL) {
                ret = os_snprintf(pos, end - pos, "qual=%d\n", bss->qual);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return 0;
                pos += ret;
        }
 
        if (mask & WPA_BSS_MASK_NOISE) {
                ret = os_snprintf(pos, end - pos, "noise=%d\n", bss->noise);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return 0;
                pos += ret;
        }
 
        if (mask & WPA_BSS_MASK_LEVEL) {
                ret = os_snprintf(pos, end - pos, "level=%d\n", bss->level);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return 0;
                pos += ret;
        }
@@ -3181,45 +4004,45 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
        if (mask & WPA_BSS_MASK_TSF) {
                ret = os_snprintf(pos, end - pos, "tsf=%016llu\n",
                                  (unsigned long long) bss->tsf);
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return 0;
                pos += ret;
        }
 
        if (mask & WPA_BSS_MASK_AGE) {
-               struct os_time now;
+               struct os_reltime now;
 
-               os_get_time(&now);
+               os_get_reltime(&now);
                ret = os_snprintf(pos, end - pos, "age=%d\n",
                                  (int) (now.sec - bss->last_update.sec));
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return 0;
                pos += ret;
        }
 
        if (mask & WPA_BSS_MASK_IE) {
                ret = os_snprintf(pos, end - pos, "ie=");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return 0;
                pos += ret;
 
                ie = (const u8 *) (bss + 1);
                for (i = 0; i < bss->ie_len; i++) {
                        ret = os_snprintf(pos, end - pos, "%02x", *ie++);
-                       if (ret < 0 || ret >= end - pos)
+                       if (os_snprintf_error(end - pos, ret))
                                return 0;
                        pos += ret;
                }
 
                ret = os_snprintf(pos, end - pos, "\n");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return 0;
                pos += ret;
        }
 
        if (mask & WPA_BSS_MASK_FLAGS) {
                ret = os_snprintf(pos, end - pos, "flags=");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return 0;
                pos += ret;
 
@@ -3234,39 +4057,66 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
                pos = wpa_supplicant_wps_ie_txt(wpa_s, pos, end, bss);
                if (!ie && !ie2 && bss->caps & IEEE80211_CAP_PRIVACY) {
                        ret = os_snprintf(pos, end - pos, "[WEP]");
-                       if (ret < 0 || ret >= end - pos)
+                       if (os_snprintf_error(end - pos, ret))
                                return 0;
                        pos += ret;
                }
-               if (bss->caps & IEEE80211_CAP_IBSS) {
-                       ret = os_snprintf(pos, end - pos, "[IBSS]");
-                       if (ret < 0 || ret >= end - pos)
+               if (bss_is_dmg(bss)) {
+                       const char *s;
+                       ret = os_snprintf(pos, end - pos, "[DMG]");
+                       if (os_snprintf_error(end - pos, ret))
                                return 0;
                        pos += ret;
-               }
-               if (bss->caps & IEEE80211_CAP_ESS) {
-                       ret = os_snprintf(pos, end - pos, "[ESS]");
-                       if (ret < 0 || ret >= end - pos)
+                       switch (bss->caps & IEEE80211_CAP_DMG_MASK) {
+                       case IEEE80211_CAP_DMG_IBSS:
+                               s = "[IBSS]";
+                               break;
+                       case IEEE80211_CAP_DMG_AP:
+                               s = "[ESS]";
+                               break;
+                       case IEEE80211_CAP_DMG_PBSS:
+                               s = "[PBSS]";
+                               break;
+                       default:
+                               s = "";
+                               break;
+                       }
+                       ret = os_snprintf(pos, end - pos, "%s", s);
+                       if (os_snprintf_error(end - pos, ret))
                                return 0;
                        pos += ret;
+               } else {
+                       if (bss->caps & IEEE80211_CAP_IBSS) {
+                               ret = os_snprintf(pos, end - pos, "[IBSS]");
+                               if (os_snprintf_error(end - pos, ret))
+                                       return 0;
+                               pos += ret;
+                       }
+                       if (bss->caps & IEEE80211_CAP_ESS) {
+                               ret = os_snprintf(pos, end - pos, "[ESS]");
+                               if (os_snprintf_error(end - pos, ret))
+                                       return 0;
+                               pos += ret;
+                       }
                }
-               if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE)) {
+               if (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) ||
+                   wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
                        ret = os_snprintf(pos, end - pos, "[P2P]");
-                       if (ret < 0 || ret >= end - pos)
+                       if (os_snprintf_error(end - pos, ret))
                                return 0;
                        pos += ret;
                }
 #ifdef CONFIG_HS20
                if (wpa_bss_get_vendor_ie(bss, HS20_IE_VENDOR_TYPE)) {
                        ret = os_snprintf(pos, end - pos, "[HS20]");
-                       if (ret < 0 || ret >= end - pos)
+                       if (os_snprintf_error(end - pos, ret))
                                return 0;
                        pos += ret;
                }
 #endif /* CONFIG_HS20 */
 
                ret = os_snprintf(pos, end - pos, "\n");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return 0;
                pos += ret;
        }
@@ -3274,7 +4124,7 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
        if (mask & WPA_BSS_MASK_SSID) {
                ret = os_snprintf(pos, end - pos, "ssid=%s\n",
                                  wpa_ssid_txt(bss->ssid, bss->ssid_len));
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return 0;
                pos += ret;
        }
@@ -3307,8 +4157,10 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
                                                  WFD_IE_VENDOR_TYPE);
                if (wfd) {
                        ret = os_snprintf(pos, end - pos, "wfd_subelems=");
-                       if (ret < 0 || ret >= end - pos)
+                       if (os_snprintf_error(end - pos, ret)) {
+                               wpabuf_free(wfd);
                                return 0;
+                       }
                        pos += ret;
 
                        pos += wpa_snprintf_hex(pos, end - pos,
@@ -3317,7 +4169,7 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
                        wpabuf_free(wfd);
 
                        ret = os_snprintf(pos, end - pos, "\n");
-                       if (ret < 0 || ret >= end - pos)
+                       if (os_snprintf_error(end - pos, ret))
                                return 0;
                        pos += ret;
                }
@@ -3327,6 +4179,8 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 #ifdef CONFIG_INTERWORKING
        if ((mask & WPA_BSS_MASK_INTERNETW) && bss->anqp) {
                struct wpa_bss_anqp *anqp = bss->anqp;
+               pos = anqp_add_hex(pos, end, "anqp_capability_list",
+                                  anqp->capability_list);
                pos = anqp_add_hex(pos, end, "anqp_venue_name",
                                   anqp->venue_name);
                pos = anqp_add_hex(pos, end, "anqp_network_auth_type",
@@ -3341,19 +4195,50 @@ static int print_bss_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
                pos = anqp_add_hex(pos, end, "anqp_domain_name",
                                   anqp->domain_name);
 #ifdef CONFIG_HS20
+               pos = anqp_add_hex(pos, end, "hs20_capability_list",
+                                  anqp->hs20_capability_list);
                pos = anqp_add_hex(pos, end, "hs20_operator_friendly_name",
                                   anqp->hs20_operator_friendly_name);
                pos = anqp_add_hex(pos, end, "hs20_wan_metrics",
                                   anqp->hs20_wan_metrics);
                pos = anqp_add_hex(pos, end, "hs20_connection_capability",
                                   anqp->hs20_connection_capability);
+               pos = anqp_add_hex(pos, end, "hs20_operating_class",
+                                  anqp->hs20_operating_class);
+               pos = anqp_add_hex(pos, end, "hs20_osu_providers_list",
+                                  anqp->hs20_osu_providers_list);
 #endif /* CONFIG_HS20 */
        }
 #endif /* CONFIG_INTERWORKING */
 
+#ifdef CONFIG_MESH
+       if (mask & WPA_BSS_MASK_MESH_SCAN) {
+               ie = (const u8 *) (bss + 1);
+               ret = wpas_mesh_scan_result_text(ie, bss->ie_len, pos, end);
+               if (ret < 0 || ret >= end - pos)
+                       return 0;
+               pos += ret;
+       }
+#endif /* CONFIG_MESH */
+
+       if (mask & WPA_BSS_MASK_SNR) {
+               ret = os_snprintf(pos, end - pos, "snr=%d\n", bss->snr);
+               if (os_snprintf_error(end - pos, ret))
+                       return 0;
+               pos += ret;
+       }
+
+       if (mask & WPA_BSS_MASK_EST_THROUGHPUT) {
+               ret = os_snprintf(pos, end - pos, "est_throughput=%d\n",
+                                 bss->est_throughput);
+               if (os_snprintf_error(end - pos, ret))
+                       return 0;
+               pos += ret;
+       }
+
        if (mask & WPA_BSS_MASK_DELIM) {
                ret = os_snprintf(pos, end - pos, "====\n");
-               if (ret < 0 || ret >= end - pos)
+               if (os_snprintf_error(end - pos, ret))
                        return 0;
                pos += ret;
        }
@@ -3373,7 +4258,7 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
        struct dl_list *next;
        int ret = 0;
        int len;
-       char *ctmp;
+       char *ctmp, *end = buf + buflen;
        unsigned long mask = WPA_BSS_MASK_ALL;
 
        if (os_strncmp(cmd, "RANGE=", 6) == 0) {
@@ -3482,8 +4367,16 @@ static int wpa_supplicant_ctrl_iface_bss(struct wpa_supplicant *wpa_s,
                if (bss == bsslast) {
                        if ((mask & WPA_BSS_MASK_DELIM) && len &&
                            (bss == dl_list_last(&wpa_s->bss_id,
-                                                struct wpa_bss, list_id)))
-                               os_snprintf(buf - 5, 5, "####\n");
+                                                struct wpa_bss, list_id))) {
+                               int res;
+
+                               res = os_snprintf(buf - 5, end - buf + 5,
+                                                 "####\n");
+                               if (os_snprintf_error(end - buf + 5, res)) {
+                                       wpa_printf(MSG_DEBUG,
+                                                  "Could not add end delim");
+                               }
+                       }
                        break;
                }
                next = bss->list_id.next;
@@ -3528,7 +4421,7 @@ static int wpa_supplicant_ctrl_iface_bss_expire_count(
 }
 
 
-static int wpa_supplicant_ctrl_iface_bss_flush(
+static void wpa_supplicant_ctrl_iface_bss_flush(
        struct wpa_supplicant *wpa_s, char *cmd)
 {
        int flush_age = atoi(cmd);
@@ -3537,10 +4430,10 @@ static int wpa_supplicant_ctrl_iface_bss_flush(
                wpa_bss_flush(wpa_s);
        else
                wpa_bss_flush_by_age(wpa_s, flush_age);
-       return 0;
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
 static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s)
 {
        wpa_printf(MSG_DEBUG, "Dropping SA without deauthentication");
@@ -3562,6 +4455,7 @@ static void wpa_supplicant_ctrl_iface_drop_sa(struct wpa_supplicant *wpa_s)
                                   MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
        wpa_sm_drop_sa(wpa_s->wpa);
 }
+#endif /* CONFIG_TESTING_OPTIONS */
 
 
 static int wpa_supplicant_ctrl_iface_roam(struct wpa_supplicant *wpa_s,
@@ -3614,9 +4508,18 @@ static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd)
        unsigned int timeout = atoi(cmd);
        enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL;
        u8 dev_id[ETH_ALEN], *_dev_id = NULL;
+       u8 dev_type[WPS_DEV_TYPE_LEN], *_dev_type = NULL;
        char *pos;
        unsigned int search_delay;
+       const char *_seek[P2P_MAX_QUERY_HASH + 1], **seek = NULL;
+       u8 seek_count = 0;
+       int freq = 0;
 
+       if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+               wpa_dbg(wpa_s, MSG_INFO,
+                       "Reject P2P_FIND since interface is disabled");
+               return -1;
+       }
        if (os_strstr(cmd, "type=social"))
                type = P2P_FIND_ONLY_SOCIAL;
        else if (os_strstr(cmd, "type=progressive"))
@@ -3630,6 +4533,14 @@ static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd)
                _dev_id = dev_id;
        }
 
+       pos = os_strstr(cmd, "dev_type=");
+       if (pos) {
+               pos += 9;
+               if (wps_dev_type_str2bin(pos, dev_type) < 0)
+                       return -1;
+               _dev_type = dev_type;
+       }
+
        pos = os_strstr(cmd, "delay=");
        if (pos) {
                pos += 6;
@@ -3637,8 +4548,181 @@ static int p2p_ctrl_find(struct wpa_supplicant *wpa_s, char *cmd)
        } else
                search_delay = wpas_p2p_search_delay(wpa_s);
 
-       return wpas_p2p_find(wpa_s, timeout, type, 0, NULL, _dev_id,
-                            search_delay);
+       /* Must be searched for last, because it adds nul termination */
+       pos = os_strstr(cmd, " seek=");
+       while (pos && seek_count < P2P_MAX_QUERY_HASH + 1) {
+               char *term;
+
+               term = os_strchr(pos + 1, ' ');
+               _seek[seek_count++] = pos + 6;
+               seek = _seek;
+               pos = os_strstr(pos + 6, " seek=");
+
+               if (term)
+                       *term = '\0';
+       }
+       if (seek_count > P2P_MAX_QUERY_HASH) {
+               seek[0] = NULL;
+               seek_count = 1;
+       }
+
+       pos = os_strstr(cmd, "freq=");
+       if (pos) {
+               pos += 5;
+               freq = atoi(pos);
+               if (freq <= 0)
+                       return -1;
+       }
+
+       return wpas_p2p_find(wpa_s, timeout, type, _dev_type != NULL, _dev_type,
+                            _dev_id, search_delay, seek_count, seek, freq);
+}
+
+
+static struct p2ps_provision * p2p_parse_asp_provision_cmd(const char *cmd)
+{
+       struct p2ps_provision *p2ps_prov;
+       char *pos;
+       size_t info_len = 0;
+       char *info = NULL;
+       u8 role = P2PS_SETUP_NONE;
+       long long unsigned val;
+
+       pos = os_strstr(cmd, "info=");
+       if (pos) {
+               pos += 5;
+               info_len = os_strlen(pos);
+
+               if (info_len) {
+                       info = os_malloc(info_len + 1);
+                       if (info) {
+                               info_len = utf8_unescape(pos, info_len,
+                                                        info, info_len + 1);
+                       } else
+                               info_len = 0;
+               }
+       }
+
+       p2ps_prov = os_zalloc(sizeof(struct p2ps_provision) + info_len + 1);
+       if (p2ps_prov == NULL) {
+               os_free(info);
+               return NULL;
+       }
+
+       if (info) {
+               os_memcpy(p2ps_prov->info, info, info_len);
+               p2ps_prov->info[info_len] = '\0';
+               os_free(info);
+       }
+
+       pos = os_strstr(cmd, "status=");
+       if (pos)
+               p2ps_prov->status = atoi(pos + 7);
+       else
+               p2ps_prov->status = -1;
+
+       pos = os_strstr(cmd, "adv_id=");
+       if (!pos || sscanf(pos + 7, "%llx", &val) != 1 || val > 0xffffffffULL)
+               goto invalid_args;
+       p2ps_prov->adv_id = val;
+
+       pos = os_strstr(cmd, "method=");
+       if (pos)
+               p2ps_prov->method = strtol(pos + 7, NULL, 16);
+       else
+               p2ps_prov->method = 0;
+
+       pos = os_strstr(cmd, "session=");
+       if (!pos || sscanf(pos + 8, "%llx", &val) != 1 || val > 0xffffffffULL)
+               goto invalid_args;
+       p2ps_prov->session_id = val;
+
+       pos = os_strstr(cmd, "adv_mac=");
+       if (!pos || hwaddr_aton(pos + 8, p2ps_prov->adv_mac))
+               goto invalid_args;
+
+       pos = os_strstr(cmd, "session_mac=");
+       if (!pos || hwaddr_aton(pos + 12, p2ps_prov->session_mac))
+               goto invalid_args;
+
+       /* force conncap with tstCap (no sanity checks) */
+       pos = os_strstr(cmd, "tstCap=");
+       if (pos) {
+               role = strtol(pos + 7, NULL, 16);
+       } else {
+               pos = os_strstr(cmd, "role=");
+               if (pos) {
+                       role = strtol(pos + 5, NULL, 16);
+                       if (role != P2PS_SETUP_CLIENT &&
+                           role != P2PS_SETUP_GROUP_OWNER)
+                               role = P2PS_SETUP_NONE;
+               }
+       }
+       p2ps_prov->role = role;
+
+       return p2ps_prov;
+
+invalid_args:
+       os_free(p2ps_prov);
+       return NULL;
+}
+
+
+static int p2p_ctrl_asp_provision_resp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+       u8 addr[ETH_ALEN];
+       struct p2ps_provision *p2ps_prov;
+       char *pos;
+
+       /* <addr> id=<adv_id> [role=<conncap>] [info=<infodata>] */
+
+       wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd);
+
+       if (hwaddr_aton(cmd, addr))
+               return -1;
+
+       pos = cmd + 17;
+       if (*pos != ' ')
+               return -1;
+
+       p2ps_prov = p2p_parse_asp_provision_cmd(pos);
+       if (!p2ps_prov)
+               return -1;
+
+       if (p2ps_prov->status < 0) {
+               os_free(p2ps_prov);
+               return -1;
+       }
+
+       return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP,
+                                 p2ps_prov);
+}
+
+
+static int p2p_ctrl_asp_provision(struct wpa_supplicant *wpa_s, char *cmd)
+{
+       u8 addr[ETH_ALEN];
+       struct p2ps_provision *p2ps_prov;
+       char *pos;
+
+       /* <addr> id=<adv_id> adv_mac=<adv_mac> conncap=<conncap>
+        *        session=<ses_id> mac=<ses_mac> [info=<infodata>]
+        */
+
+       wpa_printf(MSG_DEBUG, "%s: %s", __func__, cmd);
+       if (hwaddr_aton(cmd, addr))
+               return -1;
+
+       pos = cmd + 17;
+       if (*pos != ' ')
+               return -1;
+
+       p2ps_prov = p2p_parse_asp_provision_cmd(pos);
+       if (!p2ps_prov)
+               return -1;
+
+       return wpas_p2p_prov_disc(wpa_s, addr, NULL, WPAS_P2P_PD_FOR_ASP,
+                                 p2ps_prov);
 }
 
 
@@ -3658,12 +4742,20 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
        int go_intent = -1;
        int freq = 0;
        int pd;
-       int ht40;
+       int ht40, vht;
+
+       if (!wpa_s->global->p2p_init_wpa_s)
+               return -1;
+       if (wpa_s->global->p2p_init_wpa_s != wpa_s) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Direct P2P_CONNECT command to %s",
+                       wpa_s->global->p2p_init_wpa_s->ifname);
+               wpa_s = wpa_s->global->p2p_init_wpa_s;
+       }
 
-       /* <addr> <"pbc" | "pin" | PIN> [label|display|keypad]
+       /* <addr> <"pbc" | "pin" | PIN> [label|display|keypad|p2ps]
         * [persistent|persistent=<network id>]
         * [join] [auth] [go_intent=<0..15>] [freq=<in MHz>] [provdisc]
-        * [ht40] */
+        * [ht40] [vht] [auto] */
 
        if (hwaddr_aton(cmd, addr))
                return -1;
@@ -3691,7 +4783,9 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
        auth = os_strstr(pos, " auth") != NULL;
        automatic = os_strstr(pos, " auto") != NULL;
        pd = os_strstr(pos, " provdisc") != NULL;
-       ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40;
+       vht = (os_strstr(cmd, " vht") != NULL) || wpa_s->conf->p2p_go_vht;
+       ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 ||
+               vht;
 
        pos2 = os_strstr(pos, " go_intent=");
        if (pos2) {
@@ -3722,6 +4816,8 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
                        *pos++ = '\0';
                        if (os_strncmp(pos, "display", 7) == 0)
                                wps_method = WPS_PIN_DISPLAY;
+                       else if (os_strncmp(pos, "p2ps", 4) == 0)
+                               wps_method = WPS_P2PS;
                }
                if (!wps_pin_str_valid(pin)) {
                        os_memcpy(buf, "FAIL-INVALID-PIN\n", 17);
@@ -3732,7 +4828,7 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
        new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
                                   persistent_group, automatic, join,
                                   auth, go_intent, freq, persistent_id, pd,
-                                  ht40);
+                                  ht40, vht);
        if (new_pin == -2) {
                os_memcpy(buf, "FAIL-CHANNEL-UNAVAILABLE\n", 25);
                return 25;
@@ -3745,7 +4841,7 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
                return -1;
        if (wps_method == WPS_PIN_DISPLAY && pin == NULL) {
                ret = os_snprintf(buf, buflen, "%08d", new_pin);
-               if (ret < 0 || (size_t) ret >= buflen)
+               if (os_snprintf_error(buflen, ret))
                        return -1;
                return ret;
        }
@@ -3755,48 +4851,14 @@ static int p2p_ctrl_connect(struct wpa_supplicant *wpa_s, char *cmd,
 }
 
 
-#if defined(TIZEN_EXT_ENROLLEE) && defined(CONFIG_WPS)
-static int p2p_ctrl_wps_enrollee(struct wpa_supplicant *wpa_s, char *cmd, char *buf, size_t buflen)
-{
-       u8 addr[ETH_ALEN];
-       char *pos;
-       char *pin = NULL;
-       enum p2p_wps_method wps_method;
-
-       /* <addr> [PIN#] <pbc |display|keypad> */
-
-       if (hwaddr_aton(cmd, addr))
-               return -1;
-
-       pos = cmd + 17;
-       if (*pos != ' ')
-               return -1;
-       pos++;
-
-       if (os_strstr(pos, "display")) {
-               wps_method = WPS_PIN_DISPLAY;
-       } else if (os_strstr(pos, "keypad")) {
-               wps_method = WPS_PIN_KEYPAD;
-       } else
-               wps_method = WPS_PBC;
-
-       if (wps_method == WPS_PIN_DISPLAY || wps_method == WPS_PIN_KEYPAD) {
-               pin = pos;
-               pin[8] = '\0';
-       }
-
-       if (wpas_p2p_wps_enrollee(wpa_s, addr, pin, wps_method))
-               return -1;
-
-       os_memcpy(buf, "OK\n", 3);
-       return 3;
-}
-#endif /* TIZEN_EXT_ENROLLEE && CONFIG_WPS */
-
-
 static int p2p_ctrl_listen(struct wpa_supplicant *wpa_s, char *cmd)
 {
        unsigned int timeout = atoi(cmd);
+       if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+               wpa_dbg(wpa_s, MSG_INFO,
+                       "Reject P2P_LISTEN since interface is disabled");
+               return -1;
+       }
        return wpas_p2p_listen(wpa_s, timeout);
 }
 
@@ -3822,7 +4884,7 @@ static int p2p_ctrl_prov_disc(struct wpa_supplicant *wpa_s, char *cmd)
        else if (os_strstr(pos, " auto") != NULL)
                use = WPAS_P2P_PD_AUTO;
 
-       return wpas_p2p_prov_disc(wpa_s, addr, pos, use);
+       return wpas_p2p_prov_disc(wpa_s, addr, pos, use, NULL);
 }
 
 
@@ -3875,6 +4937,40 @@ static int p2p_ctrl_serv_disc_req(struct wpa_supplicant *wpa_s, char *cmd,
        } else if (os_strncmp(pos, "wifi-display ", 13) == 0) {
                ref = wpas_p2p_sd_request_wifi_display(wpa_s, dst, pos + 13);
 #endif /* CONFIG_WIFI_DISPLAY */
+       } else if (os_strncmp(pos, "asp ", 4) == 0) {
+               char *svc_str;
+               char *svc_info = NULL;
+               u32 id;
+
+               pos += 4;
+               if (sscanf(pos, "%x", &id) != 1 || id > 0xff)
+                       return -1;
+
+               pos = os_strchr(pos, ' ');
+               if (pos == NULL || pos[1] == '\0' || pos[1] == ' ')
+                       return -1;
+
+               svc_str = pos + 1;
+
+               pos = os_strchr(svc_str, ' ');
+
+               if (pos)
+                       *pos++ = '\0';
+
+               /* All remaining data is the svc_info string */
+               if (pos && pos[0] && pos[0] != ' ') {
+                       len = os_strlen(pos);
+
+                       /* Unescape in place */
+                       len = utf8_unescape(pos, len, pos, len);
+                       if (len > 0xff)
+                               return -1;
+
+                       svc_info = pos;
+               }
+
+               ref = wpas_p2p_sd_request_asp(wpa_s, dst, (u8) id,
+                                             svc_str, svc_info);
        } else {
                len = os_strlen(pos);
                if (len & 1)
@@ -3894,7 +4990,7 @@ static int p2p_ctrl_serv_disc_req(struct wpa_supplicant *wpa_s, char *cmd,
        if (ref == 0)
                return -1;
        res = os_snprintf(buf, buflen, "%llx", (long long unsigned) ref);
-       if (res < 0 || (unsigned) res >= buflen)
+       if (os_snprintf_error(buflen, res))
                return -1;
        return res;
 }
@@ -4037,69 +5133,182 @@ static int p2p_ctrl_service_add_upnp(struct wpa_supplicant *wpa_s, char *cmd)
 }
 
 
-static int p2p_ctrl_service_add(struct wpa_supplicant *wpa_s, char *cmd)
+static int p2p_ctrl_service_add_asp(struct wpa_supplicant *wpa_s,
+                                   u8 replace, char *cmd)
 {
        char *pos;
+       char *adv_str;
+       u32 auto_accept, adv_id, svc_state, config_methods;
+       char *svc_info = NULL;
 
        pos = os_strchr(cmd, ' ');
        if (pos == NULL)
                return -1;
        *pos++ = '\0';
 
-       if (os_strcmp(cmd, "bonjour") == 0)
-               return p2p_ctrl_service_add_bonjour(wpa_s, pos);
-       if (os_strcmp(cmd, "upnp") == 0)
-               return p2p_ctrl_service_add_upnp(wpa_s, pos);
-       wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
-       return -1;
-}
-
-
-static int p2p_ctrl_service_del_bonjour(struct wpa_supplicant *wpa_s,
-                                       char *cmd)
-{
-       size_t len;
-       struct wpabuf *query;
-       int ret;
-
-       len = os_strlen(cmd);
-       if (len & 1)
-               return -1;
-       len /= 2;
-       query = wpabuf_alloc(len);
-       if (query == NULL)
-               return -1;
-       if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) {
-               wpabuf_free(query);
+       /* Auto-Accept value is mandatory, and must be one of the
+        * single values (0, 1, 2, 4) */
+       auto_accept = atoi(cmd);
+       switch (auto_accept) {
+       case P2PS_SETUP_NONE: /* No auto-accept */
+       case P2PS_SETUP_NEW:
+       case P2PS_SETUP_CLIENT:
+       case P2PS_SETUP_GROUP_OWNER:
+               break;
+       default:
                return -1;
        }
 
-       ret = wpas_p2p_service_del_bonjour(wpa_s, query);
-       wpabuf_free(query);
-       return ret;
-}
-
-
-static int p2p_ctrl_service_del_upnp(struct wpa_supplicant *wpa_s, char *cmd)
-{
-       char *pos;
-       u8 version;
-
+       /* Advertisement ID is mandatory */
+       cmd = pos;
        pos = os_strchr(cmd, ' ');
        if (pos == NULL)
                return -1;
        *pos++ = '\0';
 
-       if (hexstr2bin(cmd, &version, 1) < 0)
+       /* Handle Adv_ID == 0 (wildcard "org.wi-fi.wfds") internally. */
+       if (sscanf(cmd, "%x", &adv_id) != 1 || adv_id == 0)
                return -1;
 
-       return wpas_p2p_service_del_upnp(wpa_s, version, pos);
-}
+       /* Only allow replacements if exist, and adds if not */
+       if (wpas_p2p_service_p2ps_id_exists(wpa_s, adv_id)) {
+               if (!replace)
+                       return -1;
+       } else {
+               if (replace)
+                       return -1;
+       }
 
+       /* svc_state between 0 - 0xff is mandatory */
+       if (sscanf(pos, "%x", &svc_state) != 1 || svc_state > 0xff)
+               return -1;
 
-static int p2p_ctrl_service_del(struct wpa_supplicant *wpa_s, char *cmd)
-{
-       char *pos;
+       pos = os_strchr(pos, ' ');
+       if (pos == NULL)
+               return -1;
+
+       /* config_methods is mandatory */
+       pos++;
+       if (sscanf(pos, "%x", &config_methods) != 1)
+               return -1;
+
+       if (!(config_methods &
+             (WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD | WPS_CONFIG_P2PS)))
+               return -1;
+
+       pos = os_strchr(pos, ' ');
+       if (pos == NULL)
+               return -1;
+
+       pos++;
+       adv_str = pos;
+
+       /* Advertisement string is mandatory */
+       if (!pos[0] || pos[0] == ' ')
+               return -1;
+
+       /* Terminate svc string */
+       pos = os_strchr(pos, ' ');
+       if (pos != NULL)
+               *pos++ = '\0';
+
+       /* Service and Response Information are optional */
+       if (pos && pos[0]) {
+               size_t len;
+
+               /* Note the bare ' included, which cannot exist legally
+                * in unescaped string. */
+               svc_info = os_strstr(pos, "svc_info='");
+
+               if (svc_info) {
+                       svc_info += 9;
+                       len = os_strlen(svc_info);
+                       utf8_unescape(svc_info, len, svc_info, len);
+               }
+       }
+
+       return wpas_p2p_service_add_asp(wpa_s, auto_accept, adv_id, adv_str,
+                                       (u8) svc_state, (u16) config_methods,
+                                       svc_info);
+}
+
+
+static int p2p_ctrl_service_add(struct wpa_supplicant *wpa_s, char *cmd)
+{
+       char *pos;
+
+       pos = os_strchr(cmd, ' ');
+       if (pos == NULL)
+               return -1;
+       *pos++ = '\0';
+
+       if (os_strcmp(cmd, "bonjour") == 0)
+               return p2p_ctrl_service_add_bonjour(wpa_s, pos);
+       if (os_strcmp(cmd, "upnp") == 0)
+               return p2p_ctrl_service_add_upnp(wpa_s, pos);
+       if (os_strcmp(cmd, "asp") == 0)
+               return p2p_ctrl_service_add_asp(wpa_s, 0, pos);
+       wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
+       return -1;
+}
+
+
+static int p2p_ctrl_service_del_bonjour(struct wpa_supplicant *wpa_s,
+                                       char *cmd)
+{
+       size_t len;
+       struct wpabuf *query;
+       int ret;
+
+       len = os_strlen(cmd);
+       if (len & 1)
+               return -1;
+       len /= 2;
+       query = wpabuf_alloc(len);
+       if (query == NULL)
+               return -1;
+       if (hexstr2bin(cmd, wpabuf_put(query, len), len) < 0) {
+               wpabuf_free(query);
+               return -1;
+       }
+
+       ret = wpas_p2p_service_del_bonjour(wpa_s, query);
+       wpabuf_free(query);
+       return ret;
+}
+
+
+static int p2p_ctrl_service_del_upnp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+       char *pos;
+       u8 version;
+
+       pos = os_strchr(cmd, ' ');
+       if (pos == NULL)
+               return -1;
+       *pos++ = '\0';
+
+       if (hexstr2bin(cmd, &version, 1) < 0)
+               return -1;
+
+       return wpas_p2p_service_del_upnp(wpa_s, version, pos);
+}
+
+
+static int p2p_ctrl_service_del_asp(struct wpa_supplicant *wpa_s, char *cmd)
+{
+       u32 adv_id;
+
+       if (sscanf(cmd, "%x", &adv_id) != 1)
+               return -1;
+
+       return wpas_p2p_service_del_asp(wpa_s, adv_id);
+}
+
+
+static int p2p_ctrl_service_del(struct wpa_supplicant *wpa_s, char *cmd)
+{
+       char *pos;
 
        pos = os_strchr(cmd, ' ');
        if (pos == NULL)
@@ -4110,6 +5319,25 @@ static int p2p_ctrl_service_del(struct wpa_supplicant *wpa_s, char *cmd)
                return p2p_ctrl_service_del_bonjour(wpa_s, pos);
        if (os_strcmp(cmd, "upnp") == 0)
                return p2p_ctrl_service_del_upnp(wpa_s, pos);
+       if (os_strcmp(cmd, "asp") == 0)
+               return p2p_ctrl_service_del_asp(wpa_s, pos);
+       wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
+       return -1;
+}
+
+
+static int p2p_ctrl_service_replace(struct wpa_supplicant *wpa_s, char *cmd)
+{
+       char *pos;
+
+       pos = os_strchr(cmd, ' ');
+       if (pos == NULL)
+               return -1;
+       *pos++ = '\0';
+
+       if (os_strcmp(cmd, "asp") == 0)
+               return p2p_ctrl_service_add_asp(wpa_s, 1, pos);
+
        wpa_printf(MSG_DEBUG, "Unknown service '%s'", cmd);
        return -1;
 }
@@ -4135,7 +5363,7 @@ static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd)
        struct wpa_ssid *ssid;
        u8 *_peer = NULL, peer[ETH_ALEN];
        int freq = 0, pref_freq = 0;
-       int ht40;
+       int ht40, vht;
 
        id = atoi(cmd);
        pos = os_strstr(cmd, " peer=");
@@ -4169,9 +5397,12 @@ static int p2p_ctrl_invite_persistent(struct wpa_supplicant *wpa_s, char *cmd)
                        return -1;
        }
 
-       ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40;
+       vht = (os_strstr(cmd, " vht") != NULL) || wpa_s->conf->p2p_go_vht;
+       ht40 = (os_strstr(cmd, " ht40") != NULL) || wpa_s->conf->p2p_go_ht40 ||
+               vht;
 
-       return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, ht40, pref_freq);
+       return wpas_p2p_invite(wpa_s, _peer, ssid, NULL, freq, ht40, vht,
+                              pref_freq);
 }
 
 
@@ -4218,7 +5449,8 @@ static int p2p_ctrl_invite(struct wpa_supplicant *wpa_s, char *cmd)
 
 
 static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
-                                        char *cmd, int freq, int ht40)
+                                        char *cmd, int freq, int ht40,
+                                        int vht)
 {
        int id;
        struct wpa_ssid *ssid;
@@ -4232,32 +5464,49 @@ static int p2p_ctrl_group_add_persistent(struct wpa_supplicant *wpa_s,
                return -1;
        }
 
-       return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, ht40, NULL);
+       return wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, ht40, vht,
+                                            NULL, 0);
 }
 
 
 static int p2p_ctrl_group_add(struct wpa_supplicant *wpa_s, char *cmd)
 {
-       int freq = 0, ht40;
+       int freq = 0, ht40, vht;
+       char *passphrase = NULL;
        char *pos;
 
        pos = os_strstr(cmd, "freq=");
        if (pos)
                freq = atoi(pos + 5);
 
-       ht40 = (os_strstr(cmd, "ht40") != NULL) || wpa_s->conf->p2p_go_ht40;
+       vht = (os_strstr(cmd, "vht") != NULL) || wpa_s->conf->p2p_go_vht;
+       ht40 = (os_strstr(cmd, "ht40") != NULL) || wpa_s->conf->p2p_go_ht40 ||
+               vht;
+
+       pos = os_strstr(cmd, "passphrase");
+       if(pos) {
+
+               wpa_printf(MSG_DEBUG, "pos : %s", pos);
+               passphrase = pos + 11;
+               wpa_printf(MSG_DEBUG, "passphrase : %s",passphrase);
+               pos = os_strchr(passphrase, ' ');
+               if(pos != NULL)
+                       *pos = '\0';
+               wpa_printf(MSG_DEBUG, "passphrase : %s",passphrase);
+       }
 
        if (os_strncmp(cmd, "persistent=", 11) == 0)
                return p2p_ctrl_group_add_persistent(wpa_s, cmd + 11, freq,
-                                                    ht40);
+                                                    ht40, vht);
        if (os_strcmp(cmd, "persistent") == 0 ||
            os_strncmp(cmd, "persistent ", 11) == 0)
-               return wpas_p2p_group_add(wpa_s, 1, freq, ht40);
+               return wpas_p2p_group_add(wpa_s, 1, freq, ht40, vht, passphrase);
        if (os_strncmp(cmd, "freq=", 5) == 0)
-               return wpas_p2p_group_add(wpa_s, 0, freq, ht40);
+               return wpas_p2p_group_add(wpa_s, 0, freq, ht40, vht, passphrase);
        if (ht40)
-               return wpas_p2p_group_add(wpa_s, 0, freq, ht40);
-
+               return wpas_p2p_group_add(wpa_s, 0, freq, ht40, vht, passphrase);
+       if(passphrase)
+               return wpas_p2p_group_add(wpa_s, 0, freq, ht40,  vht, passphrase);
        wpa_printf(MSG_DEBUG, "CTRL: Invalid P2P_GROUP_ADD parameters '%s'",
                   cmd);
        return -1;
@@ -4323,7 +5572,7 @@ static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd,
                          info->dev_capab,
                          info->group_capab,
                          info->level);
-       if (res < 0 || res >= end - pos)
+       if (os_snprintf_error(end - pos, res))
                return pos - buf;
        pos += res;
 
@@ -4334,7 +5583,7 @@ static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd,
                res = os_snprintf(pos, end - pos, "sec_dev_type=%s\n",
                                  wps_dev_type_bin2str(t, devtype,
                                                       sizeof(devtype)));
-               if (res < 0 || res >= end - pos)
+               if (os_snprintf_error(end - pos, res))
                        return pos - buf;
                pos += res;
        }
@@ -4342,7 +5591,7 @@ static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd,
        ssid = wpas_p2p_get_persistent(wpa_s, info->p2p_device_addr, NULL, 0);
        if (ssid) {
                res = os_snprintf(pos, end - pos, "persistent=%d\n", ssid->id);
-               if (res < 0 || res >= end - pos)
+               if (os_snprintf_error(end - pos, res))
                        return pos - buf;
                pos += res;
        }
@@ -4352,6 +5601,22 @@ static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd,
                return pos - buf;
        pos += res;
 
+       if (info->vendor_elems) {
+               res = os_snprintf(pos, end - pos, "vendor_elems=");
+               if (os_snprintf_error(end - pos, res))
+                       return pos - buf;
+               pos += res;
+
+               pos += wpa_snprintf_hex(pos, end - pos,
+                                       wpabuf_head(info->vendor_elems),
+                                       wpabuf_len(info->vendor_elems));
+
+               res = os_snprintf(pos, end - pos, "\n");
+               if (os_snprintf_error(end - pos, res))
+                       return pos - buf;
+               pos += res;
+       }
+
        return pos - buf;
 }
 
@@ -4359,48 +5624,21 @@ static int p2p_ctrl_peer(struct wpa_supplicant *wpa_s, char *cmd,
 static int p2p_ctrl_disallow_freq(struct wpa_supplicant *wpa_s,
                                  const char *param)
 {
-       struct wpa_freq_range *freq = NULL, *n;
-       unsigned int count = 0, i;
-       const char *pos, *pos2, *pos3;
+       unsigned int i;
 
        if (wpa_s->global->p2p == NULL)
                return -1;
 
-       /*
-        * param includes comma separated frequency range.
-        * For example: 2412-2432,2462,5000-6000
-        */
-       pos = param;
-       while (pos && pos[0]) {
-               n = os_realloc_array(freq, count + 1,
-                                    sizeof(struct wpa_freq_range));
-               if (n == NULL) {
-                       os_free(freq);
-                       return -1;
-               }
-               freq = n;
-               freq[count].min = atoi(pos);
-               pos2 = os_strchr(pos, '-');
-               pos3 = os_strchr(pos, ',');
-               if (pos2 && (!pos3 || pos2 < pos3)) {
-                       pos2++;
-                       freq[count].max = atoi(pos2);
-               } else
-                       freq[count].max = freq[count].min;
-               pos = pos3;
-               if (pos)
-                       pos++;
-               count++;
-       }
+       if (freq_range_list_parse(&wpa_s->global->p2p_disallow_freq, param) < 0)
+               return -1;
 
-       for (i = 0; i < count; i++) {
+       for (i = 0; i < wpa_s->global->p2p_disallow_freq.num; i++) {
+               struct wpa_freq_range *freq;
+               freq = &wpa_s->global->p2p_disallow_freq.range[i];
                wpa_printf(MSG_DEBUG, "P2P: Disallowed frequency range %u-%u",
-                          freq[i].min, freq[i].max);
+                          freq->min, freq->max);
        }
 
-       os_free(wpa_s->global->p2p_disallow_freq);
-       wpa_s->global->p2p_disallow_freq = freq;
-       wpa_s->global->num_p2p_disallow_freq = count;
        wpas_p2p_update_channel_list(wpa_s);
        return 0;
 }
@@ -4431,7 +5669,7 @@ static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd)
 
        if (os_strcmp(cmd, "listen_channel") == 0) {
                return p2p_set_listen_channel(wpa_s->global->p2p, 81,
-                                             atoi(param));
+                                             atoi(param), 1);
        }
 
        if (os_strcmp(cmd, "ssid_postfix") == 0) {
@@ -4591,6 +5829,21 @@ static int p2p_ctrl_set(struct wpa_supplicant *wpa_s, char *cmd)
                                        max_disc_int, max_disc_tu);
        }
 
+       if (os_strcmp(cmd, "per_sta_psk") == 0) {
+               wpa_s->global->p2p_per_sta_psk = !!atoi(param);
+               return 0;
+       }
+
+#ifdef CONFIG_WPS_NFC
+       if (os_strcmp(cmd, "nfc_tag") == 0)
+               return wpas_p2p_nfc_tag_enabled(wpa_s, !!atoi(param));
+#endif /* CONFIG_WPS_NFC */
+
+       if (os_strcmp(cmd, "disable_ip_addr_req") == 0) {
+               wpa_s->p2p_disable_ip_addr_req = !!atoi(param);
+               return 0;
+       }
+
        wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown P2P_SET field value '%s'",
                   cmd);
 
@@ -4602,6 +5855,7 @@ static void p2p_ctrl_flush(struct wpa_supplicant *wpa_s)
 {
        os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
        wpa_s->force_long_sd = 0;
+       wpas_p2p_stop_find(wpa_s);
        if (wpa_s->global->p2p)
                p2p_flush(wpa_s->global->p2p);
 }
@@ -4656,11 +5910,92 @@ static int p2p_ctrl_ext_listen(struct wpa_supplicant *wpa_s, char *cmd)
        return wpas_p2p_ext_listen(wpa_s, period, interval);
 }
 
+
+static int p2p_ctrl_remove_client(struct wpa_supplicant *wpa_s, const char *cmd)
+{
+       const char *pos;
+       u8 peer[ETH_ALEN];
+       int iface_addr = 0;
+
+       pos = cmd;
+       if (os_strncmp(pos, "iface=", 6) == 0) {
+               iface_addr = 1;
+               pos += 6;
+       }
+       if (hwaddr_aton(pos, peer))
+               return -1;
+
+       wpas_p2p_remove_client(wpa_s, peer, iface_addr);
+       return 0;
+}
+
 #endif /* CONFIG_P2P */
 
 
+static int * freq_range_to_channel_list(struct wpa_supplicant *wpa_s, char *val)
+{
+       struct wpa_freq_range_list ranges;
+       int *freqs = NULL;
+       struct hostapd_hw_modes *mode;
+       u16 i;
+
+       if (wpa_s->hw.modes == NULL)
+               return NULL;
+
+       os_memset(&ranges, 0, sizeof(ranges));
+       if (freq_range_list_parse(&ranges, val) < 0)
+               return NULL;
+
+       for (i = 0; i < wpa_s->hw.num_modes; i++) {
+               int j;
+
+               mode = &wpa_s->hw.modes[i];
+               for (j = 0; j < mode->num_channels; j++) {
+                       unsigned int freq;
+
+                       if (mode->channels[j].flag & HOSTAPD_CHAN_DISABLED)
+                               continue;
+
+                       freq = mode->channels[j].freq;
+                       if (!freq_range_list_includes(&ranges, freq))
+                               continue;
+
+                       int_array_add_unique(&freqs, freq);
+               }
+       }
+
+       os_free(ranges.range);
+       return freqs;
+}
+
+
 #ifdef CONFIG_INTERWORKING
-static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst)
+
+static int ctrl_interworking_select(struct wpa_supplicant *wpa_s, char *param)
+{
+       int auto_sel = 0;
+       int *freqs = NULL;
+
+       if (param) {
+               char *pos;
+
+               auto_sel = os_strstr(param, "auto") != NULL;
+
+               pos = os_strstr(param, "freq=");
+               if (pos) {
+                       freqs = freq_range_to_channel_list(wpa_s, pos + 5);
+                       if (freqs == NULL)
+                               return -1;
+               }
+
+       }
+
+       return interworking_select(wpa_s, auto_sel, freqs);
+}
+
+
+static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst,
+                                    int only_add)
 {
        u8 bssid[ETH_ALEN];
        struct wpa_bss *bss;
@@ -4677,7 +6012,28 @@ static int ctrl_interworking_connect(struct wpa_supplicant *wpa_s, char *dst)
                return -1;
        }
 
-       return interworking_connect(wpa_s, bss);
+       if (bss->ssid_len == 0) {
+               int found = 0;
+
+               wpa_printf(MSG_DEBUG, "Selected BSS entry for " MACSTR
+                          " does not have SSID information", MAC2STR(bssid));
+
+               dl_list_for_each_reverse(bss, &wpa_s->bss, struct wpa_bss,
+                                        list) {
+                       if (os_memcmp(bss->bssid, bssid, ETH_ALEN) == 0 &&
+                           bss->ssid_len > 0) {
+                               found = 1;
+                               break;
+                       }
+               }
+
+               if (!found)
+                       return -1;
+               wpa_printf(MSG_DEBUG,
+                          "Found another matching BSS entry with SSID");
+       }
+
+       return interworking_connect(wpa_s, bss, only_add);
 }
 
 
@@ -4689,15 +6045,29 @@ static int get_anqp(struct wpa_supplicant *wpa_s, char *dst)
 #define MAX_ANQP_INFO_ID 100
        u16 id[MAX_ANQP_INFO_ID];
        size_t num_id = 0;
+       u32 subtypes = 0;
 
        used = hwaddr_aton2(dst, dst_addr);
        if (used < 0)
                return -1;
        pos = dst + used;
+       if (*pos == ' ')
+               pos++;
        while (num_id < MAX_ANQP_INFO_ID) {
-               id[num_id] = atoi(pos);
-               if (id[num_id])
-                       num_id++;
+               if (os_strncmp(pos, "hs20:", 5) == 0) {
+#ifdef CONFIG_HS20
+                       int num = atoi(pos + 5);
+                       if (num <= 0 || num > 31)
+                               return -1;
+                       subtypes |= BIT(num);
+#else /* CONFIG_HS20 */
+                       return -1;
+#endif /* CONFIG_HS20 */
+               } else {
+                       id[num_id] = atoi(pos);
+                       if (id[num_id])
+                               num_id++;
+               }
                pos = os_strchr(pos + 1, ',');
                if (pos == NULL)
                        break;
@@ -4707,7 +6077,7 @@ static int get_anqp(struct wpa_supplicant *wpa_s, char *dst)
        if (num_id == 0)
                return -1;
 
-       return anqp_send_req(wpa_s, dst_addr, id, num_id);
+       return anqp_send_req(wpa_s, dst_addr, id, num_id, subtypes);
 }
 
 
@@ -4783,9 +6153,8 @@ static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf,
        int used;
        char *pos;
        size_t resp_len, start, requested_len;
-
-       if (!wpa_s->last_gas_resp)
-               return -1;
+       struct wpabuf *resp;
+       int ret;
 
        used = hwaddr_aton2(cmd, addr);
        if (used < 0)
@@ -4796,11 +6165,18 @@ static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf,
                pos++;
        dialog_token = atoi(pos);
 
-       if (os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) != 0 ||
-           dialog_token != wpa_s->last_gas_dialog_token)
+       if (wpa_s->last_gas_resp &&
+           os_memcmp(addr, wpa_s->last_gas_addr, ETH_ALEN) == 0 &&
+           dialog_token == wpa_s->last_gas_dialog_token)
+               resp = wpa_s->last_gas_resp;
+       else if (wpa_s->prev_gas_resp &&
+                os_memcmp(addr, wpa_s->prev_gas_addr, ETH_ALEN) == 0 &&
+                dialog_token == wpa_s->prev_gas_dialog_token)
+               resp = wpa_s->prev_gas_resp;
+       else
                return -1;
 
-       resp_len = wpabuf_len(wpa_s->last_gas_resp);
+       resp_len = wpabuf_len(resp);
        start = 0;
        requested_len = resp_len;
 
@@ -4821,9 +6197,24 @@ static int gas_response_get(struct wpa_supplicant *wpa_s, char *cmd, char *buf,
        if (requested_len * 2 + 1 > buflen)
                return os_snprintf(buf, buflen, "FAIL-Too long response");
 
-       return wpa_snprintf_hex(buf, buflen,
-                               wpabuf_head_u8(wpa_s->last_gas_resp) + start,
-                               requested_len);
+       ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(resp) + start,
+                              requested_len);
+
+       if (start + requested_len == resp_len) {
+               /*
+                * Free memory by dropping the response after it has been
+                * fetched.
+                */
+               if (resp == wpa_s->prev_gas_resp) {
+                       wpabuf_free(wpa_s->prev_gas_resp);
+                       wpa_s->prev_gas_resp = NULL;
+               } else {
+                       wpabuf_free(wpa_s->last_gas_resp);
+                       wpa_s->last_gas_resp = NULL;
+               }
+       }
+
+       return ret;
 }
 #endif /* CONFIG_INTERWORKING */
 
@@ -4841,6 +6232,8 @@ static int get_hs20_anqp(struct wpa_supplicant *wpa_s, char *dst)
        if (used < 0)
                return -1;
        pos = dst + used;
+       if (*pos == ' ')
+               pos++;
        for (;;) {
                int num = atoi(pos);
                if (num <= 0 || num > 31)
@@ -4911,7 +6304,7 @@ static int hs20_get_nai_home_realm_list(struct wpa_supplicant *wpa_s,
        if (len == 0 && cred && cred->realm)
                return hs20_nai_home_realm_list(wpa_s, dst_addr, cred->realm);
 
-       if (len % 1)
+       if (len & 1)
                return -1;
        len /= 2;
        buf = os_malloc(len);
@@ -4930,16 +6323,28 @@ static int hs20_get_nai_home_realm_list(struct wpa_supplicant *wpa_s,
        return ret;
 }
 
-#endif /* CONFIG_HS20 */
-
 
-static int wpa_supplicant_ctrl_iface_sta_autoconnect(
-       struct wpa_supplicant *wpa_s, char *cmd)
+static int hs20_icon_request(struct wpa_supplicant *wpa_s, char *cmd)
 {
-       wpa_s->auto_reconnect_disabled = atoi(cmd) == 0 ? 1 : 0;
-       return 0;
+       u8 dst_addr[ETH_ALEN];
+       int used;
+       char *icon;
+
+       used = hwaddr_aton2(cmd, dst_addr);
+       if (used < 0)
+               return -1;
+
+       while (cmd[used] == ' ')
+               used++;
+       icon = &cmd[used];
+
+       wpa_s->fetch_osu_icon_in_progress = 0;
+       return hs20_anqp_send_req(wpa_s, dst_addr, BIT(HS20_STYPE_ICON_REQUEST),
+                                 (u8 *) icon, os_strlen(icon));
 }
 
+#endif /* CONFIG_HS20 */
+
 
 #ifdef CONFIG_AUTOSCAN
 
@@ -5038,28 +6443,6 @@ static int wpas_ctrl_iface_wnm_bss_query(struct wpa_supplicant *wpa_s, char *cmd
 #endif /* CONFIG_WNM */
 
 
-/* Get string representation of channel width */
-static const char * channel_width_name(enum chan_width width)
-{
-       switch (width) {
-       case CHAN_WIDTH_20_NOHT:
-               return "20 MHz (no HT)";
-       case CHAN_WIDTH_20:
-               return "20 MHz";
-       case CHAN_WIDTH_40:
-               return "40 MHz";
-       case CHAN_WIDTH_80:
-               return "80 MHz";
-       case CHAN_WIDTH_80P80:
-               return "80+80 MHz";
-       case CHAN_WIDTH_160:
-               return "160 MHz";
-       default:
-               return "unknown";
-       }
-}
-
-
 static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf,
                                      size_t buflen)
 {
@@ -5074,110 +6457,1366 @@ static int wpa_supplicant_signal_poll(struct wpa_supplicant *wpa_s, char *buf,
        pos = buf;
        end = buf + buflen;
 
-       ret = os_snprintf(pos, end - pos, "RSSI=%d\nLINKSPEED=%d\n"
-                         "NOISE=%d\nFREQUENCY=%u\n",
-                         si.current_signal, si.current_txrate / 1000,
-                         si.current_noise, si.frequency);
-       if (ret < 0 || ret > end - pos)
-               return -1;
-       pos += ret;
+       ret = os_snprintf(pos, end - pos, "RSSI=%d\nLINKSPEED=%d\n"
+                         "NOISE=%d\nFREQUENCY=%u\n",
+                         si.current_signal, si.current_txrate / 1000,
+                         si.current_noise, si.frequency);
+       if (os_snprintf_error(end - pos, ret))
+               return -1;
+       pos += ret;
+
+       if (si.chanwidth != CHAN_WIDTH_UNKNOWN) {
+               ret = os_snprintf(pos, end - pos, "WIDTH=%s\n",
+                                 channel_width_to_string(si.chanwidth));
+               if (os_snprintf_error(end - pos, ret))
+                       return -1;
+               pos += ret;
+       }
+
+       if (si.center_frq1 > 0 && si.center_frq2 > 0) {
+               ret = os_snprintf(pos, end - pos,
+                                 "CENTER_FRQ1=%d\nCENTER_FRQ2=%d\n",
+                                 si.center_frq1, si.center_frq2);
+               if (os_snprintf_error(end - pos, ret))
+                       return -1;
+               pos += ret;
+       }
+
+       if (si.avg_signal) {
+               ret = os_snprintf(pos, end - pos,
+                                 "AVG_RSSI=%d\n", si.avg_signal);
+               if (os_snprintf_error(end - pos, ret))
+                       return -1;
+               pos += ret;
+       }
+
+       return pos - buf;
+}
+
+
+static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf,
+                                     size_t buflen)
+{
+       struct hostap_sta_driver_data sta;
+       int ret;
+
+       ret = wpa_drv_pktcnt_poll(wpa_s, &sta);
+       if (ret)
+               return -1;
+
+       ret = os_snprintf(buf, buflen, "TXGOOD=%lu\nTXBAD=%lu\nRXGOOD=%lu\n",
+                         sta.tx_packets, sta.tx_retry_failed, sta.rx_packets);
+       if (os_snprintf_error(buflen, ret))
+               return -1;
+       return ret;
+}
+
+
+#ifdef ANDROID
+static int wpa_supplicant_driver_cmd(struct wpa_supplicant *wpa_s, char *cmd,
+                                    char *buf, size_t buflen)
+{
+       int ret;
+
+       ret = wpa_drv_driver_cmd(wpa_s, cmd, buf, buflen);
+       if (ret == 0) {
+               if (os_strncasecmp(cmd, "COUNTRY", 7) == 0) {
+                       struct p2p_data *p2p = wpa_s->global->p2p;
+                       if (p2p) {
+                               char country[3];
+                               country[0] = cmd[8];
+                               country[1] = cmd[9];
+                               country[2] = 0x04;
+                               p2p_set_country(p2p, country);
+                       }
+               }
+               ret = os_snprintf(buf, buflen, "%s\n", "OK");
+               if (os_snprintf_error(buflen, ret))
+                       ret = -1;
+       }
+       return ret;
+}
+#endif /* ANDROID */
+
+
+static int wpa_supplicant_vendor_cmd(struct wpa_supplicant *wpa_s, char *cmd,
+                                    char *buf, size_t buflen)
+{
+       int ret;
+       char *pos;
+       u8 *data = NULL;
+       unsigned int vendor_id, subcmd;
+       struct wpabuf *reply;
+       size_t data_len = 0;
+
+       /* cmd: <vendor id> <subcommand id> [<hex formatted data>] */
+       vendor_id = strtoul(cmd, &pos, 16);
+       if (!isblank(*pos))
+               return -EINVAL;
+
+       subcmd = strtoul(pos, &pos, 10);
+
+       if (*pos != '\0') {
+               if (!isblank(*pos++))
+                       return -EINVAL;
+               data_len = os_strlen(pos);
+       }
+
+       if (data_len) {
+               data_len /= 2;
+               data = os_malloc(data_len);
+               if (!data)
+                       return -1;
+
+               if (hexstr2bin(pos, data, data_len)) {
+                       wpa_printf(MSG_DEBUG,
+                                  "Vendor command: wrong parameter format");
+                       os_free(data);
+                       return -EINVAL;
+               }
+       }
+
+       reply = wpabuf_alloc((buflen - 1) / 2);
+       if (!reply) {
+               os_free(data);
+               return -1;
+       }
+
+       ret = wpa_drv_vendor_cmd(wpa_s, vendor_id, subcmd, data, data_len,
+                                reply);
+
+       if (ret == 0)
+               ret = wpa_snprintf_hex(buf, buflen, wpabuf_head_u8(reply),
+                                      wpabuf_len(reply));
+
+       wpabuf_free(reply);
+       os_free(data);
+
+       return ret;
+}
+
+
+static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_P2P
+       struct wpa_supplicant *p2p_wpa_s = wpa_s->global->p2p_init_wpa_s ?
+               wpa_s->global->p2p_init_wpa_s : wpa_s;
+#endif /* CONFIG_P2P */
+
+       wpa_dbg(wpa_s, MSG_DEBUG, "Flush all wpa_supplicant state");
+
+#ifdef CONFIG_P2P
+       wpas_p2p_cancel(p2p_wpa_s);
+       p2p_ctrl_flush(p2p_wpa_s);
+       wpas_p2p_group_remove(p2p_wpa_s, "*");
+       wpas_p2p_service_flush(p2p_wpa_s);
+       p2p_wpa_s->global->p2p_disabled = 0;
+       p2p_wpa_s->global->p2p_per_sta_psk = 0;
+       p2p_wpa_s->conf->num_sec_device_types = 0;
+       p2p_wpa_s->p2p_disable_ip_addr_req = 0;
+       os_free(p2p_wpa_s->global->p2p_go_avoid_freq.range);
+       p2p_wpa_s->global->p2p_go_avoid_freq.range = NULL;
+       p2p_wpa_s->global->pending_p2ps_group = 0;
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_WPS_TESTING
+       wps_version_number = 0x20;
+       wps_testing_dummy_cred = 0;
+       wps_corrupt_pkhash = 0;
+#endif /* CONFIG_WPS_TESTING */
+#ifdef CONFIG_WPS
+       wpa_s->wps_fragment_size = 0;
+       wpas_wps_cancel(wpa_s);
+       wps_registrar_flush(wpa_s->wps->registrar);
+#endif /* CONFIG_WPS */
+       wpa_s->after_wps = 0;
+       wpa_s->known_wps_freq = 0;
+
+#ifdef CONFIG_TDLS
+#ifdef CONFIG_TDLS_TESTING
+       extern unsigned int tdls_testing;
+       tdls_testing = 0;
+#endif /* CONFIG_TDLS_TESTING */
+       wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL);
+       wpa_tdls_enable(wpa_s->wpa, 1);
+#endif /* CONFIG_TDLS */
+
+       eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL);
+       wpa_supplicant_stop_countermeasures(wpa_s, NULL);
+
+       wpa_s->no_keep_alive = 0;
+       wpa_s->own_disconnect_req = 0;
+
+       os_free(wpa_s->disallow_aps_bssid);
+       wpa_s->disallow_aps_bssid = NULL;
+       wpa_s->disallow_aps_bssid_count = 0;
+       os_free(wpa_s->disallow_aps_ssid);
+       wpa_s->disallow_aps_ssid = NULL;
+       wpa_s->disallow_aps_ssid_count = 0;
+
+       wpa_s->set_sta_uapsd = 0;
+       wpa_s->sta_uapsd = 0;
+
+       wpa_drv_radio_disable(wpa_s, 0);
+       wpa_blacklist_clear(wpa_s);
+       wpa_s->extra_blacklist_count = 0;
+       wpa_supplicant_ctrl_iface_remove_network(wpa_s, "all");
+       wpa_supplicant_ctrl_iface_remove_cred(wpa_s, "all");
+       wpa_config_flush_blobs(wpa_s->conf);
+       wpa_s->conf->auto_interworking = 0;
+       wpa_s->conf->okc = 0;
+
+       wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
+       rsn_preauth_deinit(wpa_s->wpa);
+
+       wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_LIFETIME, 43200);
+       wpa_sm_set_param(wpa_s->wpa, RSNA_PMK_REAUTH_THRESHOLD, 70);
+       wpa_sm_set_param(wpa_s->wpa, RSNA_SA_TIMEOUT, 60);
+       eapol_sm_notify_logoff(wpa_s->eapol, FALSE);
+
+       radio_remove_works(wpa_s, NULL, 1);
+       wpa_s->ext_work_in_progress = 0;
+
+       wpa_s->next_ssid = NULL;
+
+#ifdef CONFIG_INTERWORKING
+       hs20_cancel_fetch_osu(wpa_s);
+#endif /* CONFIG_INTERWORKING */
+
+       wpa_s->ext_mgmt_frame_handling = 0;
+       wpa_s->ext_eapol_frame_io = 0;
+#ifdef CONFIG_TESTING_OPTIONS
+       wpa_s->extra_roc_dur = 0;
+       wpa_s->test_failure = WPAS_TEST_FAILURE_NONE;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+       wpa_s->disconnected = 0;
+       os_free(wpa_s->next_scan_freqs);
+       wpa_s->next_scan_freqs = NULL;
+
+       wpa_bss_flush(wpa_s);
+       if (!dl_list_empty(&wpa_s->bss)) {
+               wpa_printf(MSG_DEBUG,
+                          "BSS table not empty after flush: %u entries, current_bss=%p bssid="
+                          MACSTR " pending_bssid=" MACSTR,
+                          dl_list_len(&wpa_s->bss), wpa_s->current_bss,
+                          MAC2STR(wpa_s->bssid),
+                          MAC2STR(wpa_s->pending_bssid));
+       }
+}
+
+
+static int wpas_ctrl_radio_work_show(struct wpa_supplicant *wpa_s,
+                                    char *buf, size_t buflen)
+{
+       struct wpa_radio_work *work;
+       char *pos, *end;
+       struct os_reltime now, diff;
+
+       pos = buf;
+       end = buf + buflen;
+
+       os_get_reltime(&now);
+
+       dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list)
+       {
+               int ret;
+
+               os_reltime_sub(&now, &work->time, &diff);
+               ret = os_snprintf(pos, end - pos, "%s@%s:%u:%u:%ld.%06ld\n",
+                                 work->type, work->wpa_s->ifname, work->freq,
+                                 work->started, diff.sec, diff.usec);
+               if (os_snprintf_error(end - pos, ret))
+                       break;
+               pos += ret;
+       }
+
+       return pos - buf;
+}
+
+
+static void wpas_ctrl_radio_work_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_radio_work *work = eloop_ctx;
+       struct wpa_external_work *ework = work->ctx;
+
+       wpa_dbg(work->wpa_s, MSG_DEBUG,
+               "Timing out external radio work %u (%s)",
+               ework->id, work->type);
+       wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_TIMEOUT "%u", ework->id);
+       work->wpa_s->ext_work_in_progress = 0;
+       radio_work_done(work);
+       os_free(ework);
+}
+
+
+static void wpas_ctrl_radio_work_cb(struct wpa_radio_work *work, int deinit)
+{
+       struct wpa_external_work *ework = work->ctx;
+
+       if (deinit) {
+               if (work->started)
+                       eloop_cancel_timeout(wpas_ctrl_radio_work_timeout,
+                                            work, NULL);
+
+               os_free(ework);
+               return;
+       }
+
+       wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting external radio work %u (%s)",
+               ework->id, ework->type);
+       wpa_msg(work->wpa_s, MSG_INFO, EXT_RADIO_WORK_START "%u", ework->id);
+       work->wpa_s->ext_work_in_progress = 1;
+       if (!ework->timeout)
+               ework->timeout = 10;
+       eloop_register_timeout(ework->timeout, 0, wpas_ctrl_radio_work_timeout,
+                              work, NULL);
+}
+
+
+static int wpas_ctrl_radio_work_add(struct wpa_supplicant *wpa_s, char *cmd,
+                                   char *buf, size_t buflen)
+{
+       struct wpa_external_work *ework;
+       char *pos, *pos2;
+       size_t type_len;
+       int ret;
+       unsigned int freq = 0;
+
+       /* format: <name> [freq=<MHz>] [timeout=<seconds>] */
+
+       ework = os_zalloc(sizeof(*ework));
+       if (ework == NULL)
+               return -1;
+
+       pos = os_strchr(cmd, ' ');
+       if (pos) {
+               type_len = pos - cmd;
+               pos++;
+
+               pos2 = os_strstr(pos, "freq=");
+               if (pos2)
+                       freq = atoi(pos2 + 5);
+
+               pos2 = os_strstr(pos, "timeout=");
+               if (pos2)
+                       ework->timeout = atoi(pos2 + 8);
+       } else {
+               type_len = os_strlen(cmd);
+       }
+       if (4 + type_len >= sizeof(ework->type))
+               type_len = sizeof(ework->type) - 4 - 1;
+       os_strlcpy(ework->type, "ext:", sizeof(ework->type));
+       os_memcpy(ework->type + 4, cmd, type_len);
+       ework->type[4 + type_len] = '\0';
+
+       wpa_s->ext_work_id++;
+       if (wpa_s->ext_work_id == 0)
+               wpa_s->ext_work_id++;
+       ework->id = wpa_s->ext_work_id;
+
+       if (radio_add_work(wpa_s, freq, ework->type, 0, wpas_ctrl_radio_work_cb,
+                          ework) < 0) {
+               os_free(ework);
+               return -1;
+       }
+
+       ret = os_snprintf(buf, buflen, "%u", ework->id);
+       if (os_snprintf_error(buflen, ret))
+               return -1;
+       return ret;
+}
+
+
+static int wpas_ctrl_radio_work_done(struct wpa_supplicant *wpa_s, char *cmd)
+{
+       struct wpa_radio_work *work;
+       unsigned int id = atoi(cmd);
+
+       dl_list_for_each(work, &wpa_s->radio->work, struct wpa_radio_work, list)
+       {
+               struct wpa_external_work *ework;
+
+               if (os_strncmp(work->type, "ext:", 4) != 0)
+                       continue;
+               ework = work->ctx;
+               if (id && ework->id != id)
+                       continue;
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "Completed external radio work %u (%s)",
+                       ework->id, ework->type);
+               eloop_cancel_timeout(wpas_ctrl_radio_work_timeout, work, NULL);
+               wpa_s->ext_work_in_progress = 0;
+               radio_work_done(work);
+               os_free(ework);
+               return 3; /* "OK\n" */
+       }
+
+       return -1;
+}
+
+
+static int wpas_ctrl_radio_work(struct wpa_supplicant *wpa_s, char *cmd,
+                               char *buf, size_t buflen)
+{
+       if (os_strcmp(cmd, "show") == 0)
+               return wpas_ctrl_radio_work_show(wpa_s, buf, buflen);
+       if (os_strncmp(cmd, "add ", 4) == 0)
+               return wpas_ctrl_radio_work_add(wpa_s, cmd + 4, buf, buflen);
+       if (os_strncmp(cmd, "done ", 5) == 0)
+               return wpas_ctrl_radio_work_done(wpa_s, cmd + 4);
+       return -1;
+}
+
+
+void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_radio_work *work, *tmp;
+
+       if (!wpa_s || !wpa_s->radio)
+               return;
+
+       dl_list_for_each_safe(work, tmp, &wpa_s->radio->work,
+                             struct wpa_radio_work, list) {
+               struct wpa_external_work *ework;
+
+               if (os_strncmp(work->type, "ext:", 4) != 0)
+                       continue;
+               ework = work->ctx;
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "Flushing%s external radio work %u (%s)",
+                       work->started ? " started" : "", ework->id,
+                       ework->type);
+               if (work->started)
+                       eloop_cancel_timeout(wpas_ctrl_radio_work_timeout,
+                                            work, NULL);
+               radio_work_done(work);
+               os_free(ework);
+       }
+}
+
+
+static void wpas_ctrl_eapol_response(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       eapol_sm_notify_ctrl_response(wpa_s->eapol);
+}
+
+
+static int scan_id_list_parse(struct wpa_supplicant *wpa_s, const char *value,
+                             unsigned int *scan_id_count, int scan_id[])
+{
+       const char *pos = value;
+
+       while (pos) {
+               if (*pos == ' ' || *pos == '\0')
+                       break;
+               if (*scan_id_count == MAX_SCAN_ID)
+                       return -1;
+               scan_id[(*scan_id_count)++] = atoi(pos);
+               pos = os_strchr(pos, ',');
+               if (pos)
+                       pos++;
+       }
+
+       return 0;
+}
+
+
+static void wpas_ctrl_scan(struct wpa_supplicant *wpa_s, char *params,
+                          char *reply, int reply_size, int *reply_len)
+{
+       char *pos;
+       unsigned int manual_scan_passive = 0;
+       unsigned int manual_scan_use_id = 0;
+       unsigned int manual_scan_only_new = 0;
+       unsigned int scan_only = 0;
+       unsigned int scan_id_count = 0;
+       int scan_id[MAX_SCAN_ID];
+       void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
+                                struct wpa_scan_results *scan_res);
+       int *manual_scan_freqs = NULL;
+
+       if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+               *reply_len = -1;
+               return;
+       }
+
+       if (radio_work_pending(wpa_s, "scan")) {
+               wpa_printf(MSG_DEBUG,
+                          "Pending scan scheduled - reject new request");
+               *reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n");
+               return;
+       }
+
+       if (params) {
+               if (os_strncasecmp(params, "TYPE=ONLY", 9) == 0)
+                       scan_only = 1;
+
+               pos = os_strstr(params, "freq=");
+               if (pos) {
+                       manual_scan_freqs = freq_range_to_channel_list(wpa_s,
+                                                                      pos + 5);
+                       if (manual_scan_freqs == NULL) {
+                               *reply_len = -1;
+                               goto done;
+                       }
+               }
+
+               pos = os_strstr(params, "passive=");
+               if (pos)
+                       manual_scan_passive = !!atoi(pos + 8);
+
+               pos = os_strstr(params, "use_id=");
+               if (pos)
+                       manual_scan_use_id = atoi(pos + 7);
+
+               pos = os_strstr(params, "only_new=1");
+               if (pos)
+                       manual_scan_only_new = 1;
+
+               pos = os_strstr(params, "scan_id=");
+               if (pos && scan_id_list_parse(wpa_s, pos + 8, &scan_id_count,
+                                             scan_id) < 0) {
+                       *reply_len = -1;
+                       goto done;
+               }
+       }
+
+       if (scan_only)
+               scan_res_handler = scan_only_handler;
+       else if (wpa_s->scan_res_handler == scan_only_handler)
+               scan_res_handler = NULL;
+       else
+               scan_res_handler = wpa_s->scan_res_handler;
+
+       if (!wpa_s->sched_scanning && !wpa_s->scanning &&
+           ((wpa_s->wpa_state <= WPA_SCANNING) ||
+            (wpa_s->wpa_state == WPA_COMPLETED))) {
+               wpa_s->manual_scan_passive = manual_scan_passive;
+               wpa_s->manual_scan_use_id = manual_scan_use_id;
+               wpa_s->manual_scan_only_new = manual_scan_only_new;
+               wpa_s->scan_id_count = scan_id_count;
+               os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int));
+               wpa_s->scan_res_handler = scan_res_handler;
+               os_free(wpa_s->manual_scan_freqs);
+               wpa_s->manual_scan_freqs = manual_scan_freqs;
+               manual_scan_freqs = NULL;
+
+               wpa_s->normal_scans = 0;
+               wpa_s->scan_req = MANUAL_SCAN_REQ;
+               wpa_s->after_wps = 0;
+               wpa_s->known_wps_freq = 0;
+               wpa_supplicant_req_scan(wpa_s, 0, 0);
+               if (wpa_s->manual_scan_use_id) {
+                       wpa_s->manual_scan_id++;
+                       wpa_dbg(wpa_s, MSG_DEBUG, "Assigned scan id %u",
+                               wpa_s->manual_scan_id);
+                       *reply_len = os_snprintf(reply, reply_size, "%u\n",
+                                                wpa_s->manual_scan_id);
+               }
+       } else if (wpa_s->sched_scanning) {
+               wpa_s->manual_scan_passive = manual_scan_passive;
+               wpa_s->manual_scan_use_id = manual_scan_use_id;
+               wpa_s->manual_scan_only_new = manual_scan_only_new;
+               wpa_s->scan_id_count = scan_id_count;
+               os_memcpy(wpa_s->scan_id, scan_id, scan_id_count * sizeof(int));
+               wpa_s->scan_res_handler = scan_res_handler;
+               os_free(wpa_s->manual_scan_freqs);
+               wpa_s->manual_scan_freqs = manual_scan_freqs;
+               manual_scan_freqs = NULL;
+
+               wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to allow requested full scan to proceed");
+               wpa_supplicant_cancel_sched_scan(wpa_s);
+               wpa_s->scan_req = MANUAL_SCAN_REQ;
+               wpa_supplicant_req_scan(wpa_s, 0, 0);
+               if (wpa_s->manual_scan_use_id) {
+                       wpa_s->manual_scan_id++;
+                       *reply_len = os_snprintf(reply, reply_size, "%u\n",
+                                                wpa_s->manual_scan_id);
+                       wpa_dbg(wpa_s, MSG_DEBUG, "Assigned scan id %u",
+                               wpa_s->manual_scan_id);
+               }
+       } else {
+               wpa_printf(MSG_DEBUG, "Ongoing scan action - reject new request");
+               *reply_len = os_snprintf(reply, reply_size, "FAIL-BUSY\n");
+       }
+
+done:
+       os_free(manual_scan_freqs);
+}
+
+
+#ifdef CONFIG_TESTING_OPTIONS
+
+static void wpas_ctrl_iface_mgmt_tx_cb(struct wpa_supplicant *wpa_s,
+                                      unsigned int freq, const u8 *dst,
+                                      const u8 *src, const u8 *bssid,
+                                      const u8 *data, size_t data_len,
+                                      enum offchannel_send_action_result
+                                      result)
+{
+       wpa_msg(wpa_s, MSG_INFO, "MGMT-TX-STATUS freq=%u dst=" MACSTR
+               " src=" MACSTR " bssid=" MACSTR " result=%s",
+               freq, MAC2STR(dst), MAC2STR(src), MAC2STR(bssid),
+               result == OFFCHANNEL_SEND_ACTION_SUCCESS ?
+               "SUCCESS" : (result == OFFCHANNEL_SEND_ACTION_NO_ACK ?
+                            "NO_ACK" : "FAILED"));
+}
+
+
+static int wpas_ctrl_iface_mgmt_tx(struct wpa_supplicant *wpa_s, char *cmd)
+{
+       char *pos, *param;
+       size_t len;
+       u8 *buf, da[ETH_ALEN], bssid[ETH_ALEN];
+       int res, used;
+       int freq = 0, no_cck = 0, wait_time = 0;
+
+       /* <DA> <BSSID> [freq=<MHz>] [wait_time=<ms>] [no_cck=1]
+        *    <action=Action frame payload> */
+
+       wpa_printf(MSG_DEBUG, "External MGMT TX: %s", cmd);
+
+       pos = cmd;
+       used = hwaddr_aton2(pos, da);
+       if (used < 0)
+               return -1;
+       pos += used;
+       while (*pos == ' ')
+               pos++;
+       used = hwaddr_aton2(pos, bssid);
+       if (used < 0)
+               return -1;
+       pos += used;
+
+       param = os_strstr(pos, " freq=");
+       if (param) {
+               param += 6;
+               freq = atoi(param);
+       }
+
+       param = os_strstr(pos, " no_cck=");
+       if (param) {
+               param += 8;
+               no_cck = atoi(param);
+       }
+
+       param = os_strstr(pos, " wait_time=");
+       if (param) {
+               param += 11;
+               wait_time = atoi(param);
+       }
+
+       param = os_strstr(pos, " action=");
+       if (param == NULL)
+               return -1;
+       param += 8;
+
+       len = os_strlen(param);
+       if (len & 1)
+               return -1;
+       len /= 2;
+
+       buf = os_malloc(len);
+       if (buf == NULL)
+               return -1;
+
+       if (hexstr2bin(param, buf, len) < 0) {
+               os_free(buf);
+               return -1;
+       }
+
+       res = offchannel_send_action(wpa_s, freq, da, wpa_s->own_addr, bssid,
+                                    buf, len, wait_time,
+                                    wpas_ctrl_iface_mgmt_tx_cb, no_cck);
+       os_free(buf);
+       return res;
+}
+
+
+static void wpas_ctrl_iface_mgmt_tx_done(struct wpa_supplicant *wpa_s)
+{
+       wpa_printf(MSG_DEBUG, "External MGMT TX - done waiting");
+       offchannel_send_action_done(wpa_s);
+}
+
+
+static int wpas_ctrl_iface_driver_event(struct wpa_supplicant *wpa_s, char *cmd)
+{
+       char *pos, *param;
+       union wpa_event_data event;
+       enum wpa_event_type ev;
+
+       /* <event name> [parameters..] */
+
+       wpa_dbg(wpa_s, MSG_DEBUG, "Testing - external driver event: %s", cmd);
+
+       pos = cmd;
+       param = os_strchr(pos, ' ');
+       if (param)
+               *param++ = '\0';
+
+       os_memset(&event, 0, sizeof(event));
+
+       if (os_strcmp(cmd, "INTERFACE_ENABLED") == 0) {
+               ev = EVENT_INTERFACE_ENABLED;
+       } else if (os_strcmp(cmd, "INTERFACE_DISABLED") == 0) {
+               ev = EVENT_INTERFACE_DISABLED;
+       } else if (os_strcmp(cmd, "AVOID_FREQUENCIES") == 0) {
+               ev = EVENT_AVOID_FREQUENCIES;
+               if (param == NULL)
+                       param = "";
+               if (freq_range_list_parse(&event.freq_range, param) < 0)
+                       return -1;
+               wpa_supplicant_event(wpa_s, ev, &event);
+               os_free(event.freq_range.range);
+               return 0;
+       } else {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Testing - unknown driver event: %s",
+                       cmd);
+               return -1;
+       }
+
+       wpa_supplicant_event(wpa_s, ev, &event);
+
+       return 0;
+}
+
+
+static int wpas_ctrl_iface_eapol_rx(struct wpa_supplicant *wpa_s, char *cmd)
+{
+       char *pos;
+       u8 src[ETH_ALEN], *buf;
+       int used;
+       size_t len;
+
+       wpa_printf(MSG_DEBUG, "External EAPOL RX: %s", cmd);
+
+       pos = cmd;
+       used = hwaddr_aton2(pos, src);
+       if (used < 0)
+               return -1;
+       pos += used;
+       while (*pos == ' ')
+               pos++;
+
+       len = os_strlen(pos);
+       if (len & 1)
+               return -1;
+       len /= 2;
+
+       buf = os_malloc(len);
+       if (buf == NULL)
+               return -1;
+
+       if (hexstr2bin(pos, buf, len) < 0) {
+               os_free(buf);
+               return -1;
+       }
+
+       wpa_supplicant_rx_eapol(wpa_s, src, buf, len);
+       os_free(buf);
+
+       return 0;
+}
+
+
+static u16 ipv4_hdr_checksum(const void *buf, size_t len)
+{
+       size_t i;
+       u32 sum = 0;
+       const u16 *pos = buf;
+
+       for (i = 0; i < len / 2; i++)
+               sum += *pos++;
+
+       while (sum >> 16)
+               sum = (sum & 0xffff) + (sum >> 16);
+
+       return sum ^ 0xffff;
+}
+
+
+#define HWSIM_PACKETLEN 1500
+#define HWSIM_IP_LEN (HWSIM_PACKETLEN - sizeof(struct ether_header))
+
+void wpas_data_test_rx(void *ctx, const u8 *src_addr, const u8 *buf, size_t len)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       const struct ether_header *eth;
+       const struct iphdr *ip;
+       const u8 *pos;
+       unsigned int i;
+
+       if (len != HWSIM_PACKETLEN)
+               return;
+
+       eth = (const struct ether_header *) buf;
+       ip = (const struct iphdr *) (eth + 1);
+       pos = (const u8 *) (ip + 1);
+
+       if (ip->ihl != 5 || ip->version != 4 ||
+           ntohs(ip->tot_len) != HWSIM_IP_LEN)
+               return;
+
+       for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++) {
+               if (*pos != (u8) i)
+                       return;
+               pos++;
+       }
+
+       wpa_msg(wpa_s, MSG_INFO, "DATA-TEST-RX " MACSTR " " MACSTR,
+               MAC2STR(eth->ether_dhost), MAC2STR(eth->ether_shost));
+}
+
+
+static int wpas_ctrl_iface_data_test_config(struct wpa_supplicant *wpa_s,
+                                           char *cmd)
+{
+       int enabled = atoi(cmd);
+
+       if (!enabled) {
+               if (wpa_s->l2_test) {
+                       l2_packet_deinit(wpa_s->l2_test);
+                       wpa_s->l2_test = NULL;
+                       wpa_dbg(wpa_s, MSG_DEBUG, "test data: Disabled");
+               }
+               return 0;
+       }
+
+       if (wpa_s->l2_test)
+               return 0;
+
+       wpa_s->l2_test = l2_packet_init(wpa_s->ifname, wpa_s->own_addr,
+                                       ETHERTYPE_IP, wpas_data_test_rx,
+                                       wpa_s, 1);
+       if (wpa_s->l2_test == NULL)
+               return -1;
+
+       wpa_dbg(wpa_s, MSG_DEBUG, "test data: Enabled");
+
+       return 0;
+}
+
+
+static int wpas_ctrl_iface_data_test_tx(struct wpa_supplicant *wpa_s, char *cmd)
+{
+       u8 dst[ETH_ALEN], src[ETH_ALEN];
+       char *pos;
+       int used;
+       long int val;
+       u8 tos;
+       u8 buf[HWSIM_PACKETLEN];
+       struct ether_header *eth;
+       struct iphdr *ip;
+       u8 *dpos;
+       unsigned int i;
+
+       if (wpa_s->l2_test == NULL)
+               return -1;
+
+       /* format: <dst> <src> <tos> */
+
+       pos = cmd;
+       used = hwaddr_aton2(pos, dst);
+       if (used < 0)
+               return -1;
+       pos += used;
+       while (*pos == ' ')
+               pos++;
+       used = hwaddr_aton2(pos, src);
+       if (used < 0)
+               return -1;
+       pos += used;
+
+       val = strtol(pos, NULL, 0);
+       if (val < 0 || val > 0xff)
+               return -1;
+       tos = val;
+
+       eth = (struct ether_header *) buf;
+       os_memcpy(eth->ether_dhost, dst, ETH_ALEN);
+       os_memcpy(eth->ether_shost, src, ETH_ALEN);
+       eth->ether_type = htons(ETHERTYPE_IP);
+       ip = (struct iphdr *) (eth + 1);
+       os_memset(ip, 0, sizeof(*ip));
+       ip->ihl = 5;
+       ip->version = 4;
+       ip->ttl = 64;
+       ip->tos = tos;
+       ip->tot_len = htons(HWSIM_IP_LEN);
+       ip->protocol = 1;
+       ip->saddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 1);
+       ip->daddr = htonl(192 << 24 | 168 << 16 | 1 << 8 | 2);
+       ip->check = ipv4_hdr_checksum(ip, sizeof(*ip));
+       dpos = (u8 *) (ip + 1);
+       for (i = 0; i < HWSIM_IP_LEN - sizeof(*ip); i++)
+               *dpos++ = i;
+
+       if (l2_packet_send(wpa_s->l2_test, dst, ETHERTYPE_IP, buf,
+                          HWSIM_PACKETLEN) < 0)
+               return -1;
+
+       wpa_dbg(wpa_s, MSG_DEBUG, "test data: TX dst=" MACSTR " src=" MACSTR
+               " tos=0x%x", MAC2STR(dst), MAC2STR(src), tos);
+
+       return 0;
+}
+
+
+static int wpas_ctrl_iface_data_test_frame(struct wpa_supplicant *wpa_s,
+                                          char *cmd)
+{
+       u8 *buf;
+       struct ether_header *eth;
+       struct l2_packet_data *l2 = NULL;
+       size_t len;
+       u16 ethertype;
+       int res = -1;
+
+       len = os_strlen(cmd);
+       if (len & 1 || len < ETH_HLEN * 2)
+               return -1;
+       len /= 2;
+
+       buf = os_malloc(len);
+       if (buf == NULL)
+               return -1;
+
+       if (hexstr2bin(cmd, buf, len) < 0)
+               goto done;
+
+       eth = (struct ether_header *) buf;
+       ethertype = ntohs(eth->ether_type);
+
+       l2 = l2_packet_init(wpa_s->ifname, wpa_s->own_addr, ethertype,
+                           wpas_data_test_rx, wpa_s, 1);
+       if (l2 == NULL)
+               goto done;
+
+       res = l2_packet_send(l2, eth->ether_dhost, ethertype, buf, len);
+       wpa_dbg(wpa_s, MSG_DEBUG, "test data: TX frame res=%d", res);
+done:
+       if (l2)
+               l2_packet_deinit(l2);
+       os_free(buf);
+
+       return res < 0 ? -1 : 0;
+}
+
+
+static int wpas_ctrl_test_alloc_fail(struct wpa_supplicant *wpa_s, char *cmd)
+{
+#ifdef WPA_TRACE_BFD
+       extern char wpa_trace_fail_func[256];
+       extern unsigned int wpa_trace_fail_after;
+       char *pos;
+
+       wpa_trace_fail_after = atoi(cmd);
+       pos = os_strchr(cmd, ':');
+       if (pos) {
+               pos++;
+               os_strlcpy(wpa_trace_fail_func, pos,
+                          sizeof(wpa_trace_fail_func));
+       } else {
+               wpa_trace_fail_after = 0;
+       }
+       return 0;
+#else /* WPA_TRACE_BFD */
+       return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+
+static int wpas_ctrl_get_alloc_fail(struct wpa_supplicant *wpa_s,
+                                   char *buf, size_t buflen)
+{
+#ifdef WPA_TRACE_BFD
+       extern char wpa_trace_fail_func[256];
+       extern unsigned int wpa_trace_fail_after;
+
+       return os_snprintf(buf, buflen, "%u:%s", wpa_trace_fail_after,
+                          wpa_trace_fail_func);
+#else /* WPA_TRACE_BFD */
+       return -1;
+#endif /* WPA_TRACE_BFD */
+}
+
+#endif /* CONFIG_TESTING_OPTIONS */
+
+
+static void wpas_ctrl_vendor_elem_update(struct wpa_supplicant *wpa_s)
+{
+       unsigned int i;
+       char buf[30];
+
+       wpa_printf(MSG_DEBUG, "Update vendor elements");
+
+       for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) {
+               if (wpa_s->vendor_elem[i]) {
+                       int res;
+
+                       res = os_snprintf(buf, sizeof(buf), "frame[%u]", i);
+                       if (!os_snprintf_error(sizeof(buf), res)) {
+                               wpa_hexdump_buf(MSG_DEBUG, buf,
+                                               wpa_s->vendor_elem[i]);
+                       }
+               }
+       }
+
+#ifdef CONFIG_P2P
+       if (wpa_s->parent == wpa_s &&
+           wpa_s->global->p2p &&
+           !wpa_s->global->p2p_disabled)
+               p2p_set_vendor_elems(wpa_s->global->p2p, wpa_s->vendor_elem);
+#endif /* CONFIG_P2P */
+}
+
+
+static struct wpa_supplicant *
+wpas_ctrl_vendor_elem_iface(struct wpa_supplicant *wpa_s,
+                           enum wpa_vendor_elem_frame frame)
+{
+       switch (frame) {
+#ifdef CONFIG_P2P
+       case VENDOR_ELEM_PROBE_REQ_P2P:
+       case VENDOR_ELEM_PROBE_RESP_P2P:
+       case VENDOR_ELEM_PROBE_RESP_P2P_GO:
+       case VENDOR_ELEM_BEACON_P2P_GO:
+       case VENDOR_ELEM_P2P_PD_REQ:
+       case VENDOR_ELEM_P2P_PD_RESP:
+       case VENDOR_ELEM_P2P_GO_NEG_REQ:
+       case VENDOR_ELEM_P2P_GO_NEG_RESP:
+       case VENDOR_ELEM_P2P_GO_NEG_CONF:
+       case VENDOR_ELEM_P2P_INV_REQ:
+       case VENDOR_ELEM_P2P_INV_RESP:
+       case VENDOR_ELEM_P2P_ASSOC_REQ:
+               return wpa_s->parent;
+#endif /* CONFIG_P2P */
+       default:
+               return wpa_s;
+       }
+}
+
+
+static int wpas_ctrl_vendor_elem_add(struct wpa_supplicant *wpa_s, char *cmd)
+{
+       char *pos = cmd;
+       int frame;
+       size_t len;
+       struct wpabuf *buf;
+       struct ieee802_11_elems elems;
+
+       frame = atoi(pos);
+       if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
+               return -1;
+       wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame);
+
+       pos = os_strchr(pos, ' ');
+       if (pos == NULL)
+               return -1;
+       pos++;
+
+       len = os_strlen(pos);
+       if (len == 0)
+               return 0;
+       if (len & 1)
+               return -1;
+       len /= 2;
+
+       buf = wpabuf_alloc(len);
+       if (buf == NULL)
+               return -1;
+
+       if (hexstr2bin(pos, wpabuf_put(buf, len), len) < 0) {
+               wpabuf_free(buf);
+               return -1;
+       }
+
+       if (ieee802_11_parse_elems(wpabuf_head_u8(buf), len, &elems, 0) ==
+           ParseFailed) {
+               wpabuf_free(buf);
+               return -1;
+       }
+
+       if (wpa_s->vendor_elem[frame] == NULL) {
+               wpa_s->vendor_elem[frame] = buf;
+               wpas_ctrl_vendor_elem_update(wpa_s);
+               return 0;
+       }
+
+       if (wpabuf_resize(&wpa_s->vendor_elem[frame], len) < 0) {
+               wpabuf_free(buf);
+               return -1;
+       }
+
+       wpabuf_put_buf(wpa_s->vendor_elem[frame], buf);
+       wpabuf_free(buf);
+       wpas_ctrl_vendor_elem_update(wpa_s);
+
+       return 0;
+}
+
+
+static int wpas_ctrl_vendor_elem_get(struct wpa_supplicant *wpa_s, char *cmd,
+                                    char *buf, size_t buflen)
+{
+       int frame = atoi(cmd);
+
+       if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
+               return -1;
+       wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame);
+
+       if (wpa_s->vendor_elem[frame] == NULL)
+               return 0;
+
+       return wpa_snprintf_hex(buf, buflen,
+                               wpabuf_head_u8(wpa_s->vendor_elem[frame]),
+                               wpabuf_len(wpa_s->vendor_elem[frame]));
+}
+
+
+static int wpas_ctrl_vendor_elem_remove(struct wpa_supplicant *wpa_s, char *cmd)
+{
+       char *pos = cmd;
+       int frame;
+       size_t len;
+       u8 *buf;
+       struct ieee802_11_elems elems;
+       u8 *ie, *end;
+
+       frame = atoi(pos);
+       if (frame < 0 || frame >= NUM_VENDOR_ELEM_FRAMES)
+               return -1;
+       wpa_s = wpas_ctrl_vendor_elem_iface(wpa_s, frame);
+
+       pos = os_strchr(pos, ' ');
+       if (pos == NULL)
+               return -1;
+       pos++;
+
+       if (*pos == '*') {
+               wpabuf_free(wpa_s->vendor_elem[frame]);
+               wpa_s->vendor_elem[frame] = NULL;
+               wpas_ctrl_vendor_elem_update(wpa_s);
+               return 0;
+       }
+
+       if (wpa_s->vendor_elem[frame] == NULL)
+               return -1;
+
+       len = os_strlen(pos);
+       if (len == 0)
+               return 0;
+       if (len & 1)
+               return -1;
+       len /= 2;
+
+       buf = os_malloc(len);
+       if (buf == NULL)
+               return -1;
+
+       if (hexstr2bin(pos, buf, len) < 0) {
+               os_free(buf);
+               return -1;
+       }
+
+       if (ieee802_11_parse_elems(buf, len, &elems, 0) == ParseFailed) {
+               os_free(buf);
+               return -1;
+       }
+
+       ie = wpabuf_mhead_u8(wpa_s->vendor_elem[frame]);
+       end = ie + wpabuf_len(wpa_s->vendor_elem[frame]);
+
+       for (; ie + 1 < end; ie += 2 + ie[1]) {
+               if (ie + len > end)
+                       break;
+               if (os_memcmp(ie, buf, len) != 0)
+                       continue;
+
+               if (wpabuf_len(wpa_s->vendor_elem[frame]) == len) {
+                       wpabuf_free(wpa_s->vendor_elem[frame]);
+                       wpa_s->vendor_elem[frame] = NULL;
+               } else {
+                       os_memmove(ie, ie + len,
+                                  end - (ie + len));
+                       wpa_s->vendor_elem[frame]->used -= len;
+               }
+               os_free(buf);
+               wpas_ctrl_vendor_elem_update(wpa_s);
+               return 0;
+       }
+
+       os_free(buf);
+
+       return -1;
+}
+
+
+static void wpas_ctrl_neighbor_rep_cb(void *ctx, struct wpabuf *neighbor_rep)
+{
+       struct wpa_supplicant *wpa_s = ctx;
 
-       if (si.chanwidth != CHAN_WIDTH_UNKNOWN) {
-               ret = os_snprintf(pos, end - pos, "WIDTH=%s\n",
-                                 channel_width_name(si.chanwidth));
-               if (ret < 0 || ret > end - pos)
-                       return -1;
-               pos += ret;
+       if (neighbor_rep) {
+               wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_RXED
+                            "length=%u",
+                            (unsigned int) wpabuf_len(neighbor_rep));
+               wpabuf_free(neighbor_rep);
+       } else {
+               wpa_msg_ctrl(wpa_s, MSG_INFO, RRM_EVENT_NEIGHBOR_REP_FAILED);
        }
+}
 
-       if (si.center_frq1 > 0 && si.center_frq2 > 0) {
-               ret = os_snprintf(pos, end - pos,
-                                 "CENTER_FRQ1=%d\nCENTER_FRQ2=%d\n",
-                                 si.center_frq1, si.center_frq2);
-               if (ret < 0 || ret > end - pos)
-                       return -1;
-               pos += ret;
-       }
 
-       if (si.avg_signal) {
-               ret = os_snprintf(pos, end - pos,
-                                 "AVG_RSSI=%d\n", si.avg_signal);
-               if (ret < 0 || ret >= end - pos)
+static int wpas_ctrl_iface_send_neigbor_rep(struct wpa_supplicant *wpa_s,
+                                           char *cmd)
+{
+       struct wpa_ssid ssid;
+       struct wpa_ssid *ssid_p = NULL;
+       int ret = 0;
+
+       if (os_strncmp(cmd, " ssid=", 6) == 0) {
+               ssid.ssid_len = os_strlen(cmd + 6);
+               if (ssid.ssid_len > 32)
                        return -1;
-               pos += ret;
+               ssid.ssid = (u8 *) (cmd + 6);
+               ssid_p = &ssid;
        }
 
-       return pos - buf;
+       ret = wpas_rrm_send_neighbor_rep_request(wpa_s, ssid_p,
+                                                wpas_ctrl_neighbor_rep_cb,
+                                                wpa_s);
+
+       return ret;
 }
 
 
-static int wpa_supplicant_pktcnt_poll(struct wpa_supplicant *wpa_s, char *buf,
-                                     size_t buflen)
+static int wpas_ctrl_iface_erp_flush(struct wpa_supplicant *wpa_s)
 {
-       struct hostap_sta_driver_data sta;
-       int ret;
-
-       ret = wpa_drv_pktcnt_poll(wpa_s, &sta);
-       if (ret)
-               return -1;
-
-       ret = os_snprintf(buf, buflen, "TXGOOD=%lu\nTXBAD=%lu\nRXGOOD=%lu\n",
-                         sta.tx_packets, sta.tx_retry_failed, sta.rx_packets);
-       if (ret < 0 || (size_t) ret > buflen)
-               return -1;
-       return ret;
+       eapol_sm_erp_flush(wpa_s->eapol);
+       return 0;
 }
 
 
-static void wpa_supplicant_ctrl_iface_flush(struct wpa_supplicant *wpa_s)
+static int wpas_ctrl_iface_mac_rand_scan(struct wpa_supplicant *wpa_s,
+                                        char *cmd)
 {
-       wpa_dbg(wpa_s, MSG_DEBUG, "Flush all wpa_supplicant state");
+       char *token, *context = NULL;
+       unsigned int enable = ~0, type = 0;
+       u8 _addr[ETH_ALEN], _mask[ETH_ALEN];
+       u8 *addr = NULL, *mask = NULL;
+
+       while ((token = str_token(cmd, " ", &context))) {
+               if (os_strcasecmp(token, "scan") == 0) {
+                       type |= MAC_ADDR_RAND_SCAN;
+               } else if (os_strcasecmp(token, "sched") == 0) {
+                       type |= MAC_ADDR_RAND_SCHED_SCAN;
+               } else if (os_strcasecmp(token, "pno") == 0) {
+                       type |= MAC_ADDR_RAND_PNO;
+               } else if (os_strcasecmp(token, "all") == 0) {
+                       type = wpa_s->mac_addr_rand_supported;
+               } else if (os_strncasecmp(token, "enable=", 7) == 0) {
+                       enable = atoi(token + 7);
+               } else if (os_strncasecmp(token, "addr=", 5) == 0) {
+                       addr = _addr;
+                       if (hwaddr_aton(token + 5, addr)) {
+                               wpa_printf(MSG_INFO,
+                                          "CTRL: Invalid MAC address: %s",
+                                          token);
+                               return -1;
+                       }
+               } else if (os_strncasecmp(token, "mask=", 5) == 0) {
+                       mask = _mask;
+                       if (hwaddr_aton(token + 5, mask)) {
+                               wpa_printf(MSG_INFO,
+                                          "CTRL: Invalid MAC address mask: %s",
+                                          token);
+                               return -1;
+                       }
+               } else {
+                       wpa_printf(MSG_INFO,
+                                  "CTRL: Invalid MAC_RAND_SCAN parameter: %s",
+                                  token);
+                       return -1;
+               }
+       }
 
-#ifdef CONFIG_P2P
-       wpas_p2p_stop_find(wpa_s);
-       p2p_ctrl_flush(wpa_s);
-       wpas_p2p_group_remove(wpa_s, "*");
-#endif /* CONFIG_P2P */
+       if (!type) {
+               wpa_printf(MSG_INFO, "CTRL: MAC_RAND_SCAN no type specified");
+               return -1;
+       }
 
-#ifdef CONFIG_WPS_TESTING
-       wps_version_number = 0x20;
-       wps_testing_dummy_cred = 0;
-#endif /* CONFIG_WPS_TESTING */
-#ifdef CONFIG_WPS
-       wpas_wps_cancel(wpa_s);
-#endif /* CONFIG_WPS */
+       if ((wpa_s->mac_addr_rand_supported & type) != type) {
+               wpa_printf(MSG_INFO,
+                          "CTRL: MAC_RAND_SCAN types=%u != supported=%u",
+                          type, wpa_s->mac_addr_rand_supported);
+               return -1;
+       }
 
-#ifdef CONFIG_TDLS_TESTING
-       extern unsigned int tdls_testing;
-       tdls_testing = 0;
-#endif /* CONFIG_TDLS_TESTING */
-#ifdef CONFIG_TDLS
-       wpa_drv_tdls_oper(wpa_s, TDLS_ENABLE, NULL);
-       wpa_tdls_enable(wpa_s->wpa, 1);
-#endif /* CONFIG_TDLS */
+       if (enable > 1) {
+               wpa_printf(MSG_INFO,
+                          "CTRL: MAC_RAND_SCAN enable=<0/1> not specified");
+               return -1;
+       }
 
-       eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL);
-       wpa_supplicant_stop_countermeasures(wpa_s, NULL);
+       if (!enable) {
+               wpas_mac_addr_rand_scan_clear(wpa_s, type);
+               if (wpa_s->pno) {
+                       if (type & MAC_ADDR_RAND_PNO) {
+                               wpas_stop_pno(wpa_s);
+                               wpas_start_pno(wpa_s);
+                       }
+               } else if (wpa_s->sched_scanning &&
+                          (type & MAC_ADDR_RAND_SCHED_SCAN)) {
+                       /* simulate timeout to restart the sched scan */
+                       wpa_s->sched_scan_timed_out = 1;
+                       wpa_s->prev_sched_ssid = NULL;
+                       wpa_supplicant_cancel_sched_scan(wpa_s);
+               }
+               return 0;
+       }
 
-       wpa_s->no_keep_alive = 0;
+       if ((addr && !mask) || (!addr && mask)) {
+               wpa_printf(MSG_INFO,
+                          "CTRL: MAC_RAND_SCAN invalid addr/mask combination");
+               return -1;
+       }
 
-       os_free(wpa_s->disallow_aps_bssid);
-       wpa_s->disallow_aps_bssid = NULL;
-       wpa_s->disallow_aps_bssid_count = 0;
-       os_free(wpa_s->disallow_aps_ssid);
-       wpa_s->disallow_aps_ssid = NULL;
-       wpa_s->disallow_aps_ssid_count = 0;
+       if (addr && mask && (!(mask[0] & 0x01) || (addr[0] & 0x01))) {
+               wpa_printf(MSG_INFO,
+                          "CTRL: MAC_RAND_SCAN cannot allow multicast address");
+               return -1;
+       }
 
-       wpa_s->set_sta_uapsd = 0;
-       wpa_s->sta_uapsd = 0;
+       if (type & MAC_ADDR_RAND_SCAN) {
+               wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCAN,
+                                           addr, mask);
+       }
 
-       wpa_drv_radio_disable(wpa_s, 0);
+       if (type & MAC_ADDR_RAND_SCHED_SCAN) {
+               wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_SCHED_SCAN,
+                                           addr, mask);
 
-       wpa_bss_flush(wpa_s);
-       wpa_blacklist_clear(wpa_s);
-       wpa_s->extra_blacklist_count = 0;
-       wpa_supplicant_ctrl_iface_remove_network(wpa_s, "all");
-       wpa_supplicant_ctrl_iface_remove_cred(wpa_s, "all");
+               if (wpa_s->sched_scanning && !wpa_s->pno) {
+                       /* simulate timeout to restart the sched scan */
+                       wpa_s->sched_scan_timed_out = 1;
+                       wpa_s->prev_sched_ssid = NULL;
+                       wpa_supplicant_cancel_sched_scan(wpa_s);
+               }
+       }
+
+       if (type & MAC_ADDR_RAND_PNO) {
+               wpas_mac_addr_rand_scan_set(wpa_s, MAC_ADDR_RAND_PNO,
+                                           addr, mask);
+               if (wpa_s->pno) {
+                       wpas_stop_pno(wpa_s);
+                       wpas_start_pno(wpa_s);
+               }
+       }
+
+       return 0;
 }
 
 
@@ -5186,22 +7825,27 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
 {
        char *reply;
        const int reply_size = 4096;
-       int ctrl_rsp = 0;
        int reply_len;
 
        if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0 ||
-           os_strncmp(buf, "SET_NETWORK ", 12) == 0 ||
-           os_strncmp(buf, "WPS_NFC_TAG_READ", 16) == 0 ||
-           os_strncmp(buf, "NFC_REPORT_HANDOVER", 19) == 0 ||
-           os_strncmp(buf, "NFC_RX_HANDOVER_SEL", 19) == 0) {
+           os_strncmp(buf, "SET_NETWORK ", 12) == 0) {
+               if (wpa_debug_show_keys)
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "Control interface command '%s'", buf);
+               else
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "Control interface command '%s [REMOVED]'",
+                               os_strncmp(buf, WPA_CTRL_RSP,
+                                          os_strlen(WPA_CTRL_RSP)) == 0 ?
+                               WPA_CTRL_RSP : "SET_NETWORK");
+       } else if (os_strncmp(buf, "WPS_NFC_TAG_READ", 16) == 0 ||
+                  os_strncmp(buf, "NFC_REPORT_HANDOVER", 19) == 0) {
                wpa_hexdump_ascii_key(MSG_DEBUG, "RX ctrl_iface",
                                      (const u8 *) buf, os_strlen(buf));
        } else {
                int level = MSG_DEBUG;
                if (os_strcmp(buf, "PING") == 0)
                        level = MSG_EXCESSIVE;
-               wpa_hexdump_ascii(level, "RX ctrl_iface",
-                                 (const u8 *) buf, os_strlen(buf));
                wpa_dbg(wpa_s, level, "Control interface command '%s'", buf);
        }
 
@@ -5228,13 +7872,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        } else if (os_strcmp(buf, "MIB") == 0) {
                reply_len = wpa_sm_get_mib(wpa_s->wpa, reply, reply_size);
                if (reply_len >= 0) {
-                       int res;
-                       res = eapol_sm_get_mib(wpa_s->eapol, reply + reply_len,
-                                              reply_size - reply_len);
-                       if (res < 0)
-                               reply_len = -1;
-                       else
-                               reply_len += res;
+                       reply_len += eapol_sm_get_mib(wpa_s->eapol,
+                                                     reply + reply_len,
+                                                     reply_size - reply_len);
                }
        } else if (os_strncmp(buf, "STATUS", 6) == 0) {
                reply_len = wpa_supplicant_ctrl_iface_status(
@@ -5242,9 +7882,14 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        } else if (os_strcmp(buf, "PMKSA") == 0) {
                reply_len = wpa_sm_pmksa_cache_list(wpa_s->wpa, reply,
                                                    reply_size);
+       } else if (os_strcmp(buf, "PMKSA_FLUSH") == 0) {
+               wpa_sm_pmksa_cache_flush(wpa_s->wpa, NULL);
        } else if (os_strncmp(buf, "SET ", 4) == 0) {
                if (wpa_supplicant_ctrl_iface_set(wpa_s, buf + 4))
                        reply_len = -1;
+       } else if (os_strncmp(buf, "DUMP", 4) == 0) {
+               reply_len = wpa_config_dump_values(wpa_s->conf,
+                                                  reply, reply_size);
        } else if (os_strncmp(buf, "GET ", 4) == 0) {
                reply_len = wpa_supplicant_ctrl_iface_get(wpa_s, buf + 4,
                                                          reply, reply_size);
@@ -5257,6 +7902,14 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                        reply_len = -1;
                else
                        wpas_request_connection(wpa_s);
+       } else if (os_strcmp(buf, "REATTACH") == 0) {
+               if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED ||
+                   !wpa_s->current_ssid)
+                       reply_len = -1;
+               else {
+                       wpa_s->reattach = 1;
+                       wpas_request_connection(wpa_s);
+               }
        } else if (os_strcmp(buf, "RECONNECT") == 0) {
                if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
                        reply_len = -1;
@@ -5325,12 +7978,6 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        } else if (os_strncmp(buf, "NFC_GET_HANDOVER_SEL ", 21) == 0) {
                reply_len = wpas_ctrl_nfc_get_handover_sel(
                        wpa_s, buf + 21, reply, reply_size);
-       } else if (os_strncmp(buf, "NFC_RX_HANDOVER_REQ ", 20) == 0) {
-               reply_len = wpas_ctrl_nfc_rx_handover_req(
-                       wpa_s, buf + 20, reply, reply_size);
-       } else if (os_strncmp(buf, "NFC_RX_HANDOVER_SEL ", 20) == 0) {
-               if (wpas_ctrl_nfc_rx_handover_sel(wpa_s, buf + 20))
-                       reply_len = -1;
        } else if (os_strncmp(buf, "NFC_REPORT_HANDOVER ", 20) == 0) {
                if (wpas_ctrl_nfc_report_handover(wpa_s, buf + 20))
                        reply_len = -1;
@@ -5351,8 +7998,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                if (wpas_wps_er_start(wpa_s, buf + 13))
                        reply_len = -1;
        } else if (os_strcmp(buf, "WPS_ER_STOP") == 0) {
-               if (wpas_wps_er_stop(wpa_s))
-                       reply_len = -1;
+               wpas_wps_er_stop(wpa_s);
        } else if (os_strncmp(buf, "WPS_ER_PIN ", 11) == 0) {
                if (wpa_supplicant_ctrl_iface_wps_er_pin(wpa_s, buf + 11))
                        reply_len = -1;
@@ -5391,23 +8037,39 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                if (wpa_supplicant_ctrl_iface_ibss_rsn(wpa_s, buf + 9))
                        reply_len = -1;
 #endif /* CONFIG_IBSS_RSN */
+#ifdef CONFIG_MESH
+       } else if (os_strncmp(buf, "MESH_INTERFACE_ADD ", 19) == 0) {
+               reply_len = wpa_supplicant_ctrl_iface_mesh_interface_add(
+                       wpa_s, buf + 19, reply, reply_size);
+       } else if (os_strcmp(buf, "MESH_INTERFACE_ADD") == 0) {
+               reply_len = wpa_supplicant_ctrl_iface_mesh_interface_add(
+                       wpa_s, "", reply, reply_size);
+       } else if (os_strncmp(buf, "MESH_GROUP_ADD ", 15) == 0) {
+               if (wpa_supplicant_ctrl_iface_mesh_group_add(wpa_s, buf + 15))
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "MESH_GROUP_REMOVE ", 18) == 0) {
+               if (wpa_supplicant_ctrl_iface_mesh_group_remove(wpa_s,
+                                                               buf + 18))
+                       reply_len = -1;
+#endif /* CONFIG_MESH */
 #ifdef CONFIG_P2P
        } else if (os_strncmp(buf, "P2P_FIND ", 9) == 0) {
-               if (p2p_ctrl_find(wpa_s, buf + 9))
+               if (p2p_ctrl_find(wpa_s, buf + 8))
                        reply_len = -1;
        } else if (os_strcmp(buf, "P2P_FIND") == 0) {
                if (p2p_ctrl_find(wpa_s, ""))
                        reply_len = -1;
        } else if (os_strcmp(buf, "P2P_STOP_FIND") == 0) {
                wpas_p2p_stop_find(wpa_s);
+       } else if (os_strncmp(buf, "P2P_ASP_PROVISION ", 18) == 0) {
+               if (p2p_ctrl_asp_provision(wpa_s, buf + 18))
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "P2P_ASP_PROVISION_RESP ", 23) == 0) {
+               if (p2p_ctrl_asp_provision_resp(wpa_s, buf + 23))
+                       reply_len = -1;
        } else if (os_strncmp(buf, "P2P_CONNECT ", 12) == 0) {
                reply_len = p2p_ctrl_connect(wpa_s, buf + 12, reply,
                                             reply_size);
-#if defined(TIZEN_EXT_ENROLLEE) && defined(CONFIG_WPS)
-       } else if (os_strncmp(buf, "WPS_ENROLLEE ", 13) == 0) {
-               wpa_printf(MSG_INFO , "[%s] : %s" , __func__ , buf);
-               reply_len = p2p_ctrl_wps_enrollee(wpa_s, buf + 13, reply, reply_size);
-#endif
        } else if (os_strncmp(buf, "P2P_LISTEN ", 11) == 0) {
                if (p2p_ctrl_listen(wpa_s, buf + 11))
                        reply_len = -1;
@@ -5418,7 +8080,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                if (wpas_p2p_group_remove(wpa_s, buf + 17))
                        reply_len = -1;
        } else if (os_strcmp(buf, "P2P_GROUP_ADD") == 0) {
-               if (wpas_p2p_group_add(wpa_s, 0, 0, 0))
+               if (wpas_p2p_group_add(wpa_s, 0, 0, 0, 0, NULL))
                        reply_len = -1;
        } else if (os_strncmp(buf, "P2P_GROUP_ADD ", 14) == 0) {
                if (p2p_ctrl_group_add(wpa_s, buf + 14))
@@ -5450,6 +8112,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        } else if (os_strncmp(buf, "P2P_SERVICE_DEL ", 16) == 0) {
                if (p2p_ctrl_service_del(wpa_s, buf + 16) < 0)
                        reply_len = -1;
+       } else if (os_strncmp(buf, "P2P_SERVICE_REP ", 16) == 0) {
+               if (p2p_ctrl_service_replace(wpa_s, buf + 16) < 0)
+                       reply_len = -1;
        } else if (os_strncmp(buf, "P2P_REJECT ", 11) == 0) {
                if (p2p_ctrl_reject(wpa_s, buf + 11) < 0)
                        reply_len = -1;
@@ -5482,6 +8147,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        } else if (os_strcmp(buf, "P2P_EXT_LISTEN") == 0) {
                if (p2p_ctrl_ext_listen(wpa_s, "") < 0)
                        reply_len = -1;
+       } else if (os_strncmp(buf, "P2P_REMOVE_CLIENT ", 18) == 0) {
+               if (p2p_ctrl_remove_client(wpa_s, buf + 18) < 0)
+                       reply_len = -1;
 #endif /* CONFIG_P2P */
 #ifdef CONFIG_WIFI_DISPLAY
        } else if (os_strncmp(buf, "WFD_SUBELEM_SET ", 16) == 0) {
@@ -5497,13 +8165,26 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                        reply_len = -1;
        } else if (os_strcmp(buf, "STOP_FETCH_ANQP") == 0) {
                interworking_stop_fetch_anqp(wpa_s);
-       } else if (os_strncmp(buf, "INTERWORKING_SELECT", 19) == 0) {
-               if (interworking_select(wpa_s, os_strstr(buf + 19, "auto") !=
-                                       NULL) < 0)
+       } else if (os_strcmp(buf, "INTERWORKING_SELECT") == 0) {
+               if (ctrl_interworking_select(wpa_s, NULL) < 0)
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "INTERWORKING_SELECT ", 20) == 0) {
+               if (ctrl_interworking_select(wpa_s, buf + 20) < 0)
                        reply_len = -1;
        } else if (os_strncmp(buf, "INTERWORKING_CONNECT ", 21) == 0) {
-               if (ctrl_interworking_connect(wpa_s, buf + 21) < 0)
+               if (ctrl_interworking_connect(wpa_s, buf + 21, 0) < 0)
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "INTERWORKING_ADD_NETWORK ", 25) == 0) {
+               int id;
+
+               id = ctrl_interworking_connect(wpa_s, buf + 25, 1);
+               if (id < 0)
                        reply_len = -1;
+               else {
+                       reply_len = os_snprintf(reply, reply_size, "%d\n", id);
+                       if (os_snprintf_error(reply_size, reply_len))
+                               reply_len = -1;
+               }
        } else if (os_strncmp(buf, "ANQP_GET ", 9) == 0) {
                if (get_anqp(wpa_s, buf + 9) < 0)
                        reply_len = -1;
@@ -5521,14 +8202,28 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        } else if (os_strncmp(buf, "HS20_GET_NAI_HOME_REALM_LIST ", 29) == 0) {
                if (hs20_get_nai_home_realm_list(wpa_s, buf + 29) < 0)
                        reply_len = -1;
+       } else if (os_strncmp(buf, "HS20_ICON_REQUEST ", 18) == 0) {
+               if (hs20_icon_request(wpa_s, buf + 18) < 0)
+                       reply_len = -1;
+       } else if (os_strcmp(buf, "FETCH_OSU") == 0) {
+               if (hs20_fetch_osu(wpa_s) < 0)
+                       reply_len = -1;
+       } else if (os_strcmp(buf, "CANCEL_FETCH_OSU") == 0) {
+               hs20_cancel_fetch_osu(wpa_s);
 #endif /* CONFIG_HS20 */
        } else if (os_strncmp(buf, WPA_CTRL_RSP, os_strlen(WPA_CTRL_RSP)) == 0)
        {
                if (wpa_supplicant_ctrl_iface_ctrl_rsp(
                            wpa_s, buf + os_strlen(WPA_CTRL_RSP)))
                        reply_len = -1;
-               else
-                       ctrl_rsp = 1;
+               else {
+                       /*
+                        * Notify response from timeout to allow the control
+                        * interface response to be sent first.
+                        */
+                       eloop_register_timeout(0, 0, wpas_ctrl_eapol_response,
+                                              wpa_s, NULL);
+               }
        } else if (os_strcmp(buf, "RECONFIGURE") == 0) {
                if (wpa_supplicant_reload_configuration(wpa_s))
                        reply_len = -1;
@@ -5543,9 +8238,12 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        } else if (os_strncmp(buf, "LOG_LEVEL", 9) == 0) {
                reply_len = wpa_supplicant_ctrl_iface_log_level(
                        wpa_s, buf + 9, reply, reply_size);
+       } else if (os_strncmp(buf, "LIST_NETWORKS ", 14) == 0) {
+               reply_len = wpa_supplicant_ctrl_iface_list_networks(
+                       wpa_s, buf + 14, reply, reply_size);
        } else if (os_strcmp(buf, "LIST_NETWORKS") == 0) {
                reply_len = wpa_supplicant_ctrl_iface_list_networks(
-                       wpa_s, reply, reply_size);
+                       wpa_s, NULL, reply, reply_size);
        } else if (os_strcmp(buf, "DISCONNECT") == 0) {
 #ifdef CONFIG_SME
                wpa_s->sme.prev_bssid_set = 0;
@@ -5556,34 +8254,10 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                wpa_supplicant_cancel_scan(wpa_s);
                wpa_supplicant_deauthenticate(wpa_s,
                                              WLAN_REASON_DEAUTH_LEAVING);
-       } else if (os_strcmp(buf, "SCAN") == 0 ||
-                  os_strncmp(buf, "SCAN ", 5) == 0) {
-               if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
-                       reply_len = -1;
-               else {
-                       if (os_strlen(buf) > 4 &&
-                           os_strncasecmp(buf + 5, "TYPE=ONLY", 9) == 0)
-                               wpa_s->scan_res_handler = scan_only_handler;
-                       if (!wpa_s->sched_scanning && !wpa_s->scanning &&
-                           ((wpa_s->wpa_state <= WPA_SCANNING) ||
-                            (wpa_s->wpa_state == WPA_COMPLETED))) {
-                               wpa_s->normal_scans = 0;
-                               wpa_s->scan_req = MANUAL_SCAN_REQ;
-                               wpa_supplicant_req_scan(wpa_s, 0, 0);
-                       } else if (wpa_s->sched_scanning) {
-                               wpa_printf(MSG_DEBUG, "Stop ongoing "
-                                          "sched_scan to allow requested "
-                                          "full scan to proceed");
-                               wpa_supplicant_cancel_sched_scan(wpa_s);
-                               wpa_s->scan_req = MANUAL_SCAN_REQ;
-                               wpa_supplicant_req_scan(wpa_s, 0, 0);
-                       } else {
-                               wpa_printf(MSG_DEBUG, "Ongoing scan action - "
-                                          "reject new request");
-                               reply_len = os_snprintf(reply, reply_size,
-                                                       "FAIL-BUSY\n");
-                       }
-               }
+       } else if (os_strcmp(buf, "SCAN") == 0) {
+               wpas_ctrl_scan(wpa_s, NULL, reply, reply_size, &reply_len);
+       } else if (os_strncmp(buf, "SCAN ", 5) == 0) {
+               wpas_ctrl_scan(wpa_s, buf + 5, reply, reply_size, &reply_len);
        } else if (os_strcmp(buf, "SCAN_RESULTS") == 0) {
                reply_len = wpa_supplicant_ctrl_iface_scan_results(
                        wpa_s, reply, reply_size);
@@ -5608,6 +8282,9 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        } else if (os_strncmp(buf, "GET_NETWORK ", 12) == 0) {
                reply_len = wpa_supplicant_ctrl_iface_get_network(
                        wpa_s, buf + 12, reply, reply_size);
+       } else if (os_strncmp(buf, "DUP_NETWORK ", 12) == 0) {
+               if (wpa_supplicant_ctrl_iface_dup_network(wpa_s, buf + 12))
+                       reply_len = -1;
        } else if (os_strcmp(buf, "LIST_CREDS") == 0) {
                reply_len = wpa_supplicant_ctrl_iface_list_creds(
                        wpa_s, reply, reply_size);
@@ -5620,6 +8297,10 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        } else if (os_strncmp(buf, "SET_CRED ", 9) == 0) {
                if (wpa_supplicant_ctrl_iface_set_cred(wpa_s, buf + 9))
                        reply_len = -1;
+       } else if (os_strncmp(buf, "GET_CRED ", 9) == 0) {
+               reply_len = wpa_supplicant_ctrl_iface_get_cred(wpa_s, buf + 9,
+                                                              reply,
+                                                              reply_size);
 #ifndef CONFIG_NO_CONFIG_WRITE
        } else if (os_strcmp(buf, "SAVE_CONFIG") == 0) {
                if (wpa_supplicant_ctrl_iface_save_config(wpa_s))
@@ -5658,19 +8339,26 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        } else if (os_strncmp(buf, "DISASSOCIATE ", 13) == 0) {
                if (ap_ctrl_iface_sta_disassociate(wpa_s, buf + 13))
                        reply_len = -1;
+       } else if (os_strncmp(buf, "CHAN_SWITCH ", 12) == 0) {
+               if (ap_ctrl_iface_chanswitch(wpa_s, buf + 12))
+                       reply_len = -1;
+       } else if (os_strcmp(buf, "STOP_AP") == 0) {
+               if (wpas_ap_stop_ap(wpa_s))
+                       reply_len = -1;
 #endif /* CONFIG_AP */
        } else if (os_strcmp(buf, "SUSPEND") == 0) {
                wpas_notify_suspend(wpa_s->global);
        } else if (os_strcmp(buf, "RESUME") == 0) {
                wpas_notify_resume(wpa_s->global);
+#ifdef CONFIG_TESTING_OPTIONS
        } else if (os_strcmp(buf, "DROP_SA") == 0) {
                wpa_supplicant_ctrl_iface_drop_sa(wpa_s);
+#endif /* CONFIG_TESTING_OPTIONS */
        } else if (os_strncmp(buf, "ROAM ", 5) == 0) {
                if (wpa_supplicant_ctrl_iface_roam(wpa_s, buf + 5))
                        reply_len = -1;
        } else if (os_strncmp(buf, "STA_AUTOCONNECT ", 16) == 0) {
-               if (wpa_supplicant_ctrl_iface_sta_autoconnect(wpa_s, buf + 16))
-                       reply_len = -1;
+               wpa_s->auto_reconnect_disabled = atoi(buf + 16) == 0;
        } else if (os_strncmp(buf, "BSS_EXPIRE_AGE ", 15) == 0) {
                if (wpa_supplicant_ctrl_iface_bss_expire_age(wpa_s, buf + 15))
                        reply_len = -1;
@@ -5679,8 +8367,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                                                               buf + 17))
                        reply_len = -1;
        } else if (os_strncmp(buf, "BSS_FLUSH ", 10) == 0) {
-               if (wpa_supplicant_ctrl_iface_bss_flush(wpa_s, buf + 10))
-                       reply_len = -1;
+               wpa_supplicant_ctrl_iface_bss_flush(wpa_s, buf + 10);
 #ifdef CONFIG_TDLS
        } else if (os_strncmp(buf, "TDLS_DISCOVER ", 14) == 0) {
                if (wpa_supplicant_ctrl_iface_tdls_discover(wpa_s, buf + 14))
@@ -5691,7 +8378,23 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        } else if (os_strncmp(buf, "TDLS_TEARDOWN ", 14) == 0) {
                if (wpa_supplicant_ctrl_iface_tdls_teardown(wpa_s, buf + 14))
                        reply_len = -1;
+       } else if (os_strncmp(buf, "TDLS_CHAN_SWITCH ", 17) == 0) {
+               if (wpa_supplicant_ctrl_iface_tdls_chan_switch(wpa_s,
+                                                              buf + 17))
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "TDLS_CANCEL_CHAN_SWITCH ", 24) == 0) {
+               if (wpa_supplicant_ctrl_iface_tdls_cancel_chan_switch(wpa_s,
+                                                                     buf + 24))
+                       reply_len = -1;
 #endif /* CONFIG_TDLS */
+       } else if (os_strcmp(buf, "WMM_AC_STATUS") == 0) {
+               reply_len = wpas_wmm_ac_status(wpa_s, reply, reply_size);
+       } else if (os_strncmp(buf, "WMM_AC_ADDTS ", 13) == 0) {
+               if (wmm_ac_ctrl_addts(wpa_s, buf + 13))
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "WMM_AC_DELTS ", 13) == 0) {
+               if (wmm_ac_ctrl_delts(wpa_s, buf + 13))
+                       reply_len = -1;
        } else if (os_strncmp(buf, "SIGNAL_POLL", 11) == 0) {
                reply_len = wpa_supplicant_signal_poll(wpa_s, reply,
                                                       reply_size);
@@ -5703,6 +8406,14 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                if (wpa_supplicant_ctrl_iface_autoscan(wpa_s, buf + 9))
                        reply_len = -1;
 #endif /* CONFIG_AUTOSCAN */
+#ifdef ANDROID
+       } else if (os_strncmp(buf, "DRIVER ", 7) == 0) {
+               reply_len = wpa_supplicant_driver_cmd(wpa_s, buf + 7, reply,
+                                                     reply_size);
+#endif /* ANDROID */
+       } else if (os_strncmp(buf, "VENDOR ", 7) == 0) {
+               reply_len = wpa_supplicant_vendor_cmd(wpa_s, buf + 7, reply,
+                                                     reply_size);
        } else if (os_strcmp(buf, "REAUTHENTICATE") == 0) {
                pmksa_cache_clear_current(wpa_s->wpa);
                eapol_sm_request_reauth(wpa_s->eapol);
@@ -5710,12 +8421,59 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
        } else if (os_strncmp(buf, "WNM_SLEEP ", 10) == 0) {
                if (wpas_ctrl_iface_wnm_sleep(wpa_s, buf + 10))
                        reply_len = -1;
-       } else if (os_strncmp(buf, "WNM_BSS_QUERY ", 10) == 0) {
-               if (wpas_ctrl_iface_wnm_bss_query(wpa_s, buf + 10))
+       } else if (os_strncmp(buf, "WNM_BSS_QUERY ", 14) == 0) {
+               if (wpas_ctrl_iface_wnm_bss_query(wpa_s, buf + 14))
                                reply_len = -1;
 #endif /* CONFIG_WNM */
        } else if (os_strcmp(buf, "FLUSH") == 0) {
                wpa_supplicant_ctrl_iface_flush(wpa_s);
+       } else if (os_strncmp(buf, "RADIO_WORK ", 11) == 0) {
+               reply_len = wpas_ctrl_radio_work(wpa_s, buf + 11, reply,
+                                                reply_size);
+#ifdef CONFIG_TESTING_OPTIONS
+       } else if (os_strncmp(buf, "MGMT_TX ", 8) == 0) {
+               if (wpas_ctrl_iface_mgmt_tx(wpa_s, buf + 8) < 0)
+                       reply_len = -1;
+       } else if (os_strcmp(buf, "MGMT_TX_DONE") == 0) {
+               wpas_ctrl_iface_mgmt_tx_done(wpa_s);
+       } else if (os_strncmp(buf, "DRIVER_EVENT ", 13) == 0) {
+               if (wpas_ctrl_iface_driver_event(wpa_s, buf + 13) < 0)
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "EAPOL_RX ", 9) == 0) {
+               if (wpas_ctrl_iface_eapol_rx(wpa_s, buf + 9) < 0)
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "DATA_TEST_CONFIG ", 17) == 0) {
+               if (wpas_ctrl_iface_data_test_config(wpa_s, buf + 17) < 0)
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "DATA_TEST_TX ", 13) == 0) {
+               if (wpas_ctrl_iface_data_test_tx(wpa_s, buf + 13) < 0)
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "DATA_TEST_FRAME ", 16) == 0) {
+               if (wpas_ctrl_iface_data_test_frame(wpa_s, buf + 16) < 0)
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "TEST_ALLOC_FAIL ", 16) == 0) {
+               if (wpas_ctrl_test_alloc_fail(wpa_s, buf + 16) < 0)
+                       reply_len = -1;
+       } else if (os_strcmp(buf, "GET_ALLOC_FAIL") == 0) {
+               reply_len = wpas_ctrl_get_alloc_fail(wpa_s, reply, reply_size);
+#endif /* CONFIG_TESTING_OPTIONS */
+       } else if (os_strncmp(buf, "VENDOR_ELEM_ADD ", 16) == 0) {
+               if (wpas_ctrl_vendor_elem_add(wpa_s, buf + 16) < 0)
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "VENDOR_ELEM_GET ", 16) == 0) {
+               reply_len = wpas_ctrl_vendor_elem_get(wpa_s, buf + 16, reply,
+                                                     reply_size);
+       } else if (os_strncmp(buf, "VENDOR_ELEM_REMOVE ", 19) == 0) {
+               if (wpas_ctrl_vendor_elem_remove(wpa_s, buf + 19) < 0)
+                       reply_len = -1;
+       } else if (os_strncmp(buf, "NEIGHBOR_REP_REQUEST", 20) == 0) {
+               if (wpas_ctrl_iface_send_neigbor_rep(wpa_s, buf + 20))
+                       reply_len = -1;
+       } else if (os_strcmp(buf, "ERP_FLUSH") == 0) {
+               wpas_ctrl_iface_erp_flush(wpa_s);
+       } else if (os_strncmp(buf, "MAC_RAND_SCAN ", 14) == 0) {
+               if (wpas_ctrl_iface_mac_rand_scan(wpa_s, buf + 14))
+                       reply_len = -1;
        } else {
                os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
                reply_len = 16;
@@ -5726,9 +8484,6 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                reply_len = 5;
        }
 
-       if (ctrl_rsp)
-               eapol_sm_notify_ctrl_response(wpa_s->eapol);
-
        *resp_len = reply_len;
        return reply;
 }
@@ -5807,7 +8562,7 @@ static int wpa_supplicant_global_iface_add(struct wpa_global *global,
        if (wpa_supplicant_get_iface(global, iface.ifname))
                return -1;
 
-       return wpa_supplicant_add_iface(global, &iface) ? 0 : -1;
+       return wpa_supplicant_add_iface(global, &iface, NULL) ? 0 : -1;
 }
 
 
@@ -5869,7 +8624,7 @@ static int wpa_supplicant_global_iface_list(struct wpa_global *global,
                res = os_snprintf(pos, end - pos, "%s\t%s\t%s\n",
                                  tmp->drv_name, tmp->ifname,
                                  tmp->desc ? tmp->desc : "");
-               if (res < 0 || res >= end - pos) {
+               if (os_snprintf_error(end - pos, res)) {
                        *pos = '\0';
                        break;
                }
@@ -5895,7 +8650,7 @@ static int wpa_supplicant_global_iface_interfaces(struct wpa_global *global,
 
        while (wpa_s) {
                res = os_snprintf(pos, end - pos, "%s\n", wpa_s->ifname);
-               if (res < 0 || res >= end - pos) {
+               if (os_snprintf_error(end - pos, res)) {
                        *pos = '\0';
                        break;
                }
@@ -5935,6 +8690,7 @@ static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global,
 {
 #ifdef CONFIG_P2P
        static const char * cmd[] = {
+               "LIST_NETWORKS",
                "P2P_FIND",
                "P2P_STOP_FIND",
                "P2P_LISTEN",
@@ -5949,6 +8705,11 @@ static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global,
                NULL
        };
        static const char * prefix[] = {
+#ifdef ANDROID
+               "DRIVER ",
+#endif /* ANDROID */
+               "GET_NETWORK ",
+               "REMOVE_NETWORK ",
                "P2P_FIND ",
                "P2P_CONNECT ",
                "P2P_LISTEN ",
@@ -5961,6 +8722,7 @@ static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global,
                "P2P_SERV_DISC_EXTERNAL ",
                "P2P_SERVICE_ADD ",
                "P2P_SERVICE_DEL ",
+               "P2P_SERVICE_REP ",
                "P2P_REJECT ",
                "P2P_INVITE ",
                "P2P_PEER ",
@@ -5968,6 +8730,14 @@ static char * wpas_global_ctrl_iface_redir_p2p(struct wpa_global *global,
                "P2P_UNAUTHORIZE ",
                "P2P_PRESENCE_REQ ",
                "P2P_EXT_LISTEN ",
+               "P2P_REMOVE_CLIENT ",
+               "WPS_NFC_TOKEN ",
+               "WPS_NFC_TAG_READ ",
+               "NFC_GET_HANDOVER_SEL ",
+               "NFC_GET_HANDOVER_REQ ",
+               "NFC_REPORT_HANDOVER ",
+               "P2P_ASP_PROVISION ",
+               "P2P_ASP_PROVISION_RESP ",
                NULL
        };
        int found = 0;
@@ -6026,6 +8796,112 @@ static char * wpas_global_ctrl_iface_redir(struct wpa_global *global,
 }
 
 
+static int wpas_global_ctrl_iface_set(struct wpa_global *global, char *cmd)
+{
+       char *value;
+
+       value = os_strchr(cmd, ' ');
+       if (value == NULL)
+               return -1;
+       *value++ = '\0';
+
+       wpa_printf(MSG_DEBUG, "GLOBAL_CTRL_IFACE SET '%s'='%s'", cmd, value);
+
+#ifdef CONFIG_WIFI_DISPLAY
+       if (os_strcasecmp(cmd, "wifi_display") == 0) {
+               wifi_display_enable(global, !!atoi(value));
+               return 0;
+       }
+#endif /* CONFIG_WIFI_DISPLAY */
+
+       /* Restore cmd to its original value to allow redirection */
+       value[-1] = ' ';
+
+       return -1;
+}
+
+
+#ifndef CONFIG_NO_CONFIG_WRITE
+static int wpas_global_ctrl_iface_save_config(struct wpa_global *global)
+{
+       int ret = 0, saved = 0;
+       struct wpa_supplicant *wpa_s;
+
+       for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               if (!wpa_s->conf->update_config) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Not allowed to update configuration (update_config=0)");
+                       continue;
+               }
+
+               if (wpa_config_write(wpa_s->confname, wpa_s->conf)) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Failed to update configuration");
+                       ret = 1;
+               } else {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "CTRL_IFACE: SAVE_CONFIG - Configuration updated");
+                       saved++;
+               }
+       }
+
+       if (!saved && !ret) {
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "CTRL_IFACE: SAVE_CONFIG - No configuration files could be updated");
+               ret = 1;
+       }
+
+       return ret;
+}
+#endif /* CONFIG_NO_CONFIG_WRITE */
+
+
+static int wpas_global_ctrl_iface_status(struct wpa_global *global,
+                                        char *buf, size_t buflen)
+{
+       char *pos, *end;
+       int ret;
+       struct wpa_supplicant *wpa_s;
+
+       pos = buf;
+       end = buf + buflen;
+
+#ifdef CONFIG_P2P
+       if (global->p2p && !global->p2p_disabled) {
+               ret = os_snprintf(pos, end - pos, "p2p_device_address=" MACSTR
+                                 "\n"
+                                 "p2p_state=%s\n",
+                                 MAC2STR(global->p2p_dev_addr),
+                                 p2p_get_state_txt(global->p2p));
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       } else if (global->p2p) {
+               ret = os_snprintf(pos, end - pos, "p2p_state=DISABLED\n");
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+#endif /* CONFIG_P2P */
+
+#ifdef CONFIG_WIFI_DISPLAY
+       ret = os_snprintf(pos, end - pos, "wifi_display=%d\n",
+                         !!global->wifi_display);
+       if (os_snprintf_error(end - pos, ret))
+               return pos - buf;
+       pos += ret;
+#endif /* CONFIG_WIFI_DISPLAY */
+
+       for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               ret = os_snprintf(pos, end - pos, "ifname=%s\n"
+                                 "address=" MACSTR "\n",
+                                 wpa_s->ifname, MAC2STR(wpa_s->own_addr));
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+
+       return pos - buf;
+}
+
+
 char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global,
                                                char *buf, size_t *resp_len)
 {
@@ -6083,6 +8959,37 @@ char * wpa_supplicant_global_ctrl_iface_process(struct wpa_global *global,
                wpas_notify_suspend(global);
        } else if (os_strcmp(buf, "RESUME") == 0) {
                wpas_notify_resume(global);
+       } else if (os_strncmp(buf, "SET ", 4) == 0) {
+               if (wpas_global_ctrl_iface_set(global, buf + 4)) {
+#ifdef CONFIG_P2P
+                       if (global->p2p_init_wpa_s) {
+                               os_free(reply);
+                               /* Check if P2P redirection would work for this
+                                * command. */
+                               return wpa_supplicant_ctrl_iface_process(
+                                       global->p2p_init_wpa_s,
+                                       buf, resp_len);
+                       }
+#endif /* CONFIG_P2P */
+                       reply_len = -1;
+               }
+#ifndef CONFIG_NO_CONFIG_WRITE
+       } else if (os_strcmp(buf, "SAVE_CONFIG") == 0) {
+               if (wpas_global_ctrl_iface_save_config(global))
+                       reply_len = -1;
+#endif /* CONFIG_NO_CONFIG_WRITE */
+       } else if (os_strcmp(buf, "STATUS") == 0) {
+               reply_len = wpas_global_ctrl_iface_status(global, reply,
+                                                         reply_size);
+#ifdef CONFIG_MODULE_TESTS
+       } else if (os_strcmp(buf, "MODULE_TESTS") == 0) {
+               int wpas_module_tests(void);
+               if (wpas_module_tests() < 0)
+                       reply_len = -1;
+#endif /* CONFIG_MODULE_TESTS */
+       } else if (os_strncmp(buf, "RELOG", 5) == 0) {
+               if (wpa_debug_reopen_file() < 0)
+                       reply_len = -1;
        } else {
                os_memcpy(reply, "UNKNOWN COMMAND\n", 16);
                reply_len = 16;
index a329ef3..d54cc07 100644 (file)
@@ -32,7 +32,7 @@ char * wpa_supplicant_ctrl_iface_process(struct wpa_supplicant *wpa_s,
                                         char *buf, size_t *resp_len);
 
 /**
- * wpa_supplicant_ctrl_iface_process - Process global ctrl_iface command
+ * wpa_supplicant_global_ctrl_iface_process - Process global ctrl_iface command
  * @global: Pointer to global data from wpa_supplicant_init()
  * @buf: Received command buffer (nul terminated string)
  * @resp_len: Variable to be set to the response length
@@ -113,6 +113,8 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global);
 void wpa_supplicant_global_ctrl_iface_deinit(
        struct ctrl_iface_global_priv *priv);
 
+void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s);
+
 #else /* CONFIG_CTRL_IFACE */
 
 static inline struct ctrl_iface_priv *
@@ -148,6 +150,10 @@ wpa_supplicant_global_ctrl_iface_deinit(struct ctrl_iface_global_priv *priv)
 {
 }
 
+static inline void wpas_ctrl_radio_work_flush(struct wpa_supplicant *wpa_s)
+{
+}
+
 #endif /* CONFIG_CTRL_IFACE */
 
 #endif /* CTRL_IFACE_H */
index f3b660d..bf6a3df 100644 (file)
  */
 struct wpa_ctrl_dst {
        struct wpa_ctrl_dst *next;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+       struct sockaddr_in6 addr;
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
        struct sockaddr_in addr;
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
        socklen_t addrlen;
        int debug_level;
        int errors;
@@ -51,43 +55,73 @@ static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
 
 
 static int wpa_supplicant_ctrl_iface_attach(struct ctrl_iface_priv *priv,
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+                                           struct sockaddr_in6 *from,
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
                                            struct sockaddr_in *from,
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
                                            socklen_t fromlen)
 {
        struct wpa_ctrl_dst *dst;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+       char addr[INET6_ADDRSTRLEN];
+#endif /* CONFIG_UDP_IPV6 */
 
        dst = os_zalloc(sizeof(*dst));
        if (dst == NULL)
                return -1;
-       os_memcpy(&dst->addr, from, sizeof(struct sockaddr_in));
+       os_memcpy(&dst->addr, from, sizeof(*from));
        dst->addrlen = fromlen;
        dst->debug_level = MSG_INFO;
        dst->next = priv->ctrl_dst;
        priv->ctrl_dst = dst;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d",
+                  inet_ntop(AF_INET6, &from->sin6_addr, addr, sizeof(*from)),
+                  ntohs(from->sin6_port));
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
        wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor attached %s:%d",
                   inet_ntoa(from->sin_addr), ntohs(from->sin_port));
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
        return 0;
 }
 
 
 static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv,
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+                                           struct sockaddr_in6 *from,
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
                                            struct sockaddr_in *from,
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
                                            socklen_t fromlen)
 {
        struct wpa_ctrl_dst *dst, *prev = NULL;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+       char addr[INET6_ADDRSTRLEN];
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 
        dst = priv->ctrl_dst;
        while (dst) {
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+               if (from->sin6_port == dst->addr.sin6_port &&
+                   !os_memcmp(&from->sin6_addr, &dst->addr.sin6_addr,
+                              sizeof(from->sin6_addr))) {
+                       wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached %s:%d",
+                                  inet_ntop(AF_INET6, &from->sin6_addr, addr,
+                                            sizeof(*from)),
+                                  ntohs(from->sin6_port));
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
                if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr &&
                    from->sin_port == dst->addr.sin_port) {
+                       wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached "
+                                  "%s:%d", inet_ntoa(from->sin_addr),
+                                  ntohs(from->sin_port));
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
                        if (prev == NULL)
                                priv->ctrl_dst = dst->next;
                        else
                                prev->next = dst->next;
                        os_free(dst);
-                       wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached "
-                                  "%s:%d", inet_ntoa(from->sin_addr),
-                                  ntohs(from->sin_port));
                        return 0;
                }
                prev = dst;
@@ -98,21 +132,38 @@ static int wpa_supplicant_ctrl_iface_detach(struct ctrl_iface_priv *priv,
 
 
 static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv,
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+                                          struct sockaddr_in6 *from,
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
                                           struct sockaddr_in *from,
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
                                           socklen_t fromlen,
                                           char *level)
 {
        struct wpa_ctrl_dst *dst;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+       char addr[INET6_ADDRSTRLEN];
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 
        wpa_printf(MSG_DEBUG, "CTRL_IFACE LEVEL %s", level);
 
        dst = priv->ctrl_dst;
        while (dst) {
+#if CONFIG_CTRL_IFACE_UDP_IPV6
+               if (from->sin6_port == dst->addr.sin6_port &&
+                   !os_memcmp(&from->sin6_addr, &dst->addr.sin6_addr,
+                              sizeof(from->sin6_addr))) {
+                       wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor level %s:%d",
+                                  inet_ntop(AF_INET6, &from->sin6_addr, addr,
+                                            sizeof(*from)),
+                                  ntohs(from->sin6_port));
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
                if (from->sin_addr.s_addr == dst->addr.sin_addr.s_addr &&
                    from->sin_port == dst->addr.sin_port) {
                        wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor "
                                   "level %s:%d", inet_ntoa(from->sin_addr),
                                   ntohs(from->sin_port));
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
                        dst->debug_level = atoi(level);
                        return 0;
                }
@@ -150,7 +201,14 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
        struct ctrl_iface_priv *priv = sock_ctx;
        char buf[256], *pos;
        int res;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+       struct sockaddr_in6 from;
+#ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
+       char addr[INET6_ADDRSTRLEN];
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
        struct sockaddr_in from;
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
        socklen_t fromlen = sizeof(from);
        char *reply = NULL;
        size_t reply_len = 0;
@@ -160,11 +218,19 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
        res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
                       (struct sockaddr *) &from, &fromlen);
        if (res < 0) {
-               perror("recvfrom(ctrl_iface)");
+               wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+                          strerror(errno));
                return;
        }
 
 #ifndef CONFIG_CTRL_IFACE_UDP_REMOTE
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+       inet_ntop(AF_INET6, &from.sin6_addr, addr, sizeof(from));
+       if (os_strcmp(addr, "::1")) {
+               wpa_printf(MSG_DEBUG, "CTRL: Drop packet from unexpected source %s",
+                          addr);
+       }
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
        if (from.sin_addr.s_addr != htonl((127 << 24) | 1)) {
                /*
                 * The OS networking stack is expected to drop this kind of
@@ -176,6 +242,7 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
                           "source %s", inet_ntoa(from.sin_addr));
                return;
        }
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
 
        buf[res] = '\0';
@@ -269,8 +336,14 @@ struct ctrl_iface_priv *
 wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
 {
        struct ctrl_iface_priv *priv;
-       struct sockaddr_in addr;
        int port = WPA_CTRL_IFACE_PORT;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+       struct sockaddr_in6 addr;
+       int domain = PF_INET6;
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
+       struct sockaddr_in addr;
+       int domain = PF_INET;
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 
        priv = os_zalloc(sizeof(*priv));
        if (priv == NULL)
@@ -282,26 +355,39 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
        if (wpa_s->conf->ctrl_interface == NULL)
                return priv;
 
-       priv->sock = socket(PF_INET, SOCK_DGRAM, 0);
+       priv->sock = socket(domain, SOCK_DGRAM, 0);
        if (priv->sock < 0) {
-               perror("socket(PF_INET)");
+               wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
                goto fail;
        }
 
        os_memset(&addr, 0, sizeof(addr));
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+       addr.sin6_family = AF_INET6;
+#ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
+       addr.sin6_addr = in6addr_any;
+#else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+       inet_pton(AF_INET6, "::1", &addr.sin6_addr);
+#endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
        addr.sin_family = AF_INET;
 #ifdef CONFIG_CTRL_IFACE_UDP_REMOTE
        addr.sin_addr.s_addr = INADDR_ANY;
 #else /* CONFIG_CTRL_IFACE_UDP_REMOTE */
        addr.sin_addr.s_addr = htonl((127 << 24) | 1);
 #endif /* CONFIG_CTRL_IFACE_UDP_REMOTE */
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 try_again:
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+       addr.sin6_port = htons(port);
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
        addr.sin_port = htons(port);
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
        if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
                port--;
                if ((WPA_CTRL_IFACE_PORT - port) < WPA_CTRL_IFACE_PORT_LIMIT)
                        goto try_again;
-               perror("bind(AF_INET)");
+               wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
                goto fail;
        }
 
@@ -331,13 +417,13 @@ void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
                eloop_unregister_read_sock(priv->sock);
                if (priv->ctrl_dst) {
                        /*
-                        * Wait a second before closing the control socket if
+                        * Wait before closing the control socket if
                         * there are any attached monitors in order to allow
                         * them to receive any pending messages.
                         */
                        wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached "
                                   "monitors to receive messages");
-                       os_sleep(1, 0);
+                       os_sleep(0, 100000);
                }
                close(priv->sock);
                priv->sock = -1;
@@ -362,6 +448,9 @@ static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
        int idx;
        char *sbuf;
        int llen;
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+       char addr[INET6_ADDRSTRLEN];
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
 
        dst = priv->ctrl_dst;
        if (priv->sock < 0 || dst == NULL)
@@ -381,13 +470,22 @@ static void wpa_supplicant_ctrl_iface_send(struct ctrl_iface_priv *priv,
        while (dst) {
                next = dst->next;
                if (level >= dst->debug_level) {
+#ifdef CONFIG_CTRL_IFACE_UDP_IPV6
+                       wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d",
+                                  inet_ntop(AF_INET6, &dst->addr.sin6_addr,
+                                            addr, sizeof(dst->addr)),
+                                  ntohs(dst->addr.sin6_port));
+#else /* CONFIG_CTRL_IFACE_UDP_IPV6 */
                        wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor send %s:%d",
                                   inet_ntoa(dst->addr.sin_addr),
                                   ntohs(dst->addr.sin_port));
+#endif /* CONFIG_CTRL_IFACE_UDP_IPV6 */
                        if (sendto(priv->sock, sbuf, llen + len, 0,
                                   (struct sockaddr *) &dst->addr,
                                   sizeof(dst->addr)) < 0) {
-                               perror("sendto(CTRL_IFACE monitor)");
+                               wpa_printf(MSG_ERROR,
+                                          "sendto(CTRL_IFACE monitor): %s",
+                                          strerror(errno));
                                dst->errors++;
                                if (dst->errors > 10) {
                                        wpa_supplicant_ctrl_iface_detach(
@@ -456,7 +554,8 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx,
        res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
                       (struct sockaddr *) &from, &fromlen);
        if (res < 0) {
-               perror("recvfrom(ctrl_iface)");
+               wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+                          strerror(errno));
                return;
        }
 
@@ -539,7 +638,7 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
 
        priv->sock = socket(PF_INET, SOCK_DGRAM, 0);
        if (priv->sock < 0) {
-               perror("socket(PF_INET)");
+               wpa_printf(MSG_ERROR, "socket(PF_INET): %s", strerror(errno));
                goto fail;
        }
 
@@ -557,7 +656,7 @@ try_again:
                if ((port - WPA_GLOBAL_CTRL_IFACE_PORT) <
                    WPA_GLOBAL_CTRL_IFACE_PORT_LIMIT)
                        goto try_again;
-               perror("bind(AF_INET)");
+               wpa_printf(MSG_ERROR, "bind(AF_INET): %s", strerror(errno));
                goto fail;
        }
 
index 1b4b9b0..b1ac766 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / UNIX domain socket -based control interface
- * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -47,6 +47,7 @@ struct ctrl_iface_priv {
        struct wpa_supplicant *wpa_s;
        int sock;
        struct dl_list ctrl_dst;
+       int android_control_socket;
 };
 
 
@@ -54,20 +55,29 @@ struct ctrl_iface_global_priv {
        struct wpa_global *global;
        int sock;
        struct dl_list ctrl_dst;
+       int android_control_socket;
 };
 
 
-static void wpa_supplicant_ctrl_iface_send(const char *ifname, int sock,
+static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s,
+                                          const char *ifname, int sock,
                                           struct dl_list *ctrl_dst,
                                           int level, const char *buf,
-                                          size_t len);
+                                          size_t len,
+                                          struct ctrl_iface_priv *priv,
+                                          struct ctrl_iface_global_priv *gp);
+static int wpas_ctrl_iface_reinit(struct wpa_supplicant *wpa_s,
+                                 struct ctrl_iface_priv *priv);
+static int wpas_ctrl_iface_global_reinit(struct wpa_global *global,
+                                        struct ctrl_iface_global_priv *priv);
 
 
 static int wpa_supplicant_ctrl_iface_attach(struct dl_list *ctrl_dst,
                                            struct sockaddr_un *from,
-                                           socklen_t fromlen)
+                                           socklen_t fromlen, int global)
 {
        struct wpa_ctrl_dst *dst;
+       char addr_txt[200];
 
        dst = os_zalloc(sizeof(*dst));
        if (dst == NULL)
@@ -76,9 +86,11 @@ static int wpa_supplicant_ctrl_iface_attach(struct dl_list *ctrl_dst,
        dst->addrlen = fromlen;
        dst->debug_level = MSG_INFO;
        dl_list_add(ctrl_dst, &dst->list);
-       wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor attached",
-                   (u8 *) from->sun_path,
-                   fromlen - offsetof(struct sockaddr_un, sun_path));
+       printf_encode(addr_txt, sizeof(addr_txt),
+                     (u8 *) from->sun_path,
+                     fromlen - offsetof(struct sockaddr_un, sun_path));
+       wpa_printf(MSG_DEBUG, "CTRL_IFACE %smonitor attached %s",
+                  global ? "global " : "", addr_txt);
        return 0;
 }
 
@@ -94,12 +106,15 @@ static int wpa_supplicant_ctrl_iface_detach(struct dl_list *ctrl_dst,
                    os_memcmp(from->sun_path, dst->addr.sun_path,
                              fromlen - offsetof(struct sockaddr_un, sun_path))
                    == 0) {
+                       char addr_txt[200];
+                       printf_encode(addr_txt, sizeof(addr_txt),
+                                     (u8 *) from->sun_path,
+                                     fromlen -
+                                     offsetof(struct sockaddr_un, sun_path));
+                       wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor detached %s",
+                                  addr_txt);
                        dl_list_del(&dst->list);
                        os_free(dst);
-                       wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor detached",
-                                   (u8 *) from->sun_path,
-                                   fromlen -
-                                   offsetof(struct sockaddr_un, sun_path));
                        return 0;
                }
        }
@@ -121,11 +136,13 @@ static int wpa_supplicant_ctrl_iface_level(struct ctrl_iface_priv *priv,
                    os_memcmp(from->sun_path, dst->addr.sun_path,
                              fromlen - offsetof(struct sockaddr_un, sun_path))
                    == 0) {
-                       wpa_hexdump(MSG_DEBUG, "CTRL_IFACE changed monitor "
-                                   "level", (u8 *) from->sun_path,
-                                   fromlen -
-                                   offsetof(struct sockaddr_un, sun_path));
+                       char addr_txt[200];
                        dst->debug_level = atoi(level);
+                       printf_encode(addr_txt, sizeof(addr_txt),
+                                     (u8 *) from->sun_path, fromlen -
+                                     offsetof(struct sockaddr_un, sun_path));
+                       wpa_printf(MSG_DEBUG, "CTRL_IFACE changed monitor level to %d for %s",
+                                  dst->debug_level, addr_txt);
                        return 0;
                }
        }
@@ -143,21 +160,22 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
        int res;
        struct sockaddr_un from;
        socklen_t fromlen = sizeof(from);
-       char *reply = NULL;
+       char *reply = NULL, *reply_buf = NULL;
        size_t reply_len = 0;
        int new_attached = 0;
 
        res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
                       (struct sockaddr *) &from, &fromlen);
        if (res < 0) {
-               perror("recvfrom(ctrl_iface)");
+               wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+                          strerror(errno));
                return;
        }
        buf[res] = '\0';
 
        if (os_strcmp(buf, "ATTACH") == 0) {
                if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst, &from,
-                                                    fromlen))
+                                                    fromlen, 0))
                        reply_len = 1;
                else {
                        new_attached = 1;
@@ -176,21 +194,49 @@ static void wpa_supplicant_ctrl_iface_receive(int sock, void *eloop_ctx,
                else
                        reply_len = 2;
        } else {
-               reply = wpa_supplicant_ctrl_iface_process(wpa_s, buf,
-                                                         &reply_len);
+               reply_buf = wpa_supplicant_ctrl_iface_process(wpa_s, buf,
+                                                             &reply_len);
+               reply = reply_buf;
+       }
+
+       if (!reply && reply_len == 1) {
+               reply = "FAIL\n";
+               reply_len = 5;
+       } else if (!reply && reply_len == 2) {
+               reply = "OK\n";
+               reply_len = 3;
        }
 
        if (reply) {
-               sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
-                      fromlen);
-               os_free(reply);
-       } else if (reply_len == 1) {
-               sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
-                      fromlen);
-       } else if (reply_len == 2) {
-               sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from,
-                      fromlen);
+               if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
+                          fromlen) < 0) {
+                       int _errno = errno;
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "ctrl_iface sendto failed: %d - %s",
+                               _errno, strerror(_errno));
+                       if (_errno == ENOBUFS || _errno == EAGAIN) {
+                               /*
+                                * The socket send buffer could be full. This
+                                * may happen if client programs are not
+                                * receiving their pending messages. Close and
+                                * reopen the socket as a workaround to avoid
+                                * getting stuck being unable to send any new
+                                * responses.
+                                */
+                               sock = wpas_ctrl_iface_reinit(wpa_s, priv);
+                               if (sock < 0) {
+                                       wpa_dbg(wpa_s, MSG_DEBUG, "Failed to reinitialize ctrl_iface socket");
+                               }
+                       }
+                       if (new_attached) {
+                               wpa_dbg(wpa_s, MSG_DEBUG, "Failed to send response to ATTACH - detaching");
+                               new_attached = 0;
+                               wpa_supplicant_ctrl_iface_detach(
+                                       &priv->ctrl_dst, &from, fromlen);
+                       }
+               }
        }
+       os_free(reply_buf);
 
        if (new_attached)
                eapol_sm_notify_ctrl_attached(wpa_s->eapol);
@@ -201,7 +247,7 @@ static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s)
 {
        char *buf;
        size_t len;
-       char *pbuf, *dir = NULL, *gid_str = NULL;
+       char *pbuf, *dir = NULL;
        int res;
 
        if (wpa_s->conf->ctrl_interface == NULL)
@@ -211,12 +257,11 @@ static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s)
        if (pbuf == NULL)
                return NULL;
        if (os_strncmp(pbuf, "DIR=", 4) == 0) {
+               char *gid_str;
                dir = pbuf + 4;
                gid_str = os_strstr(dir, " GROUP=");
-               if (gid_str) {
+               if (gid_str)
                        *gid_str = '\0';
-                       gid_str += 7;
-               }
        } else
                dir = pbuf;
 
@@ -228,7 +273,7 @@ static char * wpa_supplicant_ctrl_iface_path(struct wpa_supplicant *wpa_s)
        }
 
        res = os_snprintf(buf, len, "%s/%s", dir, wpa_s->ifname);
-       if (res < 0 || (size_t) res >= len) {
+       if (os_snprintf_error(len, res)) {
                os_free(pbuf);
                os_free(buf);
                return NULL;
@@ -261,26 +306,27 @@ static void wpa_supplicant_ctrl_iface_msg_cb(void *ctx, int level, int global,
        if (global != 2 && wpa_s->global->ctrl_iface) {
                struct ctrl_iface_global_priv *priv = wpa_s->global->ctrl_iface;
                if (!dl_list_empty(&priv->ctrl_dst)) {
-                       wpa_supplicant_ctrl_iface_send(global ? NULL :
+                       wpa_supplicant_ctrl_iface_send(wpa_s, global ? NULL :
                                                       wpa_s->ifname,
                                                       priv->sock,
                                                       &priv->ctrl_dst,
-                                                      level, txt, len);
+                                                      level, txt, len, NULL,
+                                                      priv);
                }
        }
 
        if (wpa_s->ctrl_iface == NULL)
                return;
-       wpa_supplicant_ctrl_iface_send(NULL, wpa_s->ctrl_iface->sock,
+       wpa_supplicant_ctrl_iface_send(wpa_s, NULL, wpa_s->ctrl_iface->sock,
                                       &wpa_s->ctrl_iface->ctrl_dst,
-                                      level, txt, len);
+                                      level, txt, len, wpa_s->ctrl_iface,
+                                      NULL);
 }
 
 
-struct ctrl_iface_priv *
-wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
+static int wpas_ctrl_iface_open_sock(struct wpa_supplicant *wpa_s,
+                                    struct ctrl_iface_priv *priv)
 {
-       struct ctrl_iface_priv *priv;
        struct sockaddr_un addr;
        char *fname = NULL;
        gid_t gid = 0;
@@ -290,16 +336,6 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
        char *endp;
        int flags;
 
-       priv = os_zalloc(sizeof(*priv));
-       if (priv == NULL)
-               return NULL;
-       dl_list_init(&priv->ctrl_dst);
-       priv->wpa_s = wpa_s;
-       priv->sock = -1;
-
-       if (wpa_s->conf->ctrl_interface == NULL)
-               return priv;
-
        buf = os_strdup(wpa_s->conf->ctrl_interface);
        if (buf == NULL)
                goto fail;
@@ -307,8 +343,10 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
        os_snprintf(addr.sun_path, sizeof(addr.sun_path), "wpa_%s",
                    wpa_s->conf->ctrl_interface);
        priv->sock = android_get_control_socket(addr.sun_path);
-       if (priv->sock >= 0)
+       if (priv->sock >= 0) {
+               priv->android_control_socket = 1;
                goto havesock;
+       }
 #endif /* ANDROID */
        if (os_strncmp(buf, "DIR=", 4) == 0) {
                dir = buf + 4;
@@ -327,7 +365,8 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
                        wpa_printf(MSG_DEBUG, "Using existing control "
                                   "interface directory.");
                } else {
-                       perror("mkdir[ctrl_interface]");
+                       wpa_printf(MSG_ERROR, "mkdir[ctrl_interface=%s]: %s",
+                                  dir, strerror(errno));
                        goto fail;
                }
        }
@@ -371,7 +410,8 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
        }
 
        if (gid_set && chown(dir, -1, gid) < 0) {
-               perror("chown[ctrl_interface]");
+               wpa_printf(MSG_ERROR, "chown[ctrl_interface=%s,gid=%d]: %s",
+                          dir, (int) gid, strerror(errno));
                goto fail;
        }
 
@@ -391,7 +431,7 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
 
        priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
        if (priv->sock < 0) {
-               perror("socket(PF_UNIX)");
+               wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
                goto fail;
        }
 
@@ -413,15 +453,15 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
                                   " allow connections - assuming it was left"
                                   "over from forced program termination");
                        if (unlink(fname) < 0) {
-                               perror("unlink[ctrl_iface]");
-                               wpa_printf(MSG_ERROR, "Could not unlink "
-                                          "existing ctrl_iface socket '%s'",
-                                          fname);
+                               wpa_printf(MSG_ERROR,
+                                          "Could not unlink existing ctrl_iface socket '%s': %s",
+                                          fname, strerror(errno));
                                goto fail;
                        }
                        if (bind(priv->sock, (struct sockaddr *) &addr,
                                 sizeof(addr)) < 0) {
-                               perror("supp-ctrl-iface-init: bind(PF_UNIX)");
+                               wpa_printf(MSG_ERROR, "supp-ctrl-iface-init: bind(PF_UNIX): %s",
+                                          strerror(errno));
                                goto fail;
                        }
                        wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
@@ -438,12 +478,14 @@ wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
        }
 
        if (gid_set && chown(fname, -1, gid) < 0) {
-               perror("chown[ctrl_interface/ifname]");
+               wpa_printf(MSG_ERROR, "chown[ctrl_interface=%s,gid=%d]: %s",
+                          fname, (int) gid, strerror(errno));
                goto fail;
        }
 
        if (chmod(fname, S_IRWXU | S_IRWXG) < 0) {
-               perror("chmod[ctrl_interface/ifname]");
+               wpa_printf(MSG_ERROR, "chmod[ctrl_interface=%s]: %s",
+                          fname, strerror(errno));
                goto fail;
        }
        os_free(fname);
@@ -460,7 +502,8 @@ havesock:
        if (flags >= 0) {
                flags |= O_NONBLOCK;
                if (fcntl(priv->sock, F_SETFL, flags) < 0) {
-                       perror("fcntl(ctrl, O_NONBLOCK)");
+                       wpa_printf(MSG_INFO, "fcntl(ctrl, O_NONBLOCK): %s",
+                                  strerror(errno));
                        /* Not fatal, continue on.*/
                }
        }
@@ -470,18 +513,71 @@ havesock:
        wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
 
        os_free(buf);
-       return priv;
+       return 0;
 
 fail:
-       if (priv->sock >= 0)
+       if (priv->sock >= 0) {
                close(priv->sock);
-       os_free(priv);
+               priv->sock = -1;
+       }
        if (fname) {
                unlink(fname);
                os_free(fname);
        }
        os_free(buf);
-       return NULL;
+       return -1;
+}
+
+
+struct ctrl_iface_priv *
+wpa_supplicant_ctrl_iface_init(struct wpa_supplicant *wpa_s)
+{
+       struct ctrl_iface_priv *priv;
+
+       priv = os_zalloc(sizeof(*priv));
+       if (priv == NULL)
+               return NULL;
+       dl_list_init(&priv->ctrl_dst);
+       priv->wpa_s = wpa_s;
+       priv->sock = -1;
+
+       if (wpa_s->conf->ctrl_interface == NULL)
+               return priv;
+
+       if (wpas_ctrl_iface_open_sock(wpa_s, priv) < 0) {
+               os_free(priv);
+               return NULL;
+       }
+
+       return priv;
+}
+
+
+static int wpas_ctrl_iface_reinit(struct wpa_supplicant *wpa_s,
+                                 struct ctrl_iface_priv *priv)
+{
+       int res;
+
+       if (priv->sock <= 0)
+               return -1;
+
+       /*
+        * On Android, the control socket being used may be the socket
+        * that is created when wpa_supplicant is started as a /init.*.rc
+        * service. Such a socket is maintained as a key-value pair in
+        * Android's environment. Closing this control socket would leave us
+        * in a bad state with an invalid socket descriptor.
+        */
+       if (priv->android_control_socket)
+               return priv->sock;
+
+       eloop_unregister_read_sock(priv->sock);
+       close(priv->sock);
+       priv->sock = -1;
+       res = wpas_ctrl_iface_open_sock(wpa_s, priv);
+       if (res < 0)
+               return -1;
+       return priv->sock;
 }
 
 
@@ -491,17 +587,17 @@ void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
 
        if (priv->sock > -1) {
                char *fname;
-               char *buf, *dir = NULL, *gid_str = NULL;
+               char *buf, *dir = NULL;
                eloop_unregister_read_sock(priv->sock);
                if (!dl_list_empty(&priv->ctrl_dst)) {
                        /*
-                        * Wait a second before closing the control socket if
+                        * Wait before closing the control socket if
                         * there are any attached monitors in order to allow
                         * them to receive any pending messages.
                         */
                        wpa_printf(MSG_DEBUG, "CTRL_IFACE wait for attached "
                                   "monitors to receive messages");
-                       os_sleep(1, 0);
+                       os_sleep(0, 100000);
                }
                close(priv->sock);
                priv->sock = -1;
@@ -511,16 +607,17 @@ void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
                        os_free(fname);
                }
 
+               if (priv->wpa_s->conf->ctrl_interface == NULL)
+                       goto free_dst;
                buf = os_strdup(priv->wpa_s->conf->ctrl_interface);
                if (buf == NULL)
                        goto free_dst;
                if (os_strncmp(buf, "DIR=", 4) == 0) {
+                       char *gid_str;
                        dir = buf + 4;
                        gid_str = os_strstr(dir, " GROUP=");
-                       if (gid_str) {
+                       if (gid_str)
                                *gid_str = '\0';
-                               gid_str += 7;
-                       }
                } else
                        dir = buf;
 
@@ -530,7 +627,9 @@ void wpa_supplicant_ctrl_iface_deinit(struct ctrl_iface_priv *priv)
                                           "directory not empty - leaving it "
                                           "behind");
                        } else {
-                               perror("rmdir[ctrl_interface]");
+                               wpa_printf(MSG_ERROR,
+                                          "rmdir[ctrl_interface=%s]: %s",
+                                          dir, strerror(errno));
                        }
                }
                os_free(buf);
@@ -555,10 +654,13 @@ free_dst:
  *
  * Send a packet to all monitor programs attached to the control interface.
  */
-static void wpa_supplicant_ctrl_iface_send(const char *ifname, int sock,
+static void wpa_supplicant_ctrl_iface_send(struct wpa_supplicant *wpa_s,
+                                          const char *ifname, int sock,
                                           struct dl_list *ctrl_dst,
                                           int level, const char *buf,
-                                          size_t len)
+                                          size_t len,
+                                          struct ctrl_iface_priv *priv,
+                                          struct ctrl_iface_global_priv *gp)
 {
        struct wpa_ctrl_dst *dst, *next;
        char levelstr[10];
@@ -570,7 +672,7 @@ static void wpa_supplicant_ctrl_iface_send(const char *ifname, int sock,
                return;
 
        res = os_snprintf(levelstr, sizeof(levelstr), "<%d>", level);
-       if (res < 0 || (size_t) res >= sizeof(levelstr))
+       if (os_snprintf_error(sizeof(levelstr), res))
                return;
        idx = 0;
        if (ifname) {
@@ -594,31 +696,58 @@ static void wpa_supplicant_ctrl_iface_send(const char *ifname, int sock,
        msg.msg_iov = io;
        msg.msg_iovlen = idx;
 
-       idx = 0;
        dl_list_for_each_safe(dst, next, ctrl_dst, struct wpa_ctrl_dst, list) {
-               if (level >= dst->debug_level) {
-                       wpa_hexdump(MSG_DEBUG, "CTRL_IFACE monitor send",
-                                   (u8 *) dst->addr.sun_path, dst->addrlen -
-                                   offsetof(struct sockaddr_un, sun_path));
-                       msg.msg_name = (void *) &dst->addr;
-                       msg.msg_namelen = dst->addrlen;
-                       if (sendmsg(sock, &msg, 0) < 0) {
-                               int _errno = errno;
-                               wpa_printf(MSG_INFO, "CTRL_IFACE monitor[%d]: "
-                                          "%d - %s",
-                                          idx, errno, strerror(errno));
-                               dst->errors++;
-                               if (dst->errors > 1000 ||
-                                   (_errno != ENOBUFS && dst->errors > 10) ||
-                                   _errno == ENOENT) {
-                                       wpa_supplicant_ctrl_iface_detach(
-                                               ctrl_dst, &dst->addr,
-                                               dst->addrlen);
-                               }
-                       } else
-                               dst->errors = 0;
+               int _errno;
+               char addr_txt[200];
+
+               if (level < dst->debug_level)
+                       continue;
+
+               printf_encode(addr_txt, sizeof(addr_txt),
+                             (u8 *) dst->addr.sun_path, dst->addrlen -
+                             offsetof(struct sockaddr_un, sun_path));
+               msg.msg_name = (void *) &dst->addr;
+               msg.msg_namelen = dst->addrlen;
+               if (sendmsg(sock, &msg, MSG_DONTWAIT) >= 0) {
+                       wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor sent successfully to %s",
+                                  addr_txt);
+                       dst->errors = 0;
+                       continue;
+               }
+
+               _errno = errno;
+               wpa_printf(MSG_DEBUG, "CTRL_IFACE monitor[%s]: %d - %s",
+                          addr_txt, errno, strerror(errno));
+               dst->errors++;
+
+               if (dst->errors > 10 || _errno == ENOENT || _errno == EPERM) {
+                       wpa_printf(MSG_INFO, "CTRL_IFACE: Detach monitor %s that cannot receive messages",
+                               addr_txt);
+                       wpa_supplicant_ctrl_iface_detach(ctrl_dst, &dst->addr,
+                                                        dst->addrlen);
+               }
+
+               if (_errno == ENOBUFS || _errno == EAGAIN) {
+                       /*
+                        * The socket send buffer could be full. This may happen
+                        * if client programs are not receiving their pending
+                        * messages. Close and reopen the socket as a workaround
+                        * to avoid getting stuck being unable to send any new
+                        * responses.
+                        */
+                       if (priv)
+                               sock = wpas_ctrl_iface_reinit(wpa_s, priv);
+                       else if (gp)
+                               sock = wpas_ctrl_iface_global_reinit(
+                                       wpa_s->global, gp);
+                       else
+                               break;
+                       if (sock < 0) {
+                               wpa_dbg(wpa_s, MSG_DEBUG,
+                                       "Failed to reinitialize ctrl_iface socket");
+                               break;
+                       }
                }
-               idx++;
        }
 }
 
@@ -638,7 +767,8 @@ void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
                res = recvfrom(priv->sock, buf, sizeof(buf) - 1, 0,
                               (struct sockaddr *) &from, &fromlen);
                if (res < 0) {
-                       perror("recvfrom(ctrl_iface)");
+                       wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+                                  strerror(errno));
                        continue;
                }
                buf[res] = '\0';
@@ -646,19 +776,32 @@ void wpa_supplicant_ctrl_iface_wait(struct ctrl_iface_priv *priv)
                if (os_strcmp(buf, "ATTACH") == 0) {
                        /* handle ATTACH signal of first monitor interface */
                        if (!wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst,
-                                                             &from, fromlen)) {
-                               sendto(priv->sock, "OK\n", 3, 0,
-                                      (struct sockaddr *) &from, fromlen);
+                                                             &from, fromlen,
+                                                             0)) {
+                               if (sendto(priv->sock, "OK\n", 3, 0,
+                                          (struct sockaddr *) &from, fromlen) <
+                                   0) {
+                                       wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s",
+                                                  strerror(errno));
+                               }
                                /* OK to continue */
                                return;
                        } else {
-                               sendto(priv->sock, "FAIL\n", 5, 0,
-                                      (struct sockaddr *) &from, fromlen);
+                               if (sendto(priv->sock, "FAIL\n", 5, 0,
+                                          (struct sockaddr *) &from, fromlen) <
+                                   0) {
+                                       wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s",
+                                                  strerror(errno));
+                               }
                        }
                } else {
                        /* return FAIL for all other signals */
-                       sendto(priv->sock, "FAIL\n", 5, 0,
-                              (struct sockaddr *) &from, fromlen);
+                       if (sendto(priv->sock, "FAIL\n", 5, 0,
+                                  (struct sockaddr *) &from, fromlen) < 0) {
+                               wpa_printf(MSG_DEBUG,
+                                          "ctrl_iface sendto failed: %s",
+                                          strerror(errno));
+                       }
                }
        }
 }
@@ -671,24 +814,25 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx,
 {
        struct wpa_global *global = eloop_ctx;
        struct ctrl_iface_global_priv *priv = sock_ctx;
-       char buf[256];
+       char buf[4096];
        int res;
        struct sockaddr_un from;
        socklen_t fromlen = sizeof(from);
-       char *reply = NULL;
+       char *reply = NULL, *reply_buf = NULL;
        size_t reply_len;
 
        res = recvfrom(sock, buf, sizeof(buf) - 1, 0,
                       (struct sockaddr *) &from, &fromlen);
        if (res < 0) {
-               perror("recvfrom(ctrl_iface)");
+               wpa_printf(MSG_ERROR, "recvfrom(ctrl_iface): %s",
+                          strerror(errno));
                return;
        }
        buf[res] = '\0';
 
        if (os_strcmp(buf, "ATTACH") == 0) {
                if (wpa_supplicant_ctrl_iface_attach(&priv->ctrl_dst, &from,
-                                                    fromlen))
+                                                    fromlen, 1))
                        reply_len = 1;
                else
                        reply_len = 2;
@@ -699,39 +843,36 @@ static void wpa_supplicant_global_ctrl_iface_receive(int sock, void *eloop_ctx,
                else
                        reply_len = 2;
        } else {
-               reply = wpa_supplicant_global_ctrl_iface_process(global, buf,
-                                                                &reply_len);
+               reply_buf = wpa_supplicant_global_ctrl_iface_process(
+                       global, buf, &reply_len);
+               reply = reply_buf;
+       }
+
+       if (!reply && reply_len == 1) {
+               reply = "FAIL\n";
+               reply_len = 5;
+       } else if (!reply && reply_len == 2) {
+               reply = "OK\n";
+               reply_len = 3;
        }
 
        if (reply) {
-               sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
-                      fromlen);
-               os_free(reply);
-       } else if (reply_len == 1) {
-               sendto(sock, "FAIL\n", 5, 0, (struct sockaddr *) &from,
-                      fromlen);
-       } else if (reply_len == 2) {
-               sendto(sock, "OK\n", 3, 0, (struct sockaddr *) &from, fromlen);
+               if (sendto(sock, reply, reply_len, 0, (struct sockaddr *) &from,
+                          fromlen) < 0) {
+                       wpa_printf(MSG_DEBUG, "ctrl_iface sendto failed: %s",
+                               strerror(errno));
+               }
        }
+       os_free(reply_buf);
 }
 
 
-struct ctrl_iface_global_priv *
-wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
+static int wpas_global_ctrl_iface_open_sock(struct wpa_global *global,
+                                           struct ctrl_iface_global_priv *priv)
 {
-       struct ctrl_iface_global_priv *priv;
        struct sockaddr_un addr;
        const char *ctrl = global->params.ctrl_interface;
-
-       priv = os_zalloc(sizeof(*priv));
-       if (priv == NULL)
-               return NULL;
-       dl_list_init(&priv->ctrl_dst);
-       priv->global = global;
-       priv->sock = -1;
-
-       if (ctrl == NULL)
-               return priv;
+       int flags;
 
        wpa_printf(MSG_DEBUG, "Global control interface '%s'", ctrl);
 
@@ -745,6 +886,7 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
                }
                wpa_printf(MSG_DEBUG, "Using Android control socket '%s'",
                           ctrl + 9);
+               priv->android_control_socket = 1;
                goto havesock;
        }
 
@@ -759,6 +901,7 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
                        wpa_printf(MSG_DEBUG,
                                   "Using Android control socket '%s'",
                                   ctrl);
+                       priv->android_control_socket = 1;
                        goto havesock;
                }
        }
@@ -766,7 +909,7 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
 
        priv->sock = socket(PF_UNIX, SOCK_DGRAM, 0);
        if (priv->sock < 0) {
-               perror("socket(PF_UNIX)");
+               wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
                goto fail;
        }
 
@@ -783,7 +926,8 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
                if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) <
                    0) {
                        wpa_printf(MSG_ERROR, "supp-global-ctrl-iface-init: "
-                                  "bind(PF_UNIX) failed: %s", strerror(errno));
+                                  "bind(PF_UNIX;%s) failed: %s",
+                                  ctrl, strerror(errno));
                        goto fail;
                }
                wpa_printf(MSG_DEBUG, "Using Abstract control socket '%s'",
@@ -793,23 +937,23 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
 
        os_strlcpy(addr.sun_path, ctrl, sizeof(addr.sun_path));
        if (bind(priv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
-               perror("supp-global-ctrl-iface-init (will try fixup): "
-                      "bind(PF_UNIX)");
+               wpa_printf(MSG_INFO, "supp-global-ctrl-iface-init(%s) (will try fixup): bind(PF_UNIX): %s",
+                          ctrl, strerror(errno));
                if (connect(priv->sock, (struct sockaddr *) &addr,
                            sizeof(addr)) < 0) {
                        wpa_printf(MSG_DEBUG, "ctrl_iface exists, but does not"
                                   " allow connections - assuming it was left"
                                   "over from forced program termination");
                        if (unlink(ctrl) < 0) {
-                               perror("unlink[ctrl_iface]");
-                               wpa_printf(MSG_ERROR, "Could not unlink "
-                                          "existing ctrl_iface socket '%s'",
-                                          ctrl);
+                               wpa_printf(MSG_ERROR,
+                                          "Could not unlink existing ctrl_iface socket '%s': %s",
+                                          ctrl, strerror(errno));
                                goto fail;
                        }
                        if (bind(priv->sock, (struct sockaddr *) &addr,
                                 sizeof(addr)) < 0) {
-                               perror("supp-glb-iface-init: bind(PF_UNIX)");
+                               wpa_printf(MSG_ERROR, "supp-glb-iface-init: bind(PF_UNIX;%s): %s",
+                                          ctrl, strerror(errno));
                                goto fail;
                        }
                        wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
@@ -851,30 +995,110 @@ wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
                                   (int) gid);
                }
                if (chown(ctrl, -1, gid) < 0) {
-                       perror("chown[global_ctrl_interface/ifname]");
+                       wpa_printf(MSG_ERROR,
+                                  "chown[global_ctrl_interface=%s,gid=%d]: %s",
+                                  ctrl, (int) gid, strerror(errno));
                        goto fail;
                }
 
                if (chmod(ctrl, S_IRWXU | S_IRWXG) < 0) {
-                       perror("chmod[global_ctrl_interface/ifname]");
+                       wpa_printf(MSG_ERROR,
+                                  "chmod[global_ctrl_interface=%s]: %s",
+                                  ctrl, strerror(errno));
                        goto fail;
                }
        } else {
-               chmod(ctrl, S_IRWXU);
+               if (chmod(ctrl, S_IRWXU) < 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "chmod[global_ctrl_interface=%s](S_IRWXU): %s",
+                                  ctrl, strerror(errno));
+                       /* continue anyway since group change was not required
+                        */
+               }
        }
 
 havesock:
+
+       /*
+        * Make socket non-blocking so that we don't hang forever if
+        * target dies unexpectedly.
+        */
+       flags = fcntl(priv->sock, F_GETFL);
+       if (flags >= 0) {
+               flags |= O_NONBLOCK;
+               if (fcntl(priv->sock, F_SETFL, flags) < 0) {
+                       wpa_printf(MSG_INFO, "fcntl(ctrl, O_NONBLOCK): %s",
+                                  strerror(errno));
+                       /* Not fatal, continue on.*/
+               }
+       }
+
        eloop_register_read_sock(priv->sock,
                                 wpa_supplicant_global_ctrl_iface_receive,
                                 global, priv);
 
-       return priv;
+       return 0;
 
 fail:
-       if (priv->sock >= 0)
+       if (priv->sock >= 0) {
                close(priv->sock);
-       os_free(priv);
-       return NULL;
+               priv->sock = -1;
+       }
+       return -1;
+}
+
+
+struct ctrl_iface_global_priv *
+wpa_supplicant_global_ctrl_iface_init(struct wpa_global *global)
+{
+       struct ctrl_iface_global_priv *priv;
+
+       priv = os_zalloc(sizeof(*priv));
+       if (priv == NULL)
+               return NULL;
+       dl_list_init(&priv->ctrl_dst);
+       priv->global = global;
+       priv->sock = -1;
+
+       if (global->params.ctrl_interface == NULL)
+               return priv;
+
+       if (wpas_global_ctrl_iface_open_sock(global, priv) < 0) {
+               os_free(priv);
+               return NULL;
+       }
+
+       wpa_msg_register_cb(wpa_supplicant_ctrl_iface_msg_cb);
+
+       return priv;
+}
+
+
+static int wpas_ctrl_iface_global_reinit(struct wpa_global *global,
+                                        struct ctrl_iface_global_priv *priv)
+{
+       int res;
+
+       if (priv->sock <= 0)
+               return -1;
+
+       /*
+        * On Android, the control socket being used may be the socket
+        * that is created when wpa_supplicant is started as a /init.*.rc
+        * service. Such a socket is maintained as a key-value pair in
+        * Android's environment. Closing this control socket would leave us
+        * in a bad state with an invalid socket descriptor.
+        */
+       if (priv->android_control_socket)
+               return priv->sock;
+
+       eloop_unregister_read_sock(priv->sock);
+       close(priv->sock);
+       priv->sock = -1;
+       res = wpas_global_ctrl_iface_open_sock(global, priv);
+       if (res < 0)
+               return -1;
+       return priv->sock;
 }
 
 
index d64c65c..f355ebe 100644 (file)
@@ -1,7 +1,7 @@
 all: libwpadbus.a
 
 clean:
-       rm -f *~ *.o *.d
+       rm -f *~ *.o *.d *.gcno *.gcda *.gcov
        rm -f libwpadbus.a
 
 install:
index 6caf740..7ef6cad 100644 (file)
@@ -165,6 +165,7 @@ static void process_timeout(void *eloop_ctx, void *sock_ctx)
 static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data)
 {
        struct wpas_dbus_priv *priv = data;
+
        if (!dbus_timeout_get_enabled(timeout))
                return TRUE;
 
@@ -180,6 +181,7 @@ static dbus_bool_t add_timeout(DBusTimeout *timeout, void *data)
 static void remove_timeout(DBusTimeout *timeout, void *data)
 {
        struct wpas_dbus_priv *priv = data;
+
        eloop_cancel_timeout(process_timeout, priv, timeout);
        dbus_timeout_set_data(timeout, NULL, NULL);
 }
@@ -244,8 +246,7 @@ static int integrate_with_eloop(struct wpas_dbus_priv *priv)
                                                   remove_timeout,
                                                   timeout_toggled, priv,
                                                   NULL)) {
-               wpa_printf(MSG_ERROR, "dbus: Failed to set callback "
-                          "functions");
+               wpa_printf(MSG_ERROR, "dbus: Failed to set callback functions");
                return -1;
        }
 
@@ -259,12 +260,12 @@ static int integrate_with_eloop(struct wpas_dbus_priv *priv)
 
 
 static DBusHandlerResult disconnect_filter(DBusConnection *conn,
-                                           DBusMessage *message, void *data)
+                                          DBusMessage *message, void *data)
 {
        struct wpas_dbus_priv *priv = data;
 
        if (dbus_message_is_signal(message, DBUS_INTERFACE_LOCAL,
-                                  "Disconnected")) {
+                                  "Disconnected")) {
                wpa_printf(MSG_DEBUG, "dbus: bus disconnected, terminating");
                dbus_connection_set_exit_on_disconnect(conn, FALSE);
                wpa_supplicant_terminate_proc(priv->global);
@@ -284,10 +285,11 @@ static int wpas_dbus_init_common(struct wpas_dbus_priv *priv)
        priv->con = dbus_bus_get(DBUS_BUS_SYSTEM, &error);
        if (priv->con) {
                dbus_connection_add_filter(priv->con, disconnect_filter, priv,
-                                          NULL);
+                                          NULL);
        } else {
-               wpa_printf(MSG_ERROR, "dbus: Could not acquire the system "
-                          "bus: %s - %s", error.name, error.message);
+               wpa_printf(MSG_ERROR,
+                          "dbus: Could not acquire the system bus: %s - %s",
+                          error.name, error.message);
                ret = -1;
        }
        dbus_error_free(&error);
@@ -309,7 +311,7 @@ static int wpas_dbus_init_common_finish(struct wpas_dbus_priv *priv)
         * FIXME: is there a better solution to this problem?
         */
        eloop_register_timeout(0, 50, dispatch_initial_dbus_messages,
-                              priv->con, NULL);
+                              priv->con, NULL);
 
        return 0;
 }
@@ -320,6 +322,8 @@ static void wpas_dbus_deinit_common(struct wpas_dbus_priv *priv)
        if (priv->con) {
                eloop_cancel_timeout(dispatch_initial_dbus_messages,
                                     priv->con, NULL);
+               eloop_cancel_timeout(process_timeout, priv, ELOOP_ALL_CTX);
+
                dbus_connection_set_watch_functions(priv->con, NULL, NULL,
                                                    NULL, NULL, NULL);
                dbus_connection_set_timeout_functions(priv->con, NULL, NULL,
@@ -343,26 +347,14 @@ struct wpas_dbus_priv * wpas_dbus_init(struct wpa_global *global)
                return NULL;
        priv->global = global;
 
-       if (wpas_dbus_init_common(priv) < 0) {
-               wpas_dbus_deinit(priv);
-               return NULL;
-       }
-
+       if (wpas_dbus_init_common(priv) < 0 ||
 #ifdef CONFIG_CTRL_IFACE_DBUS_NEW
-       if (wpas_dbus_ctrl_iface_init(priv) < 0) {
-               wpas_dbus_deinit(priv);
-               return NULL;
-       }
+           wpas_dbus_ctrl_iface_init(priv) < 0 ||
 #endif /* CONFIG_CTRL_IFACE_DBUS_NEW */
-
 #ifdef CONFIG_CTRL_IFACE_DBUS
-       if (wpa_supplicant_dbus_ctrl_iface_init(priv) < 0) {
-               wpas_dbus_deinit(priv);
-               return NULL;
-       }
+           wpa_supplicant_dbus_ctrl_iface_init(priv) < 0 ||
 #endif /* CONFIG_CTRL_IFACE_DBUS */
-
-       if (wpas_dbus_init_common_finish(priv) < 0) {
+           wpas_dbus_init_common_finish(priv) < 0) {
                wpas_dbus_deinit(priv);
                return NULL;
        }
index 61a9430..a0c44eb 100644 (file)
@@ -66,7 +66,7 @@ dbus_bool_t wpa_dbus_dict_close_write(DBusMessageIter *iter,
 
 const char * wpa_dbus_type_as_string(const int type)
 {
-       switch(type) {
+       switch (type) {
        case DBUS_TYPE_BYTE:
                return DBUS_TYPE_BYTE_AS_STRING;
        case DBUS_TYPE_BOOLEAN:
@@ -106,11 +106,8 @@ static dbus_bool_t _wpa_dbus_add_dict_entry_start(
                                              iter_dict_entry))
                return FALSE;
 
-       if (!dbus_message_iter_append_basic(iter_dict_entry, DBUS_TYPE_STRING,
-                                           &key))
-               return FALSE;
-
-       return TRUE;
+       return dbus_message_iter_append_basic(iter_dict_entry, DBUS_TYPE_STRING,
+                                             &key);
 }
 
 
@@ -120,10 +117,8 @@ static dbus_bool_t _wpa_dbus_add_dict_entry_end(
 {
        if (!dbus_message_iter_close_container(iter_dict_entry, iter_dict_val))
                return FALSE;
-       if (!dbus_message_iter_close_container(iter_dict, iter_dict_entry))
-               return FALSE;
 
-       return TRUE;
+       return dbus_message_iter_close_container(iter_dict, iter_dict_entry);
 }
 
 
@@ -143,22 +138,15 @@ static dbus_bool_t _wpa_dbus_add_dict_entry_basic(DBusMessageIter *iter_dict,
                return FALSE;
 
        if (!_wpa_dbus_add_dict_entry_start(iter_dict, &iter_dict_entry,
-                                           key, value_type))
-               return FALSE;
-
-       if (!dbus_message_iter_open_container(&iter_dict_entry,
+                                           key, value_type) ||
+           !dbus_message_iter_open_container(&iter_dict_entry,
                                              DBUS_TYPE_VARIANT,
-                                             type_as_string, &iter_dict_val))
-               return FALSE;
-
-       if (!dbus_message_iter_append_basic(&iter_dict_val, value_type, value))
+                                             type_as_string, &iter_dict_val) ||
+           !dbus_message_iter_append_basic(&iter_dict_val, value_type, value))
                return FALSE;
 
-       if (!_wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry,
-                                         &iter_dict_val))
-               return FALSE;
-
-       return TRUE;
+       return _wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry,
+                                           &iter_dict_val);
 }
 
 
@@ -170,17 +158,13 @@ static dbus_bool_t _wpa_dbus_add_dict_entry_byte_array(
        dbus_uint32_t i;
 
        if (!_wpa_dbus_add_dict_entry_start(iter_dict, &iter_dict_entry,
-                                           key, DBUS_TYPE_ARRAY))
-               return FALSE;
-
-       if (!dbus_message_iter_open_container(&iter_dict_entry,
+                                           key, DBUS_TYPE_ARRAY) ||
+           !dbus_message_iter_open_container(&iter_dict_entry,
                                              DBUS_TYPE_VARIANT,
                                              DBUS_TYPE_ARRAY_AS_STRING
                                              DBUS_TYPE_BYTE_AS_STRING,
-                                             &iter_dict_val))
-               return FALSE;
-
-       if (!dbus_message_iter_open_container(&iter_dict_val, DBUS_TYPE_ARRAY,
+                                             &iter_dict_val) ||
+           !dbus_message_iter_open_container(&iter_dict_val, DBUS_TYPE_ARRAY,
                                              DBUS_TYPE_BYTE_AS_STRING,
                                              &iter_array))
                return FALSE;
@@ -195,11 +179,8 @@ static dbus_bool_t _wpa_dbus_add_dict_entry_byte_array(
        if (!dbus_message_iter_close_container(&iter_dict_val, &iter_array))
                return FALSE;
 
-       if (!_wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry,
-                                         &iter_dict_val))
-               return FALSE;
-
-       return TRUE;
+       return _wpa_dbus_add_dict_entry_end(iter_dict, &iter_dict_entry,
+                                           &iter_dict_val);
 }
 
 
@@ -428,9 +409,7 @@ dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict,
                                            const char *value,
                                            const dbus_uint32_t value_len)
 {
-       if (!key)
-               return FALSE;
-       if (!value && (value_len != 0))
+       if (!key || (!value && value_len != 0))
                return FALSE;
        return _wpa_dbus_add_dict_entry_byte_array(iter_dict, key, value,
                                                   value_len);
@@ -465,27 +444,20 @@ dbus_bool_t wpa_dbus_dict_begin_array(DBusMessageIter *iter_dict,
        err = os_snprintf(array_type, sizeof(array_type),
                          DBUS_TYPE_ARRAY_AS_STRING "%s",
                          type);
-       if (err < 0 || err > (int) sizeof(array_type))
+       if (os_snprintf_error(sizeof(array_type), err))
                return FALSE;
 
-       if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array)
-               return FALSE;
-
-       if (!_wpa_dbus_add_dict_entry_start(iter_dict, iter_dict_entry,
-                                           key, DBUS_TYPE_ARRAY))
-               return FALSE;
-
-       if (!dbus_message_iter_open_container(iter_dict_entry,
+       if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array ||
+           !_wpa_dbus_add_dict_entry_start(iter_dict, iter_dict_entry,
+                                           key, DBUS_TYPE_ARRAY) ||
+           !dbus_message_iter_open_container(iter_dict_entry,
                                              DBUS_TYPE_VARIANT,
                                              array_type,
                                              iter_dict_val))
                return FALSE;
 
-       if (!dbus_message_iter_open_container(iter_dict_val, DBUS_TYPE_ARRAY,
-                                             type, iter_array))
-               return FALSE;
-
-       return TRUE;
+       return dbus_message_iter_open_container(iter_dict_val, DBUS_TYPE_ARRAY,
+                                               type, iter_array);
 }
 
 
@@ -542,10 +514,8 @@ dbus_bool_t wpa_dbus_dict_bin_array_add_element(DBusMessageIter *iter_array,
        DBusMessageIter iter_bytes;
        size_t i;
 
-       if (!iter_array || !value)
-               return FALSE;
-
-       if (!dbus_message_iter_open_container(iter_array, DBUS_TYPE_ARRAY,
+       if (!iter_array || !value ||
+           !dbus_message_iter_open_container(iter_array, DBUS_TYPE_ARRAY,
                                              DBUS_TYPE_BYTE_AS_STRING,
                                              &iter_bytes))
                return FALSE;
@@ -557,10 +527,7 @@ dbus_bool_t wpa_dbus_dict_bin_array_add_element(DBusMessageIter *iter_array,
                        return FALSE;
        }
 
-       if (!dbus_message_iter_close_container(iter_array, &iter_bytes))
-               return FALSE;
-
-       return TRUE;
+       return dbus_message_iter_close_container(iter_array, &iter_bytes);
 }
 
 
@@ -586,17 +553,12 @@ dbus_bool_t wpa_dbus_dict_end_array(DBusMessageIter *iter_dict,
                                    DBusMessageIter *iter_dict_val,
                                    DBusMessageIter *iter_array)
 {
-       if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array)
-               return FALSE;
-
-       if (!dbus_message_iter_close_container(iter_dict_val, iter_array))
+       if (!iter_dict || !iter_dict_entry || !iter_dict_val || !iter_array ||
+           !dbus_message_iter_close_container(iter_dict_val, iter_array))
                return FALSE;
 
-       if (!_wpa_dbus_add_dict_entry_end(iter_dict, iter_dict_entry,
-                                         iter_dict_val))
-               return FALSE;
-
-       return TRUE;
+       return _wpa_dbus_add_dict_entry_end(iter_dict, iter_dict_entry,
+                                           iter_dict_val);
 }
 
 
@@ -619,12 +581,8 @@ dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict,
        DBusMessageIter iter_dict_entry, iter_dict_val, iter_array;
        dbus_uint32_t i;
 
-       if (!key)
-               return FALSE;
-       if (!items && (num_items != 0))
-               return FALSE;
-
-       if (!wpa_dbus_dict_begin_string_array(iter_dict, key,
+       if (!key || (!items && num_items != 0) ||
+           !wpa_dbus_dict_begin_string_array(iter_dict, key,
                                              &iter_dict_entry, &iter_dict_val,
                                              &iter_array))
                return FALSE;
@@ -635,11 +593,8 @@ dbus_bool_t wpa_dbus_dict_append_string_array(DBusMessageIter *iter_dict,
                        return FALSE;
        }
 
-       if (!wpa_dbus_dict_end_string_array(iter_dict, &iter_dict_entry,
-                                           &iter_dict_val, &iter_array))
-               return FALSE;
-
-       return TRUE;
+       return wpa_dbus_dict_end_string_array(iter_dict, &iter_dict_entry,
+                                             &iter_dict_val, &iter_array);
 }
 
 
@@ -662,12 +617,9 @@ dbus_bool_t wpa_dbus_dict_append_wpabuf_array(DBusMessageIter *iter_dict,
        DBusMessageIter iter_dict_entry, iter_dict_val, iter_array;
        dbus_uint32_t i;
 
-       if (!key)
-               return FALSE;
-       if (!items && (num_items != 0))
-               return FALSE;
-
-       if (!wpa_dbus_dict_begin_array(iter_dict, key,
+       if (!key ||
+           (!items && num_items != 0) ||
+           !wpa_dbus_dict_begin_array(iter_dict, key,
                                       DBUS_TYPE_ARRAY_AS_STRING
                                       DBUS_TYPE_BYTE_AS_STRING,
                                       &iter_dict_entry, &iter_dict_val,
@@ -681,11 +633,8 @@ dbus_bool_t wpa_dbus_dict_append_wpabuf_array(DBusMessageIter *iter_dict,
                        return FALSE;
        }
 
-       if (!wpa_dbus_dict_end_array(iter_dict, &iter_dict_entry,
-                                    &iter_dict_val, &iter_array))
-               return FALSE;
-
-       return TRUE;
+       return wpa_dbus_dict_end_array(iter_dict, &iter_dict_entry,
+                                      &iter_dict_val, &iter_array);
 }
 
 
@@ -707,16 +656,25 @@ dbus_bool_t wpa_dbus_dict_open_read(DBusMessageIter *iter,
                                    DBusMessageIter *iter_dict,
                                    DBusError *error)
 {
+       int type;
+
+       wpa_printf(MSG_MSGDUMP, "%s: start reading a dict entry", __func__);
        if (!iter || !iter_dict) {
                dbus_set_error_const(error, DBUS_ERROR_FAILED,
-                                    "[internal] missing message iterators");
+                                    "[internal] missing message iterators");
                return FALSE;
        }
 
-       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
+       type = dbus_message_iter_get_arg_type(iter);
+       if (type != DBUS_TYPE_ARRAY ||
            dbus_message_iter_get_element_type(iter) != DBUS_TYPE_DICT_ENTRY) {
+               wpa_printf(MSG_DEBUG,
+                          "%s: unexpected message argument types (arg=%c element=%c)",
+                          __func__, type,
+                          type != DBUS_TYPE_ARRAY ? '?' :
+                          dbus_message_iter_get_element_type(iter));
                dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
-                                    "unexpected message argument types");
+                                    "unexpected message argument types");
                return FALSE;
        }
 
@@ -742,7 +700,6 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_byte_array(
        if (!buffer)
                return FALSE;
 
-       entry->bytearray_value = buffer;
        entry->array_len = 0;
        while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_BYTE) {
                char byte;
@@ -753,21 +710,22 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_byte_array(
                                BYTE_ARRAY_ITEM_SIZE);
                        if (nbuffer == NULL) {
                                os_free(buffer);
-                               wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_"
-                                          "entry_get_byte_array out of "
-                                          "memory trying to retrieve the "
-                                          "string array");
+                               wpa_printf(MSG_ERROR,
+                                          "dbus: %s out of memory trying to retrieve the string array",
+                                          __func__);
                                goto done;
                        }
                        buffer = nbuffer;
                }
-               entry->bytearray_value = buffer;
 
                dbus_message_iter_get_basic(iter, &byte);
-               entry->bytearray_value[count] = byte;
+               buffer[count] = byte;
                entry->array_len = ++count;
                dbus_message_iter_next(iter);
        }
+       entry->bytearray_value = buffer;
+       wpa_hexdump_key(MSG_MSGDUMP, "dbus: byte array contents",
+                       entry->bytearray_value, entry->array_len);
 
        /* Zero-length arrays are valid. */
        if (entry->array_len == 0) {
@@ -790,18 +748,16 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_string_array(
        struct wpa_dbus_dict_entry *entry)
 {
        dbus_uint32_t count = 0;
-       dbus_bool_t success = FALSE;
        char **buffer, **nbuffer;
 
        entry->strarray_value = NULL;
+       entry->array_len = 0;
        entry->array_type = DBUS_TYPE_STRING;
 
        buffer = os_calloc(STR_ARRAY_CHUNK_SIZE, STR_ARRAY_ITEM_SIZE);
        if (buffer == NULL)
                return FALSE;
 
-       entry->strarray_value = buffer;
-       entry->array_len = 0;
        while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_STRING) {
                const char *value;
                char *str;
@@ -811,29 +767,31 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_string_array(
                                buffer, count + STR_ARRAY_CHUNK_SIZE,
                                STR_ARRAY_ITEM_SIZE);
                        if (nbuffer == NULL) {
-                               os_free(buffer);
-                               wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_"
-                                          "entry_get_string_array out of "
-                                          "memory trying to retrieve the "
-                                          "string array");
-                               goto done;
+                               wpa_printf(MSG_ERROR,
+                                          "dbus: %s out of memory trying to retrieve the string array",
+                                          __func__);
+                               goto fail;
                        }
                        buffer = nbuffer;
                }
-               entry->strarray_value = buffer;
 
                dbus_message_iter_get_basic(iter, &value);
+               wpa_printf(MSG_MSGDUMP, "%s: string_array value: %s",
+                          __func__, wpa_debug_show_keys ? value : "[omitted]");
                str = os_strdup(value);
                if (str == NULL) {
-                       wpa_printf(MSG_ERROR, "dbus: _wpa_dbus_dict_entry_get_"
-                                  "string_array out of memory trying to "
-                                  "duplicate the string array");
-                       goto done;
+                       wpa_printf(MSG_ERROR,
+                                  "dbus: %s out of memory trying to duplicate the string array",
+                                  __func__);
+                       goto fail;
                }
-               entry->strarray_value[count] = str;
-               entry->array_len = ++count;
+               buffer[count++] = str;
                dbus_message_iter_next(iter);
        }
+       entry->strarray_value = buffer;
+       entry->array_len = count;
+       wpa_printf(MSG_MSGDUMP, "%s: string_array length %u",
+                  __func__, entry->array_len);
 
        /* Zero-length arrays are valid. */
        if (entry->array_len == 0) {
@@ -841,10 +799,15 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_string_array(
                entry->strarray_value = NULL;
        }
 
-       success = TRUE;
+       return TRUE;
 
-done:
-       return success;
+fail:
+       while (count > 0) {
+               count--;
+               os_free(buffer[count]);
+       }
+       os_free(buffer);
+       return FALSE;
 }
 
 
@@ -856,15 +819,31 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_binarray(
 {
        struct wpa_dbus_dict_entry tmpentry;
        size_t buflen = 0;
-       int i;
-
-       if (dbus_message_iter_get_element_type(iter) != DBUS_TYPE_BYTE)
-               return FALSE;
+       int i, type;
 
        entry->array_type = WPAS_DBUS_TYPE_BINARRAY;
        entry->array_len = 0;
        entry->binarray_value = NULL;
 
+       type = dbus_message_iter_get_arg_type(iter);
+       wpa_printf(MSG_MSGDUMP, "%s: parsing binarray type %c", __func__, type);
+       if (type == DBUS_TYPE_INVALID) {
+               /* Likely an empty array of arrays */
+               return TRUE;
+       }
+       if (type != DBUS_TYPE_ARRAY) {
+               wpa_printf(MSG_DEBUG, "%s: not an array type: %c",
+                          __func__, type);
+               return FALSE;
+       }
+
+       type = dbus_message_iter_get_element_type(iter);
+       if (type != DBUS_TYPE_BYTE) {
+               wpa_printf(MSG_DEBUG, "%s: unexpected element type %c",
+                          __func__, type);
+               return FALSE;
+       }
+
        while (dbus_message_iter_get_arg_type(iter) == DBUS_TYPE_ARRAY) {
                DBusMessageIter iter_array;
 
@@ -881,8 +860,10 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_binarray(
                }
 
                dbus_message_iter_recurse(iter, &iter_array);
+               os_memset(&tmpentry, 0, sizeof(tmpentry));
+               tmpentry.type = DBUS_TYPE_ARRAY;
                if (_wpa_dbus_dict_entry_get_byte_array(&iter_array, &tmpentry)
-                                       == FALSE)
+                   == FALSE)
                        goto cleanup;
 
                entry->binarray_value[entry->array_len] =
@@ -895,6 +876,8 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_binarray(
                entry->array_len++;
                dbus_message_iter_next(iter);
        }
+       wpa_printf(MSG_MSGDUMP, "%s: binarray length %u",
+                  __func__, entry->array_len);
 
        return TRUE;
 
@@ -915,12 +898,11 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_array(
        dbus_bool_t success = FALSE;
        DBusMessageIter iter_array;
 
-       if (!entry)
-               return FALSE;
+       wpa_printf(MSG_MSGDUMP, "%s: array_type %c", __func__, array_type);
 
        dbus_message_iter_recurse(iter_dict_val, &iter_array);
 
-       switch (array_type) {
+       switch (array_type) {
        case DBUS_TYPE_BYTE:
                success = _wpa_dbus_dict_entry_get_byte_array(&iter_array,
                                                              entry);
@@ -932,7 +914,10 @@ static dbus_bool_t _wpa_dbus_dict_entry_get_array(
                break;
        case DBUS_TYPE_ARRAY:
                success = _wpa_dbus_dict_entry_get_binarray(&iter_array, entry);
+               break;
        default:
+               wpa_printf(MSG_MSGDUMP, "%s: unsupported array type %c",
+                          __func__, array_type);
                break;
        }
 
@@ -947,42 +932,72 @@ static dbus_bool_t _wpa_dbus_dict_fill_value_from_variant(
 
        switch (entry->type) {
        case DBUS_TYPE_OBJECT_PATH:
+               dbus_message_iter_get_basic(iter, &v);
+               wpa_printf(MSG_MSGDUMP, "%s: object path value: %s",
+                          __func__, v);
+               entry->str_value = os_strdup(v);
+               if (entry->str_value == NULL)
+                       return FALSE;
+               break;
        case DBUS_TYPE_STRING:
                dbus_message_iter_get_basic(iter, &v);
+               wpa_printf(MSG_MSGDUMP, "%s: string value: %s",
+                          __func__, wpa_debug_show_keys ? v : "[omitted]");
                entry->str_value = os_strdup(v);
                if (entry->str_value == NULL)
                        return FALSE;
                break;
        case DBUS_TYPE_BOOLEAN:
                dbus_message_iter_get_basic(iter, &entry->bool_value);
+               wpa_printf(MSG_MSGDUMP, "%s: boolean value: %d",
+                          __func__, entry->bool_value);
                break;
        case DBUS_TYPE_BYTE:
                dbus_message_iter_get_basic(iter, &entry->byte_value);
+               wpa_printf(MSG_MSGDUMP, "%s: byte value: %d",
+                          __func__, entry->byte_value);
                break;
        case DBUS_TYPE_INT16:
                dbus_message_iter_get_basic(iter, &entry->int16_value);
+               wpa_printf(MSG_MSGDUMP, "%s: int16 value: %d",
+                          __func__, entry->int16_value);
                break;
        case DBUS_TYPE_UINT16:
                dbus_message_iter_get_basic(iter, &entry->uint16_value);
+               wpa_printf(MSG_MSGDUMP, "%s: uint16 value: %d",
+                          __func__, entry->uint16_value);
                break;
        case DBUS_TYPE_INT32:
                dbus_message_iter_get_basic(iter, &entry->int32_value);
+               wpa_printf(MSG_MSGDUMP, "%s: int32 value: %d",
+                          __func__, entry->int32_value);
                break;
        case DBUS_TYPE_UINT32:
                dbus_message_iter_get_basic(iter, &entry->uint32_value);
+               wpa_printf(MSG_MSGDUMP, "%s: uint32 value: %d",
+                          __func__, entry->uint32_value);
                break;
        case DBUS_TYPE_INT64:
                dbus_message_iter_get_basic(iter, &entry->int64_value);
+               wpa_printf(MSG_MSGDUMP, "%s: int64 value: %lld",
+                          __func__, (long long int) entry->int64_value);
                break;
        case DBUS_TYPE_UINT64:
                dbus_message_iter_get_basic(iter, &entry->uint64_value);
+               wpa_printf(MSG_MSGDUMP, "%s: uint64 value: %llu",
+                          __func__,
+                          (unsigned long long int) entry->uint64_value);
                break;
        case DBUS_TYPE_DOUBLE:
                dbus_message_iter_get_basic(iter, &entry->double_value);
+               wpa_printf(MSG_MSGDUMP, "%s: double value: %f",
+                          __func__, entry->double_value);
                break;
        case DBUS_TYPE_ARRAY:
                return _wpa_dbus_dict_entry_get_array(iter, entry);
        default:
+               wpa_printf(MSG_MSGDUMP, "%s: unsupported type %c",
+                          __func__, entry->type);
                return FALSE;
        }
 
@@ -1013,26 +1028,40 @@ dbus_bool_t wpa_dbus_dict_get_entry(DBusMessageIter *iter_dict,
        int type;
        const char *key;
 
-       if (!iter_dict || !entry)
-               goto error;
-
-       if (dbus_message_iter_get_arg_type(iter_dict) != DBUS_TYPE_DICT_ENTRY)
+       if (!iter_dict || !entry ||
+           dbus_message_iter_get_arg_type(iter_dict) != DBUS_TYPE_DICT_ENTRY) {
+               wpa_printf(MSG_DEBUG, "%s: not a dict entry", __func__);
                goto error;
+       }
 
        dbus_message_iter_recurse(iter_dict, &iter_dict_entry);
        dbus_message_iter_get_basic(&iter_dict_entry, &key);
+       wpa_printf(MSG_MSGDUMP, "%s: dict entry key: %s", __func__, key);
        entry->key = key;
 
-       if (!dbus_message_iter_next(&iter_dict_entry))
+       if (!dbus_message_iter_next(&iter_dict_entry)) {
+               wpa_printf(MSG_DEBUG, "%s: no variant in dict entry", __func__);
                goto error;
+       }
        type = dbus_message_iter_get_arg_type(&iter_dict_entry);
-       if (type != DBUS_TYPE_VARIANT)
+       if (type != DBUS_TYPE_VARIANT) {
+               wpa_printf(MSG_DEBUG,
+                          "%s: unexpected dict entry variant type: %c",
+                          __func__, type);
                goto error;
+       }
 
        dbus_message_iter_recurse(&iter_dict_entry, &iter_dict_val);
        entry->type = dbus_message_iter_get_arg_type(&iter_dict_val);
-       if (!_wpa_dbus_dict_fill_value_from_variant(entry, &iter_dict_val))
+       wpa_printf(MSG_MSGDUMP, "%s: dict entry variant content type: %c",
+                  __func__, entry->type);
+       entry->array_type = DBUS_TYPE_INVALID;
+       if (!_wpa_dbus_dict_fill_value_from_variant(entry, &iter_dict_val)) {
+               wpa_printf(MSG_DEBUG,
+                          "%s: failed to fetch dict values from variant",
+                          __func__);
                goto error;
+       }
 
        dbus_message_iter_next(iter_dict);
        return TRUE;
@@ -1087,6 +1116,8 @@ void wpa_dbus_dict_entry_clear(struct wpa_dbus_dict_entry *entry)
                        os_free(entry->bytearray_value);
                        break;
                case DBUS_TYPE_STRING:
+                       if (!entry->strarray_value)
+                               break;
                        for (i = 0; i < entry->array_len; i++)
                                os_free(entry->strarray_value[i]);
                        os_free(entry->strarray_value);
index 9666349..b068431 100644 (file)
@@ -72,28 +72,28 @@ dbus_bool_t wpa_dbus_dict_append_byte_array(DBusMessageIter *iter_dict,
 
 /* Manual construction and addition of array elements */
 dbus_bool_t wpa_dbus_dict_begin_array(DBusMessageIter *iter_dict,
-                                      const char *key, const char *type,
-                                      DBusMessageIter *iter_dict_entry,
-                                      DBusMessageIter *iter_dict_val,
-                                      DBusMessageIter *iter_array);
+                                     const char *key, const char *type,
+                                     DBusMessageIter *iter_dict_entry,
+                                     DBusMessageIter *iter_dict_val,
+                                     DBusMessageIter *iter_array);
 
 dbus_bool_t wpa_dbus_dict_begin_string_array(DBusMessageIter *iter_dict,
-                                             const char *key,
-                                             DBusMessageIter *iter_dict_entry,
-                                             DBusMessageIter *iter_dict_val,
-                                             DBusMessageIter *iter_array);
+                                            const char *key,
+                                            DBusMessageIter *iter_dict_entry,
+                                            DBusMessageIter *iter_dict_val,
+                                            DBusMessageIter *iter_array);
 
 dbus_bool_t wpa_dbus_dict_string_array_add_element(DBusMessageIter *iter_array,
-                                             const char *elem);
+                                                  const char *elem);
 
 dbus_bool_t wpa_dbus_dict_bin_array_add_element(DBusMessageIter *iter_array,
                                                const u8 *value,
                                                size_t value_len);
 
 dbus_bool_t wpa_dbus_dict_end_array(DBusMessageIter *iter_dict,
-                                    DBusMessageIter *iter_dict_entry,
-                                    DBusMessageIter *iter_dict_val,
-                                    DBusMessageIter *iter_array);
+                                   DBusMessageIter *iter_dict_entry,
+                                   DBusMessageIter *iter_dict_val,
+                                   DBusMessageIter *iter_array);
 
 static inline dbus_bool_t
 wpa_dbus_dict_end_string_array(DBusMessageIter *iter_dict,
@@ -120,7 +120,11 @@ dbus_bool_t wpa_dbus_dict_append_wpabuf_array(DBusMessageIter *iter_dict,
  * Reading a dict from a DBusMessage
  */
 
-#define WPAS_DBUS_TYPE_BINARRAY (DBUS_NUMBER_OF_TYPES + 100)
+/*
+ * Used only in struct wpa_dbus_dict_entry::array_type internally to identify
+ * special binary array case.
+ */
+#define WPAS_DBUS_TYPE_BINARRAY ((int) '@')
 
 struct wpa_dbus_dict_entry {
        int type;         /** the dbus type of the dict entry's value */
old mode 100644 (file)
new mode 100755 (executable)
index ddd2c82..cf4643d
 #include "dbus_common_i.h"
 #include "dbus_new_handlers_p2p.h"
 #include "p2p/p2p.h"
+#include "../p2p_supplicant.h"
+#if defined TIZEN_EXT
+#include "ap/hostapd.h"
+#include "ap/sta_info.h"
+#include "ap/wpa_auth.h"
+#include "rsn_supp/wpa.h"
+#endif /* TIZEN_EXT */
 
 #ifdef CONFIG_AP /* until needed by something else */
 
@@ -74,8 +81,7 @@ static DBusHandlerResult noc_filter(DBusConnection *conn,
                        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
                }
 
-               for (wpa_s = priv->global->ifaces; wpa_s; wpa_s = wpa_s->next)
-               {
+               for (wpa_s = priv->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
                        if (wpa_s->preq_notify_peer != NULL &&
                            os_strcmp(name, wpa_s->preq_notify_peer) == 0 &&
                            (new_owner == NULL || os_strlen(new_owner) == 0)) {
@@ -147,22 +153,14 @@ static void wpas_dbus_signal_interface(struct wpa_supplicant *wpa_s,
 
        dbus_message_iter_init_append(msg, &iter);
        if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
-                                           &wpa_s->dbus_new_path))
-               goto err;
-
-       if (properties) {
-               if (!wpa_dbus_get_object_properties(
-                           iface, wpa_s->dbus_new_path,
-                           WPAS_DBUS_NEW_IFACE_INTERFACE, &iter))
-                       goto err;
-       }
-
-       dbus_connection_send(iface->con, msg, NULL);
-       dbus_message_unref(msg);
-       return;
-
-err:
-       wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+                                           &wpa_s->dbus_new_path) ||
+           (properties &&
+            !wpa_dbus_get_object_properties(
+                    iface, wpa_s->dbus_new_path,
+                    WPAS_DBUS_NEW_IFACE_INTERFACE, &iter)))
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       else
+               dbus_connection_send(iface->con, msg, NULL);
        dbus_message_unref(msg);
 }
 
@@ -226,9 +224,39 @@ void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s, int success)
        dbus_message_unref(msg);
 }
 
+/**
+ * wpas_dbus_signal_find_stopped - send find stopped signal
+ * @wpa_s: %wpa_supplicant network interface data
+ *
+ * Notify listeners about scan stopped
+ */
+void wpas_dbus_signal_find_stopped(struct wpa_supplicant *wpa_s)
+{
+       struct wpas_dbus_priv *iface;
+       DBusMessage *msg;
+
+       iface = wpa_s->global->dbus;
+
+       /* Do nothing if the control interface is not turned on */
+       if (iface == NULL)
+               return;
+
+       if (wpa_s->p2p_mgmt)
+               wpa_s = wpa_s->parent;
+
+       msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+                               WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+                               "FindStopped");
+       if (msg == NULL)
+               return;
+
+       dbus_connection_send(iface->con, msg, NULL);
+
+       dbus_message_unref(msg);
+}
 
 /**
- * wpas_dbus_signal_blob - Send a BSS related event signal
+ * wpas_dbus_signal_bss - Send a BSS related event signal
  * @wpa_s: %wpa_supplicant network interface data
  * @bss_obj_path: BSS object path
  * @sig_name: signal name - BSSAdded or BSSRemoved
@@ -258,22 +286,14 @@ static void wpas_dbus_signal_bss(struct wpa_supplicant *wpa_s,
 
        dbus_message_iter_init_append(msg, &iter);
        if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
-                                           &bss_obj_path))
-               goto err;
-
-       if (properties) {
-               if (!wpa_dbus_get_object_properties(iface, bss_obj_path,
-                                                   WPAS_DBUS_NEW_IFACE_BSS,
-                                                   &iter))
-                       goto err;
-       }
-
-       dbus_connection_send(iface->con, msg, NULL);
-       dbus_message_unref(msg);
-       return;
-
-err:
-       wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+                                           &bss_obj_path) ||
+           (properties &&
+            !wpa_dbus_get_object_properties(iface, bss_obj_path,
+                                            WPAS_DBUS_NEW_IFACE_BSS,
+                                            &iter)))
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       else
+               dbus_connection_send(iface->con, msg, NULL);
        dbus_message_unref(msg);
 }
 
@@ -406,23 +426,14 @@ static void wpas_dbus_signal_network(struct wpa_supplicant *wpa_s,
        dbus_message_iter_init_append(msg, &iter);
        path = net_obj_path;
        if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
-                                           &path))
-               goto err;
-
-       if (properties) {
-               if (!wpa_dbus_get_object_properties(
-                           iface, net_obj_path, WPAS_DBUS_NEW_IFACE_NETWORK,
-                           &iter))
-                       goto err;
-       }
-
-       dbus_connection_send(iface->con, msg, NULL);
-
-       dbus_message_unref(msg);
-       return;
-
-err:
-       wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+                                           &path) ||
+           (properties &&
+            !wpa_dbus_get_object_properties(
+                    iface, net_obj_path, WPAS_DBUS_NEW_IFACE_NETWORK,
+                    &iter)))
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       else
+               dbus_connection_send(iface->con, msg, NULL);
        dbus_message_unref(msg);
 }
 
@@ -512,19 +523,12 @@ void wpas_dbus_signal_network_request(struct wpa_supplicant *wpa_s,
 
        dbus_message_iter_init_append(msg, &iter);
        if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
-                                           &net_ptr))
-               goto err;
-       if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &field))
-               goto err;
-       if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &txt))
-               goto err;
-
-       dbus_connection_send(iface->con, msg, NULL);
-       dbus_message_unref(msg);
-       return;
-
-err:
-       wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+                                           &net_ptr) ||
+           !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &field) ||
+           !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &txt))
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       else
+               dbus_connection_send(iface->con, msg, NULL);
        dbus_message_unref(msg);
 }
 
@@ -542,6 +546,7 @@ void wpas_dbus_signal_network_enabled_changed(struct wpa_supplicant *wpa_s,
 {
 
        char path[WPAS_DBUS_OBJECT_PATH_MAX];
+
        os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
                    "%s/" WPAS_DBUS_NEW_NETWORKS_PART "/%d",
                    wpa_s->dbus_new_path, ssid->id);
@@ -709,9 +714,9 @@ void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s,
        DBusMessage *msg;
        DBusMessageIter iter, dict_iter;
        struct wpas_dbus_priv *iface;
-       char *auth_type[6]; /* we have six possible authorization types */
+       char *auth_type[5]; /* we have five possible authentication types */
        int at_num = 0;
-       char *encr_type[4]; /* we have four possible encryption types */
+       char *encr_type[3]; /* we have three possible encryption types */
        int et_num = 0;
 
        iface = wpa_s->global->dbus;
@@ -734,34 +739,25 @@ void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s,
                auth_type[at_num++] = "open";
        if (cred->auth_type & WPS_AUTH_WPAPSK)
                auth_type[at_num++] = "wpa-psk";
-       if (cred->auth_type & WPS_AUTH_SHARED)
-               auth_type[at_num++] = "shared";
        if (cred->auth_type & WPS_AUTH_WPA)
                auth_type[at_num++] = "wpa-eap";
        if (cred->auth_type & WPS_AUTH_WPA2)
                auth_type[at_num++] = "wpa2-eap";
        if (cred->auth_type & WPS_AUTH_WPA2PSK)
-               auth_type[at_num++] =
-               "wpa2-psk";
+               auth_type[at_num++] = "wpa2-psk";
 
        if (cred->encr_type & WPS_ENCR_NONE)
                encr_type[et_num++] = "none";
-       if (cred->encr_type & WPS_ENCR_WEP)
-               encr_type[et_num++] = "wep";
        if (cred->encr_type & WPS_ENCR_TKIP)
                encr_type[et_num++] = "tkip";
        if (cred->encr_type & WPS_ENCR_AES)
                encr_type[et_num++] = "aes";
 
-       if (wpa_s->current_ssid) {
-               if (!wpa_dbus_dict_append_byte_array(
-                           &dict_iter, "BSSID",
-                           (const char *) wpa_s->current_ssid->bssid,
-                           ETH_ALEN))
-                       goto nomem;
-       }
-
-       if (!wpa_dbus_dict_append_byte_array(&dict_iter, "SSID",
+       if ((wpa_s->current_ssid &&
+            !wpa_dbus_dict_append_byte_array(
+                    &dict_iter, "BSSID",
+                    (const char *) wpa_s->current_ssid->bssid, ETH_ALEN)) ||
+           !wpa_dbus_dict_append_byte_array(&dict_iter, "SSID",
                                             (const char *) cred->ssid,
                                             cred->ssid_len) ||
            !wpa_dbus_dict_append_string_array(&dict_iter, "AuthType",
@@ -788,6 +784,8 @@ nomem:
 
 void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s,
                                    int depth, const char *subject,
+                                   const char *altsubject[],
+                                   int num_altsubject,
                                    const char *cert_hash,
                                    const struct wpabuf *cert)
 {
@@ -808,29 +806,23 @@ void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s,
                return;
 
        dbus_message_iter_init_append(msg, &iter);
-       if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
-               goto nomem;
-
-       if (!wpa_dbus_dict_append_uint32(&dict_iter, "depth", depth) ||
-           !wpa_dbus_dict_append_string(&dict_iter, "subject", subject))
-               goto nomem;
-
-       if (cert_hash &&
-           !wpa_dbus_dict_append_string(&dict_iter, "cert_hash", cert_hash))
-               goto nomem;
-
-       if (cert &&
-           !wpa_dbus_dict_append_byte_array(&dict_iter, "cert",
-                                            wpabuf_head(cert),
-                                            wpabuf_len(cert)))
-               goto nomem;
-
-       if (!wpa_dbus_dict_close_write(&iter, &dict_iter))
-               goto nomem;
-
-       dbus_connection_send(iface->con, msg, NULL);
-
-nomem:
+       if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+           !wpa_dbus_dict_append_uint32(&dict_iter, "depth", depth) ||
+           !wpa_dbus_dict_append_string(&dict_iter, "subject", subject) ||
+           (altsubject && num_altsubject &&
+            !wpa_dbus_dict_append_string_array(&dict_iter, "altsubject",
+                                               altsubject, num_altsubject)) ||
+           (cert_hash &&
+            !wpa_dbus_dict_append_string(&dict_iter, "cert_hash",
+                                         cert_hash)) ||
+           (cert &&
+            !wpa_dbus_dict_append_byte_array(&dict_iter, "cert",
+                                             wpabuf_head(cert),
+                                             wpabuf_len(cert))) ||
+           !wpa_dbus_dict_close_write(&iter, &dict_iter))
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       else
+               dbus_connection_send(iface->con, msg, NULL);
        dbus_message_unref(msg);
 }
 
@@ -856,15 +848,12 @@ void wpas_dbus_signal_eap_status(struct wpa_supplicant *wpa_s,
 
        dbus_message_iter_init_append(msg, &iter);
 
-       if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &status)
-           ||
+       if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &status) ||
            !dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
                                            &parameter))
-               goto nomem;
-
-       dbus_connection_send(iface->con, msg, NULL);
-
-nomem:
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       else
+               dbus_connection_send(iface->con, msg, NULL);
        dbus_message_unref(msg);
 }
 
@@ -950,37 +939,40 @@ void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s,
 void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s,
                                        const char *role)
 {
-
        DBusMessage *msg;
-       DBusMessageIter iter;
+       DBusMessageIter iter, dict_iter;
        struct wpas_dbus_priv *iface = wpa_s->global->dbus;
-       char *ifname = wpa_s->ifname;
+       struct wpa_supplicant *parent;
 
        /* Do nothing if the control interface is not turned on */
        if (iface == NULL)
                return;
 
-       msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+       parent = wpa_s->parent;
+       if (parent->p2p_mgmt)
+               parent = parent->parent;
+
+       if (!wpa_s->dbus_groupobj_path)
+               return;
+
+       msg = dbus_message_new_signal(parent->dbus_new_path,
                                      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
                                      "GroupFinished");
        if (msg == NULL)
                return;
 
        dbus_message_iter_init_append(msg, &iter);
-
-       if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &ifname)) {
-               wpa_printf(MSG_ERROR, "dbus: Failed to construct GroupFinished"
-                                     "signal -not enough memory for ifname ");
-               goto err;
-       }
-
-       if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &role))
-               wpa_printf(MSG_ERROR, "dbus: Failed to construct GroupFinished"
-                                     "signal -not enough memory for role ");
+       if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+           !wpa_dbus_dict_append_object_path(&dict_iter,
+                                             "interface_object",
+                                             wpa_s->dbus_new_path) ||
+           !wpa_dbus_dict_append_string(&dict_iter, "role", role) ||
+           !wpa_dbus_dict_append_object_path(&dict_iter, "group_object",
+                                             wpa_s->dbus_groupobj_path) ||
+           !wpa_dbus_dict_close_write(&iter, &dict_iter))
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
        else
                dbus_connection_send(iface->con, msg, NULL);
-
-err:
        dbus_message_unref(msg);
 }
 
@@ -1026,6 +1018,9 @@ void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
        if (iface == NULL)
                return;
 
+       if (wpa_s->p2p_mgmt)
+               wpa_s = wpa_s->parent;
+
        if (request || !status) {
                if (config_methods & WPS_CONFIG_DISPLAY)
                        _signal = request ?
@@ -1040,9 +1035,10 @@ void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
                                   "ProvisionDiscoveryPBCResponse";
                else
                        return; /* Unknown or un-supported method */
-       } else if (!request && status)
+       } else {
                /* Explicit check for failure response */
                _signal = "ProvisionDiscoveryFailure";
+       }
 
        add_pin = ((request && (config_methods & WPS_CONFIG_DISPLAY)) ||
                   (!request && !status &&
@@ -1111,6 +1107,9 @@ void wpas_dbus_signal_p2p_go_neg_req(struct wpa_supplicant *wpa_s,
        if (iface == NULL)
                return;
 
+       if (wpa_s->p2p_mgmt)
+               wpa_s = wpa_s->parent;
+
        os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
                    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
                    wpa_s->dbus_new_path, MAC2STR(src));
@@ -1156,6 +1155,69 @@ static int wpas_dbus_get_group_obj_path(struct wpa_supplicant *wpa_s,
 }
 
 
+struct group_changed_data {
+       struct wpa_supplicant *wpa_s;
+       struct p2p_peer_info *info;
+};
+
+
+static int match_group_where_peer_is_client(struct p2p_group *group,
+                                           void *user_data)
+{
+       struct group_changed_data *data = user_data;
+       const struct p2p_group_config *cfg;
+       struct wpa_supplicant *wpa_s_go;
+
+       if (!p2p_group_is_client_connected(group, data->info->p2p_device_addr))
+               return 1;
+
+       cfg = p2p_group_get_config(group);
+
+       wpa_s_go = wpas_get_p2p_go_iface(data->wpa_s, cfg->ssid,
+                                        cfg->ssid_len);
+       if (wpa_s_go != NULL && wpa_s_go == data->wpa_s) {
+               wpas_dbus_signal_peer_groups_changed(
+                       data->wpa_s->parent, data->info->p2p_device_addr);
+               return 0;
+       }
+
+       return 1;
+}
+
+
+static void signal_peer_groups_changed(struct p2p_peer_info *info,
+                                      void *user_data)
+{
+       struct group_changed_data *data = user_data;
+       struct wpa_supplicant *wpa_s_go;
+
+       wpa_s_go = wpas_get_p2p_client_iface(data->wpa_s,
+                                            info->p2p_device_addr);
+       if (wpa_s_go != NULL && wpa_s_go == data->wpa_s) {
+               wpas_dbus_signal_peer_groups_changed(data->wpa_s->parent,
+                                                    info->p2p_device_addr);
+               return;
+       }
+
+       data->info = info;
+       p2p_loop_on_all_groups(data->wpa_s->global->p2p,
+                              match_group_where_peer_is_client, data);
+       data->info = NULL;
+}
+
+
+static void peer_groups_changed(struct wpa_supplicant *wpa_s)
+{
+       struct group_changed_data data;
+
+       os_memset(&data, 0, sizeof(data));
+       data.wpa_s = wpa_s;
+
+       p2p_loop_on_known_peers(wpa_s->global->p2p,
+                               signal_peer_groups_changed, &data);
+}
+
+
 /**
  * wpas_dbus_signal_p2p_group_started - Signals P2P group has
  * started. Emitted when a group is successfully started
@@ -1174,58 +1236,71 @@ void wpas_dbus_signal_p2p_group_started(struct wpa_supplicant *wpa_s,
        DBusMessage *msg;
        DBusMessageIter iter, dict_iter;
        struct wpas_dbus_priv *iface;
-       char group_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+       struct wpa_supplicant *parent;
+#if defined TIZEN_EXT
+       u8 ip[3 * 4];
+#endif /* TIZEN_EXT */
+
+       parent = wpa_s->parent;
+       if (parent->p2p_mgmt)
+               parent = parent->parent;
 
-       iface = wpa_s->parent->global->dbus;
+       iface = parent->global->dbus;
 
        /* Do nothing if the control interface is not turned on */
        if (iface == NULL)
                return;
 
-       if (wpas_dbus_get_group_obj_path(wpa_s, ssid, group_obj_path) < 0)
+       if (wpa_s->dbus_groupobj_path == NULL)
                return;
 
        /* New interface has been created for this group */
-       msg = dbus_message_new_signal(wpa_s->parent->dbus_new_path,
+       msg = dbus_message_new_signal(parent->dbus_new_path,
                                      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
                                      "GroupStarted");
-
        if (msg == NULL)
                return;
 
        dbus_message_iter_init_append(msg, &iter);
-       if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
-               goto nomem;
-
        /*
         * In case the device supports creating a separate interface the
         * DBus client will need to know the object path for the interface
         * object this group was created on, so include it here.
         */
-       if (!wpa_dbus_dict_append_object_path(&dict_iter,
-                                       "interface_object",
-                                       wpa_s->dbus_new_path))
-               goto nomem;
-
-       if (!wpa_dbus_dict_append_string(&dict_iter, "role",
-                                        client ? "client" : "GO"))
-               goto nomem;
-
-       if (!wpa_dbus_dict_append_object_path(&dict_iter, "group_object",
-                                            group_obj_path) ||
-          !wpa_dbus_dict_close_write(&iter, &dict_iter))
-               goto nomem;
-
-       dbus_connection_send(iface->con, msg, NULL);
-
-nomem:
+       if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+           !wpa_dbus_dict_append_object_path(&dict_iter,
+                                             "interface_object",
+                                             wpa_s->dbus_new_path) ||
+           !wpa_dbus_dict_append_string(&dict_iter, "role",
+                                        client ? "client" : "GO") ||
+           !wpa_dbus_dict_append_object_path(&dict_iter, "group_object",
+                                             wpa_s->dbus_groupobj_path))
+               goto err;
+#if defined TIZEN_EXT
+       if(client && (wpa_sm_get_p2p_ip_addr(wpa_s->wpa, ip) == 0))
+               if(!wpa_dbus_dict_append_byte_array(&dict_iter,
+                                             "IpAddr",(char *) ip, 4) ||
+                               !wpa_dbus_dict_append_byte_array(&dict_iter,
+                                             "IpAddrMask",(char *) ip + 4, 4) ||
+                               !wpa_dbus_dict_append_byte_array(&dict_iter,
+                                             "IpAddrGo",(char *) ip + 8, 4))
+                       goto err;
+#endif /* TIZEN_EXT */
+       if(!wpa_dbus_dict_close_write(&iter, &dict_iter)) {
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       } else {
+               dbus_connection_send(iface->con, msg, NULL);
+               if (client)
+                       peer_groups_changed(wpa_s);
+       }
+err:
        dbus_message_unref(msg);
 }
 
 
 /**
  *
- * Method to emit GONeogtiation Success or Failure signals based
+ * Method to emit GONegotiation Success or Failure signals based
  * on status.
  * @status: Status of the GO neg request. 0 for success, other for errors.
  */
@@ -1243,6 +1318,9 @@ void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s,
 
        iface = wpa_s->global->dbus;
 
+       if (wpa_s->p2p_mgmt)
+               wpa_s = wpa_s->parent;
+
        os_memset(freqs, 0, sizeof(freqs));
        /* Do nothing if the control interface is not turned on */
        if (iface == NULL)
@@ -1261,9 +1339,8 @@ void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s,
                return;
 
        dbus_message_iter_init_append(msg, &iter);
-       if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
-               goto err;
-       if (!wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
+       if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+           !wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
                                              path) ||
            !wpa_dbus_dict_append_int32(&dict_iter, "status", res->status))
                goto err;
@@ -1272,15 +1349,10 @@ void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s,
                int i = 0;
                int freq_list_num = 0;
 
-               if (res->role_go) {
-                       if (!wpa_dbus_dict_append_byte_array(
-                                   &dict_iter, "passphrase",
-                                   (const char *) res->passphrase,
-                                   sizeof(res->passphrase)))
-                               goto err;
-               }
-
-               if (!wpa_dbus_dict_append_string(&dict_iter, "role_go",
+               if ((res->role_go &&
+                    !wpa_dbus_dict_append_string(&dict_iter, "passphrase",
+                                                 res->passphrase)) ||
+                   !wpa_dbus_dict_append_string(&dict_iter, "role_go",
                                                 res->role_go ? "GO" :
                                                 "client") ||
                    !wpa_dbus_dict_append_int32(&dict_iter, "frequency",
@@ -1315,22 +1387,16 @@ void wpas_dbus_signal_p2p_go_neg_resp(struct wpa_supplicant *wpa_s,
                                               DBUS_TYPE_INT32_AS_STRING,
                                               &iter_dict_entry,
                                               &iter_dict_val,
-                                              &iter_dict_array))
-                       goto err;
-
-               if (!dbus_message_iter_append_fixed_array(&iter_dict_array,
+                                              &iter_dict_array) ||
+                   !dbus_message_iter_append_fixed_array(&iter_dict_array,
                                                          DBUS_TYPE_INT32,
                                                          &f_array,
-                                                         freq_list_num))
-                       goto err;
-
-               if (!wpa_dbus_dict_end_array(&dict_iter,
+                                                         freq_list_num) ||
+                   !wpa_dbus_dict_end_array(&dict_iter,
                                             &iter_dict_entry,
                                             &iter_dict_val,
-                                            &iter_dict_array))
-                       goto err;
-
-               if (!wpa_dbus_dict_append_int32(&dict_iter, "persistent_group",
+                                            &iter_dict_array) ||
+                   !wpa_dbus_dict_append_int32(&dict_iter, "persistent_group",
                                                res->persistent_group) ||
                    !wpa_dbus_dict_append_uint32(&dict_iter,
                                                 "peer_config_timeout",
@@ -1362,13 +1428,16 @@ void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s,
        DBusMessageIter iter, dict_iter;
        struct wpas_dbus_priv *iface;
 
-       wpa_printf(MSG_INFO, "%s\n", __func__);
+       wpa_printf(MSG_DEBUG, "%s", __func__);
 
        iface = wpa_s->global->dbus;
        /* Do nothing if the control interface is not turned on */
        if (iface == NULL)
                return;
 
+       if (wpa_s->p2p_mgmt)
+               wpa_s = wpa_s->parent;
+
        msg = dbus_message_new_signal(wpa_s->dbus_new_path,
                                      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
                                      "InvitationResult");
@@ -1377,23 +1446,16 @@ void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s,
                return;
 
        dbus_message_iter_init_append(msg, &iter);
-       if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
-               goto nomem;
-
-       if (!wpa_dbus_dict_append_int32(&dict_iter, "status", status))
-               goto nomem;
-       if (bssid) {
-               if (!wpa_dbus_dict_append_byte_array(&dict_iter, "BSSID",
-                                                    (const char *) bssid,
-                                                    ETH_ALEN))
-                       goto nomem;
-       }
-       if (!wpa_dbus_dict_close_write(&iter, &dict_iter))
-               goto nomem;
-
-       dbus_connection_send(iface->con, msg, NULL);
-
-nomem:
+       if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+           !wpa_dbus_dict_append_int32(&dict_iter, "status", status) ||
+           (bssid &&
+            !wpa_dbus_dict_append_byte_array(&dict_iter, "BSSID",
+                                             (const char *) bssid,
+                                             ETH_ALEN)) ||
+           !wpa_dbus_dict_close_write(&iter, &dict_iter))
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       else
+               dbus_connection_send(iface->con, msg, NULL);
        dbus_message_unref(msg);
 }
 
@@ -1405,15 +1467,22 @@ nomem:
  * constructed using p2p i/f addr used for connecting.
  *
  * @wpa_s: %wpa_supplicant network interface data
- * @member_addr: addr (p2p i/f) of the peer joining the group
+ * @peer_addr: P2P Device Address of the peer joining the group
  */
 void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s,
-                                     const u8 *member)
+                                     const u8 *peer_addr)
 {
        struct wpas_dbus_priv *iface;
        DBusMessage *msg;
        DBusMessageIter iter;
-       char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+       char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+       struct wpa_supplicant *parent;
+#if defined TIZEN_EXT
+       DBusMessageIter array_iter;
+       struct hostapd_data *hapd;
+       struct sta_info *sta;
+       u8 ip_addr[4] = {0,}, *ip = ip_addr;
+#endif /* TIZEN_EXT */
 
        iface = wpa_s->global->dbus;
 
@@ -1424,10 +1493,20 @@ void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s,
        if (!wpa_s->dbus_groupobj_path)
                return;
 
-       os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
-                       "%s/"  WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/"
+       parent = wpa_s->parent;
+       if (parent->p2p_mgmt)
+               parent = parent->parent;
+#if defined TIZEN_EXT
+       if (wpa_s->ap_iface != NULL &&
+           (hapd = wpa_s->ap_iface->bss[0]) != NULL &&
+           (sta = ap_get_sta_p2p(hapd, peer_addr)) != NULL &&
+           sta->wpa_sm != NULL)
+               wpa_auth_get_ip_addr(sta->wpa_sm, ip);
+#endif /* TIZEN_EXT */
+       os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+                       "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
                        COMPACT_MACSTR,
-                       wpa_s->dbus_groupobj_path, MAC2STR(member));
+                       parent->dbus_new_path, MAC2STR(peer_addr));
 
        msg = dbus_message_new_signal(wpa_s->dbus_groupobj_path,
                                      WPAS_DBUS_NEW_IFACE_P2P_GROUP,
@@ -1436,18 +1515,23 @@ void wpas_dbus_signal_p2p_peer_joined(struct wpa_supplicant *wpa_s,
                return;
 
        dbus_message_iter_init_append(msg, &iter);
-       path = groupmember_obj_path;
+       path = peer_obj_path;
        if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
-                                           &path))
-               goto err;
-
-       dbus_connection_send(iface->con, msg, NULL);
-
-       dbus_message_unref(msg);
-       return;
-
-err:
-       wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+                                           &path)
+#if defined TIZEN_EXT
+                       ||
+                       !dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                           DBUS_TYPE_BYTE_AS_STRING, &array_iter) ||
+                       !dbus_message_iter_append_fixed_array(&array_iter,
+                                           DBUS_TYPE_BYTE, &ip, 4) ||
+                       !dbus_message_iter_close_container(&iter, &array_iter)
+#endif /* TIZEN_EXT */
+                       ) {
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       } else {
+               dbus_connection_send(iface->con, msg, NULL);
+               wpas_dbus_signal_peer_groups_changed(parent, peer_addr);
+       }
        dbus_message_unref(msg);
 }
 
@@ -1456,18 +1540,19 @@ err:
  *
  * Method to emit a signal for a peer disconnecting the group.
  * The signal will carry path to the group member object
- * constructed using p2p i/f addr used for connecting.
+ * constructed using the P2P Device Address of the peer.
  *
  * @wpa_s: %wpa_supplicant network interface data
- * @member_addr: addr (p2p i/f) of the peer joining the group
+ * @peer_addr: P2P Device Address of the peer joining the group
  */
 void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s,
-                                     const u8 *member)
+                                           const u8 *peer_addr)
 {
        struct wpas_dbus_priv *iface;
        DBusMessage *msg;
        DBusMessageIter iter;
-       char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+       char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+       struct wpa_supplicant *parent;
 
        iface = wpa_s->global->dbus;
 
@@ -1478,10 +1563,14 @@ void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s,
        if (!wpa_s->dbus_groupobj_path)
                return;
 
-       os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
-                       "%s/"  WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/"
+       parent = wpa_s->parent;
+       if (parent->p2p_mgmt)
+               parent = parent->parent;
+
+       os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+                       "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
                        COMPACT_MACSTR,
-                       wpa_s->dbus_groupobj_path, MAC2STR(member));
+                       parent->dbus_new_path, MAC2STR(peer_addr));
 
        msg = dbus_message_new_signal(wpa_s->dbus_groupobj_path,
                                      WPAS_DBUS_NEW_IFACE_P2P_GROUP,
@@ -1490,19 +1579,15 @@ void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s,
                return;
 
        dbus_message_iter_init_append(msg, &iter);
-       path = groupmember_obj_path;
+       path = peer_obj_path;
        if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
-                                           &path))
-               goto err;
-
-       dbus_connection_send(iface->con, msg, NULL);
-
-       dbus_message_unref(msg);
-       return;
-
-err:
-       wpa_printf(MSG_ERROR, "dbus: Failed to construct PeerDisconnected "
-                             "signal");
+                                           &path)) {
+               wpa_printf(MSG_ERROR,
+                          "dbus: Failed to construct PeerDisconnected signal");
+       } else {
+               dbus_connection_send(iface->con, msg, NULL);
+               wpas_dbus_signal_peer_groups_changed(parent, peer_addr);
+       }
        dbus_message_unref(msg);
 }
 
@@ -1529,22 +1614,26 @@ void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s,
        DBusMessageIter iter, dict_iter;
        struct wpas_dbus_priv *iface;
        char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
        iface = wpa_s->global->dbus;
 
        /* Do nothing if the control interface is not turned on */
        if (iface == NULL)
                return;
 
+       if (wpa_s->p2p_mgmt)
+               wpa_s = wpa_s->parent;
+
+       /* Check if this is a known peer */
+       if (!p2p_peer_known(wpa_s->global->p2p, sa))
+               return;
+
        msg = dbus_message_new_signal(wpa_s->dbus_new_path,
                                      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
                                      "ServiceDiscoveryRequest");
        if (msg == NULL)
                return;
 
-       /* Check if this is a known peer */
-       if (!p2p_peer_known(wpa_s->global->p2p, sa))
-               goto error;
-
        os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
                    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
                    COMPACT_MACSTR, wpa_s->dbus_new_path, MAC2STR(sa));
@@ -1552,11 +1641,8 @@ void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s,
        path = peer_obj_path;
 
        dbus_message_iter_init_append(msg, &iter);
-       if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
-               goto error;
-
-
-       if (!wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
+       if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+           !wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
                                              path) ||
            !wpa_dbus_dict_append_int32(&dict_iter, "frequency", freq) ||
            !wpa_dbus_dict_append_int32(&dict_iter, "dialog_token",
@@ -1567,13 +1653,9 @@ void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s,
                                             (const char *) tlvs,
                                             tlvs_len) ||
            !wpa_dbus_dict_close_write(&iter, &dict_iter))
-               goto error;
-
-       dbus_connection_send(iface->con, msg, NULL);
-       dbus_message_unref(msg);
-       return;
-error:
-       wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       else
+               dbus_connection_send(iface->con, msg, NULL);
        dbus_message_unref(msg);
 }
 
@@ -1598,22 +1680,26 @@ void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s,
        DBusMessageIter iter, dict_iter;
        struct wpas_dbus_priv *iface;
        char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
+
        iface = wpa_s->global->dbus;
 
        /* Do nothing if the control interface is not turned on */
        if (iface == NULL)
                return;
 
+       if (wpa_s->p2p_mgmt)
+               wpa_s = wpa_s->parent;
+
+       /* Check if this is a known peer */
+       if (!p2p_peer_known(wpa_s->global->p2p, sa))
+               return;
+
        msg = dbus_message_new_signal(wpa_s->dbus_new_path,
                                      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-                               "ServiceDiscoveryResponse");
+                                     "ServiceDiscoveryResponse");
        if (msg == NULL)
                return;
 
-       /* Check if this is a known peer */
-       if (!p2p_peer_known(wpa_s->global->p2p, sa))
-               goto error;
-
        os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
                    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/"
                    COMPACT_MACSTR, wpa_s->dbus_new_path, MAC2STR(sa));
@@ -1621,10 +1707,8 @@ void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s,
        path = peer_obj_path;
 
        dbus_message_iter_init_append(msg, &iter);
-       if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
-               goto error;
-
-       if (!wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
+       if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+           !wpa_dbus_dict_append_object_path(&dict_iter, "peer_object",
                                              path) ||
            !wpa_dbus_dict_append_uint16(&dict_iter, "update_indicator",
                                         update_indic) ||
@@ -1632,17 +1716,13 @@ void wpas_dbus_signal_p2p_sd_response(struct wpa_supplicant *wpa_s,
                                             (const char *) tlvs,
                                             tlvs_len) ||
            !wpa_dbus_dict_close_write(&iter, &dict_iter))
-               goto error;
-
-
-       dbus_connection_send(iface->con, msg, NULL);
-       dbus_message_unref(msg);
-       return;
-error:
-       wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       else
+               dbus_connection_send(iface->con, msg, NULL);
        dbus_message_unref(msg);
 }
 
+
 /**
  * wpas_dbus_signal_persistent_group - Send a persistent group related
  *     event signal
@@ -1668,6 +1748,9 @@ static void wpas_dbus_signal_persistent_group(struct wpa_supplicant *wpa_s,
        if (iface == NULL)
                return;
 
+       if (wpa_s->p2p_mgmt)
+               wpa_s = wpa_s->parent;
+
        os_snprintf(pgrp_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
                    "%s/" WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/%u",
                    wpa_s->dbus_new_path, id);
@@ -1681,23 +1764,15 @@ static void wpas_dbus_signal_persistent_group(struct wpa_supplicant *wpa_s,
        dbus_message_iter_init_append(msg, &iter);
        path = pgrp_obj_path;
        if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
-                                           &path))
-               goto err;
-
-       if (properties) {
-               if (!wpa_dbus_get_object_properties(
-                           iface, pgrp_obj_path,
-                           WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, &iter))
-                       goto err;
-       }
-
-       dbus_connection_send(iface->con, msg, NULL);
-
-       dbus_message_unref(msg);
-       return;
+                                           &path) ||
+           (properties &&
+            !wpa_dbus_get_object_properties(
+                    iface, pgrp_obj_path,
+                    WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, &iter)))
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       else
+               dbus_connection_send(iface->con, msg, NULL);
 
-err:
-       wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
        dbus_message_unref(msg);
 }
 
@@ -1748,38 +1823,149 @@ void wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s,
        DBusMessage *msg;
        DBusMessageIter iter, dict_iter;
        struct wpas_dbus_priv *iface;
-       char *key = "fail";
+       char *key = "fail";
+
+       iface = wpa_s->global->dbus;
+
+       /* Do nothing if the control interface is not turned on */
+       if (iface == NULL)
+               return;
+
+       if (wpa_s->p2p_mgmt)
+               wpa_s = wpa_s->parent;
+
+       msg = dbus_message_new_signal(wpa_s->dbus_new_path,
+                                     WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+                                     "WpsFailed");
+       if (msg == NULL)
+               return;
+
+       dbus_message_iter_init_append(msg, &iter);
+
+       if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) ||
+           !wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+           !wpa_dbus_dict_append_int32(&dict_iter, "msg", fail->msg) ||
+           !wpa_dbus_dict_append_int16(&dict_iter, "config_error",
+                                       fail->config_error) ||
+           !wpa_dbus_dict_close_write(&iter, &dict_iter))
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       else
+               dbus_connection_send(iface->con, msg, NULL);
+
+       dbus_message_unref(msg);
+}
+
+
+/**
+ * wpas_dbus_signal_p2p_group_formation_failure - Signals GroupFormationFailure event
+ * @wpa_s: %wpa_supplicant network interface data
+ *
+ * Sends Event dbus signal without any param
+ */
+void wpas_dbus_signal_p2p_group_formation_failure (
+                                       struct wpa_supplicant *wpa_s)
+{
+
+       DBusMessage *msg;
+       struct wpas_dbus_priv *iface;
+       struct wpa_supplicant *parent;
+
+       iface = wpa_s->global->dbus;
+
+       /* Do nothing if the control interface is not turned on */
+       if (iface == NULL)
+               return;
+
+       parent = wpa_s->parent;
+       if (parent && parent->p2p_mgmt)
+               parent = parent->parent;
+
+       /* Do nothing if the parent control interface is not turned on */
+       if(parent == NULL || parent->dbus_new_path == NULL)
+               return;
+
+       msg = dbus_message_new_signal(parent->dbus_new_path,
+                                     WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+                                     "GroupFormationFailure");
+       if (msg == NULL)
+               return;
+
+       dbus_connection_send(iface->con, msg, NULL);
+
+       dbus_message_unref(msg);
+}
+
+/**
+ *
+ * Method to emit Invitation Received signal
+ * @wpa_s: %wpa_supplicant network interface data
+ * @sa: Source address of the Invitation Request
+ * @dev_add: GO Device Addres
+ * @bssid: P2P Group BSSID or %NULL if not received
+ * @id: persistent group id or %0 if not persistent group
+ * @op_freq : Operational frequency for the group
+ */
+
+void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+                                           const u8 *sa,const u8 *dev_add,const u8 *bssid,
+                                           int id, int op_freq)
+{
+       DBusMessage *msg;
+       DBusMessageIter iter, dict_iter;
+       struct wpas_dbus_priv *iface;
 
-       iface = wpa_s->global->dbus;
+       wpa_printf(MSG_DEBUG, "%s", __func__);
 
+       iface = wpa_s->global->dbus;
        /* Do nothing if the control interface is not turned on */
        if (iface == NULL)
                return;
 
+       if (wpa_s->p2p_mgmt)
+               wpa_s = wpa_s->parent;
+
        msg = dbus_message_new_signal(wpa_s->dbus_new_path,
                                      WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-                                     "WpsFailed");
+                                     "InvitationReceived");
        if (msg == NULL)
                return;
 
        dbus_message_iter_init_append(msg, &iter);
+       if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
+               goto nomem;
 
-       if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key) ||
-           !wpa_dbus_dict_open_write(&iter, &dict_iter) ||
-           !wpa_dbus_dict_append_int32(&dict_iter, "msg", fail->msg) ||
-           !wpa_dbus_dict_append_int16(&dict_iter, "config_error",
-                                       fail->config_error) ||
-           !wpa_dbus_dict_close_write(&iter, &dict_iter))
-               wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
-       else
-               dbus_connection_send(iface->con, msg, NULL);
+       if (sa) {
+               if (!wpa_dbus_dict_append_byte_array(&dict_iter, "sa",
+                                                    (const char *) sa, ETH_ALEN))
+                       goto nomem;
+       }
+       if (dev_add) {
+               if (!wpa_dbus_dict_append_byte_array(&dict_iter, "go_dev_add",
+                                                    (const char *) dev_add, ETH_ALEN))
+                       goto nomem;
+       }
+       if (bssid) {
+               if (!wpa_dbus_dict_append_byte_array(&dict_iter, "bssid",
+                                                    (const char *) bssid, ETH_ALEN))
+                       goto nomem;
+       }
+       if (!wpa_dbus_dict_append_int32(&dict_iter, "persistent_id", id))
+               goto nomem;
 
+       if (!wpa_dbus_dict_append_int32(&dict_iter, "op_freq", op_freq))
+               goto nomem;
+
+       if (!wpa_dbus_dict_close_write(&iter, &dict_iter))
+               goto nomem;
+
+       dbus_connection_send(iface->con, msg, NULL);
+
+nomem:
        dbus_message_unref(msg);
 }
 
 #endif /*CONFIG_P2P*/
 
-
 /**
  * wpas_dbus_signal_prop_changed - Signals change of property
  * @wpa_s: %wpa_supplicant network interface data
@@ -1884,6 +2070,9 @@ void wpas_dbus_bss_signal_prop_changed(struct wpa_supplicant *wpa_s,
        case WPAS_DBUS_BSS_PROP_IES:
                prop = "IEs";
                break;
+       case WPAS_DBUS_BSS_PROP_AGE:
+               prop = "Age";
+               break;
        default:
                wpa_printf(MSG_ERROR, "dbus: %s: Unknown Property value %d",
                           __func__, property);
@@ -1968,7 +2157,7 @@ static void wpas_dbus_register(struct wpa_dbus_object_desc *obj_desc,
 
 static const struct wpa_dbus_method_desc wpas_dbus_global_methods[] = {
        { "CreateInterface", WPAS_DBUS_NEW_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_create_interface,
+         (WPADBusMethodHandler) wpas_dbus_handler_create_interface,
          {
                  { "args", "a{sv}", ARG_IN },
                  { "path", "o", ARG_OUT },
@@ -1976,14 +2165,14 @@ static const struct wpa_dbus_method_desc wpas_dbus_global_methods[] = {
          }
        },
        { "RemoveInterface", WPAS_DBUS_NEW_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_remove_interface,
+         (WPADBusMethodHandler) wpas_dbus_handler_remove_interface,
          {
                  { "path", "o", ARG_IN },
                  END_ARGS
          }
        },
        { "GetInterface", WPAS_DBUS_NEW_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_get_interface,
+         (WPADBusMethodHandler) wpas_dbus_handler_get_interface,
          {
                  { "ifname", "s", ARG_IN },
                  { "path", "o", ARG_OUT },
@@ -2018,6 +2207,12 @@ static const struct wpa_dbus_property_desc wpas_dbus_global_properties[] = {
          wpas_dbus_getter_global_capabilities,
          NULL
        },
+#ifdef CONFIG_WIFI_DISPLAY
+       { "WFDIEs", WPAS_DBUS_NEW_INTERFACE, "ay",
+         wpas_dbus_getter_global_wfd_ies,
+         wpas_dbus_setter_global_wfd_ies
+       },
+#endif /* CONFIG_WIFI_DISPLAY */
        { NULL, NULL, NULL, NULL, NULL }
 };
 
@@ -2035,14 +2230,6 @@ static const struct wpa_dbus_signal_desc wpas_dbus_global_signals[] = {
                  END_ARGS
          }
        },
-       { "NetworkRequest", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         {
-                 { "path", "o", ARG_OUT },
-                 { "field", "s", ARG_OUT },
-                 { "text", "s", ARG_OUT },
-                 END_ARGS
-         }
-       },
        /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
        { "PropertiesChanged", WPAS_DBUS_NEW_INTERFACE,
          {
@@ -2069,8 +2256,8 @@ int wpas_dbus_ctrl_iface_init(struct wpas_dbus_priv *priv)
 
        obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
        if (!obj_desc) {
-               wpa_printf(MSG_ERROR, "Not enough memory "
-                          "to create object description");
+               wpa_printf(MSG_ERROR,
+                          "Not enough memory to create object description");
                return -1;
        }
 
@@ -2184,16 +2371,16 @@ int wpas_dbus_register_network(struct wpa_supplicant *wpa_s,
                   net_obj_path);
        obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
        if (!obj_desc) {
-               wpa_printf(MSG_ERROR, "Not enough memory "
-                          "to create object description");
+               wpa_printf(MSG_ERROR,
+                          "Not enough memory to create object description");
                goto err;
        }
 
        /* allocate memory for handlers arguments */
        arg = os_zalloc(sizeof(struct network_handler_args));
        if (!arg) {
-               wpa_printf(MSG_ERROR, "Not enough memory "
-                          "to create arguments for method");
+               wpa_printf(MSG_ERROR,
+                          "Not enough memory to create arguments for method");
                goto err;
        }
 
@@ -2308,6 +2495,10 @@ static const struct wpa_dbus_property_desc wpas_dbus_bss_properties[] = {
          wpas_dbus_getter_bss_ies,
          NULL
        },
+       { "Age", WPAS_DBUS_NEW_IFACE_BSS, "u",
+         wpas_dbus_getter_bss_age,
+         NULL
+       },
        { NULL, NULL, NULL, NULL, NULL }
 };
 
@@ -2395,15 +2586,15 @@ int wpas_dbus_register_bss(struct wpa_supplicant *wpa_s,
 
        obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
        if (!obj_desc) {
-               wpa_printf(MSG_ERROR, "Not enough memory "
-                          "to create object description");
+               wpa_printf(MSG_ERROR,
+                          "Not enough memory to create object description");
                goto err;
        }
 
        arg = os_zalloc(sizeof(struct bss_handler_args));
        if (!arg) {
-               wpa_printf(MSG_ERROR, "Not enough memory "
-                          "to create arguments for handler");
+               wpa_printf(MSG_ERROR,
+                          "Not enough memory to create arguments for handler");
                goto err;
        }
        arg->wpa_s = wpa_s;
@@ -2436,20 +2627,27 @@ err:
 
 static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
        { "Scan", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_scan,
+         (WPADBusMethodHandler) wpas_dbus_handler_scan,
          {
                  { "args", "a{sv}", ARG_IN },
                  END_ARGS
          }
        },
+       { "SignalPoll", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         (WPADBusMethodHandler) wpas_dbus_handler_signal_poll,
+         {
+                 { "args", "a{sv}", ARG_OUT },
+                 END_ARGS
+         }
+       },
        { "Disconnect", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_disconnect,
+         (WPADBusMethodHandler) wpas_dbus_handler_disconnect,
          {
                  END_ARGS
          }
        },
        { "AddNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_add_network,
+         (WPADBusMethodHandler) wpas_dbus_handler_add_network,
          {
                  { "args", "a{sv}", ARG_IN },
                  { "path", "o", ARG_OUT },
@@ -2457,33 +2655,39 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
          }
        },
        { "Reassociate", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_reassociate,
+         (WPADBusMethodHandler) wpas_dbus_handler_reassociate,
+         {
+                 END_ARGS
+         }
+       },
+       { "Reattach", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         (WPADBusMethodHandler) wpas_dbus_handler_reattach,
          {
                  END_ARGS
          }
        },
        { "RemoveNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_remove_network,
+         (WPADBusMethodHandler) wpas_dbus_handler_remove_network,
          {
                  { "path", "o", ARG_IN },
                  END_ARGS
          }
        },
        { "RemoveAllNetworks", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_remove_all_networks,
+         (WPADBusMethodHandler) wpas_dbus_handler_remove_all_networks,
          {
                  END_ARGS
          }
        },
        { "SelectNetwork", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_select_network,
+         (WPADBusMethodHandler) wpas_dbus_handler_select_network,
          {
                  { "path", "o", ARG_IN },
                  END_ARGS
          }
        },
        { "NetworkReply", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_network_reply,
+         (WPADBusMethodHandler) wpas_dbus_handler_network_reply,
          {
                  { "path", "o", ARG_IN },
                  { "field", "s", ARG_IN },
@@ -2493,7 +2697,7 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
        },
 #ifndef CONFIG_NO_CONFIG_BLOBS
        { "AddBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_add_blob,
+         (WPADBusMethodHandler) wpas_dbus_handler_add_blob,
          {
                  { "name", "s", ARG_IN },
                  { "data", "ay", ARG_IN },
@@ -2501,7 +2705,7 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
          }
        },
        { "GetBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_get_blob,
+         (WPADBusMethodHandler) wpas_dbus_handler_get_blob,
          {
                  { "name", "s", ARG_IN },
                  { "data", "ay", ARG_OUT },
@@ -2509,60 +2713,82 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
          }
        },
        { "RemoveBlob", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_remove_blob,
+         (WPADBusMethodHandler) wpas_dbus_handler_remove_blob,
          {
                  { "name", "s", ARG_IN },
                  END_ARGS
          }
        },
 #endif /* CONFIG_NO_CONFIG_BLOBS */
+       { "SetPKCS11EngineAndModulePath", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         (WPADBusMethodHandler)
+         wpas_dbus_handler_set_pkcs11_engine_and_module_path,
+         {
+                 { "pkcs11_engine_path", "s", ARG_IN },
+                 { "pkcs11_module_path", "s", ARG_IN },
+                 END_ARGS
+         }
+       },
 #ifdef CONFIG_WPS
        { "Start", WPAS_DBUS_NEW_IFACE_WPS,
-         (WPADBusMethodHandler) &wpas_dbus_handler_wps_start,
+         (WPADBusMethodHandler) wpas_dbus_handler_wps_start,
          {
                  { "args", "a{sv}", ARG_IN },
                  { "output", "a{sv}", ARG_OUT },
                  END_ARGS
          }
        },
+       { "Cancel", WPAS_DBUS_NEW_IFACE_WPS,
+         (WPADBusMethodHandler) wpas_dbus_handler_wps_cancel,
+         {
+               END_ARGS
+         }
+       },
+       { "GeneratePin", WPAS_DBUS_NEW_IFACE_WPS,
+         (WPADBusMethodHandler) wpas_dbus_handler_wps_generate_pin,
+         {
+                 { "generated_pin", "s", ARG_OUT },
+                 END_ARGS
+         }
+       },
 #endif /* CONFIG_WPS */
 #ifdef CONFIG_P2P
        { "Find", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-         (WPADBusMethodHandler)wpas_dbus_handler_p2p_find,
+         (WPADBusMethodHandler) wpas_dbus_handler_p2p_find,
          {
                  { "args", "a{sv}", ARG_IN },
                  END_ARGS
          }
        },
        { "StopFind", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-         (WPADBusMethodHandler)wpas_dbus_handler_p2p_stop_find,
+         (WPADBusMethodHandler) wpas_dbus_handler_p2p_stop_find,
          {
                  END_ARGS
          }
        },
        { "Listen", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-         (WPADBusMethodHandler)wpas_dbus_handler_p2p_listen,
+         (WPADBusMethodHandler) wpas_dbus_handler_p2p_listen,
          {
                  { "timeout", "i", ARG_IN },
                  END_ARGS
          }
        },
        { "ExtendedListen", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-         (WPADBusMethodHandler)wpas_dbus_handler_p2p_extendedlisten,
+         (WPADBusMethodHandler) wpas_dbus_handler_p2p_extendedlisten,
          {
                  { "args", "a{sv}", ARG_IN },
                  END_ARGS
          }
        },
        { "PresenceRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-         (WPADBusMethodHandler)wpas_dbus_handler_p2p_presence_request,
+         (WPADBusMethodHandler) wpas_dbus_handler_p2p_presence_request,
          {
                  { "args", "a{sv}", ARG_IN },
                  END_ARGS
          }
        },
        { "ProvisionDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-         (WPADBusMethodHandler)wpas_dbus_handler_p2p_prov_disc_req,
+         (WPADBusMethodHandler) wpas_dbus_handler_p2p_prov_disc_req,
          {
                  { "peer", "o", ARG_IN },
                  { "config_method", "s", ARG_IN },
@@ -2570,102 +2796,111 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
          }
        },
        { "Connect", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-         (WPADBusMethodHandler)wpas_dbus_handler_p2p_connect,
+         (WPADBusMethodHandler) wpas_dbus_handler_p2p_connect,
          {
                  { "args", "a{sv}", ARG_IN },
                  { "generated_pin", "s", ARG_OUT },
                  END_ARGS
          }
        },
+       { "Cancel", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+         (WPADBusMethodHandler) wpas_dbus_handler_p2p_cancel,
+         {
+                 END_ARGS
+         }
+       },
        { "GroupAdd", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-         (WPADBusMethodHandler)wpas_dbus_handler_p2p_group_add,
+         (WPADBusMethodHandler) wpas_dbus_handler_p2p_group_add,
          {
                  { "args", "a{sv}", ARG_IN },
+                 { "path", "o", ARG_OUT },
                  END_ARGS
          }
        },
        { "Invite", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-         (WPADBusMethodHandler)wpas_dbus_handler_p2p_invite,
+         (WPADBusMethodHandler) wpas_dbus_handler_p2p_invite,
          {
                  { "args", "a{sv}", ARG_IN },
                  END_ARGS
          }
        },
        { "Disconnect", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-         (WPADBusMethodHandler)wpas_dbus_handler_p2p_disconnect,
+         (WPADBusMethodHandler) wpas_dbus_handler_p2p_disconnect,
          {
                  END_ARGS
          }
        },
        { "RejectPeer", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-         (WPADBusMethodHandler)wpas_dbus_handler_p2p_rejectpeer,
+         (WPADBusMethodHandler) wpas_dbus_handler_p2p_rejectpeer,
+         {
+                 { "peer", "o", ARG_IN },
+                 END_ARGS
+         }
+       },
+       { "RemoveClient", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+         (WPADBusMethodHandler) wpas_dbus_handler_p2p_removeclient,
          {
                  { "peer", "o", ARG_IN },
+                 { "remove_persistent", "i", ARG_IN },
                  END_ARGS
          }
        },
        { "Flush", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-         (WPADBusMethodHandler)wpas_dbus_handler_p2p_flush,
+         (WPADBusMethodHandler) wpas_dbus_handler_p2p_flush,
          {
                  END_ARGS
          }
        },
        { "AddService", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-         (WPADBusMethodHandler)wpas_dbus_handler_p2p_add_service,
+         (WPADBusMethodHandler) wpas_dbus_handler_p2p_add_service,
          {
                  { "args", "a{sv}", ARG_IN },
                  END_ARGS
          }
        },
        { "DeleteService", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-         (WPADBusMethodHandler)wpas_dbus_handler_p2p_delete_service,
+         (WPADBusMethodHandler) wpas_dbus_handler_p2p_delete_service,
          {
                  { "args", "a{sv}", ARG_IN },
                  END_ARGS
          }
        },
        { "FlushService", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-         (WPADBusMethodHandler)wpas_dbus_handler_p2p_flush_service,
+         (WPADBusMethodHandler) wpas_dbus_handler_p2p_flush_service,
          {
                  END_ARGS
          }
        },
        { "ServiceDiscoveryRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-         (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_sd_req,
+         (WPADBusMethodHandler) wpas_dbus_handler_p2p_service_sd_req,
          {
                  { "args", "a{sv}", ARG_IN },
+                 { "ref", "t", ARG_OUT },
                  END_ARGS
          }
        },
        { "ServiceDiscoveryResponse", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-         (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_sd_res,
+         (WPADBusMethodHandler) wpas_dbus_handler_p2p_service_sd_res,
          {
                  { "args", "a{sv}", ARG_IN },
                  END_ARGS
          }
        },
        { "ServiceDiscoveryCancelRequest", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-         (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_sd_cancel_req,
+         (WPADBusMethodHandler) wpas_dbus_handler_p2p_service_sd_cancel_req,
          {
                  { "args", "t", ARG_IN },
                  END_ARGS
          }
        },
        { "ServiceUpdate", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-         (WPADBusMethodHandler)wpas_dbus_handler_p2p_service_update,
-         {
-                 END_ARGS
-         }
-       },
-       { "ServiceDiscoveryExternal", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-         (WPADBusMethodHandler)wpas_dbus_handler_p2p_serv_disc_external,
+         (WPADBusMethodHandler) wpas_dbus_handler_p2p_service_update,
          {
-                 { "arg", "i", ARG_IN },
                  END_ARGS
          }
        },
        { "ServiceDiscoveryExternal", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-         (WPADBusMethodHandler)wpas_dbus_handler_p2p_serv_disc_external,
+         (WPADBusMethodHandler) wpas_dbus_handler_p2p_serv_disc_external,
          {
                  { "arg", "i", ARG_IN },
                  END_ARGS
@@ -2695,7 +2930,7 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
        },
 #endif /* CONFIG_P2P */
        { "FlushBSS", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_flush_bss,
+         (WPADBusMethodHandler) wpas_dbus_handler_flush_bss,
          {
                  { "age", "u", ARG_IN },
                  END_ARGS
@@ -2716,26 +2951,57 @@ static const struct wpa_dbus_method_desc wpas_dbus_interface_methods[] = {
        },
 #endif /* CONFIG_AP */
        { "EAPLogoff", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_eap_logoff,
+         (WPADBusMethodHandler) wpas_dbus_handler_eap_logoff,
          {
                  END_ARGS
          }
        },
        { "EAPLogon", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_eap_logon,
+         (WPADBusMethodHandler) wpas_dbus_handler_eap_logon,
          {
                  END_ARGS
          }
        },
 #ifdef CONFIG_AUTOSCAN
        { "AutoScan", WPAS_DBUS_NEW_IFACE_INTERFACE,
-         (WPADBusMethodHandler) &wpas_dbus_handler_autoscan,
+         (WPADBusMethodHandler) wpas_dbus_handler_autoscan,
          {
                  { "arg", "s", ARG_IN },
                  END_ARGS
          }
        },
 #endif /* CONFIG_AUTOSCAN */
+#ifdef CONFIG_TDLS
+       { "TDLSDiscover", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         (WPADBusMethodHandler) wpas_dbus_handler_tdls_discover,
+         {
+                 { "peer_address", "s", ARG_IN },
+                 END_ARGS
+         }
+       },
+       { "TDLSSetup", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         (WPADBusMethodHandler) wpas_dbus_handler_tdls_setup,
+         {
+                 { "peer_address", "s", ARG_IN },
+                 END_ARGS
+         }
+       },
+       { "TDLSStatus", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         (WPADBusMethodHandler) wpas_dbus_handler_tdls_status,
+         {
+                 { "peer_address", "s", ARG_IN },
+                 { "status", "s", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       { "TDLSTeardown", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         (WPADBusMethodHandler) wpas_dbus_handler_tdls_teardown,
+         {
+                 { "peer_address", "s", ARG_IN },
+                 END_ARGS
+         }
+       },
+#endif /* CONFIG_TDLS */
        { NULL, NULL, NULL, { END_ARGS } }
 };
 
@@ -2812,11 +3078,23 @@ static const struct wpa_dbus_property_desc wpas_dbus_interface_properties[] = {
          wpas_dbus_getter_scan_interval,
          wpas_dbus_setter_scan_interval
        },
+       { "PKCS11EnginePath", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+         wpas_dbus_getter_pkcs11_engine_path,
+         NULL
+       },
+       { "PKCS11ModulePath", WPAS_DBUS_NEW_IFACE_INTERFACE, "s",
+         wpas_dbus_getter_pkcs11_module_path,
+         NULL
+       },
 #ifdef CONFIG_WPS
        { "ProcessCredentials", WPAS_DBUS_NEW_IFACE_WPS, "b",
          wpas_dbus_getter_process_credentials,
          wpas_dbus_setter_process_credentials
        },
+       { "ConfigMethods", WPAS_DBUS_NEW_IFACE_WPS, "s",
+         wpas_dbus_getter_config_methods,
+         wpas_dbus_setter_config_methods
+       },
 #endif /* CONFIG_WPS */
 #ifdef CONFIG_P2P
        { "P2PDeviceConfig", WPAS_DBUS_NEW_IFACE_P2PDEVICE, "a{sv}",
@@ -2932,16 +3210,9 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = {
        },
 #endif /* CONFIG_WPS */
 #ifdef CONFIG_P2P
-       { "P2PStateChanged", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
-         {
-                 { "states", "a{ss}", ARG_OUT },
-                 END_ARGS
-         }
-       },
        { "DeviceFound", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
          {
                  { "path", "o", ARG_OUT },
-                 { "properties", "a{sv}", ARG_OUT },
                  END_ARGS
          }
        },
@@ -3004,12 +3275,13 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = {
        },
        { "GONegotiationSuccess", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
          {
+                 { "properties", "a{sv}", ARG_OUT },
                  END_ARGS
          }
        },
        { "GONegotiationFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
          {
-                 { "status", "i", ARG_OUT },
+                 { "properties", "a{sv}", ARG_OUT },
                  END_ARGS
          }
        },
@@ -3026,10 +3298,21 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = {
                  END_ARGS
          }
        },
+       { "GroupFormationFailure", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+         {
+                 { "path", "o", ARG_OUT },
+                 END_ARGS
+         }
+       },
+       { "InvitationReceived", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
+         {
+               { "properties", "a{sv}", ARG_OUT },
+               END_ARGS
+         }
+       },
        { "GroupFinished", WPAS_DBUS_NEW_IFACE_P2PDEVICE,
          {
-                 { "ifname", "s", ARG_OUT },
-                 { "role", "s", ARG_OUT },
+                 { "properties", "a{sv}", ARG_OUT },
                  END_ARGS
          }
        },
@@ -3099,6 +3382,14 @@ static const struct wpa_dbus_signal_desc wpas_dbus_interface_signals[] = {
                  END_ARGS
          }
        },
+       { "NetworkRequest", WPAS_DBUS_NEW_IFACE_INTERFACE,
+         {
+                 { "path", "o", ARG_OUT },
+                 { "field", "s", ARG_OUT },
+                 { "text", "s", ARG_OUT },
+                 END_ARGS
+         }
+       },
        { NULL, NULL, { END_ARGS } }
 };
 
@@ -3125,8 +3416,8 @@ int wpas_dbus_register_interface(struct wpa_supplicant *wpa_s)
 
        obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
        if (!obj_desc) {
-               wpa_printf(MSG_ERROR, "Not enough memory "
-                          "to create object description");
+               wpa_printf(MSG_ERROR,
+                          "Not enough memory to create object description");
                goto err;
        }
 
@@ -3161,7 +3452,7 @@ int wpas_dbus_unregister_interface(struct wpa_supplicant *wpa_s)
        if (wpa_s == NULL || wpa_s->global == NULL)
                return 0;
        ctrl_iface = wpa_s->global->dbus;
-       if (ctrl_iface == NULL)
+       if (ctrl_iface == NULL || wpa_s->dbus_new_path == NULL)
                return 0;
 
        wpa_printf(MSG_DEBUG, "dbus: Unregister interface object '%s'",
@@ -3226,11 +3517,33 @@ static const struct wpa_dbus_property_desc wpas_dbus_p2p_peer_properties[] = {
          wpas_dbus_getter_p2p_peer_ies,
          NULL
        },
+       { "DeviceAddress", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
+         wpas_dbus_getter_p2p_peer_device_address,
+         NULL
+       },
+       { "InterfaceAddress", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
+               wpas_dbus_getter_p2p_peer_interface_address,
+         NULL
+       },
+       { "GODeviceAddress", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ay",
+                       wpas_dbus_getter_p2p_peer_go_device_address,
+         NULL
+       },
+       { "Groups", WPAS_DBUS_NEW_IFACE_P2P_PEER, "ao",
+         wpas_dbus_getter_p2p_peer_groups,
+         NULL
+       },
        { NULL, NULL, NULL, NULL, NULL }
 };
 
 static const struct wpa_dbus_signal_desc wpas_dbus_p2p_peer_signals[] = {
-
+       /* Deprecated: use org.freedesktop.DBus.Properties.PropertiesChanged */
+       { "PropertiesChanged", WPAS_DBUS_NEW_IFACE_P2P_PEER,
+         {
+                 { "properties", "a{sv}", ARG_OUT },
+                 END_ARGS
+         }
+       },
        { NULL, NULL, { END_ARGS } }
 };
 
@@ -3254,6 +3567,9 @@ static void wpas_dbus_signal_peer(struct wpa_supplicant *wpa_s,
        DBusMessageIter iter;
        char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX], *path;
 
+       if (wpa_s->p2p_mgmt)
+               wpa_s = wpa_s->parent;
+
        iface = wpa_s->global->dbus;
 
        /* Do nothing if the control interface is not turned on */
@@ -3273,15 +3589,10 @@ static void wpas_dbus_signal_peer(struct wpa_supplicant *wpa_s,
        path = peer_obj_path;
        if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
                                            &path))
-               goto err;
-
-       dbus_connection_send(iface->con, msg, NULL);
-
-       dbus_message_unref(msg);
-       return;
+               wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
+       else
+               dbus_connection_send(iface->con, msg, NULL);
 
-err:
-       wpa_printf(MSG_ERROR, "dbus: Failed to construct signal");
        dbus_message_unref(msg);
 }
 
@@ -3339,6 +3650,9 @@ int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr)
        if (ctrl_iface == NULL)
                return 0;
 
+       if (wpa_s->p2p_mgmt)
+               wpa_s = wpa_s->parent;
+
        os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
                    "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
                    wpa_s->dbus_new_path, MAC2STR(dev_addr));
@@ -3347,16 +3661,16 @@ int wpas_dbus_register_peer(struct wpa_supplicant *wpa_s, const u8 *dev_addr)
                   peer_obj_path);
        obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
        if (!obj_desc) {
-               wpa_printf(MSG_ERROR, "Not enough memory "
-                          "to create object description");
+               wpa_printf(MSG_ERROR,
+                          "Not enough memory to create object description");
                goto err;
        }
 
        /* allocate memory for handlers arguments */
        arg = os_zalloc(sizeof(struct peer_handler_args));
        if (!arg) {
-               wpa_printf(MSG_ERROR, "Not enough memory "
-                          "to create arguments for method");
+               wpa_printf(MSG_ERROR,
+                          "Not enough memory to create arguments for method");
                goto err;
        }
 
@@ -3398,6 +3712,10 @@ int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s,
        if (wpa_s == NULL || wpa_s->global == NULL ||
            wpa_s->dbus_new_path == NULL)
                return 0;
+
+       if (wpa_s->p2p_mgmt)
+               wpa_s = wpa_s->parent;
+
        ctrl_iface = wpa_s->global->dbus;
        if (ctrl_iface == NULL)
                return 0;
@@ -3414,6 +3732,23 @@ int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s,
 }
 
 
+void wpas_dbus_signal_peer_groups_changed(struct wpa_supplicant *wpa_s,
+                                         const u8 *dev_addr)
+{
+       char peer_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
+
+       if (wpa_s->p2p_mgmt)
+               wpa_s = wpa_s->parent;
+
+       os_snprintf(peer_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
+                   "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART "/" COMPACT_MACSTR,
+                   wpa_s->dbus_new_path, MAC2STR(dev_addr));
+
+       wpa_dbus_mark_property_changed(wpa_s->global->dbus, peer_obj_path,
+                                      WPAS_DBUS_NEW_IFACE_P2P_PEER, "Groups");
+}
+
+
 static const struct wpa_dbus_property_desc wpas_dbus_p2p_group_properties[] = {
        { "Members", WPAS_DBUS_NEW_IFACE_P2P_GROUP, "ao",
          wpas_dbus_getter_p2p_group_members,
@@ -3458,6 +3793,9 @@ static const struct wpa_dbus_signal_desc wpas_dbus_p2p_group_signals[] = {
        { "PeerJoined", WPAS_DBUS_NEW_IFACE_P2P_GROUP,
          {
                  { "peer", "o", ARG_OUT },
+#if defined TIZEN_EXT
+                 { "ip_address", "ay", ARG_OUT },
+#endif /* TIZEN_EXT */
                  END_ARGS
          }
        },
@@ -3510,8 +3848,8 @@ void wpas_dbus_register_p2p_group(struct wpa_supplicant *wpa_s,
                   group_obj_path);
        obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
        if (!obj_desc) {
-               wpa_printf(MSG_ERROR, "Not enough memory "
-                          "to create object description");
+               wpa_printf(MSG_ERROR,
+                          "Not enough memory to create object description");
                goto err;
        }
 
@@ -3548,6 +3886,9 @@ void wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s,
        if (wpa_s == NULL || wpa_s->global == NULL)
                return;
 
+       if (wpa_s->p2p_mgmt)
+               wpa_s = wpa_s->parent;
+
        ctrl_iface = wpa_s->global->dbus;
        if (ctrl_iface == NULL)
                return;
@@ -3559,6 +3900,8 @@ void wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s,
                return;
        }
 
+       peer_groups_changed(wpa_s);
+
        wpa_printf(MSG_DEBUG, "dbus: Unregister group object '%s'",
                   wpa_s->dbus_groupobj_path);
 
@@ -3570,109 +3913,6 @@ void wpas_dbus_unregister_p2p_group(struct wpa_supplicant *wpa_s,
 }
 
 static const struct wpa_dbus_property_desc
-wpas_dbus_p2p_groupmember_properties[] = {
-       { NULL, NULL, NULL, NULL, NULL }
-};
-
-/**
- * wpas_dbus_register_p2p_groupmember - Register a p2p groupmember
- * object with dbus
- * @wpa_s: wpa_supplicant interface structure
- * @p2p_if_addr: i/f addr of the device joining this group
- *
- * Registers p2p groupmember representing object with dbus
- */
-void wpas_dbus_register_p2p_groupmember(struct wpa_supplicant *wpa_s,
-                                       const u8 *p2p_if_addr)
-{
-       struct wpas_dbus_priv *ctrl_iface;
-       struct wpa_dbus_object_desc *obj_desc = NULL;
-       struct groupmember_handler_args *arg;
-       char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
-
-       /* Do nothing if the control interface is not turned on */
-       if (wpa_s == NULL || wpa_s->global == NULL)
-               return;
-
-       ctrl_iface = wpa_s->global->dbus;
-       if (ctrl_iface == NULL)
-               return;
-
-       if (!wpa_s->dbus_groupobj_path)
-               return;
-
-       os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
-               "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/" COMPACT_MACSTR,
-               wpa_s->dbus_groupobj_path, MAC2STR(p2p_if_addr));
-
-       obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
-       if (!obj_desc) {
-               wpa_printf(MSG_ERROR, "Not enough memory "
-                          "to create object description");
-               goto err;
-       }
-
-       /* allocate memory for handlers arguments */
-       arg = os_zalloc(sizeof(struct groupmember_handler_args));
-       if (!arg) {
-               wpa_printf(MSG_ERROR, "Not enough memory "
-                          "to create arguments for method");
-               goto err;
-       }
-
-       arg->wpa_s = wpa_s;
-       os_memcpy(arg->member_addr, p2p_if_addr, ETH_ALEN);
-
-       wpas_dbus_register(obj_desc, arg, wpa_dbus_free, NULL,
-                          wpas_dbus_p2p_groupmember_properties, NULL);
-
-       if (wpa_dbus_register_object_per_iface(ctrl_iface, groupmember_obj_path,
-                                              wpa_s->ifname, obj_desc))
-               goto err;
-
-       wpa_printf(MSG_INFO,
-                  "dbus: Registered group member object '%s' successfully",
-                  groupmember_obj_path);
-       return;
-
-err:
-       free_dbus_object_desc(obj_desc);
-}
-
-/**
- * wpas_dbus_unregister_p2p_groupmember - Unregister a p2p groupmember
- * object with dbus
- * @wpa_s: wpa_supplicant interface structure
- * @p2p_if_addr: i/f addr of the device joining this group
- *
- * Unregisters p2p groupmember representing object with dbus
- */
-void wpas_dbus_unregister_p2p_groupmember(struct wpa_supplicant *wpa_s,
-                                         const u8 *p2p_if_addr)
-{
-       struct wpas_dbus_priv *ctrl_iface;
-       char groupmember_obj_path[WPAS_DBUS_OBJECT_PATH_MAX];
-
-       /* Do nothing if the control interface is not turned on */
-       if (wpa_s == NULL || wpa_s->global == NULL)
-               return;
-
-       ctrl_iface = wpa_s->global->dbus;
-       if (ctrl_iface == NULL)
-               return;
-
-       if (!wpa_s->dbus_groupobj_path)
-               return;
-
-       os_snprintf(groupmember_obj_path, WPAS_DBUS_OBJECT_PATH_MAX,
-               "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART "/" COMPACT_MACSTR,
-               wpa_s->dbus_groupobj_path, MAC2STR(p2p_if_addr));
-
-       wpa_dbus_unregister_object_per_iface(ctrl_iface, groupmember_obj_path);
-}
-
-
-static const struct wpa_dbus_property_desc
        wpas_dbus_persistent_group_properties[] = {
        { "Properties", WPAS_DBUS_NEW_IFACE_PERSISTENT_GROUP, "a{sv}",
          wpas_dbus_getter_persistent_group_properties,
@@ -3709,6 +3949,9 @@ int wpas_dbus_register_persistent_group(struct wpa_supplicant *wpa_s,
        if (ssid->disabled != 2 && !ssid->p2p_persistent_group)
                return -1; /* should we return w/o complaining? */
 
+       if (wpa_s->p2p_mgmt)
+               wpa_s = wpa_s->parent;
+
        ctrl_iface = wpa_s->global->dbus;
        if (ctrl_iface == NULL)
                return 0;
@@ -3725,8 +3968,8 @@ int wpas_dbus_register_persistent_group(struct wpa_supplicant *wpa_s,
                   pgrp_obj_path);
        obj_desc = os_zalloc(sizeof(struct wpa_dbus_object_desc));
        if (!obj_desc) {
-               wpa_printf(MSG_ERROR, "dbus: Not enough memory to create "
-                          "object description");
+               wpa_printf(MSG_ERROR,
+                          "dbus: Not enough memory to create object description");
                goto err;
        }
 
@@ -3737,8 +3980,8 @@ int wpas_dbus_register_persistent_group(struct wpa_supplicant *wpa_s,
        /* allocate memory for handlers arguments */
        arg = os_zalloc(sizeof(struct network_handler_args));
        if (!arg) {
-               wpa_printf(MSG_ERROR, "dbus: Not enough memory to create "
-                          "arguments for method");
+               wpa_printf(MSG_ERROR,
+                          "dbus: Not enough memory to create arguments for method");
                goto err;
        }
 
@@ -3788,6 +4031,10 @@ int wpas_dbus_unregister_persistent_group(struct wpa_supplicant *wpa_s,
        if (wpa_s == NULL || wpa_s->global == NULL ||
            wpa_s->dbus_new_path == NULL)
                return 0;
+
+       if (wpa_s->p2p_mgmt)
+               wpa_s = wpa_s->parent;
+
        ctrl_iface = wpa_s->global->dbus;
        if (ctrl_iface == NULL)
                return 0;
old mode 100644 (file)
new mode 100755 (executable)
index 61c480a..93b0c88
@@ -41,6 +41,7 @@ enum wpas_dbus_bss_prop {
        WPAS_DBUS_BSS_PROP_RSN,
        WPAS_DBUS_BSS_PROP_WPS,
        WPAS_DBUS_BSS_PROP_IES,
+       WPAS_DBUS_BSS_PROP_AGE,
 };
 
 #define WPAS_DBUS_OBJECT_PATH_MAX 150
@@ -79,11 +80,7 @@ enum wpas_dbus_bss_prop {
 #define WPAS_DBUS_NEW_P2P_PEERS_PART   "Peers"
 #define        WPAS_DBUS_NEW_IFACE_P2P_PEER WPAS_DBUS_NEW_INTERFACE ".Peer"
 
-#define WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART    "Members"
-#define        WPAS_DBUS_NEW_IFACE_P2P_GROUPMEMBER \
-       WPAS_DBUS_NEW_INTERFACE ".GroupMember"
-
-/* Errors */
+/* Top-level Errors */
 #define WPAS_DBUS_ERROR_UNKNOWN_ERROR \
        WPAS_DBUS_NEW_INTERFACE ".UnknownError"
 #define WPAS_DBUS_ERROR_INVALID_ARGS \
@@ -91,6 +88,8 @@ enum wpas_dbus_bss_prop {
 
 #define WPAS_DBUS_ERROR_IFACE_EXISTS \
        WPAS_DBUS_NEW_INTERFACE ".InterfaceExists"
+#define WPAS_DBUS_ERROR_IFACE_DISABLED            \
+       WPAS_DBUS_NEW_INTERFACE ".InterfaceDisabled"
 #define WPAS_DBUS_ERROR_IFACE_UNKNOWN \
        WPAS_DBUS_NEW_INTERFACE ".InterfaceUnknown"
 
@@ -118,6 +117,9 @@ enum wpas_dbus_bss_prop {
 #define WPAS_DBUS_ERROR_SUBSCRIPTION_EPERM \
        WPAS_DBUS_NEW_INTERFACE ".SubscriptionNotYou"
 
+/* Interface-level errors */
+#define WPAS_DBUS_ERROR_IFACE_SCAN_ERROR \
+       WPAS_DBUS_NEW_IFACE_INTERFACE ".ScanError"
 
 void wpas_dbus_subscribe_noc(struct wpas_dbus_priv *priv);
 void wpas_dbus_unsubscribe_noc(struct wpas_dbus_priv *priv);
@@ -143,6 +145,7 @@ void wpas_dbus_signal_network_request(struct wpa_supplicant *wpa_s,
                                      enum wpa_ctrl_req_type rtype,
                                      const char *default_text);
 void wpas_dbus_signal_scan_done(struct wpa_supplicant *wpa_s, int success);
+void wpas_dbus_signal_find_stopped(struct wpa_supplicant *wpa_s);
 void wpas_dbus_signal_wps_cred(struct wpa_supplicant *wpa_s,
                               const struct wps_credential *cred);
 void wpas_dbus_signal_wps_event_m2d(struct wpa_supplicant *wpa_s,
@@ -172,6 +175,8 @@ int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s,
                                  const u8 *dev_addr);
 void wpas_dbus_signal_peer_device_lost(struct wpa_supplicant *wpa_s,
                                           const u8 *dev_addr);
+void wpas_dbus_signal_peer_groups_changed(struct wpa_supplicant *wpa_s,
+                                         const u8 *dev_addr);
 void wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s,
                                        const char *role);
 void wpas_dbus_signal_p2p_provision_discovery(struct wpa_supplicant *wpa_s,
@@ -196,10 +201,6 @@ int wpas_dbus_unregister_persistent_group(struct wpa_supplicant *wpa_s,
                                          int nid);
 void wpas_dbus_signal_p2p_invitation_result(struct wpa_supplicant *wpa_s,
                                            int status, const u8 *bssid);
-void wpas_dbus_register_p2p_groupmember(struct wpa_supplicant *wpa_s,
-                                       const u8 *p2p_if_addr);
-void wpas_dbus_unregister_p2p_groupmember(struct wpa_supplicant *wpa_s,
-                                         const u8 *p2p_if_addr);
 void wpas_dbus_signal_p2p_peer_disconnected(struct wpa_supplicant *wpa_s,
                                            const u8 *member);
 void wpas_dbus_signal_p2p_sd_request(struct wpa_supplicant *wpa_s,
@@ -215,6 +216,8 @@ void wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s,
                                     struct wps_event_fail *fail);
 void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s,
                                    int depth, const char *subject,
+                                   const char *altsubject[],
+                                   int num_altsubject,
                                    const char *cert_hash,
                                    const struct wpabuf *cert);
 void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s,
@@ -226,6 +229,10 @@ void wpas_dbus_signal_sta_authorized(struct wpa_supplicant *wpa_s,
                                     const u8 *sta);
 void wpas_dbus_signal_sta_deauthorized(struct wpa_supplicant *wpa_s,
                                       const u8 *sta);
+void wpas_dbus_signal_p2p_group_formation_failure (struct wpa_supplicant *wpa_s);
+void wpas_dbus_signal_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+                                           const u8 *sa,const u8 *dev_add,const u8 *bssid,
+                                           int id, int op_freq);
 
 #else /* CONFIG_CTRL_IFACE_DBUS_NEW */
 
@@ -355,6 +362,12 @@ static inline int wpas_dbus_unregister_peer(struct wpa_supplicant *wpa_s,
 }
 
 static inline void
+wpas_dbus_signal_peer_groups_changed(struct wpa_supplicant *wpa_s,
+                                    const u8 *dev_addr)
+{
+}
+
+static inline void
 wpas_dbus_signal_p2p_group_removed(struct wpa_supplicant *wpa_s,
                                   const char *role)
 {
@@ -478,6 +491,8 @@ wpas_dbus_signal_p2p_wps_failed(struct wpa_supplicant *wpa_s,
 static inline void wpas_dbus_signal_certification(struct wpa_supplicant *wpa_s,
                                                  int depth,
                                                  const char *subject,
+                                                 const char *altsubject[],
+                                                 int num_altsubject,
                                                  const char *cert_hash,
                                                  const struct wpabuf *cert)
 {
old mode 100644 (file)
new mode 100755 (executable)
index 478d02f..e1d2cd4
@@ -2,7 +2,7 @@
  * WPA Supplicant / dbus-based control interface
  * Copyright (c) 2006, Dan Williams <dcbw@redhat.com> and Red Hat, Inc.
  * Copyright (c) 2009-2010, Witold Sowa <witold.sowa@gmail.com>
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 #include "dbus_new_handlers.h"
 #include "dbus_dict_helpers.h"
 #include "dbus_common_i.h"
+#include "drivers/driver.h"
 
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-extern int wpa_debug_timestamp;
-
-static const char *debug_strings[] = {
+static const char * const debug_strings[] = {
        "excessive", "msgdump", "debug", "info", "warning", "error", NULL
 };
 
 
 /**
- * wpas_dbus_error_unknown_error - Return a new InvalidArgs error message
+ * wpas_dbus_error_unknown_error - Return a new UnknownError error message
  * @message: Pointer to incoming dbus message this error refers to
  * @arg: Optional string appended to error message
  * Returns: a dbus error message
@@ -48,20 +45,6 @@ static const char *debug_strings[] = {
 DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message,
                                            const char *arg)
 {
-       /*
-        * This function can be called as a result of a failure
-        * within internal getter calls, which will call this function
-        * with a NULL message parameter.  However, dbus_message_new_error
-        * looks very unkindly (i.e, abort()) on a NULL message, so
-        * in this case, we should not call it.
-        */
-       if (message == NULL) {
-               wpa_printf(MSG_INFO, "dbus: wpas_dbus_error_unknown_error "
-                          "called with NULL message (arg=%s)",
-                          arg ? arg : "N/A");
-               return NULL;
-       }
-
        return dbus_message_new_error(message, WPAS_DBUS_ERROR_UNKNOWN_ERROR,
                                      arg);
 }
@@ -76,9 +59,9 @@ DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message,
  */
 static DBusMessage * wpas_dbus_error_iface_unknown(DBusMessage *message)
 {
-       return dbus_message_new_error(message, WPAS_DBUS_ERROR_IFACE_UNKNOWN,
-                                     "wpa_supplicant knows nothing about "
-                                     "this interface.");
+       return dbus_message_new_error(
+               message, WPAS_DBUS_ERROR_IFACE_UNKNOWN,
+               "wpa_supplicant knows nothing about this interface.");
 }
 
 
@@ -91,9 +74,9 @@ static DBusMessage * wpas_dbus_error_iface_unknown(DBusMessage *message)
  */
 static DBusMessage * wpas_dbus_error_network_unknown(DBusMessage *message)
 {
-       return dbus_message_new_error(message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN,
-                                     "There is no such a network in this "
-                                     "interface.");
+       return dbus_message_new_error(
+               message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN,
+               "There is no such a network in this interface.");
 }
 
 
@@ -109,9 +92,9 @@ DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message,
 {
        DBusMessage *reply;
 
-       reply = dbus_message_new_error(message, WPAS_DBUS_ERROR_INVALID_ARGS,
-                                      "Did not receive correct message "
-                                      "arguments.");
+       reply = dbus_message_new_error(
+               message, WPAS_DBUS_ERROR_INVALID_ARGS,
+               "Did not receive correct message arguments.");
        if (arg != NULL)
                dbus_message_append_args(reply, DBUS_TYPE_STRING, &arg,
                                         DBUS_TYPE_INVALID);
@@ -120,7 +103,31 @@ DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message,
 }
 
 
-static const char *dont_quote[] = {
+/**
+ * wpas_dbus_error_scan_error - Return a new ScanError error message
+ * @message: Pointer to incoming dbus message this error refers to
+ * @error: Optional string to be used as the error message
+ * Returns: a dbus error message
+ *
+ * Convenience function to create and return a scan error
+ */
+static DBusMessage * wpas_dbus_error_scan_error(DBusMessage *message,
+                                               const char *error)
+{
+       return dbus_message_new_error(message,
+                                     WPAS_DBUS_ERROR_IFACE_SCAN_ERROR,
+                                     error);
+}
+
+
+DBusMessage * wpas_dbus_error_no_memory(DBusMessage *message)
+{
+       wpa_printf(MSG_DEBUG, "dbus: Failed to allocate memory");
+       return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY, NULL);
+}
+
+
+static const char * const dont_quote[] = {
        "key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap",
        "opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path",
        "bssid", "scan_freq", "freq_list", NULL
@@ -129,6 +136,7 @@ static const char *dont_quote[] = {
 static dbus_bool_t should_quote_opt(const char *key)
 {
        int i = 0;
+
        while (dont_quote[i] != NULL) {
                if (os_strcmp(key, dont_quote[i]) == 0)
                        return FALSE;
@@ -215,7 +223,7 @@ dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s,
 
                                ret = os_snprintf(value, size, "\"%s\"",
                                                  entry.str_value);
-                               if (ret < 0 || (size_t) ret != (size - 1))
+                               if (os_snprintf_error(size, ret))
                                        goto error;
                        } else {
                                value = os_strdup(entry.str_value);
@@ -229,7 +237,7 @@ dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s,
 
                        ret = os_snprintf(value, size, "%u",
                                          entry.uint32_value);
-                       if (ret <= 0)
+                       if (os_snprintf_error(size, ret))
                                goto error;
                } else if (entry.type == DBUS_TYPE_INT32) {
                        value = os_zalloc(size);
@@ -238,7 +246,7 @@ dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s,
 
                        ret = os_snprintf(value, size, "%d",
                                          entry.int32_value);
-                       if (ret <= 0)
+                       if (os_snprintf_error(size, ret))
                                goto error;
                } else
                        goto error;
@@ -246,6 +254,19 @@ dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s,
                if (wpa_config_set(ssid, entry.key, value, 0) < 0)
                        goto error;
 
+               if (os_strcmp(entry.key, "bssid") != 0 &&
+                   os_strcmp(entry.key, "priority") != 0)
+                       wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
+
+               if (wpa_s->current_ssid == ssid ||
+                   wpa_s->current_ssid == NULL) {
+                       /*
+                        * Invalidate the EAP session cache if anything in the
+                        * current or previously used configuration changes.
+                        */
+                       eapol_sm_invalidate_cached_session(wpa_s->eapol);
+               }
+
                if ((os_strcmp(entry.key, "psk") == 0 &&
                     value[0] == '"' && ssid->ssid_len) ||
                    (os_strcmp(entry.key, "ssid") == 0 && ssid->passphrase))
@@ -254,6 +275,7 @@ dbus_bool_t set_network_properties(struct wpa_supplicant *wpa_s,
                        wpa_config_update_prio_list(wpa_s->conf);
 
                os_free(value);
+               value = NULL;
                wpa_dbus_dict_entry_clear(&entry);
        }
 
@@ -287,27 +309,21 @@ dbus_bool_t wpas_dbus_simple_property_getter(DBusMessageIter *iter,
 
        if (!dbus_type_is_basic(type)) {
                dbus_set_error(error, DBUS_ERROR_FAILED,
-                              "%s: given type is not basic", __func__);
+                              "%s: given type is not basic", __func__);
                return FALSE;
        }
 
        if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
-                                             wpa_dbus_type_as_string(type),
-                                             &variant_iter))
-               goto error;
-
-       if (!dbus_message_iter_append_basic(&variant_iter, type, val))
-               goto error;
-
-       if (!dbus_message_iter_close_container(iter, &variant_iter))
-               goto error;
+                                             wpa_dbus_type_as_string(type),
+                                             &variant_iter) ||
+           !dbus_message_iter_append_basic(&variant_iter, type, val) ||
+           !dbus_message_iter_close_container(iter, &variant_iter)) {
+               dbus_set_error(error, DBUS_ERROR_FAILED,
+                              "%s: error constructing reply", __func__);
+               return FALSE;
+       }
 
        return TRUE;
-
-error:
-       dbus_set_error(error, DBUS_ERROR_FAILED,
-                      "%s: error constructing reply", __func__);
-       return FALSE;
 }
 
 
@@ -370,7 +386,7 @@ dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter,
 
        if (!dbus_type_is_basic(type)) {
                dbus_set_error(error, DBUS_ERROR_FAILED,
-                              "%s: given type is not basic", __func__);
+                              "%s: given type is not basic", __func__);
                return FALSE;
        }
 
@@ -378,20 +394,15 @@ dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter,
        type_str[1] = sub_type_str[0];
 
        if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
-                                             type_str, &variant_iter)) {
-               dbus_set_error(error, DBUS_ERROR_FAILED,
-                              "%s: failed to construct message 1", __func__);
-               return FALSE;
-       }
-
-       if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+                                             type_str, &variant_iter) ||
+           !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
                                              sub_type_str, &array_iter)) {
                dbus_set_error(error, DBUS_ERROR_FAILED,
-                              "%s: failed to construct message 2", __func__);
+                              "%s: failed to construct message", __func__);
                return FALSE;
        }
 
-       switch(type) {
+       switch (type) {
        case DBUS_TYPE_BYTE:
        case DBUS_TYPE_BOOLEAN:
                element_size = 1;
@@ -417,24 +428,24 @@ dbus_bool_t wpas_dbus_simple_array_property_getter(DBusMessageIter *iter,
                break;
        default:
                dbus_set_error(error, DBUS_ERROR_FAILED,
-                              "%s: unknown element type %d", __func__, type);
+                              "%s: unknown element type %d", __func__, type);
                return FALSE;
        }
 
        for (i = 0; i < array_len; i++) {
-               dbus_message_iter_append_basic(&array_iter, type,
-                                              array + i * element_size);
-       }
-
-       if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) {
-               dbus_set_error(error, DBUS_ERROR_FAILED,
-                              "%s: failed to construct message 3", __func__);
-               return FALSE;
+               if (!dbus_message_iter_append_basic(&array_iter, type,
+                                                   array + i * element_size)) {
+                       dbus_set_error(error, DBUS_ERROR_FAILED,
+                                      "%s: failed to construct message 2.5",
+                                      __func__);
+                       return FALSE;
+               }
        }
 
-       if (!dbus_message_iter_close_container(iter, &variant_iter)) {
+       if (!dbus_message_iter_close_container(&variant_iter, &array_iter) ||
+           !dbus_message_iter_close_container(iter, &variant_iter)) {
                dbus_set_error(error, DBUS_ERROR_FAILED,
-                              "%s: failed to construct message 4", __func__);
+                              "%s: failed to construct message 3", __func__);
                return FALSE;
        }
 
@@ -477,34 +488,25 @@ dbus_bool_t wpas_dbus_simple_array_array_property_getter(DBusMessageIter *iter,
        inner_type_str[1] = sub_type_str[0];
 
        if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
-                                             type_str, &variant_iter)) {
-               dbus_set_error(error, DBUS_ERROR_FAILED,
-                              "%s: failed to construct message 1", __func__);
-               return FALSE;
-       }
-       if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+                                             type_str, &variant_iter) ||
+           !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
                                              inner_type_str, &array_iter)) {
                dbus_set_error(error, DBUS_ERROR_FAILED,
-                              "%s: failed to construct message 2", __func__);
+                              "%s: failed to construct message", __func__);
                return FALSE;
        }
 
-       for (i = 0; i < array_len; i++) {
+       for (i = 0; i < array_len && array[i]; i++) {
                wpa_dbus_dict_bin_array_add_element(&array_iter,
                                                    wpabuf_head(array[i]),
                                                    wpabuf_len(array[i]));
 
        }
 
-       if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) {
-               dbus_set_error(error, DBUS_ERROR_FAILED,
-                              "%s: failed to close message 2", __func__);
-               return FALSE;
-       }
-
-       if (!dbus_message_iter_close_container(iter, &variant_iter)) {
+       if (!dbus_message_iter_close_container(&variant_iter, &array_iter) ||
+           !dbus_message_iter_close_container(iter, &variant_iter)) {
                dbus_set_error(error, DBUS_ERROR_FAILED,
-                              "%s: failed to close message 1", __func__);
+                              "%s: failed to close message", __func__);
                return FALSE;
        }
 
@@ -542,30 +544,34 @@ DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message,
        while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
                if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
                        goto error;
-               if (!os_strcmp(entry.key, "Driver") &&
-                   (entry.type == DBUS_TYPE_STRING)) {
+               if (os_strcmp(entry.key, "Driver") == 0 &&
+                   entry.type == DBUS_TYPE_STRING) {
+                       os_free(driver);
                        driver = os_strdup(entry.str_value);
                        wpa_dbus_dict_entry_clear(&entry);
                        if (driver == NULL)
-                               goto error;
-               } else if (!os_strcmp(entry.key, "Ifname") &&
-                          (entry.type == DBUS_TYPE_STRING)) {
+                               goto oom;
+               } else if (os_strcmp(entry.key, "Ifname") == 0 &&
+                          entry.type == DBUS_TYPE_STRING) {
+                       os_free(ifname);
                        ifname = os_strdup(entry.str_value);
                        wpa_dbus_dict_entry_clear(&entry);
                        if (ifname == NULL)
-                               goto error;
-               } else if (!os_strcmp(entry.key, "ConfigFile") &&
-                          (entry.type == DBUS_TYPE_STRING)) {
+                               goto oom;
+               } else if (os_strcmp(entry.key, "ConfigFile") == 0 &&
+                          entry.type == DBUS_TYPE_STRING) {
+                       os_free(confname);
                        confname = os_strdup(entry.str_value);
                        wpa_dbus_dict_entry_clear(&entry);
                        if (confname == NULL)
-                               goto error;
-               } else if (!os_strcmp(entry.key, "BridgeIfname") &&
-                          (entry.type == DBUS_TYPE_STRING)) {
+                               goto oom;
+               } else if (os_strcmp(entry.key, "BridgeIfname") == 0 &&
+                          entry.type == DBUS_TYPE_STRING) {
+                       os_free(bridge_ifname);
                        bridge_ifname = os_strdup(entry.str_value);
                        wpa_dbus_dict_entry_clear(&entry);
                        if (bridge_ifname == NULL)
-                               goto error;
+                               goto oom;
                } else {
                        wpa_dbus_dict_entry_clear(&entry);
                        goto error;
@@ -575,33 +581,40 @@ DBusMessage * wpas_dbus_handler_create_interface(DBusMessage *message,
        if (ifname == NULL)
                goto error; /* Required Ifname argument missing */
 
+#if defined TIZEN_WLAN_BOARD_SPRD
+       if (confname == NULL)
+               confname = os_strdup("/etc/wpa_supplicant/wpa_supplicant.conf");
+#endif /* TIZEN_WLAN_BOARD_SPRD */
+
        /*
         * Try to get the wpa_supplicant record for this iface, return
         * an error if we already control it.
         */
        if (wpa_supplicant_get_iface(global, ifname) != NULL) {
-               reply = dbus_message_new_error(message,
-                                              WPAS_DBUS_ERROR_IFACE_EXISTS,
-                                              "wpa_supplicant already "
-                                              "controls this interface.");
+               reply = dbus_message_new_error(
+                       message, WPAS_DBUS_ERROR_IFACE_EXISTS,
+                       "wpa_supplicant already controls this interface.");
        } else {
                struct wpa_supplicant *wpa_s;
                struct wpa_interface iface;
+
                os_memset(&iface, 0, sizeof(iface));
                iface.driver = driver;
                iface.ifname = ifname;
                iface.confname = confname;
                iface.bridge_ifname = bridge_ifname;
                /* Otherwise, have wpa_supplicant attach to it. */
-               if ((wpa_s = wpa_supplicant_add_iface(global, &iface))) {
+               wpa_s = wpa_supplicant_add_iface(global, &iface, NULL);
+               if (wpa_s) {
                        const char *path = wpa_s->dbus_new_path;
+
                        reply = dbus_message_new_method_return(message);
                        dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH,
-                                                &path, DBUS_TYPE_INVALID);
+                                                &path, DBUS_TYPE_INVALID);
                } else {
                        reply = wpas_dbus_error_unknown_error(
-                               message, "wpa_supplicant couldn't grab this "
-                               "interface.");
+                               message,
+                               "wpa_supplicant couldn't grab this interface.");
                }
        }
 
@@ -615,6 +628,9 @@ out:
 error:
        reply = wpas_dbus_error_invalid_args(message, NULL);
        goto out;
+oom:
+       reply = wpas_dbus_error_no_memory(message);
+       goto out;
 }
 
 
@@ -644,8 +660,8 @@ DBusMessage * wpas_dbus_handler_remove_interface(DBusMessage *message,
                reply = wpas_dbus_error_iface_unknown(message);
        else if (wpa_supplicant_remove_iface(global, wpa_s, 0)) {
                reply = wpas_dbus_error_unknown_error(
-                       message, "wpa_supplicant couldn't remove this "
-                       "interface.");
+                       message,
+                       "wpa_supplicant couldn't remove this interface.");
        }
 
        return reply;
@@ -679,13 +695,11 @@ DBusMessage * wpas_dbus_handler_get_interface(DBusMessage *message,
        path = wpa_s->dbus_new_path;
        reply = dbus_message_new_method_return(message);
        if (reply == NULL)
-               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                             NULL);
+               return wpas_dbus_error_no_memory(message);
        if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
                                      DBUS_TYPE_INVALID)) {
                dbus_message_unref(reply);
-               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                             NULL);
+               return wpas_dbus_error_no_memory(message);
        }
 
        return reply;
@@ -728,8 +742,8 @@ dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter,
  * Getter for "DebugTimestamp" property.
  */
 dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter,
-                                             DBusError *error,
-                                             void *user_data)
+                                            DBusError *error,
+                                            void *user_data)
 {
        return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
                                                &wpa_debug_timestamp, error);
@@ -784,8 +798,8 @@ dbus_bool_t wpas_dbus_setter_debug_level(DBusMessageIter *iter,
        if (val < 0 ||
            wpa_supplicant_set_debug_params(global, val, wpa_debug_timestamp,
                                            wpa_debug_show_keys)) {
-               dbus_set_error_const(error, DBUS_ERROR_FAILED, "wrong debug "
-                                    "level value");
+               dbus_set_error_const(error, DBUS_ERROR_FAILED,
+                                    "wrong debug level value");
                return FALSE;
        }
 
@@ -935,8 +949,8 @@ dbus_bool_t wpas_dbus_getter_eap_methods(DBusMessageIter *iter,
  * and P2P that are determined at compile time.
  */
 dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter,
-                                                DBusError *error,
-                                                void *user_data)
+                                                DBusError *error,
+                                                void *user_data)
 {
        const char *capabilities[5] = { NULL, NULL, NULL, NULL, NULL };
        size_t num_items = 0;
@@ -965,8 +979,8 @@ static int wpas_dbus_get_scan_type(DBusMessage *message, DBusMessageIter *var,
                                   char **type, DBusMessage **reply)
 {
        if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_STRING) {
-               wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-                          "Type must be a string");
+               wpa_printf(MSG_DEBUG, "%s[dbus]: Type must be a string",
+                          __func__);
                *reply = wpas_dbus_error_invalid_args(
                        message, "Wrong Type value type. String required");
                return -1;
@@ -988,36 +1002,36 @@ static int wpas_dbus_get_scan_ssids(DBusMessage *message, DBusMessageIter *var,
        int len;
 
        if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) {
-               wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ssids "
-                          "must be an array of arrays of bytes");
+               wpa_printf(MSG_DEBUG,
+                          "%s[dbus]: ssids must be an array of arrays of bytes",
+                          __func__);
                *reply = wpas_dbus_error_invalid_args(
-                       message, "Wrong SSIDs value type. Array of arrays of "
-                       "bytes required");
+                       message,
+                       "Wrong SSIDs value type. Array of arrays of bytes required");
                return -1;
        }
 
        dbus_message_iter_recurse(var, &array_iter);
 
        if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_ARRAY ||
-           dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE)
-       {
-               wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ssids "
-                          "must be an array of arrays of bytes");
+           dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE) {
+               wpa_printf(MSG_DEBUG,
+                          "%s[dbus]: ssids must be an array of arrays of bytes",
+                          __func__);
                *reply = wpas_dbus_error_invalid_args(
-                       message, "Wrong SSIDs value type. Array of arrays of "
-                       "bytes required");
+                       message,
+                       "Wrong SSIDs value type. Array of arrays of bytes required");
                return -1;
        }
 
-       while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY)
-       {
+       while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY) {
                if (ssids_num >= WPAS_MAX_SCAN_SSIDS) {
-                       wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-                                  "Too many ssids specified on scan dbus "
-                                  "call");
+                       wpa_printf(MSG_DEBUG,
+                                  "%s[dbus]: Too many ssids specified on scan dbus call",
+                                  __func__);
                        *reply = wpas_dbus_error_invalid_args(
-                               message, "Too many ssids specified. Specify "
-                               "at most four");
+                               message,
+                               "Too many ssids specified. Specify at most four");
                        return -1;
                }
 
@@ -1027,9 +1041,8 @@ static int wpas_dbus_get_scan_ssids(DBusMessage *message, DBusMessageIter *var,
 
                if (len > MAX_SSID_LEN) {
                        wpa_printf(MSG_DEBUG,
-                                  "wpas_dbus_handler_scan[dbus]: "
-                                  "SSID too long (len=%d max_len=%d)",
-                                  len, MAX_SSID_LEN);
+                                  "%s[dbus]: SSID too long (len=%d max_len=%d)",
+                                  __func__, len, MAX_SSID_LEN);
                        *reply = wpas_dbus_error_invalid_args(
                                message, "Invalid SSID: too long");
                        return -1;
@@ -1038,12 +1051,7 @@ static int wpas_dbus_get_scan_ssids(DBusMessage *message, DBusMessageIter *var,
                if (len != 0) {
                        ssid = os_malloc(len);
                        if (ssid == NULL) {
-                               wpa_printf(MSG_DEBUG,
-                                          "wpas_dbus_handler_scan[dbus]: "
-                                          "out of memory. Cannot allocate "
-                                          "memory for SSID");
-                               *reply = dbus_message_new_error(
-                                       message, DBUS_ERROR_NO_MEMORY, NULL);
+                               *reply = wpas_dbus_error_no_memory(message);
                                return -1;
                        }
                        os_memcpy(ssid, val, len);
@@ -1075,28 +1083,28 @@ static int wpas_dbus_get_scan_ies(DBusMessage *message, DBusMessageIter *var,
        int len;
 
        if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) {
-               wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ies must "
-                          "be an array of arrays of bytes");
+               wpa_printf(MSG_DEBUG,
+                          "%s[dbus]: ies must be an array of arrays of bytes",
+                          __func__);
                *reply = wpas_dbus_error_invalid_args(
-                       message, "Wrong IEs value type. Array of arrays of "
-                       "bytes required");
+                       message,
+                       "Wrong IEs value type. Array of arrays of bytes required");
                return -1;
        }
 
        dbus_message_iter_recurse(var, &array_iter);
 
        if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_ARRAY ||
-           dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE)
-       {
-               wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: ies must "
-                          "be an array of arrays of bytes");
+           dbus_message_iter_get_element_type(&array_iter) != DBUS_TYPE_BYTE) {
+               wpa_printf(MSG_DEBUG,
+                          "%s[dbus]: ies must be an array of arrays of bytes",
+                          __func__);
                *reply = wpas_dbus_error_invalid_args(
                        message, "Wrong IEs value type. Array required");
                return -1;
        }
 
-       while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY)
-       {
+       while (dbus_message_iter_get_arg_type(&array_iter) == DBUS_TYPE_ARRAY) {
                dbus_message_iter_recurse(&array_iter, &sub_array_iter);
 
                dbus_message_iter_get_fixed_array(&sub_array_iter, &val, &len);
@@ -1107,12 +1115,8 @@ static int wpas_dbus_get_scan_ies(DBusMessage *message, DBusMessageIter *var,
 
                nies = os_realloc(ies, ies_len + len);
                if (nies == NULL) {
-                       wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-                                  "out of memory. Cannot allocate memory for "
-                                  "IE");
                        os_free(ies);
-                       *reply = dbus_message_new_error(
-                               message, DBUS_ERROR_NO_MEMORY, NULL);
+                       *reply = wpas_dbus_error_no_memory(message);
                        return -1;
                }
                ies = nies;
@@ -1138,11 +1142,12 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message,
        int freqs_num = 0;
 
        if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_ARRAY) {
-               wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-                          "Channels must be an array of structs");
+               wpa_printf(MSG_DEBUG,
+                          "%s[dbus]: Channels must be an array of structs",
+                          __func__);
                *reply = wpas_dbus_error_invalid_args(
-                       message, "Wrong Channels value type. Array of structs "
-                       "required");
+                       message,
+                       "Wrong Channels value type. Array of structs required");
                return -1;
        }
 
@@ -1150,11 +1155,11 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message,
 
        if (dbus_message_iter_get_arg_type(&array_iter) != DBUS_TYPE_STRUCT) {
                wpa_printf(MSG_DEBUG,
-                          "wpas_dbus_handler_scan[dbus]: Channels must be an "
-                          "array of structs");
+                          "%s[dbus]: Channels must be an array of structs",
+                          __func__);
                *reply = wpas_dbus_error_invalid_args(
-                       message, "Wrong Channels value type. Array of structs "
-                       "required");
+                       message,
+                       "Wrong Channels value type. Array of structs required");
                return -1;
        }
 
@@ -1166,14 +1171,14 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message,
 
                if (dbus_message_iter_get_arg_type(&sub_array_iter) !=
                    DBUS_TYPE_UINT32) {
-                       wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-                                  "Channel must by specified by struct of "
-                                  "two UINT32s %c",
+                       wpa_printf(MSG_DEBUG,
+                                  "%s[dbus]: Channel must by specified by struct of two UINT32s %c",
+                                  __func__,
                                   dbus_message_iter_get_arg_type(
                                           &sub_array_iter));
                        *reply = wpas_dbus_error_invalid_args(
-                               message, "Wrong Channel struct. Two UINT32s "
-                               "required");
+                               message,
+                               "Wrong Channel struct. Two UINT32s required");
                        os_free(freqs);
                        return -1;
                }
@@ -1182,9 +1187,9 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message,
                if (!dbus_message_iter_next(&sub_array_iter) ||
                    dbus_message_iter_get_arg_type(&sub_array_iter) !=
                    DBUS_TYPE_UINT32) {
-                       wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-                                  "Channel must by specified by struct of "
-                                  "two UINT32s");
+                       wpa_printf(MSG_DEBUG,
+                                  "%s[dbus]: Channel must by specified by struct of two UINT32s",
+                                  __func__);
                        *reply = wpas_dbus_error_invalid_args(
                                message,
                                "Wrong Channel struct. Two UINT32s required");
@@ -1204,11 +1209,7 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message,
                        freqs = nfreqs;
                }
                if (freqs == NULL) {
-                       wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-                                  "out of memory. can't allocate memory for "
-                                  "freqs");
-                       *reply = dbus_message_new_error(
-                               message, DBUS_ERROR_NO_MEMORY, NULL);
+                       *reply = wpas_dbus_error_no_memory(message);
                        return -1;
                }
 
@@ -1223,10 +1224,7 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message,
                os_free(freqs);
        freqs = nfreqs;
        if (freqs == NULL) {
-               wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-                          "out of memory. Can't allocate memory for freqs");
-               *reply = dbus_message_new_error(
-                       message, DBUS_ERROR_NO_MEMORY, NULL);
+               *reply = wpas_dbus_error_no_memory(message);
                return -1;
        }
        freqs[freqs_num] = 0;
@@ -1236,6 +1234,23 @@ static int wpas_dbus_get_scan_channels(DBusMessage *message,
 }
 
 
+static int wpas_dbus_get_scan_allow_roam(DBusMessage *message,
+                                        DBusMessageIter *var,
+                                        dbus_bool_t *allow,
+                                        DBusMessage **reply)
+{
+       if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_BOOLEAN) {
+               wpa_printf(MSG_DEBUG, "%s[dbus]: Type must be a boolean",
+                          __func__);
+               *reply = wpas_dbus_error_invalid_args(
+                       message, "Wrong Type value type. Boolean required");
+               return -1;
+       }
+       dbus_message_iter_get_basic(var, allow);
+       return 0;
+}
+
+
 /**
  * wpas_dbus_handler_scan - Request a wireless scan on an interface
  * @message: Pointer to incoming dbus message
@@ -1254,6 +1269,7 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
        char *key = NULL, *type = NULL;
        struct wpa_driver_scan_params params;
        size_t i;
+       dbus_bool_t allow_roam = 1;
 
        os_memset(&params, 0, sizeof(params));
 
@@ -1262,7 +1278,7 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
        dbus_message_iter_recurse(&iter, &dict_iter);
 
        while (dbus_message_iter_get_arg_type(&dict_iter) ==
-                       DBUS_TYPE_DICT_ENTRY) {
+              DBUS_TYPE_DICT_ENTRY) {
                dbus_message_iter_recurse(&dict_iter, &entry_iter);
                dbus_message_iter_get_basic(&entry_iter, &key);
                dbus_message_iter_next(&entry_iter);
@@ -1284,9 +1300,15 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
                        if (wpas_dbus_get_scan_channels(message, &variant_iter,
                                                        &params, &reply) < 0)
                                goto out;
+               } else if (os_strcmp(key, "AllowRoam") == 0) {
+                       if (wpas_dbus_get_scan_allow_roam(message,
+                                                         &variant_iter,
+                                                         &allow_roam,
+                                                         &reply) < 0)
+                               goto out;
                } else {
-                       wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-                                  "Unknown argument %s", key);
+                       wpa_printf(MSG_DEBUG, "%s[dbus]: Unknown argument %s",
+                                  __func__, key);
                        reply = wpas_dbus_error_invalid_args(message, key);
                        goto out;
                }
@@ -1295,27 +1317,31 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
        }
 
        if (!type) {
-               wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-                          "Scan type not specified");
+               wpa_printf(MSG_DEBUG, "%s[dbus]: Scan type not specified",
+                          __func__);
                reply = wpas_dbus_error_invalid_args(message, key);
                goto out;
        }
 
-       if (!os_strcmp(type, "passive")) {
+       if (os_strcmp(type, "passive") == 0) {
                if (params.num_ssids || params.extra_ies_len) {
-                       wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-                                  "SSIDs or IEs specified for passive scan.");
+                       wpa_printf(MSG_DEBUG,
+                                  "%s[dbus]: SSIDs or IEs specified for passive scan.",
+                                  __func__);
                        reply = wpas_dbus_error_invalid_args(
-                               message, "You can specify only Channels in "
-                               "passive scan");
+                               message,
+                               "You can specify only Channels in passive scan");
                        goto out;
                } else if (params.freqs && params.freqs[0]) {
-                       wpa_supplicant_trigger_scan(wpa_s, &params);
+                       if (wpa_supplicant_trigger_scan(wpa_s, &params)) {
+                               reply = wpas_dbus_error_scan_error(
+                                       message, "Scan request rejected");
+                       }
                } else {
                        wpa_s->scan_req = MANUAL_SCAN_REQ;
                        wpa_supplicant_req_scan(wpa_s, 0, 0);
                }
-       } else if (!os_strcmp(type, "active")) {
+       } else if (os_strcmp(type, "active") == 0) {
                if (!params.num_ssids) {
                        /* Add wildcard ssid */
                        params.num_ssids++;
@@ -1323,15 +1349,21 @@ DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
 #ifdef CONFIG_AUTOSCAN
                autoscan_deinit(wpa_s);
 #endif /* CONFIG_AUTOSCAN */
-               wpa_supplicant_trigger_scan(wpa_s, &params);
+               if (wpa_supplicant_trigger_scan(wpa_s, &params)) {
+                       reply = wpas_dbus_error_scan_error(
+                               message, "Scan request rejected");
+               }
        } else {
-               wpa_printf(MSG_DEBUG, "wpas_dbus_handler_scan[dbus]: "
-                          "Unknown scan type: %s", type);
+               wpa_printf(MSG_DEBUG, "%s[dbus]: Unknown scan type: %s",
+                          __func__, type);
                reply = wpas_dbus_error_invalid_args(message,
                                                     "Wrong scan type");
                goto out;
        }
 
+       if (!allow_roam)
+               wpa_s->scan_res_handler = scan_only_handler;
+
 out:
        for (i = 0; i < WPAS_MAX_SCAN_SSIDS; i++)
                os_free((u8 *) params.ssids[i].ssid);
@@ -1341,6 +1373,72 @@ out:
 }
 
 
+/**
+ * wpas_dbus_handler_signal_poll - Request immediate signal properties
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "SignalPoll" method call of a network device. Requests
+ * that wpa_supplicant read signal properties like RSSI, noise, and link
+ * speed and return them.
+ */
+DBusMessage * wpas_dbus_handler_signal_poll(DBusMessage *message,
+                                           struct wpa_supplicant *wpa_s)
+{
+       struct wpa_signal_info si;
+       DBusMessage *reply = NULL;
+       DBusMessageIter iter, iter_dict, variant_iter;
+       int ret;
+
+       ret = wpa_drv_signal_poll(wpa_s, &si);
+       if (ret) {
+               return dbus_message_new_error(message, DBUS_ERROR_FAILED,
+                                             "Failed to read signal");
+       }
+
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL)
+               goto nomem;
+
+       dbus_message_iter_init_append(reply, &iter);
+
+       if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT,
+                                             "a{sv}", &variant_iter) ||
+           !wpa_dbus_dict_open_write(&variant_iter, &iter_dict) ||
+           !wpa_dbus_dict_append_int32(&iter_dict, "rssi",
+                                       si.current_signal) ||
+           !wpa_dbus_dict_append_int32(&iter_dict, "linkspeed",
+                                       si.current_txrate / 1000) ||
+           !wpa_dbus_dict_append_int32(&iter_dict, "noise",
+                                       si.current_noise) ||
+           !wpa_dbus_dict_append_uint32(&iter_dict, "frequency",
+                                        si.frequency) ||
+           (si.chanwidth != CHAN_WIDTH_UNKNOWN &&
+            !wpa_dbus_dict_append_string(
+                    &iter_dict, "width",
+                    channel_width_to_string(si.chanwidth))) ||
+           (si.center_frq1 > 0 && si.center_frq2 > 0 &&
+            (!wpa_dbus_dict_append_int32(&iter_dict, "center-frq1",
+                                         si.center_frq1) ||
+             !wpa_dbus_dict_append_int32(&iter_dict, "center-frq2",
+                                         si.center_frq2))) ||
+           (si.avg_signal &&
+            !wpa_dbus_dict_append_int32(&iter_dict, "avg-rssi",
+                                        si.avg_signal)) ||
+           !wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
+           !dbus_message_iter_close_container(&iter, &variant_iter))
+               goto nomem;
+
+       return reply;
+
+nomem:
+       if (reply)
+               dbus_message_unref(reply);
+       return wpas_dbus_error_no_memory(message);
+}
+
+
 /*
  * wpas_dbus_handler_disconnect - Terminate the current connection
  * @message: Pointer to incoming dbus message
@@ -1387,12 +1485,11 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message,
 
        ssid = wpa_config_add_network(wpa_s->conf);
        if (ssid == NULL) {
-               wpa_printf(MSG_ERROR, "wpas_dbus_handler_add_network[dbus]: "
-                          "can't add new interface.");
+               wpa_printf(MSG_ERROR, "%s[dbus]: can't add new interface.",
+                          __func__);
                reply = wpas_dbus_error_unknown_error(
                        message,
-                       "wpa_supplicant could not add "
-                       "a network on this interface.");
+                       "wpa_supplicant could not add a network on this interface.");
                goto err;
        }
        wpas_notify_network_added(wpa_s, ssid);
@@ -1401,9 +1498,9 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message,
 
        dbus_error_init(&error);
        if (!set_network_properties(wpa_s, ssid, &iter, &error)) {
-               wpa_printf(MSG_DEBUG, "wpas_dbus_handler_add_network[dbus]:"
-                          "control interface couldn't set network "
-                          "properties");
+               wpa_printf(MSG_DEBUG,
+                          "%s[dbus]: control interface couldn't set network properties",
+                          __func__);
                reply = wpas_dbus_reply_new_from_error(message, &error,
                                                       DBUS_ERROR_INVALID_ARGS,
                                                       "Failed to add network");
@@ -1418,15 +1515,13 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message,
 
        reply = dbus_message_new_method_return(message);
        if (reply == NULL) {
-               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                              NULL);
+               reply = wpas_dbus_error_no_memory(message);
                goto err;
        }
        if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
                                      DBUS_TYPE_INVALID)) {
                dbus_message_unref(reply);
-               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                              NULL);
+               reply = wpas_dbus_error_no_memory(message);
                goto err;
        }
 
@@ -1442,10 +1537,10 @@ err:
 
 
 /**
- * wpas_dbus_handler_reassociate - Reassociate to current AP
+ * wpas_dbus_handler_reassociate - Reassociate
  * @message: Pointer to incoming dbus message
  * @wpa_s: wpa_supplicant structure for a network interface
- * Returns: NotConnected DBus error message if not connected
+ * Returns: InterfaceDisabled DBus error message if disabled
  * or NULL otherwise.
  *
  * Handler function for "Reassociate" method call of network interface.
@@ -1453,7 +1548,30 @@ err:
 DBusMessage * wpas_dbus_handler_reassociate(DBusMessage *message,
                                            struct wpa_supplicant *wpa_s)
 {
+       if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED) {
+               wpas_request_connection(wpa_s);
+               return NULL;
+       }
+
+       return dbus_message_new_error(message, WPAS_DBUS_ERROR_IFACE_DISABLED,
+                                     "This interface is disabled");
+}
+
+
+/**
+ * wpas_dbus_handler_reattach - Reattach to current AP
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NotConnected DBus error message if not connected
+ * or NULL otherwise.
+ *
+ * Handler function for "Reattach" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_reattach(DBusMessage *message,
+                                        struct wpa_supplicant *wpa_s)
+{
        if (wpa_s->current_ssid != NULL) {
+               wpa_s->reattach = 1;
                wpas_request_connection(wpa_s);
                return NULL;
        }
@@ -1476,7 +1594,7 @@ DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message,
 {
        DBusMessage *reply = NULL;
        const char *op;
-       char *iface = NULL, *net_id = NULL;
+       char *iface, *net_id;
        int id;
        struct wpa_ssid *ssid;
        int was_disabled;
@@ -1486,7 +1604,9 @@ DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message,
 
        /* Extract the network ID and ensure the network */
        /* is actually a child of this interface */
-       iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL);
+       iface = wpas_dbus_new_decompose_object_path(op,
+                                                   WPAS_DBUS_NEW_NETWORKS_PART,
+                                                   &net_id);
        if (iface == NULL || net_id == NULL ||
            os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
                reply = wpas_dbus_error_invalid_args(message, op);
@@ -1510,30 +1630,28 @@ DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message,
 
        wpas_notify_network_removed(wpa_s, ssid);
 
-       if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
-               wpa_printf(MSG_ERROR,
-                          "wpas_dbus_handler_remove_network[dbus]: "
-                          "error occurred when removing network %d", id);
-               reply = wpas_dbus_error_unknown_error(
-                       message, "error removing the specified network on "
-                       "this interface.");
-               goto out;
-       }
-
        if (ssid == wpa_s->current_ssid)
                wpa_supplicant_deauthenticate(wpa_s,
                                              WLAN_REASON_DEAUTH_LEAVING);
        else if (!was_disabled && wpa_s->sched_scanning) {
-               wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to remove "
-                          "network from filters");
+               wpa_printf(MSG_DEBUG,
+                          "Stop ongoing sched_scan to remove network from filters");
                wpa_supplicant_cancel_sched_scan(wpa_s);
                wpa_supplicant_req_scan(wpa_s, 0, 0);
        }
 
+       if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
+               wpa_printf(MSG_ERROR,
+                          "%s[dbus]: error occurred when removing network %d",
+                          __func__, id);
+               reply = wpas_dbus_error_unknown_error(
+                       message,
+                       "error removing the specified network on is interface.");
+               goto out;
+       }
 
 out:
        os_free(iface);
-       os_free(net_id);
        return reply;
 }
 
@@ -1546,9 +1664,8 @@ static void remove_network(void *arg, struct wpa_ssid *ssid)
 
        if (wpa_config_remove_network(wpa_s->conf, ssid->id) < 0) {
                wpa_printf(MSG_ERROR,
-                          "wpas_dbus_handler_remove_all_networks[dbus]: "
-                          "error occurred when removing network %d",
-                          ssid->id);
+                          "%s[dbus]: error occurred when removing network %d",
+                          __func__, ssid->id);
                return;
        }
 
@@ -1591,7 +1708,7 @@ DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message,
 {
        DBusMessage *reply = NULL;
        const char *op;
-       char *iface = NULL, *net_id = NULL;
+       char *iface, *net_id;
        int id;
        struct wpa_ssid *ssid;
 
@@ -1600,7 +1717,9 @@ DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message,
 
        /* Extract the network ID and ensure the network */
        /* is actually a child of this interface */
-       iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL);
+       iface = wpas_dbus_new_decompose_object_path(op,
+                                                   WPAS_DBUS_NEW_NETWORKS_PART,
+                                                   &net_id);
        if (iface == NULL || net_id == NULL ||
            os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
                reply = wpas_dbus_error_invalid_args(message, op);
@@ -1625,7 +1744,6 @@ DBusMessage * wpas_dbus_handler_select_network(DBusMessage *message,
 
 out:
        os_free(iface);
-       os_free(net_id);
        return reply;
 }
 
@@ -1644,20 +1762,22 @@ DBusMessage * wpas_dbus_handler_network_reply(DBusMessage *message,
 #ifdef IEEE8021X_EAPOL
        DBusMessage *reply = NULL;
        const char *op, *field, *value;
-       char *iface = NULL, *net_id = NULL;
+       char *iface, *net_id;
        int id;
        struct wpa_ssid *ssid;
 
        if (!dbus_message_get_args(message, NULL,
-                                  DBUS_TYPE_OBJECT_PATH, &op,
-                                  DBUS_TYPE_STRING, &field,
-                                  DBUS_TYPE_STRING, &value,
-                                  DBUS_TYPE_INVALID))
+                                  DBUS_TYPE_OBJECT_PATH, &op,
+                                  DBUS_TYPE_STRING, &field,
+                                  DBUS_TYPE_STRING, &value,
+                                  DBUS_TYPE_INVALID))
                return wpas_dbus_error_invalid_args(message, NULL);
 
        /* Extract the network ID and ensure the network */
        /* is actually a child of this interface */
-       iface = wpas_dbus_new_decompose_object_path(op, 0, &net_id, NULL);
+       iface = wpas_dbus_new_decompose_object_path(op,
+                                                   WPAS_DBUS_NEW_NETWORKS_PART,
+                                                   &net_id);
        if (iface == NULL || net_id == NULL ||
            os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
                reply = wpas_dbus_error_invalid_args(message, op);
@@ -1687,7 +1807,6 @@ DBusMessage * wpas_dbus_handler_network_reply(DBusMessage *message,
 
 out:
        os_free(iface);
-       os_free(net_id);
        return reply;
 #else /* IEEE8021X_EAPOL */
        wpa_printf(MSG_DEBUG, "CTRL_IFACE: 802.1X not included");
@@ -1733,26 +1852,18 @@ DBusMessage * wpas_dbus_handler_add_blob(DBusMessage *message,
 
        blob = os_zalloc(sizeof(*blob));
        if (!blob) {
-               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                              NULL);
+               reply = wpas_dbus_error_no_memory(message);
                goto err;
        }
 
        blob->data = os_malloc(blob_len);
-       if (!blob->data) {
-               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                              NULL);
+       blob->name = os_strdup(blob_name);
+       if (!blob->data || !blob->name) {
+               reply = wpas_dbus_error_no_memory(message);
                goto err;
        }
        os_memcpy(blob->data, blob_data, blob_len);
-
        blob->len = blob_len;
-       blob->name = os_strdup(blob_name);
-       if (!blob->name) {
-               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                              NULL);
-               goto err;
-       }
 
        wpa_config_set_blob(wpa_s->conf, blob);
        wpas_notify_blob_added(wpa_s, blob->name);
@@ -1797,39 +1908,21 @@ DBusMessage * wpas_dbus_handler_get_blob(DBusMessage *message,
        }
 
        reply = dbus_message_new_method_return(message);
-       if (!reply) {
-               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                              NULL);
-               goto out;
-       }
+       if (!reply)
+               return wpas_dbus_error_no_memory(message);
 
        dbus_message_iter_init_append(reply, &iter);
 
        if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
                                              DBUS_TYPE_BYTE_AS_STRING,
-                                             &array_iter)) {
-               dbus_message_unref(reply);
-               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                              NULL);
-               goto out;
-       }
-
-       if (!dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE,
-                                                 &(blob->data), blob->len)) {
-               dbus_message_unref(reply);
-               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                              NULL);
-               goto out;
-       }
-
-       if (!dbus_message_iter_close_container(&iter, &array_iter)) {
+                                             &array_iter) ||
+           !dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE,
+                                                 &(blob->data), blob->len) ||
+           !dbus_message_iter_close_container(&iter, &array_iter)) {
                dbus_message_unref(reply);
-               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                              NULL);
-               goto out;
+               reply = wpas_dbus_error_no_memory(message);
        }
 
-out:
        return reply;
 }
 
@@ -1911,11 +2004,10 @@ DBusMessage * wpas_dbus_handler_autoscan(DBusMessage *message,
 
        if (arg != NULL && os_strlen(arg) > 0) {
                char *tmp;
+
                tmp = os_strdup(arg);
                if (tmp == NULL) {
-                       reply = dbus_message_new_error(message,
-                                                      DBUS_ERROR_NO_MEMORY,
-                                                      NULL);
+                       reply = wpas_dbus_error_no_memory(message);
                } else {
                        os_free(wpa_s->conf->autoscan);
                        wpa_s->conf->autoscan = tmp;
@@ -1970,73 +2062,284 @@ DBusMessage * wpas_dbus_handler_eap_logon(DBusMessage *message,
 }
 
 
-/**
- * wpas_dbus_getter_capabilities - Return interface capabilities
- * @iter: Pointer to incoming dbus message iter
- * @error: Location to store error on failure
- * @user_data: Function specific data
- * Returns: TRUE on success, FALSE on failure
- *
- * Getter for "Capabilities" property of an interface.
- */
-dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
-                                         DBusError *error, void *user_data)
+#ifdef CONFIG_TDLS
+
+static int get_peer_hwaddr_helper(DBusMessage *message, const char *func_name,
+                                 u8 *peer_address, DBusMessage **error)
 {
-       struct wpa_supplicant *wpa_s = user_data;
-       struct wpa_driver_capa capa;
-       int res;
-       DBusMessageIter iter_dict, iter_dict_entry, iter_dict_val, iter_array,
-               variant_iter;
-       const char *scans[] = { "active", "passive", "ssid" };
+       const char *peer_string;
 
-       if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
-                                             "a{sv}", &variant_iter))
-               goto nomem;
+       *error = NULL;
 
-       if (!wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
-               goto nomem;
+       if (!dbus_message_get_args(message, NULL,
+                                  DBUS_TYPE_STRING, &peer_string,
+                                  DBUS_TYPE_INVALID)) {
+               *error = wpas_dbus_error_invalid_args(message, NULL);
+               return -1;
+       }
 
-       res = wpa_drv_get_capa(wpa_s, &capa);
+       if (hwaddr_aton(peer_string, peer_address)) {
+               wpa_printf(MSG_DEBUG, "%s: invalid address '%s'",
+                          func_name, peer_string);
+               *error = wpas_dbus_error_invalid_args(
+                       message, "Invalid hardware address format");
+               return -1;
+       }
 
-       /***** pairwise cipher */
-       if (res < 0) {
-               const char *args[] = {"ccmp", "tkip", "none"};
-               if (!wpa_dbus_dict_append_string_array(
-                           &iter_dict, "Pairwise", args,
-                           sizeof(args) / sizeof(char*)))
-                       goto nomem;
-       } else {
-               if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Pairwise",
-                                                     &iter_dict_entry,
-                                                     &iter_dict_val,
-                                                     &iter_array))
-                       goto nomem;
+       return 0;
+}
 
-               if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "ccmp"))
-                               goto nomem;
-               }
 
-               if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "gcmp"))
-                               goto nomem;
-               }
+/*
+ * wpas_dbus_handler_tdls_discover - Discover TDLS peer
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "TDLSDiscover" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message,
+                                             struct wpa_supplicant *wpa_s)
+{
+       u8 peer[ETH_ALEN];
+       DBusMessage *error_reply;
+       int ret;
 
-               if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "tkip"))
-                               goto nomem;
-               }
+       if (get_peer_hwaddr_helper(message, __func__, peer, &error_reply) < 0)
+               return error_reply;
 
-               if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "none"))
-                               goto nomem;
-               }
+       wpa_printf(MSG_DEBUG, "DBUS TDLS_DISCOVER " MACSTR, MAC2STR(peer));
 
-               if (!wpa_dbus_dict_end_string_array(&iter_dict,
+       if (wpa_tdls_is_external_setup(wpa_s->wpa))
+               ret = wpa_tdls_send_discovery_request(wpa_s->wpa, peer);
+       else
+               ret = wpa_drv_tdls_oper(wpa_s, TDLS_DISCOVERY_REQ, peer);
+
+       if (ret) {
+               return wpas_dbus_error_unknown_error(
+                       message, "error performing TDLS discovery");
+       }
+
+       return NULL;
+}
+
+
+/*
+ * wpas_dbus_handler_tdls_setup - Setup TDLS session
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "TDLSSetup" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_tdls_setup(DBusMessage *message,
+                                          struct wpa_supplicant *wpa_s)
+{
+       u8 peer[ETH_ALEN];
+       DBusMessage *error_reply;
+       int ret;
+
+       if (get_peer_hwaddr_helper(message, __func__, peer, &error_reply) < 0)
+               return error_reply;
+
+       wpa_printf(MSG_DEBUG, "DBUS TDLS_SETUP " MACSTR, MAC2STR(peer));
+
+       wpa_tdls_remove(wpa_s->wpa, peer);
+       if (wpa_tdls_is_external_setup(wpa_s->wpa))
+               ret = wpa_tdls_start(wpa_s->wpa, peer);
+       else
+               ret = wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, peer);
+
+       if (ret) {
+               return wpas_dbus_error_unknown_error(
+                       message, "error performing TDLS setup");
+       }
+
+       return NULL;
+}
+
+
+/*
+ * wpas_dbus_handler_tdls_status - Return TDLS session status
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: A string representing the state of the link to this TDLS peer
+ *
+ * Handler function for "TDLSStatus" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_tdls_status(DBusMessage *message,
+                                           struct wpa_supplicant *wpa_s)
+{
+       u8 peer[ETH_ALEN];
+       DBusMessage *reply;
+       const char *tdls_status;
+
+       if (get_peer_hwaddr_helper(message, __func__, peer, &reply) < 0)
+               return reply;
+
+       wpa_printf(MSG_DEBUG, "DBUS TDLS_STATUS " MACSTR, MAC2STR(peer));
+
+       tdls_status = wpa_tdls_get_link_status(wpa_s->wpa, peer);
+
+       reply = dbus_message_new_method_return(message);
+       dbus_message_append_args(reply, DBUS_TYPE_STRING,
+                                &tdls_status, DBUS_TYPE_INVALID);
+       return reply;
+}
+
+
+/*
+ * wpas_dbus_handler_tdls_teardown - Teardown TDLS session
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * Returns: NULL indicating success or DBus error message on failure
+ *
+ * Handler function for "TDLSTeardown" method call of network interface.
+ */
+DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message,
+                                             struct wpa_supplicant *wpa_s)
+{
+       u8 peer[ETH_ALEN];
+       DBusMessage *error_reply;
+       int ret;
+
+       if (get_peer_hwaddr_helper(message, __func__, peer, &error_reply) < 0)
+               return error_reply;
+
+       wpa_printf(MSG_DEBUG, "DBUS TDLS_TEARDOWN " MACSTR, MAC2STR(peer));
+
+       if (wpa_tdls_is_external_setup(wpa_s->wpa))
+               ret = wpa_tdls_teardown_link(
+                       wpa_s->wpa, peer,
+                       WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED);
+       else
+               ret = wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN, peer);
+
+       if (ret) {
+               return wpas_dbus_error_unknown_error(
+                       message, "error performing TDLS teardown");
+       }
+
+       return NULL;
+}
+
+#endif /* CONFIG_TDLS */
+
+
+/**
+ * wpas_dbus_handler_set_pkcs11_engine_and_module_path - Set PKCS #11 engine and module path
+ * @message: Pointer to incoming dbus message
+ * @wpa_s: %wpa_supplicant data structure
+ * Returns: A dbus message containing an error on failure or NULL on success
+ *
+ * Sets the PKCS #11 engine and module path.
+ */
+DBusMessage * wpas_dbus_handler_set_pkcs11_engine_and_module_path(
+       DBusMessage *message, struct wpa_supplicant *wpa_s)
+{
+       DBusMessageIter iter;
+       char *value = NULL;
+       char *pkcs11_engine_path = NULL;
+       char *pkcs11_module_path = NULL;
+
+       dbus_message_iter_init(message, &iter);
+       dbus_message_iter_get_basic(&iter, &value);
+       if (value == NULL) {
+               return dbus_message_new_error(
+                       message, DBUS_ERROR_INVALID_ARGS,
+                       "Invalid pkcs11_engine_path argument");
+       }
+       /* Empty path defaults to NULL */
+       if (os_strlen(value))
+               pkcs11_engine_path = value;
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_get_basic(&iter, &value);
+       if (value == NULL) {
+               os_free(pkcs11_engine_path);
+               return dbus_message_new_error(
+                       message, DBUS_ERROR_INVALID_ARGS,
+                       "Invalid pkcs11_module_path argument");
+       }
+       /* Empty path defaults to NULL */
+       if (os_strlen(value))
+               pkcs11_module_path = value;
+
+       if (wpas_set_pkcs11_engine_and_module_path(wpa_s, pkcs11_engine_path,
+                                                  pkcs11_module_path))
+               return dbus_message_new_error(
+                       message, DBUS_ERROR_FAILED,
+                       "Reinit of the EAPOL state machine with the new PKCS #11 engine and module path failed.");
+
+       wpa_dbus_mark_property_changed(
+               wpa_s->global->dbus, wpa_s->dbus_new_path,
+               WPAS_DBUS_NEW_IFACE_INTERFACE, "PKCS11EnginePath");
+       wpa_dbus_mark_property_changed(
+               wpa_s->global->dbus, wpa_s->dbus_new_path,
+               WPAS_DBUS_NEW_IFACE_INTERFACE, "PKCS11ModulePath");
+
+       return NULL;
+}
+
+
+/**
+ * wpas_dbus_getter_capabilities - Return interface capabilities
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Capabilities" property of an interface.
+ */
+dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
+                                         DBusError *error, void *user_data)
+{
+       struct wpa_supplicant *wpa_s = user_data;
+       struct wpa_driver_capa capa;
+       int res;
+       DBusMessageIter iter_dict, iter_dict_entry, iter_dict_val, iter_array,
+               variant_iter;
+       const char *scans[] = { "active", "passive", "ssid" };
+
+       if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
+                                             "a{sv}", &variant_iter) ||
+           !wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
+               goto nomem;
+
+       res = wpa_drv_get_capa(wpa_s, &capa);
+
+       /***** pairwise cipher */
+       if (res < 0) {
+               const char *args[] = {"ccmp", "tkip", "none"};
+
+               if (!wpa_dbus_dict_append_string_array(
+                           &iter_dict, "Pairwise", args,
+                           ARRAY_SIZE(args)))
+                       goto nomem;
+       } else {
+               if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Pairwise",
+                                                     &iter_dict_entry,
+                                                     &iter_dict_val,
+                                                     &iter_array) ||
+                   ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP_256) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "ccmp-256")) ||
+                   ((capa.enc & WPA_DRIVER_CAPA_ENC_GCMP_256) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "gcmp-256")) ||
+                   ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "ccmp")) ||
+                   ((capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "gcmp")) ||
+                   ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "tkip")) ||
+                   ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "none")) ||
+                   !wpa_dbus_dict_end_string_array(&iter_dict,
                                                    &iter_dict_entry,
                                                    &iter_dict_val,
                                                    &iter_array))
@@ -2048,48 +2351,38 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
                const char *args[] = {
                        "ccmp", "tkip", "wep104", "wep40"
                };
+
                if (!wpa_dbus_dict_append_string_array(
                            &iter_dict, "Group", args,
-                           sizeof(args) / sizeof(char*)))
+                           ARRAY_SIZE(args)))
                        goto nomem;
        } else {
                if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Group",
                                                      &iter_dict_entry,
                                                      &iter_dict_val,
-                                                     &iter_array))
-                       goto nomem;
-
-               if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "ccmp"))
-                               goto nomem;
-               }
-
-               if (capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "gcmp"))
-                               goto nomem;
-               }
-
-               if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "tkip"))
-                               goto nomem;
-               }
-
-               if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "wep104"))
-                               goto nomem;
-               }
-
-               if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "wep40"))
-                               goto nomem;
-               }
-
-               if (!wpa_dbus_dict_end_string_array(&iter_dict,
+                                                     &iter_array) ||
+                   ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP_256) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "ccmp-256")) ||
+                   ((capa.enc & WPA_DRIVER_CAPA_ENC_GCMP_256) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "gcmp-256")) ||
+                   ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "ccmp")) ||
+                   ((capa.enc & WPA_DRIVER_CAPA_ENC_GCMP) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "gcmp")) ||
+                   ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "tkip")) ||
+                   ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "wep104")) ||
+                   ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "wep40")) ||
+                   !wpa_dbus_dict_end_string_array(&iter_dict,
                                                    &iter_dict_entry,
                                                    &iter_dict_val,
                                                    &iter_array))
@@ -2107,34 +2400,28 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
                };
                if (!wpa_dbus_dict_append_string_array(
                            &iter_dict, "KeyMgmt", args,
-                           sizeof(args) / sizeof(char*)))
+                           ARRAY_SIZE(args)))
                        goto nomem;
        } else {
                if (!wpa_dbus_dict_begin_string_array(&iter_dict, "KeyMgmt",
                                                      &iter_dict_entry,
                                                      &iter_dict_val,
-                                                     &iter_array))
-                       goto nomem;
-
-               if (!wpa_dbus_dict_string_array_add_element(&iter_array,
-                                                           "none"))
-                       goto nomem;
-
-               if (!wpa_dbus_dict_string_array_add_element(&iter_array,
+                                                     &iter_array) ||
+                   !wpa_dbus_dict_string_array_add_element(&iter_array,
+                                                           "none") ||
+                   !wpa_dbus_dict_string_array_add_element(&iter_array,
                                                            "ieee8021x"))
                        goto nomem;
 
                if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
                                     WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
                        if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "wpa-eap"))
+                                   &iter_array, "wpa-eap") ||
+                           ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) &&
+                            !wpa_dbus_dict_string_array_add_element(
+                                    &iter_array, "wpa-ft-eap")))
                                goto nomem;
 
-                       if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT)
-                               if (!wpa_dbus_dict_string_array_add_element(
-                                           &iter_array, "wpa-ft-eap"))
-                                       goto nomem;
-
 /* TODO: Ensure that driver actually supports sha256 encryption. */
 #ifdef CONFIG_IEEE80211W
                        if (!wpa_dbus_dict_string_array_add_element(
@@ -2146,14 +2433,13 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
                if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
                                     WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
                        if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "wpa-psk"))
+                                   &iter_array, "wpa-psk") ||
+                           ((capa.key_mgmt &
+                             WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK) &&
+                            !wpa_dbus_dict_string_array_add_element(
+                                    &iter_array, "wpa-ft-psk")))
                                goto nomem;
 
-                       if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK)
-                               if (!wpa_dbus_dict_string_array_add_element(
-                                           &iter_array, "wpa-ft-psk"))
-                                       goto nomem;
-
 /* TODO: Ensure that driver actually supports sha256 encryption. */
 #ifdef CONFIG_IEEE80211W
                        if (!wpa_dbus_dict_string_array_add_element(
@@ -2162,11 +2448,10 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
 #endif /* CONFIG_IEEE80211W */
                }
 
-               if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "wpa-none"))
-                               goto nomem;
-               }
+               if ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) &&
+                   !wpa_dbus_dict_string_array_add_element(&iter_array,
+                                                           "wpa-none"))
+                       goto nomem;
 
 
 #ifdef CONFIG_WPS
@@ -2185,32 +2470,25 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
        /***** WPA protocol */
        if (res < 0) {
                const char *args[] = { "rsn", "wpa" };
+
                if (!wpa_dbus_dict_append_string_array(
                            &iter_dict, "Protocol", args,
-                           sizeof(args) / sizeof(char*)))
+                           ARRAY_SIZE(args)))
                        goto nomem;
        } else {
                if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Protocol",
                                                      &iter_dict_entry,
                                                      &iter_dict_val,
-                                                     &iter_array))
-                       goto nomem;
-
-               if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
-                                    WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "rsn"))
-                               goto nomem;
-               }
-
-               if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
-                                    WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "wpa"))
-                               goto nomem;
-               }
-
-               if (!wpa_dbus_dict_end_string_array(&iter_dict,
+                                                     &iter_array) ||
+                   ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+                                      WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "rsn")) ||
+                   ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+                                      WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "wpa")) ||
+                   !wpa_dbus_dict_end_string_array(&iter_dict,
                                                    &iter_dict_entry,
                                                    &iter_dict_val,
                                                    &iter_array))
@@ -2220,9 +2498,10 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
        /***** auth alg */
        if (res < 0) {
                const char *args[] = { "open", "shared", "leap" };
+
                if (!wpa_dbus_dict_append_string_array(
                            &iter_dict, "AuthAlg", args,
-                           sizeof(args) / sizeof(char*)))
+                           ARRAY_SIZE(args)))
                        goto nomem;
        } else {
                if (!wpa_dbus_dict_begin_string_array(&iter_dict, "AuthAlg",
@@ -2231,25 +2510,16 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
                                                      &iter_array))
                        goto nomem;
 
-               if (capa.auth & (WPA_DRIVER_AUTH_OPEN)) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "open"))
-                               goto nomem;
-               }
-
-               if (capa.auth & (WPA_DRIVER_AUTH_SHARED)) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "shared"))
-                               goto nomem;
-               }
-
-               if (capa.auth & (WPA_DRIVER_AUTH_LEAP)) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "leap"))
-                               goto nomem;
-               }
-
-               if (!wpa_dbus_dict_end_string_array(&iter_dict,
+               if (((capa.auth & WPA_DRIVER_AUTH_OPEN) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "open")) ||
+                   ((capa.auth & WPA_DRIVER_AUTH_SHARED) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "shared")) ||
+                   ((capa.auth & WPA_DRIVER_AUTH_LEAP) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "leap")) ||
+                   !wpa_dbus_dict_end_string_array(&iter_dict,
                                                    &iter_dict_entry,
                                                    &iter_dict_val,
                                                    &iter_array))
@@ -2258,39 +2528,25 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
 
        /***** Scan */
        if (!wpa_dbus_dict_append_string_array(&iter_dict, "Scan", scans,
-                                              sizeof(scans) / sizeof(char *)))
+                                              ARRAY_SIZE(scans)))
                goto nomem;
 
        /***** Modes */
        if (!wpa_dbus_dict_begin_string_array(&iter_dict, "Modes",
                                              &iter_dict_entry,
                                              &iter_dict_val,
-                                             &iter_array))
-               goto nomem;
-
-       if (!wpa_dbus_dict_string_array_add_element(
-                           &iter_array, "infrastructure"))
-               goto nomem;
-
-       if (!wpa_dbus_dict_string_array_add_element(
-                           &iter_array, "ad-hoc"))
-               goto nomem;
-
-       if (res >= 0) {
-               if (capa.flags & (WPA_DRIVER_FLAGS_AP)) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "ap"))
-                               goto nomem;
-               }
-
-               if (capa.flags & (WPA_DRIVER_FLAGS_P2P_CAPABLE)) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "p2p"))
-                               goto nomem;
-               }
-       }
-
-       if (!wpa_dbus_dict_end_string_array(&iter_dict,
+                                             &iter_array) ||
+           !wpa_dbus_dict_string_array_add_element(
+                   &iter_array, "infrastructure") ||
+           !wpa_dbus_dict_string_array_add_element(
+                   &iter_array, "ad-hoc") ||
+           (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_AP) &&
+            !wpa_dbus_dict_string_array_add_element(
+                    &iter_array, "ap")) ||
+           (res >= 0 && (capa.flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) &&
+            !wpa_dbus_dict_string_array_add_element(
+                    &iter_array, "p2p")) ||
+           !wpa_dbus_dict_end_string_array(&iter_dict,
                                            &iter_dict_entry,
                                            &iter_dict_val,
                                            &iter_array))
@@ -2305,9 +2561,8 @@ dbus_bool_t wpas_dbus_getter_capabilities(DBusMessageIter *iter,
                        goto nomem;
        }
 
-       if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict))
-               goto nomem;
-       if (!dbus_message_iter_close_container(iter, &variant_iter))
+       if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
+           !dbus_message_iter_close_container(iter, &variant_iter))
                goto nomem;
 
        return TRUE;
@@ -2368,7 +2623,7 @@ dbus_bool_t wpas_dbus_getter_state(DBusMessageIter *iter, DBusError *error,
  * Getter for "scanning" property.
  */
 dbus_bool_t wpas_dbus_getter_scanning(DBusMessageIter *iter, DBusError *error,
-                                      void *user_data)
+                                     void *user_data)
 {
        struct wpa_supplicant *wpa_s = user_data;
        dbus_bool_t scanning = wpa_s->scanning ? TRUE : FALSE;
@@ -2490,6 +2745,7 @@ dbus_bool_t wpas_dbus_getter_disconnect_reason(DBusMessageIter *iter,
 {
        struct wpa_supplicant *wpa_s = user_data;
        dbus_int32_t reason = wpa_s->disconnect_reason;
+
        return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32,
                                                &reason, error);
 }
@@ -2744,8 +3000,8 @@ dbus_bool_t wpas_dbus_getter_driver(DBusMessageIter *iter, DBusError *error,
        const char *driver;
 
        if (wpa_s->driver == NULL || wpa_s->driver->name == NULL) {
-               wpa_printf(MSG_DEBUG, "wpas_dbus_getter_driver[dbus]: "
-                          "wpa_s has no driver set");
+               wpa_printf(MSG_DEBUG, "%s[dbus]: wpa_s has no driver set",
+                          __func__);
                dbus_set_error(error, DBUS_ERROR_FAILED, "%s: no driver set",
                               __func__);
                return FALSE;
@@ -2865,6 +3121,7 @@ dbus_bool_t wpas_dbus_getter_bridge_ifname(DBusMessageIter *iter,
 {
        struct wpa_supplicant *wpa_s = user_data;
        const char *bridge_ifname = wpa_s->bridge_ifname;
+
        return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
                                                &bridge_ifname, error);
 }
@@ -2939,14 +3196,6 @@ dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error,
        unsigned int i = 0, num = 0;
        dbus_bool_t success = FALSE;
 
-       if (wpa_s->conf == NULL) {
-               wpa_printf(MSG_ERROR, "%s[dbus]: An error occurred getting "
-                          "networks list.", __func__);
-               dbus_set_error(error, DBUS_ERROR_FAILED, "%s: an error "
-                              "occurred getting the networks list", __func__);
-               return FALSE;
-       }
-
        for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
                if (!network_is_persistent_group(ssid))
                        num++;
@@ -2963,7 +3212,8 @@ dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error,
                        continue;
                paths[i] = os_zalloc(WPAS_DBUS_OBJECT_PATH_MAX);
                if (paths[i] == NULL) {
-                       dbus_set_error(error, DBUS_ERROR_NO_MEMORY, "no memory");
+                       dbus_set_error(error, DBUS_ERROR_NO_MEMORY,
+                                      "no memory");
                        goto out;
                }
 
@@ -2986,6 +3236,56 @@ out:
 
 
 /**
+ * wpas_dbus_getter_pkcs11_engine_path - Get PKCS #11 engine path
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: A dbus message containing the PKCS #11 engine path
+ *
+ * Getter for "PKCS11EnginePath" property.
+ */
+dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(DBusMessageIter *iter,
+                                               DBusError *error,
+                                               void *user_data)
+{
+       struct wpa_supplicant *wpa_s = user_data;
+       const char *pkcs11_engine_path;
+
+       if (wpa_s->conf->pkcs11_engine_path == NULL)
+               pkcs11_engine_path = "";
+       else
+               pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
+       return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+                                               &pkcs11_engine_path, error);
+}
+
+
+/**
+ * wpas_dbus_getter_pkcs11_module_path - Get PKCS #11 module path
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: A dbus message containing the PKCS #11 module path
+ *
+ * Getter for "PKCS11ModulePath" property.
+ */
+dbus_bool_t wpas_dbus_getter_pkcs11_module_path(DBusMessageIter *iter,
+                                               DBusError *error,
+                                               void *user_data)
+{
+       struct wpa_supplicant *wpa_s = user_data;
+       const char *pkcs11_module_path;
+
+       if (wpa_s->conf->pkcs11_module_path == NULL)
+               pkcs11_module_path = "";
+       else
+               pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
+       return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+                                               &pkcs11_module_path, error);
+}
+
+
+/**
  * wpas_dbus_getter_blobs - Get all blobs defined for this interface
  * @iter: Pointer to incoming dbus message iter
  * @error: Location to store error on failure
@@ -3054,7 +3354,7 @@ static struct wpa_bss * get_bss_helper(struct bss_handler_args *args,
 
        if (!res) {
                wpa_printf(MSG_ERROR, "%s[dbus]: no bss with id %d found",
-                          func_name, args->id);
+                          func_name, args->id);
                dbus_set_error(error, DBUS_ERROR_FAILED,
                               "%s: BSS %d not found",
                               func_name, args->id);
@@ -3159,11 +3459,22 @@ dbus_bool_t wpas_dbus_getter_bss_mode(DBusMessageIter *iter, DBusError *error,
        res = get_bss_helper(args, error, __func__);
        if (!res)
                return FALSE;
-
-       if (res->caps & IEEE80211_CAP_IBSS)
-               mode = "ad-hoc";
-       else
-               mode = "infrastructure";
+       if (bss_is_dmg(res)) {
+               switch (res->caps & IEEE80211_CAP_DMG_MASK) {
+               case IEEE80211_CAP_DMG_PBSS:
+               case IEEE80211_CAP_DMG_IBSS:
+                       mode = "ad-hoc";
+                       break;
+               case IEEE80211_CAP_DMG_AP:
+                       mode = "infrastructure";
+                       break;
+               }
+       } else {
+               if (res->caps & IEEE80211_CAP_IBSS)
+                       mode = "ad-hoc";
+               else
+                       mode = "infrastructure";
+       }
 
        return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
                                                &mode, error);
@@ -3283,8 +3594,8 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter,
 {
        DBusMessageIter iter_dict, variant_iter;
        const char *group;
-       const char *pairwise[3]; /* max 3 pairwise ciphers is supported */
-       const char *key_mgmt[7]; /* max 7 key managements may be supported */
+       const char *pairwise[5]; /* max 5 pairwise ciphers is supported */
+       const char *key_mgmt[9]; /* max 9 key managements may be supported */
        int n;
 
        if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
@@ -3308,6 +3619,14 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter,
                key_mgmt[n++] = "wpa-ft-eap";
        if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256)
                key_mgmt[n++] = "wpa-eap-sha256";
+#ifdef CONFIG_SUITEB
+       if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B)
+               key_mgmt[n++] = "wpa-eap-suite-b";
+#endif /* CONFIG_SUITEB */
+#ifdef CONFIG_SUITEB192
+       if (ie_data->key_mgmt & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+               key_mgmt[n++] = "wpa-eap-suite-b-192";
+#endif /* CONFIG_SUITEB192 */
        if (ie_data->key_mgmt & WPA_KEY_MGMT_NONE)
                key_mgmt[n++] = "wpa-none";
 
@@ -3332,6 +3651,12 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter,
        case WPA_CIPHER_WEP104:
                group = "wep104";
                break;
+       case WPA_CIPHER_CCMP_256:
+               group = "ccmp-256";
+               break;
+       case WPA_CIPHER_GCMP_256:
+               group = "gcmp-256";
+               break;
        default:
                group = "";
                break;
@@ -3348,6 +3673,10 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter,
                pairwise[n++] = "ccmp";
        if (ie_data->pairwise_cipher & WPA_CIPHER_GCMP)
                pairwise[n++] = "gcmp";
+       if (ie_data->pairwise_cipher & WPA_CIPHER_CCMP_256)
+               pairwise[n++] = "ccmp-256";
+       if (ie_data->pairwise_cipher & WPA_CIPHER_GCMP_256)
+               pairwise[n++] = "gcmp-256";
 
        if (!wpa_dbus_dict_append_string_array(&iter_dict, "Pairwise",
                                               pairwise, n))
@@ -3371,9 +3700,8 @@ static dbus_bool_t wpas_dbus_get_bss_security_prop(DBusMessageIter *iter,
                        goto nomem;
        }
 
-       if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict))
-               goto nomem;
-       if (!dbus_message_iter_close_container(iter, &variant_iter))
+       if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
+           !dbus_message_iter_close_container(iter, &variant_iter))
                goto nomem;
 
        return TRUE;
@@ -3407,12 +3735,10 @@ dbus_bool_t wpas_dbus_getter_bss_wpa(DBusMessageIter *iter, DBusError *error,
 
        os_memset(&wpa_data, 0, sizeof(wpa_data));
        ie = wpa_bss_get_vendor_ie(res, WPA_IE_VENDOR_TYPE);
-       if (ie) {
-               if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) {
-                       dbus_set_error_const(error, DBUS_ERROR_FAILED,
-                                            "failed to parse WPA IE");
-                       return FALSE;
-               }
+       if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) {
+               dbus_set_error_const(error, DBUS_ERROR_FAILED,
+                                    "failed to parse WPA IE");
+               return FALSE;
        }
 
        return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error);
@@ -3442,12 +3768,10 @@ dbus_bool_t wpas_dbus_getter_bss_rsn(DBusMessageIter *iter, DBusError *error,
 
        os_memset(&wpa_data, 0, sizeof(wpa_data));
        ie = wpa_bss_get_ie(res, WLAN_EID_RSN);
-       if (ie) {
-               if (wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) {
-                       dbus_set_error_const(error, DBUS_ERROR_FAILED,
-                                            "failed to parse RSN IE");
-                       return FALSE;
-               }
+       if (ie && wpa_parse_wpa_ie(ie, 2 + ie[1], &wpa_data) < 0) {
+               dbus_set_error_const(error, DBUS_ERROR_FAILED,
+                                    "failed to parse RSN IE");
+               return FALSE;
        }
 
        return wpas_dbus_get_bss_security_prop(iter, &wpa_data, error);
@@ -3479,10 +3803,8 @@ dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error,
                return FALSE;
 
        if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
-                                             "a{sv}", &variant_iter))
-               goto nomem;
-
-       if (!wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
+                                             "a{sv}", &variant_iter) ||
+           !wpa_dbus_dict_open_write(&variant_iter, &iter_dict))
                goto nomem;
 
 #ifdef CONFIG_WPS
@@ -3492,15 +3814,14 @@ dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error,
                        type = "pbc";
                else if (wps_is_selected_pin_registrar(wps_ie))
                        type = "pin";
+
+               wpabuf_free(wps_ie);
        }
 #endif /* CONFIG_WPS */
 
-       if (!wpa_dbus_dict_append_string(&iter_dict, "Type", type))
-               goto nomem;
-
-       if (!wpa_dbus_dict_close_write(&variant_iter, &iter_dict))
-               goto nomem;
-       if (!dbus_message_iter_close_container(iter, &variant_iter))
+       if (!wpa_dbus_dict_append_string(&iter_dict, "Type", type) ||
+           !wpa_dbus_dict_close_write(&variant_iter, &iter_dict) ||
+           !dbus_message_iter_close_container(iter, &variant_iter))
                goto nomem;
 
        return TRUE;
@@ -3537,6 +3858,35 @@ dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error,
 
 
 /**
+ * wpas_dbus_getter_bss_age - Return time in seconds since BSS was last seen
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for BSS age
+ */
+dbus_bool_t wpas_dbus_getter_bss_age(DBusMessageIter *iter, DBusError *error,
+                                    void *user_data)
+{
+       struct bss_handler_args *args = user_data;
+       struct wpa_bss *res;
+       struct os_reltime now, diff = { 0, 0 };
+       u32 age;
+
+       res = get_bss_helper(args, error, __func__);
+       if (!res)
+               return FALSE;
+
+       os_get_reltime(&now);
+       os_reltime_sub(&now, &res->last_update, &diff);
+       age = diff.sec > 0 ? diff.sec : 0;
+       return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_UINT32, &age,
+                                               error);
+}
+
+
+/**
  * wpas_dbus_getter_enabled - Check whether network is enabled or disabled
  * @iter: Pointer to incoming dbus message iter
  * @error: Location to store error on failure
@@ -3672,6 +4022,80 @@ dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter,
        return set_network_properties(net->wpa_s, ssid, &variant_iter, error);
 }
 
+#if defined TIZEN_EXT
+/**
+ * wpas_dbus_getter_passpoint - Get current passpoint on/off state
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "Passpoint" property of a configured network.
+ */
+dbus_bool_t wpas_dbus_getter_passpoint(DBusMessageIter *iter, DBusError *error,
+                                    void *user_data)
+{
+       struct wpa_supplicant *wpa_s = user_data;
+       dbus_int32_t enabled;
+
+       if (!wpa_s->conf) {
+               wpa_printf(MSG_ERROR, "No configuration found");
+               return FALSE;
+       }
+
+       enabled = (wpa_s->conf->interworking & wpa_s->conf->hs20) ? 1 : 0;
+
+       return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_INT32,
+                                               &enabled, error);
+}
+
+/**
+ * wpas_dbus_setter_passpoint - Set option for passpoint on/off
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "Passpoint(hs20, interworking, auto_interworking)" property of a configured network.
+ */
+dbus_bool_t wpas_dbus_setter_passpoint(DBusMessageIter *iter, DBusError *error,
+                                    void *user_data)
+{
+       struct wpa_supplicant *wpa_s = user_data;
+       dbus_int32_t enable;
+
+       if (!wpa_s->conf) {
+               wpa_printf(MSG_ERROR, "No configuration found");
+               return FALSE;
+       }
+
+       if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_INT32,
+                                             &enable))
+               return FALSE;
+
+       if (enable) {
+               wpa_s->conf->interworking = 1;
+               wpa_s->conf->hs20 = 1;
+               wpa_s->conf->auto_interworking = 1;
+
+               /* To do : wpa_supplicant_load_cred, SAMSUNG's Specialized Feature*/
+               /* Basic configuration file : wpa_supplicant.conf */
+               if (wpa_s->wpa_state != WPA_INTERFACE_DISABLED) {
+                       wpa_s->scan_req = 2;
+                       wpa_supplicant_req_scan(wpa_s, 0, 0);
+               }
+       } else {
+               wpa_s->conf->interworking = 0;
+               wpa_s->conf->hs20 = 0;
+               wpa_s->conf->auto_interworking = 0;
+       }
+
+       wpa_config_write(wpa_s->confname, wpa_s->conf);
+
+       return TRUE;
+}
+#endif
+
 
 #ifdef CONFIG_AP
 
@@ -3693,8 +4117,7 @@ DBusMessage * wpas_dbus_handler_subscribe_preq(
 
        name = os_strdup(dbus_message_get_sender(message));
        if (!name)
-               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                             "out of memory");
+               return wpas_dbus_error_no_memory(message);
 
        wpa_s->preq_notify_peer = name;
 
@@ -3774,28 +4197,22 @@ void wpas_dbus_signal_preq(struct wpa_supplicant *wpa_s,
 
        dbus_message_iter_init_append(msg, &iter);
 
-       if (!wpa_dbus_dict_open_write(&iter, &dict_iter))
-               goto fail;
-       if (addr && !wpa_dbus_dict_append_byte_array(&dict_iter, "addr",
-                                                    (const char *) addr,
-                                                    ETH_ALEN))
-               goto fail;
-       if (dst && !wpa_dbus_dict_append_byte_array(&dict_iter, "dst",
-                                                   (const char *) dst,
-                                                   ETH_ALEN))
-               goto fail;
-       if (bssid && !wpa_dbus_dict_append_byte_array(&dict_iter, "bssid",
-                                                     (const char *) bssid,
-                                                     ETH_ALEN))
-               goto fail;
-       if (ie && ie_len && !wpa_dbus_dict_append_byte_array(&dict_iter, "ies",
-                                                            (const char *) ie,
-                                                            ie_len))
-               goto fail;
-       if (ssi_signal && !wpa_dbus_dict_append_int32(&dict_iter, "signal",
-                                                     ssi_signal))
-               goto fail;
-       if (!wpa_dbus_dict_close_write(&iter, &dict_iter))
+       if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+           (addr && !wpa_dbus_dict_append_byte_array(&dict_iter, "addr",
+                                                     (const char *) addr,
+                                                     ETH_ALEN)) ||
+           (dst && !wpa_dbus_dict_append_byte_array(&dict_iter, "dst",
+                                                    (const char *) dst,
+                                                    ETH_ALEN)) ||
+           (bssid && !wpa_dbus_dict_append_byte_array(&dict_iter, "bssid",
+                                                      (const char *) bssid,
+                                                      ETH_ALEN)) ||
+           (ie && ie_len && !wpa_dbus_dict_append_byte_array(&dict_iter, "ies",
+                                                             (const char *) ie,
+                                                             ie_len)) ||
+           (ssi_signal && !wpa_dbus_dict_append_int32(&dict_iter, "signal",
+                                                      ssi_signal)) ||
+           !wpa_dbus_dict_close_write(&iter, &dict_iter))
                goto fail;
 
        dbus_connection_send(priv->con, msg, NULL);
old mode 100644 (file)
new mode 100755 (executable)
index fbc8358..4f94f52
@@ -55,8 +55,8 @@ dbus_bool_t wpas_dbus_getter_debug_level(DBusMessageIter *iter,
                                         void *user_data);
 
 dbus_bool_t wpas_dbus_getter_debug_timestamp(DBusMessageIter *iter,
-                                             DBusError *error,
-                                             void *user_data);
+                                            DBusError *error,
+                                            void *user_data);
 
 dbus_bool_t wpas_dbus_getter_debug_show_keys(DBusMessageIter *iter,
                                             DBusError *error,
@@ -87,6 +87,9 @@ dbus_bool_t wpas_dbus_getter_global_capabilities(DBusMessageIter *iter,
 DBusMessage * wpas_dbus_handler_scan(DBusMessage *message,
                                     struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_handler_signal_poll(DBusMessage *message,
+                                           struct wpa_supplicant *wpa_s);
+
 DBusMessage * wpas_dbus_handler_disconnect(DBusMessage *message,
                                           struct wpa_supplicant *wpa_s);
 
@@ -101,6 +104,9 @@ DBusMessage * wpas_dbus_handler_add_network(DBusMessage *message,
 DBusMessage * wpas_dbus_handler_reassociate(DBusMessage *message,
                                            struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_handler_reattach(DBusMessage *message,
+                                        struct wpa_supplicant *wpa_s);
+
 DBusMessage * wpas_dbus_handler_remove_network(DBusMessage *message,
                                               struct wpa_supplicant *wpa_s);
 
@@ -122,6 +128,19 @@ DBusMessage * wpas_dbus_handler_get_blob(DBusMessage *message,
 DBusMessage * wpas_dbus_handler_remove_blob(DBusMessage *message,
                                            struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_handler_set_pkcs11_engine_and_module_path(
+       DBusMessage *message, struct wpa_supplicant *wpa_s);
+
+#if defined TIZEN_EXT
+/* Extend to get/set hs20(passpoint) on/off */
+dbus_bool_t wpas_dbus_getter_passpoint(DBusMessageIter *iter,
+                                        DBusError *error,
+                                        void *user_data);
+dbus_bool_t wpas_dbus_setter_passpoint(DBusMessageIter *iter,
+                                        DBusError *error,
+                                        void *user_data);
+#endif
+
 DBusMessage * wpas_dbus_handler_flush_bss(DBusMessage *message,
                                          struct wpa_supplicant *wpa_s);
 
@@ -218,6 +237,14 @@ dbus_bool_t wpas_dbus_getter_bsss(DBusMessageIter *iter, DBusError *error,
 dbus_bool_t wpas_dbus_getter_networks(DBusMessageIter *iter, DBusError *error,
                                      void *user_data);
 
+dbus_bool_t wpas_dbus_getter_pkcs11_engine_path(DBusMessageIter *iter,
+                                               DBusError *error,
+                                               void *user_data);
+
+dbus_bool_t wpas_dbus_getter_pkcs11_module_path(DBusMessageIter *iter,
+                                               DBusError *error,
+                                               void *user_data);
+
 dbus_bool_t wpas_dbus_getter_blobs(DBusMessageIter *iter, DBusError *error,
                                   void *user_data);
 
@@ -254,6 +281,9 @@ dbus_bool_t wpas_dbus_getter_bss_wps(DBusMessageIter *iter, DBusError *error,
 dbus_bool_t wpas_dbus_getter_bss_ies(DBusMessageIter *iter, DBusError *error,
                                     void *user_data);
 
+dbus_bool_t wpas_dbus_getter_bss_age(DBusMessageIter *iter, DBusError *error,
+                                    void *user_data);
+
 dbus_bool_t wpas_dbus_getter_enabled(DBusMessageIter *iter, DBusError *error,
                                     void *user_data);
 
@@ -271,6 +301,12 @@ dbus_bool_t wpas_dbus_setter_network_properties(DBusMessageIter *iter,
 DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message,
                                          struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_handler_wps_cancel(DBusMessage *message,
+                                         struct wpa_supplicant *wpa_s);
+
+DBusMessage * wpas_dbus_handler_wps_generate_pin(DBusMessage *message,
+                                         struct wpa_supplicant *wpa_s);
+
 dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter,
        DBusError *error, void *user_data);
 
@@ -278,10 +314,28 @@ dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter,
                                                 DBusError *error,
                                                 void *user_data);
 
+dbus_bool_t wpas_dbus_getter_config_methods(DBusMessageIter *iter,
+                                           DBusError *error,
+                                           void *user_data);
+
+dbus_bool_t wpas_dbus_setter_config_methods(DBusMessageIter *iter,
+                                           DBusError *error,
+                                           void *user_data);
+
+DBusMessage * wpas_dbus_handler_tdls_discover(DBusMessage *message,
+                                             struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_tdls_setup(DBusMessage *message,
+                                          struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_tdls_status(DBusMessage *message,
+                                           struct wpa_supplicant *wpa_s);
+DBusMessage * wpas_dbus_handler_tdls_teardown(DBusMessage *message,
+                                             struct wpa_supplicant *wpa_s);
+
 DBusMessage * wpas_dbus_error_invalid_args(DBusMessage *message,
                                           const char *arg);
 DBusMessage * wpas_dbus_error_unknown_error(DBusMessage *message,
                                            const char *arg);
+DBusMessage * wpas_dbus_error_no_memory(DBusMessage *message);
 
 DBusMessage * wpas_dbus_handler_subscribe_preq(
        DBusMessage *message, struct wpa_supplicant *wpa_s);
old mode 100644 (file)
new mode 100755 (executable)
index 6ec96df..13a63be
@@ -26,6 +26,7 @@
 #include "ap/wps_hostapd.h"
 
 #include "../p2p_supplicant.h"
+#include "../wifi_display.h"
 
 /**
  * Parses out the mac address from the peer object path.
@@ -34,9 +35,9 @@
  * @addr - out param must be of ETH_ALEN size
  * Returns 0 if valid (including MAC), -1 otherwise
  */
-static int parse_peer_object_path(char *peer_path, u8 addr[ETH_ALEN])
+static int parse_peer_object_path(const char *peer_path, u8 addr[ETH_ALEN])
 {
-       char *p;
+       const char *p;
 
        if (!peer_path)
                return -1;
@@ -56,12 +57,12 @@ static int parse_peer_object_path(char *peer_path, u8 addr[ETH_ALEN])
  *
  * Convenience function to create and return an invalid persistent group error.
  */
-static DBusMessage * wpas_dbus_error_persistent_group_unknown(
-       DBusMessage *message)
+static DBusMessage *
+wpas_dbus_error_persistent_group_unknown(DBusMessage *message)
 {
-       return dbus_message_new_error(message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN,
-                                     "There is no such persistent group in "
-                                     "this P2P device.");
+       return dbus_message_new_error(
+               message, WPAS_DBUS_ERROR_NETWORK_UNKNOWN,
+               "There is no such persistent group in this P2P device.");
 }
 
 
@@ -73,7 +74,7 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message,
        DBusMessageIter iter;
        DBusMessageIter iter_dict;
        unsigned int timeout = 0;
-       enum p2p_discovery_type type = P2P_FIND_ONLY_SOCIAL;
+       enum p2p_discovery_type type = P2P_FIND_START_WITH_FULL;
        int num_req_dev_types = 0;
        unsigned int i;
        u8 *req_dev_types = NULL;
@@ -88,12 +89,12 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message,
                if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
                        goto error;
 
-               if (!os_strcmp(entry.key, "Timeout") &&
-                   (entry.type == DBUS_TYPE_INT32)) {
+               if (os_strcmp(entry.key, "Timeout") == 0 &&
+                   entry.type == DBUS_TYPE_INT32) {
                        timeout = entry.uint32_value;
                } else if (os_strcmp(entry.key, "RequestedDeviceTypes") == 0) {
-                       if ((entry.type != DBUS_TYPE_ARRAY) ||
-                           (entry.array_type != WPAS_DBUS_TYPE_BINARRAY))
+                       if (entry.type != DBUS_TYPE_ARRAY ||
+                           entry.array_type != WPAS_DBUS_TYPE_BINARRAY)
                                goto error_clear;
 
                        os_free(req_dev_types);
@@ -104,20 +105,20 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message,
 
                        for (i = 0; i < entry.array_len; i++) {
                                if (wpabuf_len(entry.binarray_value[i]) !=
-                                                       WPS_DEV_TYPE_LEN)
+                                   WPS_DEV_TYPE_LEN)
                                        goto error_clear;
                                os_memcpy(req_dev_types + i * WPS_DEV_TYPE_LEN,
                                          wpabuf_head(entry.binarray_value[i]),
                                          WPS_DEV_TYPE_LEN);
                        }
                        num_req_dev_types = entry.array_len;
-               } else if (!os_strcmp(entry.key, "DiscoveryType") &&
-                          (entry.type == DBUS_TYPE_STRING)) {
-                       if (!os_strcmp(entry.str_value, "start_with_full"))
+               } else if (os_strcmp(entry.key, "DiscoveryType") == 0 &&
+                          entry.type == DBUS_TYPE_STRING) {
+                       if (os_strcmp(entry.str_value, "start_with_full") == 0)
                                type = P2P_FIND_START_WITH_FULL;
-                       else if (!os_strcmp(entry.str_value, "social"))
+                       else if (os_strcmp(entry.str_value, "social") == 0)
                                type = P2P_FIND_ONLY_SOCIAL;
-                       else if (!os_strcmp(entry.str_value, "progressive"))
+                       else if (os_strcmp(entry.str_value, "progressive") == 0)
                                type = P2P_FIND_PROGRESSIVE;
                        else
                                goto error_clear;
@@ -126,8 +127,11 @@ DBusMessage * wpas_dbus_handler_p2p_find(DBusMessage *message,
                wpa_dbus_dict_entry_clear(&entry);
        }
 
+       if (wpa_s->p2p_dev)
+               wpa_s = wpa_s->p2p_dev;
+
        wpas_p2p_find(wpa_s, timeout, type, num_req_dev_types, req_dev_types,
-                     NULL, 0);
+                     NULL, 0, 0, NULL, 0);
        os_free(req_dev_types);
        return reply;
 
@@ -143,6 +147,9 @@ error:
 DBusMessage * wpas_dbus_handler_p2p_stop_find(DBusMessage *message,
                                              struct wpa_supplicant *wpa_s)
 {
+       if (wpa_s->p2p_dev)
+               wpa_s = wpa_s->p2p_dev;
+
        wpas_p2p_stop_find(wpa_s);
        return NULL;
 }
@@ -161,6 +168,9 @@ DBusMessage * wpas_dbus_handler_p2p_rejectpeer(DBusMessage *message,
        if (parse_peer_object_path(peer_object_path, peer_addr) < 0)
                return wpas_dbus_error_invalid_args(message, NULL);
 
+       if (wpa_s->p2p_dev)
+               wpa_s = wpa_s->p2p_dev;
+
        if (wpas_p2p_reject(wpa_s, peer_addr) < 0)
                return wpas_dbus_error_unknown_error(message,
                                "Failed to call wpas_p2p_reject method.");
@@ -169,6 +179,37 @@ DBusMessage * wpas_dbus_handler_p2p_rejectpeer(DBusMessage *message,
 }
 
 
+DBusMessage * wpas_dbus_handler_p2p_removeclient(DBusMessage *message,
+                                              struct wpa_supplicant *wpa_s)
+{
+       DBusMessageIter iter;
+       char *peer_object_path = NULL;
+       u8 peer_addr[ETH_ALEN];
+       int remove_persistent = 1;
+
+       dbus_message_iter_init(message, &iter);
+       dbus_message_iter_get_basic(&iter, &peer_object_path);
+
+       if (parse_peer_object_path(peer_object_path, peer_addr) < 0)
+               return wpas_dbus_error_invalid_args(message, NULL);
+
+       dbus_message_iter_next(&iter);
+       dbus_message_iter_get_basic(&iter, &remove_persistent);
+
+       if (remove_persistent)
+               remove_persistent = 0;
+       else
+               remove_persistent = 1;
+
+       if (wpa_s->p2p_dev)
+               wpa_s = wpa_s->p2p_dev;
+
+       wpas_p2p_remove_client(wpa_s, peer_addr, remove_persistent);
+
+       return NULL;
+}
+
+
 DBusMessage * wpas_dbus_handler_p2p_listen(DBusMessage *message,
                                           struct wpa_supplicant *wpa_s)
 {
@@ -176,12 +217,16 @@ DBusMessage * wpas_dbus_handler_p2p_listen(DBusMessage *message,
 
        if (!dbus_message_get_args(message, NULL, DBUS_TYPE_INT32, &timeout,
                                   DBUS_TYPE_INVALID))
-               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                             NULL);
+               return wpas_dbus_error_no_memory(message);
+
+       if (wpa_s->p2p_dev)
+               wpa_s = wpa_s->p2p_dev;
 
-       if (wpas_p2p_listen(wpa_s, (unsigned int)timeout))
-               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                             NULL);
+       if (wpas_p2p_listen(wpa_s, (unsigned int) timeout)) {
+               return dbus_message_new_error(message,
+                                             WPAS_DBUS_ERROR_UNKNOWN_ERROR,
+                                             "Could not start P2P listen");
+       }
 
        return NULL;
 }
@@ -205,17 +250,20 @@ DBusMessage * wpas_dbus_handler_p2p_extendedlisten(
                if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
                        goto error;
 
-               if (!os_strcmp(entry.key, "period") &&
-                   (entry.type == DBUS_TYPE_INT32))
+               if (os_strcmp(entry.key, "period") == 0 &&
+                   entry.type == DBUS_TYPE_INT32)
                        period = entry.uint32_value;
-               else if (!os_strcmp(entry.key, "interval") &&
-                        (entry.type == DBUS_TYPE_INT32))
+               else if (os_strcmp(entry.key, "interval") == 0 &&
+                        entry.type == DBUS_TYPE_INT32)
                        interval = entry.uint32_value;
                else
                        goto error_clear;
                wpa_dbus_dict_entry_clear(&entry);
        }
 
+       if (wpa_s->p2p_dev)
+               wpa_s = wpa_s->p2p_dev;
+
        if (wpas_p2p_ext_listen(wpa_s, period, interval))
                return wpas_dbus_error_unknown_error(
                        message, "failed to initiate a p2p_ext_listen.");
@@ -247,16 +295,16 @@ DBusMessage * wpas_dbus_handler_p2p_presence_request(
                if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
                        goto error;
 
-               if (!os_strcmp(entry.key, "duration1") &&
-                   (entry.type == DBUS_TYPE_INT32))
+               if (os_strcmp(entry.key, "duration1") == 0 &&
+                   entry.type == DBUS_TYPE_INT32)
                        dur1 = entry.uint32_value;
-               else if (!os_strcmp(entry.key, "interval1") &&
+               else if (os_strcmp(entry.key, "interval1") == 0 &&
                         entry.type == DBUS_TYPE_INT32)
                        int1 = entry.uint32_value;
-               else if (!os_strcmp(entry.key, "duration2") &&
+               else if (os_strcmp(entry.key, "duration2") == 0 &&
                         entry.type == DBUS_TYPE_INT32)
                        dur2 = entry.uint32_value;
-               else if (!os_strcmp(entry.key, "interval2") &&
+               else if (os_strcmp(entry.key, "interval2") == 0 &&
                         entry.type == DBUS_TYPE_INT32)
                        int2 = entry.uint32_value;
                else
@@ -264,6 +312,10 @@ DBusMessage * wpas_dbus_handler_p2p_presence_request(
 
                wpa_dbus_dict_entry_clear(&entry);
        }
+
+       if (wpa_s->p2p_dev)
+               wpa_s = wpa_s->p2p_dev;
+
        if (wpas_p2p_presence_req(wpa_s, dur1, int1, dur2, int2) < 0)
                return wpas_dbus_error_unknown_error(message,
                                "Failed to invoke presence request.");
@@ -288,9 +340,10 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message,
        int persistent_group = 0;
        int freq = 0;
        char *iface = NULL;
-       char *net_id_str = NULL;
        unsigned int group_id = 0;
        struct wpa_ssid *ssid;
+       char *passphrase = NULL;
+       char path_buf[WPAS_DBUS_OBJECT_PATH_MAX], *path = path_buf;
 
        dbus_message_iter_init(message, &iter);
 
@@ -301,32 +354,44 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message,
                if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
                        goto inv_args;
 
-               if (!os_strcmp(entry.key, "persistent") &&
-                   (entry.type == DBUS_TYPE_BOOLEAN)) {
-                       persistent_group = (entry.bool_value == TRUE) ? 1 : 0;
-               } else if (!os_strcmp(entry.key, "frequency") &&
-                          (entry.type == DBUS_TYPE_INT32)) {
+               if (os_strcmp(entry.key, "persistent") == 0 &&
+                   entry.type == DBUS_TYPE_BOOLEAN) {
+                       persistent_group = entry.bool_value;
+               } else if (os_strcmp(entry.key, "frequency") == 0 &&
+                          entry.type == DBUS_TYPE_INT32) {
                        freq = entry.int32_value;
                        if (freq <= 0)
                                goto inv_args_clear;
-               } else if (!os_strcmp(entry.key, "persistent_group_object") &&
+               } else if (os_strcmp(entry.key, "persistent_group_object") ==
+                          0 &&
                           entry.type == DBUS_TYPE_OBJECT_PATH)
                        pg_object_path = os_strdup(entry.str_value);
+               else if (os_strcmp(entry.key, "passphrase") == 0 &&
+                          entry.type == DBUS_TYPE_STRING &&
+                          os_strlen(entry.str_value) > 0)
+                       passphrase = os_strdup(entry.str_value);
                else
                        goto inv_args_clear;
 
                wpa_dbus_dict_entry_clear(&entry);
        }
 
+       if (wpa_s->p2p_dev)
+               wpa_s = wpa_s->p2p_dev;
+
+
        if (pg_object_path != NULL) {
+               char *net_id_str;
+
                /*
                 * A persistent group Object Path is defined meaning we want
                 * to re-invoke a persistent group.
                 */
 
-               iface = wpas_dbus_new_decompose_object_path(pg_object_path, 1,
-                                                           &net_id_str, NULL);
-               if (iface == NULL ||
+               iface = wpas_dbus_new_decompose_object_path(
+                       pg_object_path, WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART,
+                       &net_id_str);
+               if (iface == NULL || net_id_str == NULL ||
                    os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
                        reply =
                            wpas_dbus_error_invalid_args(message,
@@ -346,19 +411,37 @@ DBusMessage * wpas_dbus_handler_p2p_group_add(DBusMessage *message,
                if (ssid == NULL || ssid->disabled != 2)
                        goto inv_args;
 
-               if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0,
-                                                 NULL)) {
+               if (wpas_p2p_group_add_persistent(wpa_s, ssid, 0, freq, 0, 0, 0,
+                                                 NULL, 0)) {
                        reply = wpas_dbus_error_unknown_error(
                                message,
                                "Failed to reinvoke a persistent group");
                        goto out;
                }
-       } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0))
+       } else if (wpas_p2p_group_add(wpa_s, persistent_group, freq, 0, 0, passphrase))
                goto inv_args;
 
+       if(wpa_s->global->p2p_group_formation) {
+               os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
+                           "%s", wpa_s->global->p2p_group_formation->dbus_new_path);
+       } else {
+               os_snprintf(path, WPAS_DBUS_OBJECT_PATH_MAX,
+                           "%s", wpa_s->dbus_new_path);
+       }
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL) {
+               reply = wpas_dbus_error_no_memory(message);
+               goto out;
+       }
+       if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
+                                     DBUS_TYPE_INVALID)) {
+               dbus_message_unref(reply);
+               reply = wpas_dbus_error_no_memory(message);
+               goto out;
+       }
 out:
        os_free(pg_object_path);
-       os_free(net_id_str);
+       os_free(passphrase);
        os_free(iface);
        return reply;
 inv_args_clear:
@@ -393,8 +476,7 @@ static dbus_bool_t wpa_dbus_p2p_check_enabled(struct wpa_supplicant *wpa_s,
                                "P2P is not available for this interface");
                }
                dbus_set_error_const(error, DBUS_ERROR_FAILED,
-                                    "P2P is not available for this "
-                                    "interface");
+                                    "P2P is not available for this interface");
                return FALSE;
        }
        return TRUE;
@@ -409,6 +491,9 @@ DBusMessage * wpas_dbus_handler_p2p_flush(DBusMessage *message,
        if (!wpa_dbus_p2p_check_enabled(wpa_s, message, &reply, NULL))
                return reply;
 
+       if (wpa_s->p2p_dev)
+               wpa_s = wpa_s->p2p_dev;
+
        os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
        wpa_s->force_long_sd = 0;
        p2p_flush(wpa_s->global->p2p);
@@ -449,42 +534,42 @@ DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message,
                if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
                        goto inv_args;
 
-               if (!os_strcmp(entry.key, "peer") &&
-                   (entry.type == DBUS_TYPE_OBJECT_PATH)) {
+               if (os_strcmp(entry.key, "peer") == 0 &&
+                   entry.type == DBUS_TYPE_OBJECT_PATH) {
                        peer_object_path = os_strdup(entry.str_value);
-               } else if (!os_strcmp(entry.key, "persistent") &&
-                          (entry.type == DBUS_TYPE_BOOLEAN)) {
-                       persistent_group = (entry.bool_value == TRUE) ? 1 : 0;
-               } else if (!os_strcmp(entry.key, "join") &&
-                          (entry.type == DBUS_TYPE_BOOLEAN)) {
-                       join = (entry.bool_value == TRUE) ? 1 : 0;
-               } else if (!os_strcmp(entry.key, "authorize_only") &&
-                          (entry.type == DBUS_TYPE_BOOLEAN)) {
-                       authorize_only = (entry.bool_value == TRUE) ? 1 : 0;
-               } else if (!os_strcmp(entry.key, "frequency") &&
-                          (entry.type == DBUS_TYPE_INT32)) {
+               } else if (os_strcmp(entry.key, "persistent") == 0 &&
+                          entry.type == DBUS_TYPE_BOOLEAN) {
+                       persistent_group = entry.bool_value;
+               } else if (os_strcmp(entry.key, "join") == 0 &&
+                          entry.type == DBUS_TYPE_BOOLEAN) {
+                       join = entry.bool_value;
+               } else if (os_strcmp(entry.key, "authorize_only") == 0 &&
+                          entry.type == DBUS_TYPE_BOOLEAN) {
+                       authorize_only = entry.bool_value;
+               } else if (os_strcmp(entry.key, "frequency") == 0 &&
+                          entry.type == DBUS_TYPE_INT32) {
                        freq = entry.int32_value;
                        if (freq <= 0)
                                goto inv_args_clear;
-               } else if (!os_strcmp(entry.key, "go_intent") &&
-                          (entry.type == DBUS_TYPE_INT32)) {
+               } else if (os_strcmp(entry.key, "go_intent") == 0 &&
+                          entry.type == DBUS_TYPE_INT32) {
                        go_intent = entry.int32_value;
                        if ((go_intent < 0) || (go_intent > 15))
                                goto inv_args_clear;
-               } else if (!os_strcmp(entry.key, "wps_method") &&
-                          (entry.type == DBUS_TYPE_STRING)) {
-                       if (!os_strcmp(entry.str_value, "pbc"))
+               } else if (os_strcmp(entry.key, "wps_method") == 0 &&
+                          entry.type == DBUS_TYPE_STRING) {
+                       if (os_strcmp(entry.str_value, "pbc") == 0)
                                wps_method = WPS_PBC;
-                       else if (!os_strcmp(entry.str_value, "pin"))
+                       else if (os_strcmp(entry.str_value, "pin") == 0)
                                wps_method = WPS_PIN_DISPLAY;
-                       else if (!os_strcmp(entry.str_value, "display"))
+                       else if (os_strcmp(entry.str_value, "display") == 0)
                                wps_method = WPS_PIN_DISPLAY;
-                       else if (!os_strcmp(entry.str_value, "keypad"))
+                       else if (os_strcmp(entry.str_value, "keypad") == 0)
                                wps_method = WPS_PIN_KEYPAD;
                        else
                                goto inv_args_clear;
-               } else if (!os_strcmp(entry.key, "pin") &&
-                          (entry.type == DBUS_TYPE_STRING)) {
+               } else if (os_strcmp(entry.key, "pin") == 0 &&
+                          entry.type == DBUS_TYPE_STRING) {
                        pin = os_strdup(entry.str_value);
                } else
                        goto inv_args_clear;
@@ -492,24 +577,28 @@ DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message,
                wpa_dbus_dict_entry_clear(&entry);
        }
 
-       if (!peer_object_path || (wps_method == WPS_NOT_READY) ||
-           (parse_peer_object_path(peer_object_path, addr) < 0) ||
+       if (wps_method == WPS_NOT_READY ||
+           parse_peer_object_path(peer_object_path, addr) < 0 ||
            !p2p_peer_known(wpa_s->global->p2p, addr))
                goto inv_args;
 
        /*
         * Validate the wps_method specified and the pin value.
         */
-       if ((!pin || !pin[0]) && (wps_method == WPS_PIN_KEYPAD))
+       if ((!pin || !pin[0]) && wps_method == WPS_PIN_KEYPAD)
                goto inv_args;
 
+       if (wpa_s->p2p_dev)
+               wpa_s = wpa_s->p2p_dev;
+
        new_pin = wpas_p2p_connect(wpa_s, addr, pin, wps_method,
                                   persistent_group, 0, join, authorize_only,
-                                  go_intent, freq, -1, 0, 0);
+                                  go_intent, freq, -1, 0, 0, 0);
 
        if (new_pin >= 0) {
                char npin[9];
                char *generated_pin;
+
                os_snprintf(npin, sizeof(npin), "%08d", new_pin);
                generated_pin = npin;
                reply = dbus_message_new_method_return(message);
@@ -518,8 +607,8 @@ DBusMessage * wpas_dbus_handler_p2p_connect(DBusMessage *message,
        } else {
                switch (new_pin) {
                case -2:
-                       err_msg = "connect failed due to channel "
-                               "unavailability.";
+                       err_msg =
+                               "connect failed due to channel unavailability.";
                        iface = WPAS_DBUS_ERROR_CONNECT_CHANNEL_UNAVAILABLE;
                        break;
 
@@ -555,6 +644,21 @@ inv_args:
 }
 
 
+DBusMessage * wpas_dbus_handler_p2p_cancel(DBusMessage *message,
+                                          struct wpa_supplicant *wpa_s)
+{
+
+       if (wpa_s->p2p_dev)
+               wpa_s = wpa_s->p2p_dev;
+
+       if (wpas_p2p_cancel(wpa_s))
+               return wpas_dbus_error_unknown_error(message,
+                                                    "P2P cancel Failed");
+
+       return NULL;
+}
+
+
 DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message,
                                           struct wpa_supplicant *wpa_s)
 {
@@ -565,7 +669,6 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message,
        char *peer_object_path = NULL;
        char *pg_object_path = NULL;
        char *iface = NULL;
-       char *net_id_str = NULL;
        u8 peer_addr[ETH_ALEN];
        unsigned int group_id = 0;
        int persistent = 0;
@@ -583,12 +686,13 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message,
                if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
                        goto err;
 
-               if (!os_strcmp(entry.key, "peer") &&
-                   (entry.type == DBUS_TYPE_OBJECT_PATH)) {
+               if (os_strcmp(entry.key, "peer") == 0 &&
+                   entry.type == DBUS_TYPE_OBJECT_PATH) {
                        peer_object_path = os_strdup(entry.str_value);
                        wpa_dbus_dict_entry_clear(&entry);
-               } else if (!os_strcmp(entry.key, "persistent_group_object") &&
-                          (entry.type == DBUS_TYPE_OBJECT_PATH)) {
+               } else if (os_strcmp(entry.key, "persistent_group_object") ==
+                          0 &&
+                          entry.type == DBUS_TYPE_OBJECT_PATH) {
                        pg_object_path = os_strdup(entry.str_value);
                        persistent = 1;
                        wpa_dbus_dict_entry_clear(&entry);
@@ -598,21 +702,25 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message,
                }
        }
 
-       if (!peer_object_path ||
-           (parse_peer_object_path(peer_object_path, peer_addr) < 0) ||
-           !p2p_peer_known(wpa_s->global->p2p, peer_addr)) {
+       if (parse_peer_object_path(peer_object_path, peer_addr) < 0 ||
+           !p2p_peer_known(wpa_s->global->p2p, peer_addr))
                goto err;
-       }
+
+       if (wpa_s->p2p_dev)
+               wpa_s = wpa_s->p2p_dev;
 
        if (persistent) {
+               char *net_id_str;
                /*
                 * A group ID is defined meaning we want to re-invoke a
                 * persistent group
                 */
 
-               iface = wpas_dbus_new_decompose_object_path(pg_object_path, 1,
-                                                           &net_id_str, NULL);
-               if (iface == NULL ||
+               iface = wpas_dbus_new_decompose_object_path(
+                       pg_object_path,
+                       WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART,
+                       &net_id_str);
+               if (iface == NULL || net_id_str == NULL ||
                    os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
                        reply = wpas_dbus_error_invalid_args(message,
                                                             pg_object_path);
@@ -631,8 +739,8 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message,
                if (ssid == NULL || ssid->disabled != 2)
                        goto err;
 
-               if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0) < 0)
-               {
+               if (wpas_p2p_invite(wpa_s, peer_addr, ssid, NULL, 0, 0, 0, 0) <
+                   0) {
                        reply = wpas_dbus_error_unknown_error(
                                message,
                                "Failed to reinvoke a persistent group");
@@ -651,6 +759,7 @@ DBusMessage * wpas_dbus_handler_p2p_invite(DBusMessage *message,
        }
 
 out:
+       os_free(iface);
        os_free(pg_object_path);
        os_free(peer_object_path);
        return reply;
@@ -689,8 +798,11 @@ DBusMessage * wpas_dbus_handler_p2p_prov_disc_req(DBusMessage *message,
            os_strcmp(config_method, "pushbutton"))
                return wpas_dbus_error_invalid_args(message, NULL);
 
+       if (wpa_s->p2p_dev)
+               wpa_s = wpa_s->p2p_dev;
+
        if (wpas_p2p_prov_disc(wpa_s, peer_addr, config_method,
-                              WPAS_P2P_PD_FOR_GO_NEG) < 0)
+                              WPAS_P2P_PD_FOR_GO_NEG, NULL) < 0)
                return wpas_dbus_error_unknown_error(message,
                                "Failed to send provision discovery request");
 
@@ -718,6 +830,9 @@ dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter,
        if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error))
                return FALSE;
 
+       if (wpa_s->p2p_dev)
+               wpa_s = wpa_s->p2p_dev;
+
        if (!dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
                                              "a{sv}", &variant_iter) ||
            !wpa_dbus_dict_open_write(&variant_iter, &dict_iter))
@@ -731,8 +846,8 @@ dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter,
 
        /* Primary device type */
        if (!wpa_dbus_dict_append_byte_array(&dict_iter, "PrimaryDeviceType",
-                                            (char *)wpa_s->conf->device_type,
-                                            WPS_DEV_TYPE_LEN))
+                                            (char *) wpa_s->conf->device_type,
+                                            WPS_DEV_TYPE_LEN))
                goto err_no_mem;
 
        /* Secondary device types */
@@ -758,7 +873,27 @@ dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter,
                                             &iter_secdev_dict_array))
                        goto err_no_mem;
        }
+#if defined TIZEN_EXT
+       /* IP Address GO */
+       if (!wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrGO",
+                                            (char *) wpa_s->conf->ip_addr_go, 4))
+               goto err_no_mem;
+
+       /* IP Address mask */
+       if (!wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrMask",
+                                            (char *) wpa_s->conf->ip_addr_mask, 4))
+               goto err_no_mem;
+
+       /* IP Address Start*/
+       if (!wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrStart",
+                                            (char *) wpa_s->conf->ip_addr_start, 4))
+               goto err_no_mem;
 
+       /* IP Address End*/
+       if (!wpa_dbus_dict_append_byte_array(&dict_iter, "IpAddrEnd",
+                                            (char *) wpa_s->conf->ip_addr_end, 4))
+               goto err_no_mem;
+#endif /* TIZEN_EXT */
        /* Vendor Extensions */
        for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
                if (wpa_s->conf->wps_vendor_ext[i] == NULL)
@@ -767,65 +902,37 @@ dbus_bool_t wpas_dbus_getter_p2p_device_config(DBusMessageIter *iter,
                        wpa_s->conf->wps_vendor_ext[i];
        }
 
-       if (num_vendor_extensions &&
-           !wpa_dbus_dict_append_wpabuf_array(&dict_iter,
-                                              "VendorExtension",
-                                              vendor_ext,
-                                              num_vendor_extensions))
-               goto err_no_mem;
-
-       /* GO Intent */
-       if (!wpa_dbus_dict_append_uint32(&dict_iter, "GOIntent",
-                                        wpa_s->conf->p2p_go_intent))
-               goto err_no_mem;
-
-       /* Persistent Reconnect */
-       if (!wpa_dbus_dict_append_bool(&dict_iter, "PersistentReconnect",
-                                      wpa_s->conf->persistent_reconnect))
-               goto err_no_mem;
-
-       /* Listen Reg Class */
-       if (!wpa_dbus_dict_append_uint32(&dict_iter, "ListenRegClass",
-                                        wpa_s->conf->p2p_listen_reg_class))
-               goto err_no_mem;
-
-       /* Listen Channel */
-       if (!wpa_dbus_dict_append_uint32(&dict_iter, "ListenChannel",
-                                        wpa_s->conf->p2p_listen_channel))
-               goto err_no_mem;
-
-       /* Oper Reg Class */
-       if (!wpa_dbus_dict_append_uint32(&dict_iter, "OperRegClass",
-                                        wpa_s->conf->p2p_oper_reg_class))
-               goto err_no_mem;
-
-       /* Oper Channel */
-       if (!wpa_dbus_dict_append_uint32(&dict_iter, "OperChannel",
-                                        wpa_s->conf->p2p_oper_channel))
-               goto err_no_mem;
-
-       /* SSID Postfix */
-       if (wpa_s->conf->p2p_ssid_postfix &&
-           !wpa_dbus_dict_append_string(&dict_iter, "SsidPostfix",
-                                        wpa_s->conf->p2p_ssid_postfix))
-               goto err_no_mem;
-
-       /* Intra Bss */
-       if (!wpa_dbus_dict_append_bool(&dict_iter, "IntraBss",
-                                      wpa_s->conf->p2p_intra_bss))
-               goto err_no_mem;
-
-       /* Group Idle */
-       if (!wpa_dbus_dict_append_uint32(&dict_iter, "GroupIdle",
-                                        wpa_s->conf->p2p_group_idle))
-               goto err_no_mem;
-
-       /* Dissasociation low ack */
-       if (!wpa_dbus_dict_append_uint32(&dict_iter, "disassoc_low_ack",
-                                        wpa_s->conf->disassoc_low_ack))
-               goto err_no_mem;
-
-       if (!wpa_dbus_dict_close_write(&variant_iter, &dict_iter) ||
+       if ((num_vendor_extensions &&
+            !wpa_dbus_dict_append_wpabuf_array(&dict_iter,
+                                               "VendorExtension",
+                                               vendor_ext,
+                                               num_vendor_extensions)) ||
+           !wpa_dbus_dict_append_uint32(&dict_iter, "GOIntent",
+                                        wpa_s->conf->p2p_go_intent) ||
+           !wpa_dbus_dict_append_bool(&dict_iter, "PersistentReconnect",
+                                      wpa_s->conf->persistent_reconnect) ||
+           !wpa_dbus_dict_append_uint32(&dict_iter, "ListenRegClass",
+                                        wpa_s->conf->p2p_listen_reg_class) ||
+           !wpa_dbus_dict_append_uint32(&dict_iter, "ListenChannel",
+                                        wpa_s->conf->p2p_listen_channel) ||
+           !wpa_dbus_dict_append_uint32(&dict_iter, "OperRegClass",
+                                        wpa_s->conf->p2p_oper_reg_class) ||
+           !wpa_dbus_dict_append_uint32(&dict_iter, "OperChannel",
+                                        wpa_s->conf->p2p_oper_channel) ||
+           (wpa_s->conf->p2p_ssid_postfix &&
+            !wpa_dbus_dict_append_string(&dict_iter, "SsidPostfix",
+                                         wpa_s->conf->p2p_ssid_postfix)) ||
+           !wpa_dbus_dict_append_bool(&dict_iter, "IntraBss",
+                                      wpa_s->conf->p2p_intra_bss) ||
+           !wpa_dbus_dict_append_uint32(&dict_iter, "GroupIdle",
+                                        wpa_s->conf->p2p_group_idle) ||
+           !wpa_dbus_dict_append_uint32(&dict_iter, "disassoc_low_ack",
+                                        wpa_s->conf->disassoc_low_ack) ||
+           !wpa_dbus_dict_append_bool(&dict_iter, "NoGroupIface",
+                                      wpa_s->conf->p2p_no_group_iface) ||
+           !wpa_dbus_dict_append_uint32(&dict_iter, "p2p_search_delay",
+                                        wpa_s->conf->p2p_search_delay) ||
+           !wpa_dbus_dict_close_write(&variant_iter, &dict_iter) ||
            !dbus_message_iter_close_container(iter, &variant_iter))
                goto err_no_mem;
 
@@ -849,6 +956,9 @@ dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter,
        if (!wpa_dbus_p2p_check_enabled(wpa_s, NULL, NULL, error))
                return FALSE;
 
+       if (wpa_s->p2p_dev)
+               wpa_s = wpa_s->p2p_dev;
+
        dbus_message_iter_recurse(iter, &variant_iter);
        if (!wpa_dbus_dict_open_read(&variant_iter, &iter_dict, error))
                return FALSE;
@@ -904,8 +1014,8 @@ dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter,
                        wpa_s->conf->changed_parameters |=
                                        CFG_CHANGED_SEC_DEVICE_TYPE;
                } else if (os_strcmp(entry.key, "VendorExtension") == 0) {
-                       if ((entry.type != DBUS_TYPE_ARRAY) ||
-                           (entry.array_type != WPAS_DBUS_TYPE_BINARRAY) ||
+                       if (entry.type != DBUS_TYPE_ARRAY ||
+                           entry.array_type != WPAS_DBUS_TYPE_BINARRAY ||
                            (entry.array_len > P2P_MAX_WPS_VENDOR_EXT))
                                goto error;
 
@@ -921,30 +1031,30 @@ dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter,
                                } else
                                        wpa_s->conf->wps_vendor_ext[i] = NULL;
                        }
-               } else if ((os_strcmp(entry.key, "GOIntent") == 0) &&
-                          (entry.type == DBUS_TYPE_UINT32) &&
+               } else if (os_strcmp(entry.key, "GOIntent") == 0 &&
+                          entry.type == DBUS_TYPE_UINT32 &&
                           (entry.uint32_value <= 15))
                        wpa_s->conf->p2p_go_intent = entry.uint32_value;
-               else if ((os_strcmp(entry.key, "PersistentReconnect") == 0) &&
-                        (entry.type == DBUS_TYPE_BOOLEAN))
+               else if (os_strcmp(entry.key, "PersistentReconnect") == 0 &&
+                        entry.type == DBUS_TYPE_BOOLEAN)
                        wpa_s->conf->persistent_reconnect = entry.bool_value;
-               else if ((os_strcmp(entry.key, "ListenRegClass") == 0) &&
-                        (entry.type == DBUS_TYPE_UINT32)) {
+               else if (os_strcmp(entry.key, "ListenRegClass") == 0 &&
+                        entry.type == DBUS_TYPE_UINT32) {
                        wpa_s->conf->p2p_listen_reg_class = entry.uint32_value;
                        wpa_s->conf->changed_parameters |=
                                CFG_CHANGED_P2P_LISTEN_CHANNEL;
-               } else if ((os_strcmp(entry.key, "ListenChannel") == 0) &&
-                          (entry.type == DBUS_TYPE_UINT32)) {
+               } else if (os_strcmp(entry.key, "ListenChannel") == 0 &&
+                          entry.type == DBUS_TYPE_UINT32) {
                        wpa_s->conf->p2p_listen_channel = entry.uint32_value;
                        wpa_s->conf->changed_parameters |=
                                CFG_CHANGED_P2P_LISTEN_CHANNEL;
-               } else if ((os_strcmp(entry.key, "OperRegClass") == 0) &&
-                          (entry.type == DBUS_TYPE_UINT32)) {
+               } else if (os_strcmp(entry.key, "OperRegClass") == 0 &&
+                          entry.type == DBUS_TYPE_UINT32) {
                        wpa_s->conf->p2p_oper_reg_class = entry.uint32_value;
                        wpa_s->conf->changed_parameters |=
                                CFG_CHANGED_P2P_OPER_CHANNEL;
-               } else if ((os_strcmp(entry.key, "OperChannel") == 0) &&
-                          (entry.type == DBUS_TYPE_UINT32)) {
+               } else if (os_strcmp(entry.key, "OperChannel") == 0 &&
+                          entry.type == DBUS_TYPE_UINT32) {
                        wpa_s->conf->p2p_oper_channel = entry.uint32_value;
                        wpa_s->conf->changed_parameters |=
                                CFG_CHANGED_P2P_OPER_CHANNEL;
@@ -963,17 +1073,57 @@ dbus_bool_t wpas_dbus_setter_p2p_device_config(DBusMessageIter *iter,
 
                        wpa_s->conf->changed_parameters |=
                                        CFG_CHANGED_P2P_SSID_POSTFIX;
-               } else if ((os_strcmp(entry.key, "IntraBss") == 0) &&
-                          (entry.type == DBUS_TYPE_BOOLEAN)) {
+               } else if (os_strcmp(entry.key, "IntraBss") == 0 &&
+                          entry.type == DBUS_TYPE_BOOLEAN) {
                        wpa_s->conf->p2p_intra_bss = entry.bool_value;
                        wpa_s->conf->changed_parameters |=
                                CFG_CHANGED_P2P_INTRA_BSS;
-               } else if ((os_strcmp(entry.key, "GroupIdle") == 0) &&
-                          (entry.type == DBUS_TYPE_UINT32))
+#if defined TIZEN_EXT
+               } else if (os_strcmp(entry.key, "IpAddrGO") == 0) {
+                       if (entry.type != DBUS_TYPE_ARRAY ||
+                           entry.array_type != DBUS_TYPE_BYTE ||
+                           entry.array_len != 4)
+                               goto error;
+
+                       os_memcpy(wpa_s->conf->ip_addr_go,
+                                 entry.bytearray_value, 4);
+               } else if (os_strcmp(entry.key, "IpAddrMask") == 0) {
+                       if (entry.type != DBUS_TYPE_ARRAY ||
+                           entry.array_type != DBUS_TYPE_BYTE ||
+                           entry.array_len != 4)
+                               goto error;
+
+                       os_memcpy(wpa_s->conf->ip_addr_mask,
+                                 entry.bytearray_value, 4);
+               } else if (os_strcmp(entry.key, "IpAddrStart") == 0) {
+                       if (entry.type != DBUS_TYPE_ARRAY ||
+                           entry.array_type != DBUS_TYPE_BYTE ||
+                           entry.array_len != 4)
+                               goto error;
+
+                       os_memcpy(wpa_s->conf->ip_addr_start,
+                                 entry.bytearray_value, 4);
+               } else if (os_strcmp(entry.key, "IpAddrEnd") == 0) {
+                       if (entry.type != DBUS_TYPE_ARRAY ||
+                           entry.array_type != DBUS_TYPE_BYTE ||
+                           entry.array_len != 4)
+                               goto error;
+
+                       os_memcpy(wpa_s->conf->ip_addr_end,
+                                 entry.bytearray_value, 4);
+#endif /* TIZEN_EXT */
+               } else if (os_strcmp(entry.key, "GroupIdle") == 0 &&
+                          entry.type == DBUS_TYPE_UINT32)
                        wpa_s->conf->p2p_group_idle = entry.uint32_value;
                else if (os_strcmp(entry.key, "disassoc_low_ack") == 0 &&
                         entry.type == DBUS_TYPE_UINT32)
                        wpa_s->conf->disassoc_low_ack = entry.uint32_value;
+               else if (os_strcmp(entry.key, "NoGroupIface") == 0 &&
+                        entry.type == DBUS_TYPE_BOOLEAN)
+                       wpa_s->conf->p2p_no_group_iface = entry.bool_value;
+               else if (os_strcmp(entry.key, "p2p_search_delay") == 0 &&
+                        entry.type == DBUS_TYPE_UINT32)
+                       wpa_s->conf->p2p_search_delay = entry.uint32_value;
                else
                        goto error;
 
@@ -1127,6 +1277,7 @@ dbus_bool_t wpas_dbus_getter_p2p_role(DBusMessageIter *iter, DBusError *error,
                break;
        default:
                str = "device";
+               break;
        }
 
        return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING, &str,
@@ -1242,8 +1393,8 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type(
 
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter,
-                                                    DBusError *error,
-                                                    void *user_data)
+                                                   DBusError *error,
+                                                   void *user_data)
 {
        struct peer_handler_args *peer_args = user_data;
        const struct p2p_peer_info *info;
@@ -1267,8 +1418,8 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter,
 
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter,
-                                            DBusError *error,
-                                            void *user_data)
+                                           DBusError *error,
+                                           void *user_data)
 {
        struct peer_handler_args *peer_args = user_data;
        const struct p2p_peer_info *info;
@@ -1292,8 +1443,8 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter,
 
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter,
-                                                        DBusError *error,
-                                                        void *user_data)
+                                                       DBusError *error,
+                                                       void *user_data)
 {
        struct peer_handler_args *peer_args = user_data;
        const struct p2p_peer_info *info;
@@ -1351,8 +1502,7 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types(
        info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
                                  peer_args->p2p_device_addr, 0);
        if (info == NULL) {
-               dbus_set_error(error, DBUS_ERROR_FAILED,
-                              "failed to find peer");
+               dbus_set_error(error, DBUS_ERROR_FAILED, "failed to find peer");
                return FALSE;
        }
 
@@ -1360,18 +1510,13 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types(
                                              DBUS_TYPE_ARRAY_AS_STRING
                                              DBUS_TYPE_ARRAY_AS_STRING
                                              DBUS_TYPE_BYTE_AS_STRING,
-                                             &variant_iter)) {
-               dbus_set_error(error, DBUS_ERROR_FAILED,
-                              "%s: failed to construct message 1", __func__);
-               return FALSE;
-       }
-
-       if (!dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
+                                             &variant_iter) ||
+           !dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY,
                                              DBUS_TYPE_ARRAY_AS_STRING
                                              DBUS_TYPE_BYTE_AS_STRING,
                                              &array_iter)) {
                dbus_set_error(error, DBUS_ERROR_FAILED,
-                              "%s: failed to construct message 2", __func__);
+                              "%s: failed to construct message 1", __func__);
                return FALSE;
        }
 
@@ -1386,29 +1531,14 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types(
                        if (!dbus_message_iter_open_container(
                                    &array_iter, DBUS_TYPE_ARRAY,
                                    DBUS_TYPE_BYTE_AS_STRING,
-                                   &inner_array_iter)) {
-                               dbus_set_error(error, DBUS_ERROR_FAILED,
-                                              "%s: failed to construct "
-                                              "message 3 (%d)",
-                                              __func__, i);
-                               return FALSE;
-                       }
-
-                       if (!dbus_message_iter_append_fixed_array(
+                                   &inner_array_iter) ||
+                           !dbus_message_iter_append_fixed_array(
                                    &inner_array_iter, DBUS_TYPE_BYTE,
-                                   &sec_dev_type_list, WPS_DEV_TYPE_LEN)) {
-                               dbus_set_error(error, DBUS_ERROR_FAILED,
-                                              "%s: failed to construct "
-                                              "message 4 (%d)",
-                                              __func__, i);
-                               return FALSE;
-                       }
-
-                       if (!dbus_message_iter_close_container(
+                                   &sec_dev_type_list, WPS_DEV_TYPE_LEN) ||
+                           !dbus_message_iter_close_container(
                                    &array_iter, &inner_array_iter)) {
                                dbus_set_error(error, DBUS_ERROR_FAILED,
-                                              "%s: failed to construct "
-                                              "message 5 (%d)",
+                                              "%s: failed to construct message 2 (%d)",
                                               __func__, i);
                                return FALSE;
                        }
@@ -1417,15 +1547,10 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types(
                }
        }
 
-       if (!dbus_message_iter_close_container(&variant_iter, &array_iter)) {
-               dbus_set_error(error, DBUS_ERROR_FAILED,
-                              "%s: failed to construct message 6", __func__);
-               return FALSE;
-       }
-
-       if (!dbus_message_iter_close_container(iter, &variant_iter)) {
+       if (!dbus_message_iter_close_container(&variant_iter, &array_iter) ||
+           !dbus_message_iter_close_container(iter, &variant_iter)) {
                dbus_set_error(error, DBUS_ERROR_FAILED,
-                              "%s: failed to construct message 7", __func__);
+                              "%s: failed to construct message 3", __func__);
                return FALSE;
        }
 
@@ -1438,7 +1563,7 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter,
                                                       void *user_data)
 {
        struct wpabuf *vendor_extension[P2P_MAX_WPS_VENDOR_EXT];
-       int i, num;
+       unsigned int i, num = 0;
        struct peer_handler_args *peer_args = user_data;
        const struct p2p_peer_info *info;
 
@@ -1451,7 +1576,8 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter,
        }
 
        /* Add WPS vendor extensions attribute */
-       for (i = 0, num = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
+       os_memset(vendor_extension, 0, sizeof(vendor_extension));
+       for (i = 0; i < P2P_MAX_WPS_VENDOR_EXT; i++) {
                if (info->wps_vendor_ext[i] == NULL)
                        continue;
                vendor_extension[num] = info->wps_vendor_ext[i];
@@ -1470,15 +1596,207 @@ dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter,
 dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter,
                                          DBusError *error, void *user_data)
 {
-       dbus_bool_t success;
-       /* struct peer_handler_args *peer_args = user_data; */
+       struct peer_handler_args *peer_args = user_data;
+       const struct p2p_peer_info *info;
+
+       info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+                                 peer_args->p2p_device_addr, 0);
+       if (info == NULL) {
+               dbus_set_error(error, DBUS_ERROR_FAILED,
+                              "failed to find peer");
+               return FALSE;
+       }
+
+       if (info->wfd_subelems == NULL)
+               return wpas_dbus_simple_array_property_getter(iter,
+                                                             DBUS_TYPE_BYTE,
+                                                             NULL, 0, error);
+
+       return wpas_dbus_simple_array_property_getter(
+               iter, DBUS_TYPE_BYTE, (char *) info->wfd_subelems->buf,
+               info->wfd_subelems->used, error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_address(DBusMessageIter *iter,
+                                                    DBusError *error,
+                                                    void *user_data)
+{
+       struct peer_handler_args *peer_args = user_data;
+       const struct p2p_peer_info *info;
+
+       info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+                                 peer_args->p2p_device_addr, 0);
+       if (info == NULL) {
+               dbus_set_error(error, DBUS_ERROR_FAILED,
+                              "failed to find peer");
+               return FALSE;
+       }
 
-       success = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
-                                                        NULL, 0, error);
+       return wpas_dbus_simple_array_property_getter(
+               iter, DBUS_TYPE_BYTE, (char *) info->p2p_device_addr,
+               ETH_ALEN, error);
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_interface_address(DBusMessageIter *iter,
+                                                    DBusError *error,
+                                                    void *user_data)
+{
+       struct peer_handler_args *peer_args = user_data;
+       const struct p2p_peer_info *info;
+       u8 interface_address[ETH_ALEN];
+
+       info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+                                 peer_args->p2p_device_addr, 0);
+       if (info == NULL) {
+               dbus_set_error(error, DBUS_ERROR_FAILED,
+                              "failed to find peer");
+               return FALSE;
+       }
+
+       if (p2p_get_interface_addr(peer_args->wpa_s->global->p2p,
+                                 peer_args->p2p_device_addr, interface_address) < 0) {
+               os_memset(interface_address, 0, ETH_ALEN);
+       }
+
+       return wpas_dbus_simple_array_property_getter(
+               iter, DBUS_TYPE_BYTE, (char *) interface_address,
+               ETH_ALEN, error);
+}
+
+
+struct peer_group_data {
+       struct wpa_supplicant *wpa_s;
+       const struct p2p_peer_info *info;
+       char **paths;
+       unsigned int nb_paths;
+       int error;
+};
+
+
+static int match_group_where_peer_is_client(struct p2p_group *group,
+                                           void *user_data)
+{
+       struct peer_group_data *data = user_data;
+       const struct p2p_group_config *cfg;
+       struct wpa_supplicant *wpa_s_go;
+       char **paths;
+
+       if (!p2p_group_is_client_connected(group, data->info->p2p_device_addr))
+               return 1;
+
+       cfg = p2p_group_get_config(group);
+
+       wpa_s_go = wpas_get_p2p_go_iface(data->wpa_s, cfg->ssid,
+                                        cfg->ssid_len);
+       if (wpa_s_go == NULL)
+               return 1;
+
+       paths = os_realloc_array(data->paths, data->nb_paths + 1,
+                                sizeof(char *));
+       if (paths == NULL)
+               goto out_of_memory;
+
+       data->paths = paths;
+       data->paths[data->nb_paths] = wpa_s_go->dbus_groupobj_path;
+       data->nb_paths++;
+
+       return 1;
+
+out_of_memory:
+       data->error = ENOMEM;
+       return 0;
+}
+
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_groups(DBusMessageIter *iter,
+                                            DBusError *error,
+                                            void *user_data)
+{
+       struct peer_handler_args *peer_args = user_data;
+       const struct p2p_peer_info *info;
+       struct peer_group_data data;
+       struct wpa_supplicant *wpa_s, *wpa_s_go;
+       dbus_bool_t success = FALSE;
+
+       info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+                                 peer_args->p2p_device_addr, 0);
+       if (info == NULL) {
+               dbus_set_error(error, DBUS_ERROR_FAILED,
+                              "failed to find peer");
+               return FALSE;
+       }
+
+       os_memset(&data, 0, sizeof(data));
+
+       wpa_s = peer_args->wpa_s;
+       if (wpa_s->p2p_dev)
+               wpa_s = wpa_s->p2p_dev;
+
+       wpa_s_go = wpas_get_p2p_client_iface(wpa_s, info->p2p_device_addr);
+       if (wpa_s_go) {
+               data.paths = os_calloc(1, sizeof(char *));
+               if (data.paths == NULL)
+                       goto out_of_memory;
+               data.paths[0] = wpa_s_go->dbus_groupobj_path;
+               data.nb_paths = 1;
+       }
+
+       data.wpa_s = peer_args->wpa_s;
+       data.info = info;
+
+       p2p_loop_on_all_groups(peer_args->wpa_s->global->p2p,
+                              match_group_where_peer_is_client, &data);
+       if (data.error)
+               goto out_of_memory;
+
+       if (data.paths == NULL) {
+               return wpas_dbus_simple_array_property_getter(
+                       iter, DBUS_TYPE_OBJECT_PATH, NULL, 0, error);
+       }
+
+       success = wpas_dbus_simple_array_property_getter(iter,
+                                                        DBUS_TYPE_OBJECT_PATH,
+                                                        data.paths,
+                                                        data.nb_paths, error);
+       goto out;
+
+out_of_memory:
+       dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+out:
+       os_free(data.paths);
        return success;
 }
 
 
+dbus_bool_t wpas_dbus_getter_p2p_peer_go_device_address(DBusMessageIter *iter,
+                                                    DBusError *error,
+                                                    void *user_data)
+{
+       struct peer_handler_args *peer_args = user_data;
+       const struct p2p_peer_info *info;
+       u8 member_in_go_dev[ETH_ALEN];
+
+       info = p2p_get_peer_found(peer_args->wpa_s->global->p2p,
+                                 peer_args->p2p_device_addr, 0);
+       if (info == NULL) {
+               dbus_set_error(error, DBUS_ERROR_FAILED,
+                              "failed to find peer");
+               return FALSE;
+       }
+
+       if (p2p_get_member_in_go_dev(peer_args->wpa_s->global->p2p,
+                                 peer_args->p2p_device_addr, member_in_go_dev) < 0) {
+               os_memset(member_in_go_dev, 0, ETH_ALEN);
+       }
+
+       return wpas_dbus_simple_array_property_getter(
+               iter, DBUS_TYPE_BYTE, (char *) member_in_go_dev,
+               ETH_ALEN, error);
+}
+
+
 /**
  * wpas_dbus_getter_persistent_groups - Get array of persistent group objects
  * @iter: Pointer to incoming dbus message iter
@@ -1498,15 +1816,6 @@ dbus_bool_t wpas_dbus_getter_persistent_groups(DBusMessageIter *iter,
        unsigned int i = 0, num = 0;
        dbus_bool_t success = FALSE;
 
-       if (wpa_s->conf == NULL) {
-               wpa_printf(MSG_ERROR, "dbus: %s: "
-                          "An error occurred getting persistent groups list",
-                          __func__);
-               dbus_set_error_const(error, DBUS_ERROR_FAILED, "an error "
-                                    "occurred getting persistent groups list");
-               return FALSE;
-       }
-
        for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
                if (network_is_persistent_group(ssid))
                        num++;
@@ -1619,12 +1928,12 @@ DBusMessage * wpas_dbus_handler_add_persistent_group(
 
        ssid = wpa_config_add_network(wpa_s->conf);
        if (ssid == NULL) {
-               wpa_printf(MSG_ERROR, "dbus: %s: "
-                          "Cannot add new persistent group", __func__);
+               wpa_printf(MSG_ERROR,
+                          "dbus: %s: Cannot add new persistent group",
+                          __func__);
                reply = wpas_dbus_error_unknown_error(
                        message,
-                       "wpa_supplicant could not add "
-                       "a persistent group on this interface.");
+                       "wpa_supplicant could not add a persistent group on this interface.");
                goto err;
        }
 
@@ -1637,13 +1946,12 @@ DBusMessage * wpas_dbus_handler_add_persistent_group(
 
        dbus_error_init(&error);
        if (!set_network_properties(wpa_s, ssid, &iter, &error)) {
-               wpa_printf(MSG_DEBUG, "dbus: %s: "
-                          "Control interface could not set persistent group "
-                          "properties", __func__);
-               reply = wpas_dbus_reply_new_from_error(message, &error,
-                                                      DBUS_ERROR_INVALID_ARGS,
-                                                      "Failed to set network "
-                                                      "properties");
+               wpa_printf(MSG_DEBUG,
+                          "dbus: %s: Control interface could not set persistent group properties",
+                          __func__);
+               reply = wpas_dbus_reply_new_from_error(
+                       message, &error, DBUS_ERROR_INVALID_ARGS,
+                       "Failed to set network properties");
                dbus_error_free(&error);
                goto err;
        }
@@ -1655,15 +1963,13 @@ DBusMessage * wpas_dbus_handler_add_persistent_group(
 
        reply = dbus_message_new_method_return(message);
        if (reply == NULL) {
-               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                              NULL);
+               reply = wpas_dbus_error_no_memory(message);
                goto err;
        }
        if (!dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path,
                                      DBUS_TYPE_INVALID)) {
                dbus_message_unref(reply);
-               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                              NULL);
+               reply = wpas_dbus_error_no_memory(message);
                goto err;
        }
 
@@ -1693,7 +1999,7 @@ DBusMessage * wpas_dbus_handler_remove_persistent_group(
 {
        DBusMessage *reply = NULL;
        const char *op;
-       char *iface = NULL, *persistent_group_id = NULL;
+       char *iface = NULL, *persistent_group_id;
        int id;
        struct wpa_ssid *ssid;
 
@@ -1704,10 +2010,11 @@ DBusMessage * wpas_dbus_handler_remove_persistent_group(
         * Extract the network ID and ensure the network is actually a child of
         * this interface.
         */
-       iface = wpas_dbus_new_decompose_object_path(op, 1,
-                                                   &persistent_group_id,
-                                                   NULL);
-       if (iface == NULL || os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
+       iface = wpas_dbus_new_decompose_object_path(
+               op, WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART,
+               &persistent_group_id);
+       if (iface == NULL || persistent_group_id == NULL ||
+           os_strcmp(iface, wpa_s->dbus_new_path) != 0) {
                reply = wpas_dbus_error_invalid_args(message, op);
                goto out;
        }
@@ -1727,19 +2034,17 @@ DBusMessage * wpas_dbus_handler_remove_persistent_group(
        wpas_notify_persistent_group_removed(wpa_s, ssid);
 
        if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
-               wpa_printf(MSG_ERROR, "dbus: %s: "
-                          "error occurred when removing persistent group %d",
+               wpa_printf(MSG_ERROR,
+                          "dbus: %s: error occurred when removing persistent group %d",
                           __func__, id);
                reply = wpas_dbus_error_unknown_error(
                        message,
-                       "error removing the specified persistent group on "
-                       "this interface.");
+                       "error removing the specified persistent group on this interface.");
                goto out;
        }
 
 out:
        os_free(iface);
-       os_free(persistent_group_id);
        return reply;
 }
 
@@ -1750,8 +2055,8 @@ static void remove_persistent_group(struct wpa_supplicant *wpa_s,
        wpas_notify_persistent_group_removed(wpa_s, ssid);
 
        if (wpa_config_remove_network(wpa_s->conf, ssid->id) < 0) {
-               wpa_printf(MSG_ERROR, "dbus: %s: "
-                          "error occurred when removing persistent group %d",
+               wpa_printf(MSG_ERROR,
+                          "dbus: %s: error occurred when removing persistent group %d",
                           __func__, ssid->id);
                return;
        }
@@ -1828,9 +2133,9 @@ dbus_bool_t wpas_dbus_getter_p2p_group_members(DBusMessageIter *iter,
                if (!paths[i])
                        goto out_of_memory;
                os_snprintf(paths[i], WPAS_DBUS_OBJECT_PATH_MAX,
-                           "%s/" WPAS_DBUS_NEW_P2P_GROUPMEMBERS_PART
+                           "%s/" WPAS_DBUS_NEW_P2P_PEERS_PART
                            "/" COMPACT_MACSTR,
-                           wpa_s->dbus_groupobj_path, MAC2STR(addr));
+                           wpa_s->parent->dbus_new_path, MAC2STR(addr));
                i++;
        }
 
@@ -1859,6 +2164,7 @@ dbus_bool_t wpas_dbus_getter_p2p_group_ssid(DBusMessageIter *iter,
                                            DBusError *error, void *user_data)
 {
        struct wpa_supplicant *wpa_s = user_data;
+
        if (wpa_s->current_ssid == NULL)
                return FALSE;
        return wpas_dbus_simple_array_property_getter(
@@ -1919,15 +2225,14 @@ dbus_bool_t wpas_dbus_getter_p2p_group_passphrase(DBusMessageIter *iter,
                                                  void *user_data)
 {
        struct wpa_supplicant *wpa_s = user_data;
-       u8 role = wpas_get_p2p_role(wpa_s);
-       char *p_pass = NULL;
+       char *p_pass;
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
 
-       /* Verify correct role for this property */
-       if (role == WPAS_P2P_ROLE_GO) {
-               if (wpa_s->current_ssid == NULL)
-                       return FALSE;
-               p_pass = wpa_s->current_ssid->passphrase;
-       } else
+       if (ssid == NULL)
+               return FALSE;
+
+       p_pass = ssid->passphrase;
+       if (!p_pass)
                p_pass = "";
 
        return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
@@ -1940,20 +2245,20 @@ dbus_bool_t wpas_dbus_getter_p2p_group_psk(DBusMessageIter *iter,
                                           DBusError *error, void *user_data)
 {
        struct wpa_supplicant *wpa_s = user_data;
-       u8 role = wpas_get_p2p_role(wpa_s);
        u8 *p_psk = NULL;
        u8 psk_len = 0;
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
 
-       /* Verify correct role for this property */
-       if (role == WPAS_P2P_ROLE_CLIENT) {
-               if (wpa_s->current_ssid == NULL)
-                       return FALSE;
-               p_psk = wpa_s->current_ssid->psk;
-               psk_len = 32;
+       if (ssid == NULL)
+               return FALSE;
+
+       if (ssid->psk_set) {
+               p_psk = ssid->psk;
+               psk_len = sizeof(ssid->psk);
        }
 
        return wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
-                                                     &p_psk, psk_len, error);
+                                                     p_psk, psk_len, error);
 }
 
 
@@ -1964,8 +2269,9 @@ dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter,
        struct wpa_supplicant *wpa_s = user_data;
        struct hostapd_data *hapd;
        struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
-       int num_vendor_ext = 0;
-       int i;
+       unsigned int i, num_vendor_ext = 0;
+
+       os_memset(vendor_ext, 0, sizeof(vendor_ext));
 
        /* Verify correct role for this property */
        if (wpas_get_p2p_role(wpa_s) == WPAS_P2P_ROLE_GO) {
@@ -1976,11 +2282,9 @@ dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter,
                /* Parse WPS Vendor Extensions sent in Beacon/Probe Response */
                for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
                        if (hapd->conf->wps_vendor_ext[i] == NULL)
-                               vendor_ext[i] = NULL;
-                       else {
-                               vendor_ext[num_vendor_ext++] =
-                                       hapd->conf->wps_vendor_ext[i];
-                       }
+                               continue;
+                       vendor_ext[num_vendor_ext++] =
+                               hapd->conf->wps_vendor_ext[i];
                }
        }
 
@@ -1989,7 +2293,7 @@ dbus_bool_t wpas_dbus_getter_p2p_group_vendor_ext(DBusMessageIter *iter,
                                                            DBUS_TYPE_BYTE,
                                                            vendor_ext,
                                                            num_vendor_ext,
-                                                error);
+                                                           error);
 }
 
 
@@ -1998,7 +2302,7 @@ dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter,
                                                  void *user_data)
 {
        struct wpa_supplicant *wpa_s = user_data;
-       DBusMessageIter variant_iter, iter_dict;
+       DBusMessageIter variant_iter, iter_dict, array_iter, sub;
        struct wpa_dbus_dict_entry entry = { .type = DBUS_TYPE_STRING };
        unsigned int i;
        struct hostapd_data *hapd = NULL;
@@ -2010,6 +2314,82 @@ dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter,
                return FALSE;
 
        dbus_message_iter_recurse(iter, &variant_iter);
+       if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY)
+               return FALSE;
+
+       /*
+        * This is supposed to be array of bytearrays (aay), but the earlier
+        * implementation used a dict with "WPSVendorExtensions" as the key in
+        * this setter function which does not match the format used by the
+        * getter function. For backwards compatibility, allow both formats to
+        * be used in the setter.
+        */
+       if (dbus_message_iter_get_element_type(&variant_iter) ==
+           DBUS_TYPE_ARRAY) {
+               /* This is the proper format matching the getter */
+               struct wpabuf *vals[MAX_WPS_VENDOR_EXTENSIONS];
+
+               dbus_message_iter_recurse(&variant_iter, &array_iter);
+
+               if (dbus_message_iter_get_arg_type(&array_iter) !=
+                   DBUS_TYPE_ARRAY ||
+                   dbus_message_iter_get_element_type(&array_iter) !=
+                   DBUS_TYPE_BYTE) {
+                       wpa_printf(MSG_DEBUG,
+                                  "dbus: Not an array of array of bytes");
+                       return FALSE;
+               }
+
+               i = 0;
+               os_memset(vals, 0, sizeof(vals));
+
+               while (dbus_message_iter_get_arg_type(&array_iter) ==
+                      DBUS_TYPE_ARRAY) {
+                       char *val;
+                       int len;
+
+                       if (i == MAX_WPS_VENDOR_EXTENSIONS) {
+                               wpa_printf(MSG_DEBUG,
+                                          "dbus: Too many WPSVendorExtensions values");
+                               i = MAX_WPS_VENDOR_EXTENSIONS + 1;
+                               break;
+                       }
+
+                       dbus_message_iter_recurse(&array_iter, &sub);
+                       dbus_message_iter_get_fixed_array(&sub, &val, &len);
+                       wpa_hexdump(MSG_DEBUG, "dbus: WPSVendorExtentions[]",
+                                   val, len);
+                       vals[i] = wpabuf_alloc_copy(val, len);
+                       if (vals[i] == NULL) {
+                               i = MAX_WPS_VENDOR_EXTENSIONS + 1;
+                               break;
+                       }
+                       i++;
+                       dbus_message_iter_next(&array_iter);
+               }
+
+               if (i > MAX_WPS_VENDOR_EXTENSIONS) {
+                       for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++)
+                               wpabuf_free(vals[i]);
+                       return FALSE;
+               }
+
+               for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+                       wpabuf_free(hapd->conf->wps_vendor_ext[i]);
+                       hapd->conf->wps_vendor_ext[i] = vals[i];
+               }
+
+               hostapd_update_wps(hapd);
+
+               return TRUE;
+       }
+
+       if (dbus_message_iter_get_element_type(&variant_iter) !=
+           DBUS_TYPE_DICT_ENTRY)
+               return FALSE;
+
+       wpa_printf(MSG_DEBUG,
+                  "dbus: Try to use backwards compatibility version of WPSVendorExtensions setter");
        if (!wpa_dbus_dict_open_read(&variant_iter, &iter_dict, error))
                return FALSE;
 
@@ -2027,6 +2407,7 @@ dbus_bool_t wpas_dbus_setter_p2p_group_vendor_ext(DBusMessageIter *iter,
                                goto error;
 
                        for (i = 0; i < MAX_WPS_VENDOR_EXTENSIONS; i++) {
+                               wpabuf_free(hapd->conf->wps_vendor_ext[i]);
                                if (i < entry.array_len) {
                                        hapd->conf->wps_vendor_ext[i] =
                                                entry.binarray_value[i];
@@ -2075,30 +2456,31 @@ DBusMessage * wpas_dbus_handler_p2p_add_service(DBusMessage *message,
                if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
                        goto error;
 
-               if (!os_strcmp(entry.key, "service_type") &&
-                   (entry.type == DBUS_TYPE_STRING)) {
-                       if (!os_strcmp(entry.str_value, "upnp"))
+               if (os_strcmp(entry.key, "service_type") == 0 &&
+                   entry.type == DBUS_TYPE_STRING) {
+                       if (os_strcmp(entry.str_value, "upnp") == 0)
                                upnp = 1;
-                       else if (!os_strcmp(entry.str_value, "bonjour"))
+                       else if (os_strcmp(entry.str_value, "bonjour") == 0)
                                bonjour = 1;
                        else
                                goto error_clear;
-               } else if (!os_strcmp(entry.key, "version") &&
-                          entry.type == DBUS_TYPE_INT32) {
+               } else if (os_strcmp(entry.key, "version") == 0 &&
+                          entry.type == DBUS_TYPE_INT32) {
                        version = entry.uint32_value;
-               } else if (!os_strcmp(entry.key, "service") &&
-                            (entry.type == DBUS_TYPE_STRING)) {
+               } else if (os_strcmp(entry.key, "service") == 0 &&
+                          entry.type == DBUS_TYPE_STRING) {
+                       os_free(service);
                        service = os_strdup(entry.str_value);
-               } else if (!os_strcmp(entry.key, "query")) {
-                       if ((entry.type != DBUS_TYPE_ARRAY) ||
-                           (entry.array_type != DBUS_TYPE_BYTE))
+               } else if (os_strcmp(entry.key, "query") == 0) {
+                       if (entry.type != DBUS_TYPE_ARRAY ||
+                           entry.array_type != DBUS_TYPE_BYTE)
                                goto error_clear;
                        query = wpabuf_alloc_copy(
                                entry.bytearray_value,
                                entry.array_len);
-               } else if (!os_strcmp(entry.key, "response")) {
-                       if ((entry.type != DBUS_TYPE_ARRAY) ||
-                           (entry.array_type != DBUS_TYPE_BYTE))
+               } else if (os_strcmp(entry.key, "response") == 0) {
+                       if (entry.type != DBUS_TYPE_ARRAY ||
+                           entry.array_type != DBUS_TYPE_BYTE)
                                goto error_clear;
                        resp = wpabuf_alloc_copy(entry.bytearray_value,
                                                 entry.array_len);
@@ -2113,8 +2495,6 @@ DBusMessage * wpas_dbus_handler_p2p_add_service(DBusMessage *message,
                if (wpas_p2p_service_add_upnp(wpa_s, version, service) != 0)
                        goto error;
 
-               os_free(service);
-               service = NULL;
        } else if (bonjour == 1) {
                if (query == NULL || resp == NULL)
                        goto error;
@@ -2126,6 +2506,7 @@ DBusMessage * wpas_dbus_handler_p2p_add_service(DBusMessage *message,
        } else
                goto error;
 
+       os_free(service);
        return reply;
 error_clear:
        wpa_dbus_dict_entry_clear(&entry);
@@ -2160,11 +2541,11 @@ DBusMessage * wpas_dbus_handler_p2p_delete_service(
                if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
                        goto error;
 
-               if (!os_strcmp(entry.key, "service_type") &&
-                   (entry.type == DBUS_TYPE_STRING)) {
-                       if (!os_strcmp(entry.str_value, "upnp"))
+               if (os_strcmp(entry.key, "service_type") == 0 &&
+                   entry.type == DBUS_TYPE_STRING) {
+                       if (os_strcmp(entry.str_value, "upnp") == 0)
                                upnp = 1;
-                       else if (!os_strcmp(entry.str_value, "bonjour"))
+                       else if (os_strcmp(entry.str_value, "bonjour") == 0)
                                bonjour = 1;
                        else
                                goto error_clear;
@@ -2175,13 +2556,14 @@ DBusMessage * wpas_dbus_handler_p2p_delete_service(
                while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
                        if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
                                goto error;
-                       if (!os_strcmp(entry.key, "version") &&
+                       if (os_strcmp(entry.key, "version") == 0 &&
                            entry.type == DBUS_TYPE_INT32)
                                version = entry.uint32_value;
-                       else if (!os_strcmp(entry.key, "service") &&
-                                entry.type == DBUS_TYPE_STRING)
+                       else if (os_strcmp(entry.key, "service") == 0 &&
+                                entry.type == DBUS_TYPE_STRING) {
+                               os_free(service);
                                service = os_strdup(entry.str_value);
-                       else
+                       else
                                goto error_clear;
 
                        wpa_dbus_dict_entry_clear(&entry);
@@ -2191,7 +2573,6 @@ DBusMessage * wpas_dbus_handler_p2p_delete_service(
                        goto error;
 
                ret = wpas_p2p_service_del_upnp(wpa_s, version, service);
-               os_free(service);
                if (ret != 0)
                        goto error;
        } else if (bonjour == 1) {
@@ -2199,10 +2580,11 @@ DBusMessage * wpas_dbus_handler_p2p_delete_service(
                        if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
                                goto error;
 
-                       if (!os_strcmp(entry.key, "query")) {
-                               if ((entry.type != DBUS_TYPE_ARRAY) ||
-                                   (entry.array_type != DBUS_TYPE_BYTE))
+                       if (os_strcmp(entry.key, "query") == 0) {
+                               if (entry.type != DBUS_TYPE_ARRAY ||
+                                   entry.array_type != DBUS_TYPE_BYTE)
                                        goto error_clear;
+                               wpabuf_free(query);
                                query = wpabuf_alloc_copy(
                                        entry.bytearray_value,
                                        entry.array_len);
@@ -2218,14 +2600,17 @@ DBusMessage * wpas_dbus_handler_p2p_delete_service(
                ret = wpas_p2p_service_del_bonjour(wpa_s, query);
                if (ret != 0)
                        goto error;
-               wpabuf_free(query);
        } else
                goto error;
 
+       wpabuf_free(query);
+       os_free(service);
        return reply;
 error_clear:
        wpa_dbus_dict_entry_clear(&entry);
 error:
+       wpabuf_free(query);
+       os_free(service);
        return wpas_dbus_error_invalid_args(message, NULL);
 }
 
@@ -2261,22 +2646,22 @@ DBusMessage * wpas_dbus_handler_p2p_service_sd_req(
        while (wpa_dbus_dict_has_dict_entry(&iter_dict)) {
                if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
                        goto error;
-               if (!os_strcmp(entry.key, "peer_object") &&
+               if (os_strcmp(entry.key, "peer_object") == 0 &&
                    entry.type == DBUS_TYPE_OBJECT_PATH) {
                        peer_object_path = os_strdup(entry.str_value);
-               } else if (!os_strcmp(entry.key, "service_type") &&
+               } else if (os_strcmp(entry.key, "service_type") == 0 &&
                           entry.type == DBUS_TYPE_STRING) {
-                       if (!os_strcmp(entry.str_value, "upnp"))
+                       if (os_strcmp(entry.str_value, "upnp") == 0)
                                upnp = 1;
                        else
                                goto error_clear;
-               } else if (!os_strcmp(entry.key, "version") &&
+               } else if (os_strcmp(entry.key, "version") == 0 &&
                           entry.type == DBUS_TYPE_INT32) {
                        version = entry.uint32_value;
-               } else if (!os_strcmp(entry.key, "service") &&
+               } else if (os_strcmp(entry.key, "service") == 0 &&
                           entry.type == DBUS_TYPE_STRING) {
                        service = os_strdup(entry.str_value);
-               } else if (!os_strcmp(entry.key, "tlv")) {
+               } else if (os_strcmp(entry.key, "tlv") == 0) {
                        if (entry.type != DBUS_TYPE_ARRAY ||
                            entry.array_type != DBUS_TYPE_BYTE)
                                goto error_clear;
@@ -2354,16 +2739,17 @@ DBusMessage * wpas_dbus_handler_p2p_service_sd_res(
                if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
                        goto error;
 
-               if (!os_strcmp(entry.key, "peer_object") &&
+               if (os_strcmp(entry.key, "peer_object") == 0 &&
                    entry.type == DBUS_TYPE_OBJECT_PATH) {
                        peer_object_path = os_strdup(entry.str_value);
-               } else if (!os_strcmp(entry.key, "frequency") &&
+               } else if (os_strcmp(entry.key, "frequency") == 0 &&
                           entry.type == DBUS_TYPE_INT32) {
                        freq = entry.uint32_value;
-               } else if (!os_strcmp(entry.key, "dialog_token") &&
-                          entry.type == DBUS_TYPE_UINT32) {
+               } else if (os_strcmp(entry.key, "dialog_token") == 0 &&
+                          (entry.type == DBUS_TYPE_UINT32 ||
+                           entry.type == DBUS_TYPE_INT32)) {
                        dlg_tok = entry.uint32_value;
-               } else if (!os_strcmp(entry.key, "tlvs")) {
+               } else if (os_strcmp(entry.key, "tlvs") == 0) {
                        if (entry.type != DBUS_TYPE_ARRAY ||
                            entry.array_type != DBUS_TYPE_BYTE)
                                goto error_clear;
@@ -2374,12 +2760,9 @@ DBusMessage * wpas_dbus_handler_p2p_service_sd_res(
 
                wpa_dbus_dict_entry_clear(&entry);
        }
-       if (!peer_object_path ||
-           (parse_peer_object_path(peer_object_path, addr) < 0) ||
-           !p2p_peer_known(wpa_s->global->p2p, addr))
-               goto error;
-
-       if (tlv == NULL)
+       if (parse_peer_object_path(peer_object_path, addr) < 0 ||
+           !p2p_peer_known(wpa_s->global->p2p, addr) ||
+           tlv == NULL)
                goto error;
 
        wpas_p2p_sd_response(wpa_s, freq, addr, (u8) dlg_tok, tlv);
@@ -2407,7 +2790,7 @@ DBusMessage * wpas_dbus_handler_p2p_service_sd_cancel_req(
        if (req == 0)
                goto error;
 
-       if (!wpas_p2p_sd_cancel_request(wpa_s, req))
+       if (wpas_p2p_sd_cancel_request(wpa_s, req) < 0)
                goto error;
 
        return NULL;
@@ -2438,3 +2821,77 @@ DBusMessage * wpas_dbus_handler_p2p_serv_disc_external(
        return NULL;
 
 }
+
+
+#ifdef CONFIG_WIFI_DISPLAY
+
+dbus_bool_t wpas_dbus_getter_global_wfd_ies(DBusMessageIter *iter,
+                                           DBusError *error, void *user_data)
+{
+       struct wpa_global *global = user_data;
+       struct wpabuf *ie;
+       dbus_bool_t ret;
+
+       ie = wifi_display_get_wfd_ie(global);
+       if (ie == NULL)
+               return wpas_dbus_simple_array_property_getter(iter,
+                                                             DBUS_TYPE_BYTE,
+                                                             NULL, 0, error);
+
+       ret = wpas_dbus_simple_array_property_getter(iter, DBUS_TYPE_BYTE,
+                                                    wpabuf_head(ie),
+                                                    wpabuf_len(ie), error);
+       wpabuf_free(ie);
+
+       return ret;
+}
+
+
+dbus_bool_t wpas_dbus_setter_global_wfd_ies(DBusMessageIter *iter,
+                                           DBusError *error, void *user_data)
+{
+       struct wpa_global *global = user_data;
+       DBusMessageIter variant, array;
+       struct wpabuf *ie = NULL;
+       const u8 *data;
+       int len;
+
+       if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT)
+               goto err;
+
+       dbus_message_iter_recurse(iter, &variant);
+       if (dbus_message_iter_get_arg_type(&variant) != DBUS_TYPE_ARRAY)
+               goto err;
+
+       dbus_message_iter_recurse(&variant, &array);
+       dbus_message_iter_get_fixed_array(&array, &data, &len);
+       if (len == 0) {
+               wifi_display_enable(global, 0);
+               wifi_display_deinit(global);
+
+               return TRUE;
+       }
+
+       ie = wpabuf_alloc(len);
+       if (ie == NULL)
+               goto err;
+
+       wpabuf_put_data(ie, data, len);
+       if (wifi_display_subelem_set_from_ies(global, ie) != 0)
+               goto err;
+
+       if (global->wifi_display == 0)
+               wifi_display_enable(global, 1);
+
+       wpabuf_free(ie);
+
+       return TRUE;
+err:
+       wpabuf_free(ie);
+
+       dbus_set_error_const(error, DBUS_ERROR_INVALID_ARGS,
+                            "invalid message format");
+       return FALSE;
+}
+
+#endif /* CONFIG_WIFI_DISPLAY */
old mode 100644 (file)
new mode 100755 (executable)
index a11b3c8..2a6ebe3
@@ -14,11 +14,6 @@ struct peer_handler_args {
        u8 p2p_device_addr[ETH_ALEN];
 };
 
-struct groupmember_handler_args {
-       struct wpa_supplicant *wpa_s;
-       u8 member_addr[ETH_ALEN];
-};
-
 /*
  * P2P Device methods
  */
@@ -32,6 +27,9 @@ DBusMessage *wpas_dbus_handler_p2p_stop_find(
 DBusMessage *wpas_dbus_handler_p2p_rejectpeer(
        DBusMessage *message, struct wpa_supplicant *wpa_s);
 
+DBusMessage *wpas_dbus_handler_p2p_removeclient(
+       DBusMessage *message, struct wpa_supplicant *wpa_s);
+
 DBusMessage *wpas_dbus_handler_p2p_listen(
        DBusMessage *message, struct wpa_supplicant *wpa_s);
 
@@ -51,6 +49,10 @@ DBusMessage *wpas_dbus_handler_p2p_connect(
                DBusMessage *message,
                struct wpa_supplicant *wpa_s);
 
+DBusMessage * wpas_dbus_handler_p2p_cancel(
+               DBusMessage *message,
+               struct wpa_supplicant *wpa_s);
+
 DBusMessage *wpas_dbus_handler_p2p_invite(
                DBusMessage *message,
                struct wpa_supplicant *wpa_s);
@@ -114,39 +116,55 @@ dbus_bool_t wpas_dbus_getter_p2p_peergo(DBusMessageIter *iter,
  */
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_device_name(DBusMessageIter *iter,
-                                                  DBusError *error,
-                                                  void *user_data);
+                                                 DBusError *error,
+                                                 void *user_data);
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_primary_device_type(
        DBusMessageIter *iter, DBusError *error, void *user_data);
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_config_method(DBusMessageIter *iter,
-                                                    DBusError *error,
-                                                    void *user_data);
+                                                   DBusError *error,
+                                                   void *user_data);
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_level(DBusMessageIter *iter,
-                                            DBusError *error,
-                                            void *user_data);
+                                           DBusError *error,
+                                           void *user_data);
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_device_capability(DBusMessageIter *iter,
-                                                        DBusError *error,
-                                                        void *user_data);
+                                                       DBusError *error,
+                                                       void *user_data);
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_group_capability(DBusMessageIter *iter,
-                                                       DBusError *error,
-                                                       void *user_data);
+                                                      DBusError *error,
+                                                      void *user_data);
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_secondary_device_types(
        DBusMessageIter *iter, DBusError *error, void *user_data);
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_vendor_extension(DBusMessageIter *iter,
-                                                       DBusError *error,
-                                                       void *user_data);
+                                                      DBusError *error,
+                                                      void *user_data);
 
 dbus_bool_t wpas_dbus_getter_p2p_peer_ies(DBusMessageIter *iter,
                                          DBusError *error,
                                          void *user_data);
 
+dbus_bool_t wpas_dbus_getter_p2p_peer_device_address(DBusMessageIter *iter,
+                                                    DBusError *error,
+                                                    void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_interface_address(DBusMessageIter *iter,
+                                                    DBusError *error,
+                                                    void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_groups(DBusMessageIter *iter,
+                                            DBusError *error,
+                                            void *user_data);
+
+dbus_bool_t wpas_dbus_getter_p2p_peer_go_device_address(DBusMessageIter *iter,
+                                            DBusError *error,
+                                            void *user_data);
+
 /*
  * P2P Group properties
  */
@@ -207,5 +225,16 @@ DBusMessage * wpas_dbus_handler_remove_persistent_group(
 DBusMessage * wpas_dbus_handler_remove_all_persistent_groups(
        DBusMessage *message, struct wpa_supplicant *wpa_s);
 
+#ifdef CONFIG_WIFI_DISPLAY
+
+dbus_bool_t wpas_dbus_getter_global_wfd_ies(DBusMessageIter *iter,
+                                           DBusError *error,
+                                           void *user_data);
+
+dbus_bool_t wpas_dbus_setter_global_wfd_ies(DBusMessageIter *iter,
+                                           DBusError *error,
+                                           void *user_data);
+
+#endif /* CONFIG_WIFI_DISPLAY */
 
 #endif /* DBUS_NEW_HANDLERS_P2P_H */
old mode 100644 (file)
new mode 100755 (executable)
index 4ad5e7e..6b9fadb
@@ -41,8 +41,8 @@ static int wpas_dbus_handler_wps_role(DBusMessage *message,
        dbus_message_iter_recurse(entry_iter, &variant_iter);
        if (dbus_message_iter_get_arg_type(&variant_iter) !=
            DBUS_TYPE_STRING) {
-               wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Role type, "
-                          "string required");
+               wpa_printf(MSG_DEBUG,
+                          "dbus: WPS.Start - Wrong Role type, string required");
                *reply = wpas_dbus_error_invalid_args(message,
                                                      "Role must be a string");
                return -1;
@@ -70,10 +70,9 @@ static int wpas_dbus_handler_wps_type(DBusMessage *message,
        char *val;
 
        dbus_message_iter_recurse(entry_iter, &variant_iter);
-       if (dbus_message_iter_get_arg_type(&variant_iter) !=
-           DBUS_TYPE_STRING) {
-               wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Type type, "
-                          "string required");
+       if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_STRING) {
+               wpa_printf(MSG_DEBUG,
+                          "dbus: WPS.Start - Wrong Type type, string required");
                *reply = wpas_dbus_error_invalid_args(message,
                                                      "Type must be a string");
                return -1;
@@ -105,8 +104,8 @@ static int wpas_dbus_handler_wps_bssid(DBusMessage *message,
        if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY ||
            dbus_message_iter_get_element_type(&variant_iter) !=
            DBUS_TYPE_BYTE) {
-               wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Bssid type, "
-                          "byte array required");
+               wpa_printf(MSG_DEBUG,
+                          "dbus: WPS.Start - Wrong Bssid type, byte array required");
                *reply = wpas_dbus_error_invalid_args(
                        message, "Bssid must be a byte array");
                return -1;
@@ -114,8 +113,8 @@ static int wpas_dbus_handler_wps_bssid(DBusMessage *message,
        dbus_message_iter_recurse(&variant_iter, &array_iter);
        dbus_message_iter_get_fixed_array(&array_iter, &params->bssid, &len);
        if (len != ETH_ALEN) {
-               wpa_printf(MSG_DEBUG, "dbus: WPS.Stsrt - Wrong Bssid length "
-                          "%d", len);
+               wpa_printf(MSG_DEBUG, "dbus: WPS.Stsrt - Wrong Bssid length %d",
+                          len);
                *reply = wpas_dbus_error_invalid_args(message,
                                                      "Bssid is wrong length");
                return -1;
@@ -132,10 +131,9 @@ static int wpas_dbus_handler_wps_pin(DBusMessage *message,
        DBusMessageIter variant_iter;
 
        dbus_message_iter_recurse(entry_iter, &variant_iter);
-       if (dbus_message_iter_get_arg_type(&variant_iter) !=
-           DBUS_TYPE_STRING) {
-               wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong Pin type, "
-                          "string required");
+       if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_STRING) {
+               wpa_printf(MSG_DEBUG,
+                          "dbus: WPS.Start - Wrong Pin type, string required");
                *reply = wpas_dbus_error_invalid_args(message,
                                                      "Pin must be a string");
                return -1;
@@ -158,8 +156,8 @@ static int wpas_dbus_handler_wps_p2p_dev_addr(DBusMessage *message,
        if (dbus_message_iter_get_arg_type(&variant_iter) != DBUS_TYPE_ARRAY ||
            dbus_message_iter_get_element_type(&variant_iter) !=
            DBUS_TYPE_BYTE) {
-               wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong "
-                          "P2PDeviceAddress type, byte array required");
+               wpa_printf(MSG_DEBUG,
+                          "dbus: WPS.Start - Wrong P2PDeviceAddress type, byte array required");
                *reply = wpas_dbus_error_invalid_args(
                        message, "P2PDeviceAddress must be a byte array");
                return -1;
@@ -168,11 +166,11 @@ static int wpas_dbus_handler_wps_p2p_dev_addr(DBusMessage *message,
        dbus_message_iter_get_fixed_array(&array_iter, &params->p2p_dev_addr,
                                          &len);
        if (len != ETH_ALEN) {
-               wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Wrong "
-                          "P2PDeviceAddress length %d", len);
-               *reply = wpas_dbus_error_invalid_args(message,
-                                                     "P2PDeviceAddress "
-                                                     "has wrong length");
+               wpa_printf(MSG_DEBUG,
+                          "dbus: WPS.Start - Wrong P2PDeviceAddress length %d",
+                          len);
+               *reply = wpas_dbus_error_invalid_args(
+                       message, "P2PDeviceAddress has wrong length");
                return -1;
        }
        return 0;
@@ -249,54 +247,54 @@ DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message,
                dbus_message_iter_next(&dict_iter);
        }
 
+#ifdef CONFIG_AP
+       if (wpa_s->ap_iface && params.type == 1) {
+               if (params.pin == NULL) {
+                       wpa_printf(MSG_DEBUG,
+                                  "dbus: WPS.Start - Pin required for registrar role");
+                       return wpas_dbus_error_invalid_args(
+                               message, "Pin required for registrar role.");
+               }
+               ret = wpa_supplicant_ap_wps_pin(wpa_s,
+                                               params.bssid,
+                                               params.pin,
+                                               npin, sizeof(npin), 0);
+       } else if (wpa_s->ap_iface) {
+               ret = wpa_supplicant_ap_wps_pbc(wpa_s,
+                                               params.bssid,
+                                               params.p2p_dev_addr);
+       } else
+#endif /* CONFIG_AP */
        if (params.role == 0) {
                wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Role not specified");
                return wpas_dbus_error_invalid_args(message,
                                                    "Role not specified");
-       } else if (params.role == 1 && params.type == 0) {
+       } else if (params.role == 2) {
+               if (params.pin == NULL) {
+                       wpa_printf(MSG_DEBUG,
+                                  "dbus: WPS.Start - Pin required for registrar role");
+                       return wpas_dbus_error_invalid_args(
+                               message, "Pin required for registrar role.");
+               }
+               ret = wpas_wps_start_reg(wpa_s, params.bssid, params.pin,
+                                        NULL);
+       } else if (params.type == 0) {
                wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Type not specified");
                return wpas_dbus_error_invalid_args(message,
                                                    "Type not specified");
-       } else if (params.role == 2 && params.pin == NULL) {
-               wpa_printf(MSG_DEBUG, "dbus: WPS.Start - Pin required for "
-                          "registrar role");
-               return wpas_dbus_error_invalid_args(
-                       message, "Pin required for registrar role.");
-       }
-
-       if (params.role == 2)
-               ret = wpas_wps_start_reg(wpa_s, params.bssid, params.pin,
-                                        NULL);
-       else if (params.type == 1) {
-#ifdef CONFIG_AP
-               if (wpa_s->ap_iface)
-                       ret = wpa_supplicant_ap_wps_pin(wpa_s,
-                                                       params.bssid,
-                                                       params.pin,
-                                                       npin, sizeof(npin), 0);
-               else
-#endif /* CONFIG_AP */
-               {
-                       ret = wpas_wps_start_pin(wpa_s, params.bssid,
-                                                params.pin, 0,
-                                                DEV_PW_DEFAULT);
-                       if (ret > 0)
-                               os_snprintf(npin, sizeof(npin), "%08d", ret);
-               }
+       } else if (params.type == 1) {
+               ret = wpas_wps_start_pin(wpa_s, params.bssid,
+                                        params.pin, 0,
+                                        DEV_PW_DEFAULT);
+               if (ret > 0)
+                       os_snprintf(npin, sizeof(npin), "%08d", ret);
        } else {
-#ifdef CONFIG_AP
-               if (wpa_s->ap_iface)
-                       ret = wpa_supplicant_ap_wps_pbc(wpa_s,
-                                                       params.bssid,
-                                                       params.p2p_dev_addr);
-               else
-#endif /* CONFIG_AP */
                ret = wpas_wps_start_pbc(wpa_s, params.bssid, 0);
        }
 
        if (ret < 0) {
-               wpa_printf(MSG_DEBUG, "dbus: WPS.Start wpas_wps_failed in "
-                          "role %s and key %s",
+               wpa_printf(MSG_DEBUG,
+                          "dbus: WPS.Start wpas_wps_failed in role %s and key %s",
                           (params.role == 1 ? "enrollee" : "registrar"),
                           (params.type == 0 ? "" :
                            (params.type == 1 ? "pin" : "pbc")));
@@ -305,37 +303,59 @@ DBusMessage * wpas_dbus_handler_wps_start(DBusMessage *message,
        }
 
        reply = dbus_message_new_method_return(message);
-       if (!reply) {
-               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                             NULL);
-       }
+       if (!reply)
+               return wpas_dbus_error_no_memory(message);
 
        dbus_message_iter_init_append(reply, &iter);
-       if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) {
+       if (!wpa_dbus_dict_open_write(&iter, &dict_iter) ||
+           (os_strlen(npin) > 0 &&
+            !wpa_dbus_dict_append_string(&dict_iter, "Pin", npin)) ||
+           !wpa_dbus_dict_close_write(&iter, &dict_iter)) {
                dbus_message_unref(reply);
-               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                             NULL);
+               return wpas_dbus_error_no_memory(message);
        }
 
-       if (os_strlen(npin) > 0) {
-               if (!wpa_dbus_dict_append_string(&dict_iter, "Pin", npin)) {
-                       dbus_message_unref(reply);
-                       return dbus_message_new_error(message,
-                                                     DBUS_ERROR_NO_MEMORY,
-                                                     NULL);
-               }
-       }
+       return reply;
+}
+
+
+DBusMessage * wpas_dbus_handler_wps_cancel(DBusMessage *message,
+                                          struct wpa_supplicant *wpa_s)
+{
+       if (wpas_wps_cancel(wpa_s))
+               return wpas_dbus_error_unknown_error(message,
+                                                    "WPS cancel Failed");
+
+       return NULL;
+}
+
+DBusMessage * wpas_dbus_handler_wps_generate_pin(DBusMessage *message,
+                                          struct wpa_supplicant *wpa_s)
+{
+       DBusMessage *reply = NULL;
+       char npin[9] = { '\0' };
+       char *generated_pin;
+       int new_pin;
+
+       reply = dbus_message_new_method_return(message);
+       if (reply == NULL)
+                       return wpas_dbus_error_no_memory(message);
+
+       new_pin = wps_generate_pin();
+       os_snprintf(npin, sizeof(npin), "%08d", new_pin);
+       wpa_printf(MSG_DEBUG, "WPS: Randomly generated PIN: %s",
+                                               npin);
+       generated_pin = npin;
 
-       if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) {
+       if (!dbus_message_append_args(reply, DBUS_TYPE_STRING,
+                                        &generated_pin, DBUS_TYPE_INVALID)) {
                dbus_message_unref(reply);
-               return dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                             NULL);
+               return wpas_dbus_error_no_memory(message);
        }
 
        return reply;
 }
 
-
 /**
  * wpas_dbus_getter_process_credentials - Check if credentials are processed
  * @message: Pointer to incoming dbus message
@@ -351,7 +371,8 @@ dbus_bool_t wpas_dbus_getter_process_credentials(DBusMessageIter *iter,
                                                 void *user_data)
 {
        struct wpa_supplicant *wpa_s = user_data;
-       dbus_bool_t process = (wpa_s->conf->wps_cred_processing != 1);
+       dbus_bool_t process = wpa_s->conf->wps_cred_processing != 1;
+
        return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_BOOLEAN,
                                                &process, error);
 }
@@ -378,7 +399,7 @@ dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter,
                                              &process_credentials))
                return FALSE;
 
-       old_pc = (wpa_s->conf->wps_cred_processing != 1);
+       old_pc = wpa_s->conf->wps_cred_processing != 1;
        wpa_s->conf->wps_cred_processing = (process_credentials ? 2 : 1);
 
        if ((wpa_s->conf->wps_cred_processing != 1) != old_pc)
@@ -389,3 +410,62 @@ dbus_bool_t wpas_dbus_setter_process_credentials(DBusMessageIter *iter,
 
        return TRUE;
 }
+
+
+/**
+ * wpas_dbus_getter_config_methods - Get current WPS configuration methods
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Getter for "ConfigMethods" property. Returned boolean will be true if
+ * providing the relevant string worked, or false otherwise.
+ */
+dbus_bool_t wpas_dbus_getter_config_methods(DBusMessageIter *iter,
+                                           DBusError *error,
+                                           void *user_data)
+{
+       struct wpa_supplicant *wpa_s = user_data;
+       char *methods = wpa_s->conf->config_methods;
+
+       if (methods == NULL)
+               methods = "";
+       return wpas_dbus_simple_property_getter(iter, DBUS_TYPE_STRING,
+                                               &methods, error);
+}
+
+
+/**
+ * wpas_dbus_setter_config_methods - Set WPS configuration methods
+ * @iter: Pointer to incoming dbus message iter
+ * @error: Location to store error on failure
+ * @user_data: Function specific data
+ * Returns: TRUE on success, FALSE on failure
+ *
+ * Setter for "ConfigMethods" property. Sets the methods string, apply such
+ * change and returns true on success. Returns false otherwise.
+ */
+dbus_bool_t wpas_dbus_setter_config_methods(DBusMessageIter *iter,
+                                           DBusError *error,
+                                           void *user_data)
+{
+       struct wpa_supplicant *wpa_s = user_data;
+       char *methods, *new_methods;
+
+       if (!wpas_dbus_simple_property_setter(iter, error, DBUS_TYPE_STRING,
+                                             &methods))
+               return FALSE;
+
+       new_methods = os_strdup(methods);
+       if (!new_methods)
+               return FALSE;
+
+       os_free(wpa_s->conf->config_methods);
+       wpa_s->conf->config_methods = new_methods;
+
+       wpa_s->conf->changed_parameters |= CFG_CHANGED_CONFIG_METHODS;
+       wpa_supplicant_update_config(wpa_s);
+
+       return TRUE;
+}
old mode 100644 (file)
new mode 100755 (executable)
index cfa6a15..15b0901
@@ -15,6 +15,7 @@
 #include "dbus_common_i.h"
 #include "dbus_new.h"
 #include "dbus_new_helpers.h"
+#include "dbus_new_handlers.h"
 #include "dbus_dict_helpers.h"
 
 
@@ -38,27 +39,25 @@ static dbus_bool_t fill_dict_with_properties(
 
                if (!dbus_message_iter_open_container(dict_iter,
                                                      DBUS_TYPE_DICT_ENTRY,
-                                                     NULL, &entry_iter)) {
-                       dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
-                                            "no memory");
-                       return FALSE;
-               }
-               if (!dbus_message_iter_append_basic(&entry_iter,
+                                                     NULL, &entry_iter) ||
+                   !dbus_message_iter_append_basic(&entry_iter,
                                                    DBUS_TYPE_STRING,
-                                                   &dsc->dbus_property)) {
-                       dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY,
-                                            "no memory");
-                       return FALSE;
-               }
+                                                   &dsc->dbus_property))
+                       goto error;
 
                /* An error getting a property fails the request entirely */
                if (!dsc->getter(&entry_iter, error, user_data))
                        return FALSE;
 
-               dbus_message_iter_close_container(dict_iter, &entry_iter);
+               if (!dbus_message_iter_close_container(dict_iter, &entry_iter))
+                       goto error;
        }
 
        return TRUE;
+
+error:
+       dbus_set_error_const(error, DBUS_ERROR_NO_MEMORY, "no memory");
+       return FALSE;
 }
 
 
@@ -75,43 +74,38 @@ static dbus_bool_t fill_dict_with_properties(
  * with properties names as keys and theirs values as values.
  */
 static DBusMessage * get_all_properties(DBusMessage *message, char *interface,
-                                       struct wpa_dbus_object_desc *obj_dsc)
+                                       struct wpa_dbus_object_desc *obj_dsc)
 {
        DBusMessage *reply;
        DBusMessageIter iter, dict_iter;
        DBusError error;
 
        reply = dbus_message_new_method_return(message);
-       if (reply == NULL) {
-               wpa_printf(MSG_ERROR, "%s: out of memory creating dbus reply",
-                          __func__);
-               return NULL;
-       }
+       if (reply == NULL)
+               return wpas_dbus_error_no_memory(message);
 
        dbus_message_iter_init_append(reply, &iter);
        if (!wpa_dbus_dict_open_write(&iter, &dict_iter)) {
-               wpa_printf(MSG_ERROR, "%s: out of memory creating reply",
-                          __func__);
                dbus_message_unref(reply);
-               reply = dbus_message_new_error(message, DBUS_ERROR_NO_MEMORY,
-                                              "out of memory");
-               return reply;
+               return wpas_dbus_error_no_memory(message);
        }
 
        dbus_error_init(&error);
        if (!fill_dict_with_properties(&dict_iter, obj_dsc->properties,
-                                      interface, obj_dsc->user_data, &error))
-       {
+                                      interface, obj_dsc->user_data, &error)) {
                dbus_message_unref(reply);
-               reply = wpas_dbus_reply_new_from_error(message, &error,
-                                                      DBUS_ERROR_INVALID_ARGS,
-                                                      "No readable properties"
-                                                      " in this interface");
+               reply = wpas_dbus_reply_new_from_error(
+                       message, &error, DBUS_ERROR_INVALID_ARGS,
+                       "No readable properties in this interface");
                dbus_error_free(&error);
                return reply;
        }
 
-       wpa_dbus_dict_close_write(&iter, &dict_iter);
+       if (!wpa_dbus_dict_close_write(&iter, &dict_iter)) {
+               dbus_message_unref(reply);
+               return wpas_dbus_error_no_memory(message);
+       }
+
        return reply;
 }
 
@@ -132,8 +126,9 @@ static int is_signature_correct(DBusMessage *message,
        for (arg = method_dsc->args; arg && arg->name; arg++) {
                if (arg->dir == ARG_IN) {
                        size_t blen = registered_sig + MAX_SIG_LEN - pos;
+
                        ret = os_snprintf(pos, blen, "%s", arg->type);
-                       if (ret < 0 || (size_t) ret >= blen)
+                       if (os_snprintf_error(blen, ret))
                                return 0;
                        pos += ret;
                }
@@ -267,10 +262,13 @@ properties_get_or_set(DBusMessage *message, DBusMessageIter *iter,
        }
 
        if (os_strncmp(WPA_DBUS_PROPERTIES_GET, method,
-                      WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) == 0)
+                      WPAS_DBUS_METHOD_SIGNAL_PROP_MAX) == 0) {
+               wpa_printf(MSG_MSGDUMP, "%s: Get(%s)", __func__, property);
                return properties_get(message, property_dsc,
                                      obj_dsc->user_data);
+       }
 
+       wpa_printf(MSG_MSGDUMP, "%s: Set(%s)", __func__, property);
        return properties_set(message, property_dsc, obj_dsc->user_data);
 }
 
@@ -292,8 +290,7 @@ static DBusMessage * properties_handler(DBusMessage *message,
            !os_strncmp(WPA_DBUS_PROPERTIES_GETALL, method,
                        WPAS_DBUS_METHOD_SIGNAL_PROP_MAX)) {
                /* First argument: interface name (DBUS_TYPE_STRING) */
-               if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
-               {
+               if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
                        return dbus_message_new_error(message,
                                                      DBUS_ERROR_INVALID_ARGS,
                                                      NULL);
@@ -349,8 +346,7 @@ static DBusMessage * msg_method_handler(DBusMessage *message,
                                              NULL);
        }
 
-       return method_dsc->method_handler(message,
-                                         obj_dsc->user_data);
+       return method_dsc->method_handler(message, obj_dsc->user_data);
 }
 
 
@@ -385,8 +381,9 @@ static DBusHandlerResult message_handler(DBusConnection *connection,
        if (!method || !path || !msg_interface)
                return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
-       wpa_printf(MSG_MSGDUMP, "dbus: %s.%s (%s)",
-                  msg_interface, method, path);
+       wpa_printf(MSG_MSGDUMP, "dbus: %s.%s (%s) [%s]",
+                  msg_interface, method, path,
+                  dbus_message_get_signature(message));
 
        /* if message is introspection method call */
        if (!os_strncmp(WPA_DBUS_INTROSPECTION_METHOD, method,
@@ -398,8 +395,7 @@ static DBusHandlerResult message_handler(DBusConnection *connection,
 #else /* CONFIG_CTRL_IFACE_DBUS_INTRO */
                reply = dbus_message_new_error(
                        message, DBUS_ERROR_UNKNOWN_METHOD,
-                       "wpa_supplicant was compiled without "
-                       "introspection support.");
+                       "wpa_supplicant was compiled without introspection support.");
 #endif /* CONFIG_CTRL_IFACE_DBUS_INTRO */
        } else if (!os_strncmp(WPA_DBUS_PROPERTIES_INTERFACE, msg_interface,
                             WPAS_DBUS_INTERFACE_MAX)) {
@@ -452,6 +448,7 @@ static void free_dbus_object_desc_cb(DBusConnection *connection, void *obj_dsc)
        free_dbus_object_desc(obj_dsc);
 }
 
+
 /**
  * wpa_dbus_ctrl_iface_init - Initialize dbus control interface
  * @application_data: Pointer to application specific data structure
@@ -479,30 +476,28 @@ int wpa_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface,
        obj_desc->path = os_strdup(dbus_path);
 
        /* Register the message handler for the global dbus interface */
-       if (!dbus_connection_register_object_path(iface->con,
-                                                 dbus_path, &wpa_vtable,
-                                                 obj_desc)) {
-               wpa_printf(MSG_ERROR, "dbus: Could not set up message "
-                          "handler");
+       if (!dbus_connection_register_object_path(iface->con, dbus_path,
+                                                 &wpa_vtable, obj_desc)) {
+               wpa_printf(MSG_ERROR, "dbus: Could not set up message handler");
                return -1;
        }
 
        /* Register our service with the message bus */
        dbus_error_init(&error);
-       switch (dbus_bus_request_name(iface->con, dbus_service,
-                                     0, &error)) {
+       switch (dbus_bus_request_name(iface->con, dbus_service, 0, &error)) {
        case DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER:
                ret = 0;
                break;
        case DBUS_REQUEST_NAME_REPLY_EXISTS:
        case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
        case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
-               wpa_printf(MSG_ERROR, "dbus: Could not request service name: "
-                          "already registered");
+               wpa_printf(MSG_ERROR,
+                          "dbus: Could not request service name: already registered");
                break;
        default:
-               wpa_printf(MSG_ERROR, "dbus: Could not request service name: "
-                          "%s %s", error.name, error.message);
+               wpa_printf(MSG_ERROR,
+                          "dbus: Could not request service name: %s %s",
+                          error.name, error.message);
                break;
        }
        dbus_error_free(&error);
@@ -526,14 +521,12 @@ int wpa_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface,
  *
  * Registers a new interface with dbus and assigns it a dbus object path.
  */
-int wpa_dbus_register_object_per_iface(
-       struct wpas_dbus_priv *ctrl_iface,
-       const char *path, const char *ifname,
-       struct wpa_dbus_object_desc *obj_desc)
+int wpa_dbus_register_object_per_iface(struct wpas_dbus_priv *ctrl_iface,
+                                      const char *path, const char *ifname,
+                                      struct wpa_dbus_object_desc *obj_desc)
 {
        DBusConnection *con;
        DBusError error;
-
        DBusObjectPathVTable vtable = {
                &free_dbus_object_desc_cb, &message_handler,
                NULL, NULL, NULL, NULL
@@ -551,14 +544,12 @@ int wpa_dbus_register_object_per_iface(
        /* Register the message handler for the interface functions */
        if (!dbus_connection_try_register_object_path(con, path, &vtable,
                                                      obj_desc, &error)) {
-               if (!os_strcmp(error.name, DBUS_ERROR_OBJECT_PATH_IN_USE)) {
+               if (os_strcmp(error.name, DBUS_ERROR_OBJECT_PATH_IN_USE) == 0) {
                        wpa_printf(MSG_DEBUG, "dbus: %s", error.message);
                } else {
-                       wpa_printf(MSG_ERROR, "dbus: Could not set up message "
-                                  "handler for interface %s object %s",
-                                  ifname, path);
-                       wpa_printf(MSG_ERROR, "dbus error: %s", error.name);
-                       wpa_printf(MSG_ERROR, "dbus: %s", error.message);
+                       wpa_printf(MSG_ERROR,
+                                  "dbus: Could not set up message handler for interface %s object %s (error: %s message: %s)",
+                                  ifname, path, error.name, error.message);
                }
                dbus_error_free(&error);
                return -1;
@@ -588,13 +579,14 @@ int wpa_dbus_unregister_object_per_iface(
 
        dbus_connection_get_object_path_data(con, path, (void **) &obj_desc);
        if (!obj_desc) {
-               wpa_printf(MSG_ERROR, "dbus: %s: Could not obtain object's "
-                          "private data: %s", __func__, path);
-       } else {
-               eloop_cancel_timeout(flush_object_timeout_handler, con,
-                                    obj_desc);
+               wpa_printf(MSG_ERROR,
+                          "dbus: %s: Could not obtain object's private data: %s",
+                          __func__, path);
+               return 0;
        }
 
+       eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc);
+
        if (!dbus_connection_unregister_object_path(con, path))
                return -1;
 
@@ -623,24 +615,22 @@ static dbus_bool_t put_changed_properties(
 
                if (!dbus_message_iter_open_container(dict_iter,
                                                      DBUS_TYPE_DICT_ENTRY,
-                                                     NULL, &entry_iter))
-                       return FALSE;
-
-               if (!dbus_message_iter_append_basic(&entry_iter,
+                                                     NULL, &entry_iter) ||
+                   !dbus_message_iter_append_basic(&entry_iter,
                                                    DBUS_TYPE_STRING,
                                                    &dsc->dbus_property))
                        return FALSE;
 
                dbus_error_init(&error);
                if (!dsc->getter(&entry_iter, &error, obj_dsc->user_data)) {
-                       if (dbus_error_is_set (&error)) {
-                               wpa_printf(MSG_ERROR, "dbus: %s: Cannot get "
-                                          "new value of property %s: (%s) %s",
-                                          __func__, dsc->dbus_property,
-                                          error.name, error.message);
+                       if (dbus_error_is_set(&error)) {
+                               wpa_printf(MSG_ERROR,
+                                          "dbus: %s: Cannot get new value of property %s: (%s) %s",
+                                          __func__, dsc->dbus_property,
+                                          error.name, error.message);
                        } else {
-                               wpa_printf(MSG_ERROR, "dbus: %s: Cannot get "
-                                          "new value of property %s",
+                               wpa_printf(MSG_ERROR,
+                                          "dbus: %s: Cannot get new value of property %s",
                                           __func__, dsc->dbus_property);
                        }
                        dbus_error_free(&error);
@@ -670,38 +660,23 @@ static void do_send_prop_changed_signal(
        dbus_message_iter_init_append(msg, &signal_iter);
 
        if (!dbus_message_iter_append_basic(&signal_iter, DBUS_TYPE_STRING,
-                                           &interface))
-               goto err;
-
-       /* Changed properties dict */
-       if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
-                                             "{sv}", &dict_iter))
-               goto err;
-
-       if (!put_changed_properties(obj_dsc, interface, &dict_iter, 0))
-               goto err;
-
-       if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
-               goto err;
-
-       /* Invalidated properties array (empty) */
-       if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
-                                             "s", &dict_iter))
-               goto err;
-
-       if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
-               goto err;
-
-       dbus_connection_send(con, msg, NULL);
+                                           &interface) ||
+           /* Changed properties dict */
+           !dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
+                                             "{sv}", &dict_iter) ||
+           !put_changed_properties(obj_dsc, interface, &dict_iter, 0) ||
+           !dbus_message_iter_close_container(&signal_iter, &dict_iter) ||
+           /* Invalidated properties array (empty) */
+           !dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
+                                             "s", &dict_iter) ||
+           !dbus_message_iter_close_container(&signal_iter, &dict_iter)) {
+               wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
+                          __func__);
+       } else {
+               dbus_connection_send(con, msg, NULL);
+       }
 
-out:
        dbus_message_unref(msg);
-       return;
-
-err:
-       wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
-                  __func__);
-       goto out;
 }
 
 
@@ -719,25 +694,16 @@ static void do_send_deprecated_prop_changed_signal(
        dbus_message_iter_init_append(msg, &signal_iter);
 
        if (!dbus_message_iter_open_container(&signal_iter, DBUS_TYPE_ARRAY,
-                                             "{sv}", &dict_iter))
-               goto err;
-
-       if (!put_changed_properties(obj_dsc, interface, &dict_iter, 1))
-               goto err;
-
-       if (!dbus_message_iter_close_container(&signal_iter, &dict_iter))
-               goto err;
-
-       dbus_connection_send(con, msg, NULL);
+                                             "{sv}", &dict_iter) ||
+           !put_changed_properties(obj_dsc, interface, &dict_iter, 1) ||
+           !dbus_message_iter_close_container(&signal_iter, &dict_iter)) {
+               wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
+                          __func__);
+       } else {
+               dbus_connection_send(con, msg, NULL);
+       }
 
-out:
        dbus_message_unref(msg);
-       return;
-
-err:
-       wpa_printf(MSG_DEBUG, "dbus: %s: Failed to construct signal",
-                  __func__);
-       goto out;
 }
 
 
@@ -769,8 +735,9 @@ static void flush_object_timeout_handler(void *eloop_ctx, void *timeout_ctx)
        DBusConnection *con = eloop_ctx;
        struct wpa_dbus_object_desc *obj_desc = timeout_ctx;
 
-       wpa_printf(MSG_DEBUG, "dbus: %s: Timeout - sending changed properties "
-                  "of object %s", __func__, obj_desc->path);
+       wpa_printf(MSG_DEBUG,
+                  "dbus: %s: Timeout - sending changed properties of object %s",
+                  __func__, obj_desc->path);
        wpa_dbus_flush_object_changed_properties(con, obj_desc->path);
 }
 
@@ -840,7 +807,6 @@ void wpa_dbus_flush_object_changed_properties(DBusConnection *con,
                return;
        eloop_cancel_timeout(flush_object_timeout_handler, con, obj_desc);
 
-       dsc = obj_desc->properties;
        for (dsc = obj_desc->properties, i = 0; dsc && dsc->dbus_property;
             dsc++, i++) {
                if (obj_desc->prop_changed_flags == NULL ||
@@ -882,8 +848,9 @@ void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface,
        dbus_connection_get_object_path_data(iface->con, path,
                                             (void **) &obj_desc);
        if (!obj_desc) {
-               wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: "
-                          "could not obtain object's private data: %s", path);
+               wpa_printf(MSG_ERROR,
+                          "dbus: wpa_dbus_property_changed: could not obtain object's private data: %s",
+                          path);
                return;
        }
 
@@ -896,13 +863,14 @@ void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface,
                }
 
        if (!dsc || !dsc->dbus_property) {
-               wpa_printf(MSG_ERROR, "dbus: wpa_dbus_property_changed: "
-                          "no property %s in object %s", property, path);
+               wpa_printf(MSG_ERROR,
+                          "dbus: wpa_dbus_property_changed: no property %s in object %s",
+                          property, path);
                return;
        }
 
        if (!eloop_is_timeout_registered(flush_object_timeout_handler,
-                                        iface->con, obj_desc->path)) {
+                                        iface->con, obj_desc)) {
                eloop_register_timeout(0, WPA_DBUS_SEND_PROP_CHANGED_TIMEOUT,
                                       flush_object_timeout_handler,
                                       iface->con, obj_desc);
@@ -934,8 +902,9 @@ dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
        dbus_connection_get_object_path_data(iface->con, path,
                                             (void **) &obj_desc);
        if (!obj_desc) {
-               wpa_printf(MSG_ERROR, "dbus: %s: could not obtain object's "
-                          "private data: %s", __func__, path);
+               wpa_printf(MSG_ERROR,
+                          "dbus: %s: could not obtain object's private data: %s",
+                          __func__, path);
                return FALSE;
        }
 
@@ -949,10 +918,11 @@ dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
        if (!fill_dict_with_properties(&dict_iter, obj_desc->properties,
                                       interface, obj_desc->user_data,
                                       &error)) {
-               wpa_printf(MSG_ERROR, "dbus: %s: failed to get object"
-                          " properties: (%s) %s", __func__,
-                          dbus_error_is_set(&error) ? error.name : "none",
-                          dbus_error_is_set(&error) ? error.message : "none");
+               wpa_printf(MSG_ERROR,
+                          "dbus: %s: failed to get object properties: (%s) %s",
+                          __func__,
+                          dbus_error_is_set(&error) ? error.name : "none",
+                          dbus_error_is_set(&error) ? error.message : "none");
                dbus_error_free(&error);
                return FALSE;
        }
@@ -963,29 +933,34 @@ dbus_bool_t wpa_dbus_get_object_properties(struct wpas_dbus_priv *iface,
 /**
  * wpas_dbus_new_decompose_object_path - Decompose an interface object path into parts
  * @path: The dbus object path
- * @p2p_persistent_group: indicates whether to parse the path as a P2P
- *                        persistent group object
- * @network: (out) the configured network this object path refers to, if any
- * @bssid: (out) the scanned bssid this object path refers to, if any
- * Returns: The object path of the network interface this path refers to
+ * @sep: Separating part (e.g., "Networks" or "PersistentGroups")
+ * @item: (out) The part following the specified separator, if any
+ * Returns: The object path of the interface this path refers to
  *
- * For a given object path, decomposes the object path into object id, network,
- * and BSSID parts, if those parts exist.
+ * For a given object path, decomposes the object path into object id and
+ * requested part, if those parts exist. The caller is responsible for freeing
+ * the returned value. The *item pointer points to that allocated value and must
+ * not be freed separately.
+ *
+ * As an example, path = "/fi/w1/wpa_supplicant1/Interfaces/1/Networks/0" and
+ * sep = "Networks" would result in "/fi/w1/wpa_supplicant1/Interfaces/1"
+ * getting returned and *items set to point to "0".
  */
-char *wpas_dbus_new_decompose_object_path(const char *path,
-                                          int p2p_persistent_group,
-                                          char **network,
-                                          char **bssid)
+char * wpas_dbus_new_decompose_object_path(const char *path, const char *sep,
+                                          char **item)
 {
        const unsigned int dev_path_prefix_len =
                os_strlen(WPAS_DBUS_NEW_PATH_INTERFACES "/");
        char *obj_path_only;
-       char *next_sep;
+       char *pos;
+       size_t sep_len;
 
-       /* Be a bit paranoid about path */
-       if (!path || os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/",
-                               dev_path_prefix_len))
-               return NULL;
+       *item = NULL;
+
+       /* Verify that this starts with our interface prefix */
+       if (os_strncmp(path, WPAS_DBUS_NEW_PATH_INTERFACES "/",
+                      dev_path_prefix_len) != 0)
+               return NULL; /* not our path */
 
        /* Ensure there's something at the end of the path */
        if ((path + dev_path_prefix_len)[0] == '\0')
@@ -995,39 +970,20 @@ char *wpas_dbus_new_decompose_object_path(const char *path,
        if (obj_path_only == NULL)
                return NULL;
 
-       next_sep = os_strchr(obj_path_only + dev_path_prefix_len, '/');
-       if (next_sep != NULL) {
-               const char *net_part = os_strstr(
-                       next_sep, p2p_persistent_group ?
-                       WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART "/" :
-                       WPAS_DBUS_NEW_NETWORKS_PART "/");
-               const char *bssid_part = os_strstr(
-                       next_sep, WPAS_DBUS_NEW_BSSIDS_PART "/");
-
-               if (network && net_part) {
-                       /* Deal with a request for a configured network */
-                       const char *net_name = net_part +
-                               os_strlen(p2p_persistent_group ?
-                                         WPAS_DBUS_NEW_PERSISTENT_GROUPS_PART
-                                         "/" :
-                                         WPAS_DBUS_NEW_NETWORKS_PART "/");
-                       *network = NULL;
-                       if (os_strlen(net_name))
-                               *network = os_strdup(net_name);
-               } else if (bssid && bssid_part) {
-                       /* Deal with a request for a scanned BSSID */
-                       const char *bssid_name = bssid_part +
-                               os_strlen(WPAS_DBUS_NEW_BSSIDS_PART "/");
-                       if (os_strlen(bssid_name))
-                               *bssid = os_strdup(bssid_name);
-                       else
-                               *bssid = NULL;
-               }
+       pos = obj_path_only + dev_path_prefix_len;
+       pos = os_strchr(pos, '/');
+       if (pos == NULL)
+               return obj_path_only; /* no next item on the path */
 
-               /* Cut off interface object path before "/" */
-               *next_sep = '\0';
-       }
+        /* Separate network interface prefix from the path */
+       *pos++ = '\0';
+
+       sep_len = os_strlen(sep);
+       if (os_strncmp(pos, sep, sep_len) != 0 || pos[sep_len] != '/')
+               return obj_path_only; /* no match */
 
+        /* return a pointer to the requested item */
+       *item = pos + sep_len + 1;
        return obj_path_only;
 }
 
old mode 100644 (file)
new mode 100755 (executable)
index 6d31ad5..6e2c1f1
 
 #include <dbus/dbus.h>
 
-typedef DBusMessage * (* WPADBusMethodHandler)(DBusMessage *message,
-                                              void *user_data);
-typedef void (* WPADBusArgumentFreeFunction)(void *handler_arg);
+typedef DBusMessage * (*WPADBusMethodHandler)(DBusMessage *message,
+                                             void *user_data);
+typedef void (*WPADBusArgumentFreeFunction)(void *handler_arg);
 
-typedef dbus_bool_t (* WPADBusPropertyAccessor)(DBusMessageIter *iter,
-                                                DBusError *error,
-                                               void *user_data);
+typedef dbus_bool_t (*WPADBusPropertyAccessor)(DBusMessageIter *iter,
+                                              DBusError *error,
+                                              void *user_data);
 
 struct wpa_dbus_object_desc {
        DBusConnection *connection;
@@ -137,10 +137,8 @@ void wpa_dbus_mark_property_changed(struct wpas_dbus_priv *iface,
 DBusMessage * wpa_dbus_introspect(DBusMessage *message,
                                  struct wpa_dbus_object_desc *obj_dsc);
 
-char *wpas_dbus_new_decompose_object_path(const char *path,
-                                          int p2p_persistent_group,
-                                          char **network,
-                                          char **bssid);
+char * wpas_dbus_new_decompose_object_path(const char *path, const char *sep,
+                                          char **item);
 
 DBusMessage *wpas_dbus_reply_new_from_error(DBusMessage *message,
                                            DBusError *error,
old mode 100644 (file)
new mode 100755 (executable)
index 3b090c0..6209c67
@@ -37,14 +37,16 @@ static struct interfaces * add_interface(struct dl_list *list,
        iface = os_zalloc(sizeof(struct interfaces));
        if (!iface)
                return NULL;
+       iface->dbus_interface = os_strdup(dbus_interface);
        iface->xml = wpabuf_alloc(6000);
-       if (iface->xml == NULL) {
+       if (iface->dbus_interface == NULL || iface->xml == NULL) {
+               os_free(iface->dbus_interface);
+               wpabuf_free(iface->xml);
                os_free(iface);
                return NULL;
        }
        wpabuf_printf(iface->xml, "<interface name=\"%s\">", dbus_interface);
        dl_list_add_tail(list, &iface->list);
-       iface->dbus_interface = os_strdup(dbus_interface);
        return iface;
 }
 
@@ -96,6 +98,7 @@ static void extract_interfaces_methods(
 {
        const struct wpa_dbus_method_desc *dsc;
        struct interfaces *iface;
+
        for (dsc = methods; dsc && dsc->dbus_method; dsc++) {
                iface = add_interface(list, dsc->dbus_interface);
                if (iface)
@@ -110,6 +113,7 @@ static void extract_interfaces_signals(
 {
        const struct wpa_dbus_signal_desc *dsc;
        struct interfaces *iface;
+
        for (dsc = signals; dsc && dsc->dbus_signal; dsc++) {
                iface = add_interface(list, dsc->dbus_interface);
                if (iface)
@@ -124,6 +128,7 @@ static void extract_interfaces_properties(
 {
        const struct wpa_dbus_property_desc *dsc;
        struct interfaces *iface;
+
        for (dsc = properties; dsc && dsc->dbus_property; dsc++) {
                iface = add_interface(list, dsc->dbus_interface);
                if (iface)
@@ -154,14 +159,14 @@ static void extract_interfaces(struct dl_list *list,
 static void add_interfaces(struct dl_list *list, struct wpabuf *xml)
 {
        struct interfaces *iface, *n;
+
        dl_list_for_each_safe(iface, n, list, struct interfaces, list) {
                if (wpabuf_len(iface->xml) + 20 < wpabuf_tailroom(xml)) {
                        wpabuf_put_buf(xml, iface->xml);
                        wpabuf_put_str(xml, "</interface>");
                } else {
-                       wpa_printf(MSG_DEBUG, "dbus: Not enough room for "
-                                  "add_interfaces inspect data: tailroom %u, "
-                                  "add %u",
+                       wpa_printf(MSG_DEBUG,
+                                  "dbus: Not enough room for add_interfaces inspect data: tailroom %u, add %u",
                                   (unsigned int) wpabuf_tailroom(xml),
                                   (unsigned int) wpabuf_len(iface->xml));
                }
@@ -229,6 +234,7 @@ static void add_wpas_interfaces(struct wpabuf *xml,
                                struct wpa_dbus_object_desc *obj_dsc)
 {
        struct dl_list ifaces;
+
        dl_list_init(&ifaces);
        extract_interfaces(&ifaces, obj_dsc);
        add_interfaces(&ifaces, xml);
@@ -270,6 +276,7 @@ DBusMessage * wpa_dbus_introspect(DBusMessage *message,
        reply = dbus_message_new_method_return(message);
        if (reply) {
                const char *intro_str = wpabuf_head(xml);
+
                dbus_message_append_args(reply, DBUS_TYPE_STRING, &intro_str,
                                         DBUS_TYPE_INVALID);
        }
old mode 100644 (file)
new mode 100755 (executable)
index 85d8a78..45bb402
@@ -92,9 +92,9 @@ char * wpas_dbus_decompose_object_path(const char *path, char **network,
  */
 DBusMessage * wpas_dbus_new_invalid_iface_error(DBusMessage *message)
 {
-       return dbus_message_new_error(message, WPAS_ERROR_INVALID_IFACE,
-                                     "wpa_supplicant knows nothing about "
-                                     "this interface.");
+       return dbus_message_new_error(
+               message, WPAS_ERROR_INVALID_IFACE,
+               "wpa_supplicant knows nothing about this interface.");
 }
 
 
@@ -216,8 +216,12 @@ static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection,
        if (!msg_interface)
                goto out;
 
+       wpa_printf(MSG_MSGDUMP, "dbus[old/iface]: %s.%s (%s) [%s]",
+                  msg_interface, method, path,
+                  dbus_message_get_signature(message));
+
        iface_obj_path = wpas_dbus_decompose_object_path(path, &network,
-                                                        &bssid);
+                                                        &bssid);
        if (iface_obj_path == NULL) {
                reply = wpas_dbus_new_invalid_iface_error(message);
                goto out;
@@ -227,7 +231,7 @@ static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection,
         * wpa_supplicant structure it's supposed to (which is wpa_s)
         */
        if (wpa_supplicant_get_iface_by_dbus_path(wpa_s->global,
-                                                 iface_obj_path) != wpa_s) {
+                                                 iface_obj_path) != wpa_s) {
                reply = wpas_dbus_new_invalid_iface_error(message);
                goto out;
        }
@@ -235,6 +239,7 @@ static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection,
        if (network && !strcmp(msg_interface, WPAS_DBUS_IFACE_NETWORK)) {
                /* A method for one of this interface's configured networks */
                int nid = strtoul(network, NULL, 10);
+
                if (errno != EINVAL)
                        reply = wpas_dispatch_network_method(message, wpa_s,
                                                             nid);
@@ -275,25 +280,25 @@ static DBusHandlerResult wpas_iface_message_handler(DBusConnection *connection,
                        reply = wpas_dbus_iface_remove_blobs(message, wpa_s);
 #endif /* CONFIG_NO_CONFIG_BLOBS */
 #ifdef CONFIG_WPS
-               else if (!os_strcmp(method, "wpsPbc"))
+               else if (os_strcmp(method, "wpsPbc") == 0)
                        reply = wpas_dbus_iface_wps_pbc(message, wpa_s);
-               else if (!os_strcmp(method, "wpsPin"))
+               else if (os_strcmp(method, "wpsPin") == 0)
                        reply = wpas_dbus_iface_wps_pin(message, wpa_s);
-               else if (!os_strcmp(method, "wpsReg"))
+               else if (os_strcmp(method, "wpsReg") == 0)
                        reply = wpas_dbus_iface_wps_reg(message, wpa_s);
 #endif /* CONFIG_WPS */
-               else if (!os_strcmp(method, "flush"))
+               else if (os_strcmp(method, "flush") == 0)
                        reply = wpas_dbus_iface_flush(message, wpa_s);
        }
 
        /* If the message was handled, send back the reply */
+out:
        if (reply) {
                if (!dbus_message_get_no_reply(message))
                        dbus_connection_send(connection, reply, NULL);
                dbus_message_unref(reply);
        }
 
-out:
        os_free(iface_obj_path);
        os_free(network);
        os_free(bssid);
@@ -328,6 +333,10 @@ static DBusHandlerResult wpas_message_handler(DBusConnection *connection,
        if (!method || !path || !ctrl_iface || !msg_interface)
                return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
+       wpa_printf(MSG_MSGDUMP, "dbus[old]: %s.%s (%s) [%s]",
+                  msg_interface, method, path,
+                  dbus_message_get_signature(message));
+
        /* Validate the method interface */
        if (strcmp(msg_interface, WPAS_DBUS_INTERFACE) != 0)
                return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
@@ -381,8 +390,8 @@ void wpa_supplicant_dbus_notify_scan_results(struct wpa_supplicant *wpa_s)
                                          WPAS_DBUS_IFACE_INTERFACE,
                                          "ScanResultsAvailable");
        if (_signal == NULL) {
-               wpa_printf(MSG_ERROR, "dbus: Not enough memory to send scan "
-                          "results signal");
+               wpa_printf(MSG_ERROR,
+                          "dbus: Not enough memory to send scan results signal");
                return;
        }
        dbus_connection_send(iface->con, _signal, NULL);
@@ -426,29 +435,21 @@ void wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s,
                                          "StateChange");
        if (_signal == NULL) {
                wpa_printf(MSG_ERROR,
-                          "dbus: wpa_supplicant_dbus_notify_state_change: "
-                          "could not create dbus signal; likely out of "
-                          "memory");
+                          "dbus: %s: could not create dbus signal; likely out of memory",
+                          __func__);
                return;
        }
 
        new_state_str = wpa_supplicant_state_txt(new_state);
        old_state_str = wpa_supplicant_state_txt(old_state);
-       if (new_state_str == NULL || old_state_str == NULL) {
-               wpa_printf(MSG_ERROR,
-                          "dbus: wpa_supplicant_dbus_notify_state_change: "
-                          "Could not convert state strings");
-               goto out;
-       }
 
        if (!dbus_message_append_args(_signal,
-                                     DBUS_TYPE_STRING, &new_state_str,
-                                     DBUS_TYPE_STRING, &old_state_str,
-                                     DBUS_TYPE_INVALID)) {
+                                     DBUS_TYPE_STRING, &new_state_str,
+                                     DBUS_TYPE_STRING, &old_state_str,
+                                     DBUS_TYPE_INVALID)) {
                wpa_printf(MSG_ERROR,
-                          "dbus: wpa_supplicant_dbus_notify_state_change: "
-                          "Not enough memory to construct state change "
-                          "signal");
+                          "dbus: %s: Not enough memory to construct state change signal",
+                          __func__);
                goto out;
        }
 
@@ -480,18 +481,18 @@ void wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s)
                                          WPAS_DBUS_IFACE_INTERFACE,
                                          "Scanning");
        if (_signal == NULL) {
-               wpa_printf(MSG_ERROR, "dbus: Not enough memory to send scan "
-                          "results signal");
+               wpa_printf(MSG_ERROR,
+                          "dbus: Not enough memory to send scan results signal");
                return;
        }
 
        if (dbus_message_append_args(_signal,
-                                    DBUS_TYPE_BOOLEAN, &scanning,
-                                    DBUS_TYPE_INVALID)) {
+                                    DBUS_TYPE_BOOLEAN, &scanning,
+                                    DBUS_TYPE_INVALID)) {
                dbus_connection_send(iface->con, _signal, NULL);
        } else {
-               wpa_printf(MSG_ERROR, "dbus: Not enough memory to construct "
-                          "signal");
+               wpa_printf(MSG_ERROR,
+                          "dbus: Not enough memory to construct signal");
        }
        dbus_message_unref(_signal);
 }
@@ -516,19 +517,18 @@ void wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
                                          "WpsCred");
        if (_signal == NULL) {
                wpa_printf(MSG_ERROR,
-                          "dbus: wpa_supplicant_dbus_notify_wps_cred: "
-                          "Could not create dbus signal; likely out of "
-                          "memory");
+                          "dbus: %s: Could not create dbus signal; likely out of memory",
+                          __func__);
                return;
        }
 
        if (!dbus_message_append_args(_signal,
-                                     DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+                                     DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
                                      &cred->cred_attr, cred->cred_attr_len,
-                                     DBUS_TYPE_INVALID)) {
+                                     DBUS_TYPE_INVALID)) {
                wpa_printf(MSG_ERROR,
-                          "dbus: wpa_supplicant_dbus_notify_wps_cred: "
-                          "Not enough memory to construct signal");
+                          "dbus: %s: Not enough memory to construct signal",
+                          __func__);
                goto out;
        }
 
@@ -567,9 +567,8 @@ void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s,
                                          "Certification");
        if (_signal == NULL) {
                wpa_printf(MSG_ERROR,
-                          "dbus: wpa_supplicant_dbus_notify_certification: "
-                          "Could not create dbus signal; likely out of "
-                          "memory");
+                          "dbus: %s: Could not create dbus signal; likely out of memory",
+                          __func__);
                return;
        }
 
@@ -578,15 +577,15 @@ void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s,
        cert_hex_len = cert ? wpabuf_len(cert) : 0;
 
        if (!dbus_message_append_args(_signal,
-                                     DBUS_TYPE_INT32,&depth,
+                                     DBUS_TYPE_INT32, &depth,
                                      DBUS_TYPE_STRING, &subject,
-                                     DBUS_TYPE_STRING, &hash,
-                                     DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+                                     DBUS_TYPE_STRING, &hash,
+                                     DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
                                      &cert_hex, cert_hex_len,
-                                     DBUS_TYPE_INVALID)) {
+                                     DBUS_TYPE_INVALID)) {
                wpa_printf(MSG_ERROR,
-                          "dbus: wpa_supplicant_dbus_notify_certification: "
-                          "Not enough memory to construct signal");
+                          "dbus: %s: Not enough memory to construct signal",
+                          __func__);
                goto out;
        }
 
@@ -618,8 +617,7 @@ int wpa_supplicant_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface)
        if (!dbus_connection_register_object_path(iface->con,
                                                  WPAS_DBUS_PATH, &wpas_vtable,
                                                  iface)) {
-               wpa_printf(MSG_ERROR, "dbus: Could not set up message "
-                          "handler");
+               wpa_printf(MSG_ERROR, "dbus: Could not set up message handler");
                return -1;
        }
 
@@ -633,12 +631,13 @@ int wpa_supplicant_dbus_ctrl_iface_init(struct wpas_dbus_priv *iface)
        case DBUS_REQUEST_NAME_REPLY_EXISTS:
        case DBUS_REQUEST_NAME_REPLY_IN_QUEUE:
        case DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER:
-               wpa_printf(MSG_ERROR, "dbus: Could not request service name: "
-                          "already registered");
+               wpa_printf(MSG_ERROR,
+                          "dbus: Could not request service name: already registered");
                break;
        default:
-               wpa_printf(MSG_ERROR, "dbus: Could not request service name: "
-                          "%s %s", error.name, error.message);
+               wpa_printf(MSG_ERROR,
+                          "dbus: Could not request service name: %s %s",
+                          error.name, error.message);
                break;
        }
        dbus_error_free(&error);
@@ -687,8 +686,9 @@ int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s)
        /* Register the message handler for the interface functions */
        if (!dbus_connection_register_fallback(con, wpa_s->dbus_path, &vtable,
                                               wpa_s)) {
-               wpa_printf(MSG_ERROR, "dbus: Could not set up message "
-                          "handler for interface %s", wpa_s->ifname);
+               wpa_printf(MSG_ERROR,
+                          "dbus: Could not set up message handler for interface %s",
+                          wpa_s->ifname);
                return -1;
        }
 
@@ -712,7 +712,7 @@ int wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s)
        if (wpa_s == NULL || wpa_s->global == NULL)
                return 0;
        ctrl_iface = wpa_s->global->dbus;
-       if (ctrl_iface == NULL)
+       if (ctrl_iface == NULL || wpa_s->dbus_path == NULL)
                return 0;
 
        con = ctrl_iface->con;
old mode 100644 (file)
new mode 100755 (executable)
index e668231..451a9f8
@@ -82,7 +82,7 @@ void wpa_supplicant_dbus_notify_certification(struct wpa_supplicant *wpa_s,
                                              const struct wpabuf *cert);
 
 char * wpas_dbus_decompose_object_path(const char *path, char **network,
-                                       char **bssid);
+                                      char **bssid);
 
 int wpas_dbus_register_iface(struct wpa_supplicant *wpa_s);
 int wpas_dbus_unregister_iface(struct wpa_supplicant *wpa_s);
@@ -104,7 +104,12 @@ wpa_supplicant_dbus_notify_scanning(struct wpa_supplicant *wpa_s)
 {
 }
 
-#define wpa_supplicant_dbus_notify_state_change(w,n,o) do { } while (0)
+static inline void
+wpa_supplicant_dbus_notify_state_change(struct wpa_supplicant *wpa_s,
+                                       enum wpa_states new_state,
+                                       enum wpa_states old_state)
+{
+}
 
 static inline void
 wpa_supplicant_dbus_notify_wps_cred(struct wpa_supplicant *wpa_s,
old mode 100644 (file)
new mode 100755 (executable)
index e565de9..773ee8b
 #include "dbus_old_handlers.h"
 #include "dbus_dict_helpers.h"
 
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-extern int wpa_debug_timestamp;
-
 /**
  * wpas_dbus_new_invalid_opts_error - Return a new invalid options error message
  * @message: Pointer to incoming dbus message this error refers to
@@ -41,9 +37,9 @@ DBusMessage * wpas_dbus_new_invalid_opts_error(DBusMessage *message,
 {
        DBusMessage *reply;
 
-       reply = dbus_message_new_error(message, WPAS_ERROR_INVALID_OPTS,
-                                      "Did not receive correct message "
-                                      "arguments.");
+       reply = dbus_message_new_error(
+               message, WPAS_ERROR_INVALID_OPTS,
+               "Did not receive correct message arguments.");
        if (arg != NULL)
                dbus_message_append_args(reply, DBUS_TYPE_STRING, &arg,
                                         DBUS_TYPE_INVALID);
@@ -116,25 +112,29 @@ DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message,
                        if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
                                goto error;
                        if (!strcmp(entry.key, "driver") &&
-                           (entry.type == DBUS_TYPE_STRING)) {
+                           entry.type == DBUS_TYPE_STRING) {
+                               os_free(driver);
                                driver = os_strdup(entry.str_value);
                                wpa_dbus_dict_entry_clear(&entry);
                                if (driver == NULL)
                                        goto error;
                        } else if (!strcmp(entry.key, "driver-params") &&
-                                  (entry.type == DBUS_TYPE_STRING)) {
+                                  entry.type == DBUS_TYPE_STRING) {
+                               os_free(driver_param);
                                driver_param = os_strdup(entry.str_value);
                                wpa_dbus_dict_entry_clear(&entry);
                                if (driver_param == NULL)
                                        goto error;
                        } else if (!strcmp(entry.key, "config-file") &&
-                                  (entry.type == DBUS_TYPE_STRING)) {
+                                  entry.type == DBUS_TYPE_STRING) {
+                               os_free(confname);
                                confname = os_strdup(entry.str_value);
                                wpa_dbus_dict_entry_clear(&entry);
                                if (confname == NULL)
                                        goto error;
                        } else if (!strcmp(entry.key, "bridge-ifname") &&
-                                  (entry.type == DBUS_TYPE_STRING)) {
+                                  entry.type == DBUS_TYPE_STRING) {
+                               os_free(bridge_ifname);
                                bridge_ifname = os_strdup(entry.str_value);
                                wpa_dbus_dict_entry_clear(&entry);
                                if (bridge_ifname == NULL)
@@ -151,13 +151,13 @@ DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message,
         * an error if we already control it.
         */
        if (wpa_supplicant_get_iface(global, ifname) != NULL) {
-               reply = dbus_message_new_error(message,
-                                              WPAS_ERROR_EXISTS_ERROR,
-                                              "wpa_supplicant already "
-                                              "controls this interface.");
+               reply = dbus_message_new_error(
+                       message, WPAS_ERROR_EXISTS_ERROR,
+                       "wpa_supplicant already controls this interface.");
        } else {
                struct wpa_supplicant *wpa_s;
                struct wpa_interface iface;
+
                os_memset(&iface, 0, sizeof(iface));
                iface.ifname = ifname;
                iface.driver = driver;
@@ -165,17 +165,17 @@ DBusMessage * wpas_dbus_global_add_interface(DBusMessage *message,
                iface.confname = confname;
                iface.bridge_ifname = bridge_ifname;
                /* Otherwise, have wpa_supplicant attach to it. */
-               if ((wpa_s = wpa_supplicant_add_iface(global, &iface))) {
+               wpa_s = wpa_supplicant_add_iface(global, &iface, NULL);
+               if (wpa_s) {
                        const char *path = wpa_s->dbus_path;
+
                        reply = dbus_message_new_method_return(message);
                        dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH,
-                                                &path, DBUS_TYPE_INVALID);
+                                                &path, DBUS_TYPE_INVALID);
                } else {
-                       reply = dbus_message_new_error(message,
-                                                      WPAS_ERROR_ADD_ERROR,
-                                                      "wpa_supplicant "
-                                                      "couldn't grab this "
-                                                      "interface.");
+                       reply = dbus_message_new_error(
+                               message, WPAS_ERROR_ADD_ERROR,
+                               "wpa_supplicant couldn't grab this interface.");
                }
        }
 
@@ -226,10 +226,9 @@ DBusMessage * wpas_dbus_global_remove_interface(DBusMessage *message,
        if (!wpa_supplicant_remove_iface(global, wpa_s, 0)) {
                reply = wpas_dbus_new_success_reply(message);
        } else {
-               reply = dbus_message_new_error(message,
-                                              WPAS_ERROR_REMOVE_ERROR,
-                                              "wpa_supplicant couldn't "
-                                              "remove this interface.");
+               reply = dbus_message_new_error(
+                       message, WPAS_ERROR_REMOVE_ERROR,
+                       "wpa_supplicant couldn't remove this interface.");
        }
 
 out:
@@ -256,8 +255,8 @@ DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message,
        struct wpa_supplicant *wpa_s;
 
        if (!dbus_message_get_args(message, NULL,
-                                  DBUS_TYPE_STRING, &ifname,
-                                  DBUS_TYPE_INVALID)) {
+                                  DBUS_TYPE_STRING, &ifname,
+                                  DBUS_TYPE_INVALID)) {
                reply = wpas_dbus_new_invalid_opts_error(message, NULL);
                goto out;
        }
@@ -271,8 +270,8 @@ DBusMessage * wpas_dbus_global_get_interface(DBusMessage *message,
        path = wpa_s->dbus_path;
        reply = dbus_message_new_method_return(message);
        dbus_message_append_args(reply,
-                                DBUS_TYPE_OBJECT_PATH, &path,
-                                DBUS_TYPE_INVALID);
+                                DBUS_TYPE_OBJECT_PATH, &path,
+                                DBUS_TYPE_INVALID);
 
 out:
        return reply;
@@ -298,10 +297,10 @@ DBusMessage * wpas_dbus_global_set_debugparams(DBusMessage *message,
        dbus_bool_t debug_show_keys;
 
        if (!dbus_message_get_args(message, NULL,
-                                  DBUS_TYPE_INT32, &debug_level,
-                                  DBUS_TYPE_BOOLEAN, &debug_timestamp,
-                                  DBUS_TYPE_BOOLEAN, &debug_show_keys,
-                                  DBUS_TYPE_INVALID)) {
+                                  DBUS_TYPE_INT32, &debug_level,
+                                  DBUS_TYPE_BOOLEAN, &debug_timestamp,
+                                  DBUS_TYPE_BOOLEAN, &debug_show_keys,
+                                  DBUS_TYPE_INVALID)) {
                return wpas_dbus_new_invalid_opts_error(message, NULL);
        }
 
@@ -350,7 +349,7 @@ DBusMessage * wpas_dbus_iface_scan(DBusMessage *message,
 DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message,
                                           struct wpa_supplicant *wpa_s)
 {
-       DBusMessage *reply = NULL;
+       DBusMessage *reply;
        DBusMessageIter iter;
        DBusMessageIter sub_iter;
        struct wpa_bss *bss;
@@ -358,9 +357,10 @@ DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message,
        /* Create and initialize the return message */
        reply = dbus_message_new_method_return(message);
        dbus_message_iter_init_append(reply, &iter);
-       dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
-                                        DBUS_TYPE_OBJECT_PATH_AS_STRING,
-                                        &sub_iter);
+       if (!dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+                                             DBUS_TYPE_OBJECT_PATH_AS_STRING,
+                                             &sub_iter))
+               goto error;
 
        /* Loop through scan results and append each result's object path */
        dl_list_for_each(bss, &wpa_s->bss_id, struct wpa_bss, list_id) {
@@ -374,13 +374,21 @@ DBusMessage * wpas_dbus_iface_scan_results(DBusMessage *message,
                            "%s/" WPAS_DBUS_BSSIDS_PART "/"
                            WPAS_DBUS_BSSID_FORMAT,
                            wpa_s->dbus_path, MAC2STR(bss->bssid));
-               dbus_message_iter_append_basic(&sub_iter,
-                                              DBUS_TYPE_OBJECT_PATH, &path);
+               if (!dbus_message_iter_append_basic(&sub_iter,
+                                                   DBUS_TYPE_OBJECT_PATH,
+                                                   &path))
+                       goto error;
        }
 
-       dbus_message_iter_close_container(&iter, &sub_iter);
+       if (!dbus_message_iter_close_container(&iter, &sub_iter))
+               goto error;
 
        return reply;
+
+error:
+       dbus_message_unref(reply);
+       return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR,
+                                     "an internal error occurred returning scan results");
 }
 
 
@@ -400,84 +408,56 @@ DBusMessage * wpas_dbus_bssid_properties(DBusMessage *message,
 {
        DBusMessage *reply;
        DBusMessageIter iter, iter_dict;
-       const u8 *ie;
+       const u8 *wpa_ie, *rsn_ie, *wps_ie;
 
        /* Dump the properties into a dbus message */
        reply = dbus_message_new_method_return(message);
 
-       dbus_message_iter_init_append(reply, &iter);
-       if (!wpa_dbus_dict_open_write(&iter, &iter_dict))
-               goto error;
+       wpa_ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
+       rsn_ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+       wps_ie = wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE);
 
-       if (!wpa_dbus_dict_append_byte_array(&iter_dict, "bssid",
+       dbus_message_iter_init_append(reply, &iter);
+       if (!wpa_dbus_dict_open_write(&iter, &iter_dict) ||
+           !wpa_dbus_dict_append_byte_array(&iter_dict, "bssid",
                                             (const char *) bss->bssid,
-                                            ETH_ALEN))
-               goto error;
-
-       ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
-       if (ie) {
-               if (!wpa_dbus_dict_append_byte_array(&iter_dict, "ssid",
-                                                    (const char *) (ie + 2),
-                                                    ie[1]))
-               goto error;
-       }
-
-       ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
-       if (ie) {
-               if (!wpa_dbus_dict_append_byte_array(&iter_dict, "wpaie",
-                                                    (const char *) ie,
-                                                    ie[1] + 2))
-                       goto error;
-       }
-
-       ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
-       if (ie) {
-               if (!wpa_dbus_dict_append_byte_array(&iter_dict, "rsnie",
-                                                    (const char *) ie,
-                                                    ie[1] + 2))
-                       goto error;
+                                            ETH_ALEN) ||
+           !wpa_dbus_dict_append_byte_array(&iter_dict, "ssid",
+                                            (const char *) bss->ssid,
+                                            bss->ssid_len) ||
+           (wpa_ie &&
+            !wpa_dbus_dict_append_byte_array(&iter_dict, "wpaie",
+                                             (const char *) wpa_ie,
+                                             wpa_ie[1] + 2)) ||
+           (rsn_ie &&
+            !wpa_dbus_dict_append_byte_array(&iter_dict, "rsnie",
+                                             (const char *) rsn_ie,
+                                             rsn_ie[1] + 2)) ||
+           (wps_ie &&
+            !wpa_dbus_dict_append_byte_array(&iter_dict, "wpsie",
+                                            (const char *) wps_ie,
+                                             wps_ie[1] + 2)) ||
+           (bss->freq &&
+            !wpa_dbus_dict_append_int32(&iter_dict, "frequency", bss->freq)) ||
+           !wpa_dbus_dict_append_uint16(&iter_dict, "capabilities",
+                                        bss->caps) ||
+           (!(bss->flags & WPA_BSS_QUAL_INVALID) &&
+            !wpa_dbus_dict_append_int32(&iter_dict, "quality", bss->qual)) ||
+           (!(bss->flags & WPA_BSS_NOISE_INVALID) &&
+            !wpa_dbus_dict_append_int32(&iter_dict, "noise", bss->noise)) ||
+           (!(bss->flags & WPA_BSS_LEVEL_INVALID) &&
+            !wpa_dbus_dict_append_int32(&iter_dict, "level", bss->level)) ||
+           !wpa_dbus_dict_append_int32(&iter_dict, "maxrate",
+                                       wpa_bss_get_max_rate(bss) * 500000) ||
+           !wpa_dbus_dict_close_write(&iter, &iter_dict)) {
+               if (reply)
+                       dbus_message_unref(reply);
+               reply = dbus_message_new_error(
+                       message, WPAS_ERROR_INTERNAL_ERROR,
+                       "an internal error occurred returning BSSID properties.");
        }
 
-       ie = wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE);
-       if (ie) {
-               if (!wpa_dbus_dict_append_byte_array(&iter_dict, "wpsie",
-                                                    (const char *) ie,
-                                                    ie[1] + 2))
-                       goto error;
-       }
-
-       if (bss->freq) {
-               if (!wpa_dbus_dict_append_int32(&iter_dict, "frequency",
-                                               bss->freq))
-                       goto error;
-       }
-       if (!wpa_dbus_dict_append_uint16(&iter_dict, "capabilities",
-                                        bss->caps))
-               goto error;
-       if (!(bss->flags & WPA_BSS_QUAL_INVALID) &&
-           !wpa_dbus_dict_append_int32(&iter_dict, "quality", bss->qual))
-               goto error;
-       if (!(bss->flags & WPA_BSS_NOISE_INVALID) &&
-           !wpa_dbus_dict_append_int32(&iter_dict, "noise", bss->noise))
-               goto error;
-       if (!(bss->flags & WPA_BSS_LEVEL_INVALID) &&
-           !wpa_dbus_dict_append_int32(&iter_dict, "level", bss->level))
-               goto error;
-       if (!wpa_dbus_dict_append_int32(&iter_dict, "maxrate",
-                                       wpa_bss_get_max_rate(bss) * 500000))
-               goto error;
-
-       if (!wpa_dbus_dict_close_write(&iter, &iter_dict))
-               goto error;
-
        return reply;
-
-error:
-       if (reply)
-               dbus_message_unref(reply);
-       return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR,
-                                     "an internal error occurred returning "
-                                     "BSSID properties.");
 }
 
 
@@ -537,37 +517,27 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message,
        if (res < 0) {
                if (!strict) {
                        const char *args[] = {"CCMP", "TKIP", "NONE"};
+
                        if (!wpa_dbus_dict_append_string_array(
                                    &iter_dict, "pairwise", args,
-                                   sizeof(args) / sizeof(char*)))
+                                   ARRAY_SIZE(args)))
                                goto error;
                }
        } else {
                if (!wpa_dbus_dict_begin_string_array(&iter_dict, "pairwise",
                                                      &iter_dict_entry,
                                                      &iter_dict_val,
-                                                     &iter_array))
-                       goto error;
-
-               if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "CCMP"))
-                               goto error;
-               }
-
-               if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "TKIP"))
-                               goto error;
-               }
-
-               if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "NONE"))
-                               goto error;
-               }
-
-               if (!wpa_dbus_dict_end_string_array(&iter_dict,
+                                                     &iter_array) ||
+                   ((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "CCMP")) ||
+                   ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "TKIP")) ||
+                   ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "NONE")) ||
+                   !wpa_dbus_dict_end_string_array(&iter_dict,
                                                    &iter_dict_entry,
                                                    &iter_dict_val,
                                                    &iter_array))
@@ -580,9 +550,10 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message,
                        const char *args[] = {
                                "CCMP", "TKIP", "WEP104", "WEP40"
                        };
+
                        if (!wpa_dbus_dict_append_string_array(
                                    &iter_dict, "group", args,
-                                   sizeof(args) / sizeof(char*)))
+                                   ARRAY_SIZE(args)))
                                goto error;
                }
        } else {
@@ -592,31 +563,19 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message,
                                                      &iter_array))
                        goto error;
 
-               if (capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "CCMP"))
-                               goto error;
-               }
-
-               if (capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "TKIP"))
-                               goto error;
-               }
-
-               if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "WEP104"))
-                               goto error;
-               }
-
-               if (capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "WEP40"))
-                               goto error;
-               }
-
-               if (!wpa_dbus_dict_end_string_array(&iter_dict,
+               if (((capa.enc & WPA_DRIVER_CAPA_ENC_CCMP) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "CCMP")) ||
+                   ((capa.enc & WPA_DRIVER_CAPA_ENC_TKIP) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "TKIP")) ||
+                   ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP104) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "WEP104")) ||
+                   ((capa.enc & WPA_DRIVER_CAPA_ENC_WEP40) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "WEP40")) ||
+                   !wpa_dbus_dict_end_string_array(&iter_dict,
                                                    &iter_dict_entry,
                                                    &iter_dict_val,
                                                    &iter_array))
@@ -632,45 +591,30 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message,
                        };
                        if (!wpa_dbus_dict_append_string_array(
                                    &iter_dict, "key_mgmt", args,
-                                   sizeof(args) / sizeof(char*)))
+                                   ARRAY_SIZE(args)))
                                goto error;
                }
        } else {
                if (!wpa_dbus_dict_begin_string_array(&iter_dict, "key_mgmt",
                                                      &iter_dict_entry,
                                                      &iter_dict_val,
-                                                     &iter_array))
-                       goto error;
-
-               if (!wpa_dbus_dict_string_array_add_element(&iter_array,
-                                                           "NONE"))
-                       goto error;
-
-               if (!wpa_dbus_dict_string_array_add_element(&iter_array,
-                                                           "IEEE8021X"))
-                       goto error;
-
-               if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
-                                    WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "WPA-EAP"))
-                               goto error;
-               }
-
-               if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
-                                    WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "WPA-PSK"))
-                               goto error;
-               }
-
-               if (capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "WPA-NONE"))
-                               goto error;
-               }
-
-               if (!wpa_dbus_dict_end_string_array(&iter_dict,
+                                                     &iter_array) ||
+                   !wpa_dbus_dict_string_array_add_element(&iter_array,
+                                                           "NONE") ||
+                   !wpa_dbus_dict_string_array_add_element(&iter_array,
+                                                           "IEEE8021X") ||
+                   ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+                                      WPA_DRIVER_CAPA_KEY_MGMT_WPA2)) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "WPA-EAP")) ||
+                   ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+                                      WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "WPA-PSK")) ||
+                   ((capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_WPA_NONE) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "WPA-NONE")) ||
+                   !wpa_dbus_dict_end_string_array(&iter_dict,
                                                    &iter_dict_entry,
                                                    &iter_dict_val,
                                                    &iter_array))
@@ -681,33 +625,26 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message,
        if (res < 0) {
                if (!strict) {
                        const char *args[] = { "RSN", "WPA" };
+
                        if (!wpa_dbus_dict_append_string_array(
                                    &iter_dict, "proto", args,
-                                   sizeof(args) / sizeof(char*)))
+                                   ARRAY_SIZE(args)))
                                goto error;
                }
        } else {
                if (!wpa_dbus_dict_begin_string_array(&iter_dict, "proto",
                                                      &iter_dict_entry,
                                                      &iter_dict_val,
-                                                     &iter_array))
-                       goto error;
-
-               if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
-                                    WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "RSN"))
-                               goto error;
-               }
-
-               if (capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
-                                    WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "WPA"))
-                               goto error;
-               }
-
-               if (!wpa_dbus_dict_end_string_array(&iter_dict,
+                                                     &iter_array) ||
+                   ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA2 |
+                                      WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK)) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "RSN")) ||
+                   ((capa.key_mgmt & (WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+                                      WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK)) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "WPA")) ||
+                   !wpa_dbus_dict_end_string_array(&iter_dict,
                                                    &iter_dict_entry,
                                                    &iter_dict_val,
                                                    &iter_array))
@@ -718,37 +655,27 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message,
        if (res < 0) {
                if (!strict) {
                        const char *args[] = { "OPEN", "SHARED", "LEAP" };
+
                        if (!wpa_dbus_dict_append_string_array(
                                    &iter_dict, "auth_alg", args,
-                                   sizeof(args) / sizeof(char*)))
+                                   ARRAY_SIZE(args)))
                                goto error;
                }
        } else {
                if (!wpa_dbus_dict_begin_string_array(&iter_dict, "auth_alg",
                                                      &iter_dict_entry,
                                                      &iter_dict_val,
-                                                     &iter_array))
-                       goto error;
-
-               if (capa.auth & (WPA_DRIVER_AUTH_OPEN)) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "OPEN"))
-                               goto error;
-               }
-
-               if (capa.auth & (WPA_DRIVER_AUTH_SHARED)) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "SHARED"))
-                               goto error;
-               }
-
-               if (capa.auth & (WPA_DRIVER_AUTH_LEAP)) {
-                       if (!wpa_dbus_dict_string_array_add_element(
-                                   &iter_array, "LEAP"))
-                               goto error;
-               }
-
-               if (!wpa_dbus_dict_end_string_array(&iter_dict,
+                                                     &iter_array) ||
+                   ((capa.auth & WPA_DRIVER_AUTH_OPEN) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "OPEN")) ||
+                   ((capa.auth & WPA_DRIVER_AUTH_SHARED) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "SHARED")) ||
+                   ((capa.auth & WPA_DRIVER_AUTH_LEAP) &&
+                    !wpa_dbus_dict_string_array_add_element(
+                            &iter_array, "LEAP")) ||
+                   !wpa_dbus_dict_end_string_array(&iter_dict,
                                                    &iter_dict_entry,
                                                    &iter_dict_val,
                                                    &iter_array))
@@ -763,9 +690,9 @@ DBusMessage * wpas_dbus_iface_capabilities(DBusMessage *message,
 error:
        if (reply)
                dbus_message_unref(reply);
-       return dbus_message_new_error(message, WPAS_ERROR_INTERNAL_ERROR,
-                                     "an internal error occurred returning "
-                                     "interface capabilities.");
+       return dbus_message_new_error(
+               message, WPAS_ERROR_INTERNAL_ERROR,
+               "an internal error occurred returning interface capabilities.");
 }
 
 
@@ -786,10 +713,9 @@ DBusMessage * wpas_dbus_iface_add_network(DBusMessage *message,
 
        ssid = wpa_config_add_network(wpa_s->conf);
        if (ssid == NULL) {
-               reply = dbus_message_new_error(message,
-                                              WPAS_ERROR_ADD_NETWORK_ERROR,
-                                              "wpa_supplicant could not add "
-                                              "a network on this interface.");
+               reply = dbus_message_new_error(
+                       message, WPAS_ERROR_ADD_NETWORK_ERROR,
+                       "wpa_supplicant could not add a network on this interface.");
                goto out;
        }
        wpas_notify_network_added(wpa_s, ssid);
@@ -829,15 +755,15 @@ DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message,
        struct wpa_ssid *ssid;
 
        if (!dbus_message_get_args(message, NULL,
-                                  DBUS_TYPE_OBJECT_PATH, &op,
-                                  DBUS_TYPE_INVALID)) {
+                                  DBUS_TYPE_OBJECT_PATH, &op,
+                                  DBUS_TYPE_INVALID)) {
                reply = wpas_dbus_new_invalid_opts_error(message, NULL);
                goto out;
        }
 
        /* Extract the network ID */
        iface = wpas_dbus_decompose_object_path(op, &net_id, NULL);
-       if (iface == NULL) {
+       if (iface == NULL || net_id == NULL) {
                reply = wpas_dbus_new_invalid_network_error(message);
                goto out;
        }
@@ -857,17 +783,17 @@ DBusMessage * wpas_dbus_iface_remove_network(DBusMessage *message,
 
        wpas_notify_network_removed(wpa_s, ssid);
 
+       if (ssid == wpa_s->current_ssid)
+               wpa_supplicant_deauthenticate(wpa_s,
+                                             WLAN_REASON_DEAUTH_LEAVING);
+
        if (wpa_config_remove_network(wpa_s->conf, id) < 0) {
-               reply = dbus_message_new_error(message,
-                                              WPAS_ERROR_REMOVE_NETWORK_ERROR,
-                                              "error removing the specified "
-                                              "on this interface.");
+               reply = dbus_message_new_error(
+                       message, WPAS_ERROR_REMOVE_NETWORK_ERROR,
+                       "error removing the specified on this interface.");
                goto out;
        }
 
-       if (ssid == wpa_s->current_ssid)
-               wpa_supplicant_deauthenticate(wpa_s,
-                                             WLAN_REASON_DEAUTH_LEAVING);
        reply = wpas_dbus_new_success_reply(message);
 
 out:
@@ -877,7 +803,7 @@ out:
 }
 
 
-static const char *dont_quote[] = {
+static const char  const *dont_quote[] = {
        "key_mgmt", "proto", "pairwise", "auth_alg", "group", "eap",
        "opensc_engine_path", "pkcs11_engine_path", "pkcs11_module_path",
        "bssid", NULL
@@ -887,8 +813,9 @@ static const char *dont_quote[] = {
 static dbus_bool_t should_quote_opt(const char *key)
 {
        int i = 0;
+
        while (dont_quote[i] != NULL) {
-               if (strcmp(key, dont_quote[i]) == 0)
+               if (os_strcmp(key, dont_quote[i]) == 0)
                        return FALSE;
                i++;
        }
@@ -959,7 +886,7 @@ DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message,
                                        goto error;
                                ret = os_snprintf(value, size, "\"%s\"",
                                                  entry.str_value);
-                               if (ret < 0 || (size_t) ret != (size - 1))
+                               if (os_snprintf_error(size, ret))
                                        goto error;
                        } else {
                                value = os_strdup(entry.str_value);
@@ -972,7 +899,7 @@ DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message,
                                goto error;
                        ret = os_snprintf(value, size, "%u",
                                          entry.uint32_value);
-                       if (ret <= 0)
+                       if (os_snprintf_error(size, ret))
                                goto error;
                } else if (entry.type == DBUS_TYPE_INT32) {
                        value = os_zalloc(size);
@@ -980,7 +907,7 @@ DBusMessage * wpas_dbus_iface_set_network(DBusMessage *message,
                                goto error;
                        ret = os_snprintf(value, size, "%d",
                                          entry.int32_value);
-                       if (ret <= 0)
+                       if (os_snprintf_error(size, ret))
                                goto error;
                } else
                        goto error;
@@ -1093,7 +1020,8 @@ DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message,
                        goto out;
                }
                /* Ensure the object path really points to this interface */
-               if (os_strcmp(iface_obj_path, wpa_s->dbus_path) != 0) {
+               if (network == NULL ||
+                   os_strcmp(iface_obj_path, wpa_s->dbus_path) != 0) {
                        reply = wpas_dbus_new_invalid_network_error(message);
                        goto out;
                }
@@ -1203,25 +1131,30 @@ DBusMessage * wpas_dbus_iface_set_smartcard_modules(
                if (!wpa_dbus_dict_get_entry(&iter_dict, &entry))
                        goto error;
                if (!strcmp(entry.key, "opensc_engine_path") &&
-                   (entry.type == DBUS_TYPE_STRING)) {
+                   entry.type == DBUS_TYPE_STRING) {
+                       os_free(opensc_engine_path);
                        opensc_engine_path = os_strdup(entry.str_value);
+                       wpa_dbus_dict_entry_clear(&entry);
                        if (opensc_engine_path == NULL)
                                goto error;
                } else if (!strcmp(entry.key, "pkcs11_engine_path") &&
-                          (entry.type == DBUS_TYPE_STRING)) {
+                          entry.type == DBUS_TYPE_STRING) {
+                       os_free(pkcs11_engine_path);
                        pkcs11_engine_path = os_strdup(entry.str_value);
+                       wpa_dbus_dict_entry_clear(&entry);
                        if (pkcs11_engine_path == NULL)
                                goto error;
                } else if (!strcmp(entry.key, "pkcs11_module_path") &&
-                                (entry.type == DBUS_TYPE_STRING)) {
+                                entry.type == DBUS_TYPE_STRING) {
+                       os_free(pkcs11_module_path);
                        pkcs11_module_path = os_strdup(entry.str_value);
+                       wpa_dbus_dict_entry_clear(&entry);
                        if (pkcs11_module_path == NULL)
                                goto error;
                } else {
                        wpa_dbus_dict_entry_clear(&entry);
                        goto error;
                }
-               wpa_dbus_dict_entry_clear(&entry);
        }
 
        os_free(wpa_s->conf->opensc_engine_path);
@@ -1292,8 +1225,8 @@ DBusMessage * wpas_dbus_iface_get_scanning(DBusMessage *message,
                dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &scanning,
                                         DBUS_TYPE_INVALID);
        } else {
-               wpa_printf(MSG_ERROR, "dbus: Not enough memory to return "
-                          "scanning state");
+               wpa_printf(MSG_ERROR,
+                          "dbus: Not enough memory to return scanning state");
        }
 
        return reply;
@@ -1366,7 +1299,7 @@ DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message,
                blob->len = entry.array_len;
                os_memcpy(blob->data, (u8 *) entry.bytearray_value,
                                entry.array_len);
-               if (blob->name == NULL || blob->data == NULL) {
+               if (blob->name == NULL) {
                        wpa_config_free_blob(blob);
                        reply = dbus_message_new_error(
                                message, WPAS_ERROR_ADD_ERROR,
@@ -1405,8 +1338,8 @@ DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message,
 
        dbus_message_iter_init(message, &iter);
 
-       if ((dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_ARRAY) ||
-           (dbus_message_iter_get_element_type (&iter) != DBUS_TYPE_STRING))
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+           dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_STRING)
                return wpas_dbus_new_invalid_opts_error(message, NULL);
 
        dbus_message_iter_recurse(&iter, &array);
@@ -1416,8 +1349,7 @@ DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message,
                dbus_message_iter_get_basic(&array, &name);
                if (!os_strlen(name))
                        err_msg = "Invalid blob name.";
-
-               if (wpa_config_remove_blob(wpa_s->conf, name) != 0)
+               else if (wpa_config_remove_blob(wpa_s->conf, name) != 0)
                        err_msg = "Error removing blob.";
                else
                        wpas_notify_blob_removed(wpa_s, name);
old mode 100644 (file)
new mode 100755 (executable)
index 825bc6d..e60ad06
@@ -58,13 +58,13 @@ DBusMessage * wpas_dbus_iface_disable_network(DBusMessage *message,
                                              struct wpa_ssid *ssid);
 
 DBusMessage * wpas_dbus_iface_select_network(DBusMessage *message,
-                                             struct wpa_supplicant *wpa_s);
+                                            struct wpa_supplicant *wpa_s);
 
 DBusMessage * wpas_dbus_iface_disconnect(DBusMessage *message,
                                         struct wpa_supplicant *wpa_s);
 
 DBusMessage * wpas_dbus_iface_set_ap_scan(DBusMessage *message,
-                                          struct wpa_supplicant *wpa_s);
+                                         struct wpa_supplicant *wpa_s);
 
 DBusMessage * wpas_dbus_iface_set_smartcard_modules(
        DBusMessage *message, struct wpa_supplicant *wpa_s);
@@ -76,7 +76,7 @@ DBusMessage * wpas_dbus_iface_get_scanning(DBusMessage *message,
                                           struct wpa_supplicant *wpa_s);
 
 DBusMessage * wpas_dbus_iface_set_blobs(DBusMessage *message,
-                                       struct wpa_supplicant *wpa_s);
+                                       struct wpa_supplicant *wpa_s);
 
 DBusMessage * wpas_dbus_iface_remove_blobs(DBusMessage *message,
                                           struct wpa_supplicant *wpa_s);
old mode 100644 (file)
new mode 100755 (executable)
index bb79382..5309a53
@@ -36,7 +36,7 @@ DBusMessage * wpas_dbus_iface_wps_pbc(DBusMessage *message,
                                   DBUS_TYPE_INVALID))
                return wpas_dbus_new_invalid_opts_error(message, NULL);
 
-       if (!os_strcmp(arg_bssid, "any"))
+       if (os_strcmp(arg_bssid, "any") == 0)
                ret = wpas_wps_start_pbc(wpa_s, NULL, 0);
        else if (!hwaddr_aton(arg_bssid, bssid))
                ret = wpas_wps_start_pbc(wpa_s, bssid, 0);
@@ -46,10 +46,9 @@ DBusMessage * wpas_dbus_iface_wps_pbc(DBusMessage *message,
        }
 
        if (ret < 0) {
-               return dbus_message_new_error(message,
-                                             WPAS_ERROR_WPS_PBC_ERROR,
-                                             "Could not start PBC "
-                                             "negotiation");
+               return dbus_message_new_error(
+                       message, WPAS_ERROR_WPS_PBC_ERROR,
+                       "Could not start PBC negotiation");
        }
 
        return wpas_dbus_new_success_reply(message);
@@ -73,12 +72,13 @@ DBusMessage * wpas_dbus_iface_wps_pin(DBusMessage *message,
        char *pin = NULL;
        u8 bssid[ETH_ALEN], *_bssid = NULL;
        int ret = 0;
+       char npin[9];
 
        if (!dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg_bssid,
                                   DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID))
                return wpas_dbus_new_invalid_opts_error(message, NULL);
 
-       if (!os_strcmp(arg_bssid, "any"))
+       if (os_strcmp(arg_bssid, "any") == 0)
                _bssid = NULL;
        else if (!hwaddr_aton(arg_bssid, bssid))
                _bssid = bssid;
@@ -104,15 +104,12 @@ DBusMessage * wpas_dbus_iface_wps_pin(DBusMessage *message,
        if (reply == NULL)
                return NULL;
 
-       if (ret == 0) {
-               dbus_message_append_args(reply, DBUS_TYPE_STRING, &pin,
-                                        DBUS_TYPE_INVALID);
-       } else {
-               char npin[9];
+       if (ret > 0) {
                os_snprintf(npin, sizeof(npin), "%08d", ret);
-               dbus_message_append_args(reply, DBUS_TYPE_STRING, &npin,
-                                        DBUS_TYPE_INVALID);
+               pin = npin;
        }
+       dbus_message_append_args(reply, DBUS_TYPE_STRING, &pin,
+                                DBUS_TYPE_INVALID);
        return reply;
 }
 
@@ -138,9 +135,7 @@ DBusMessage * wpas_dbus_iface_wps_reg(DBusMessage *message,
                                   DBUS_TYPE_STRING, &pin, DBUS_TYPE_INVALID))
                return wpas_dbus_new_invalid_opts_error(message, NULL);
 
-       if (!os_strcmp(arg_bssid, "any"))
-               ret = wpas_wps_start_reg(wpa_s, NULL, pin, NULL);
-       else if (!hwaddr_aton(arg_bssid, bssid))
+       if (!hwaddr_aton(arg_bssid, bssid))
                ret = wpas_wps_start_reg(wpa_s, bssid, pin, NULL);
        else {
                return wpas_dbus_new_invalid_opts_error(message,
@@ -149,7 +144,7 @@ DBusMessage * wpas_dbus_iface_wps_reg(DBusMessage *message,
 
        if (ret < 0) {
                return dbus_message_new_error(message,
-                                             WPAS_ERROR_WPS_PBC_ERROR,
+                                             WPAS_ERROR_WPS_REG_ERROR,
                                              "Could not request credentials");
        }
 
index fe2e0db..7f627fd 100644 (file)
 # used to fix build issues on such systems (krb5.h not found).
 #CFLAGS += -I/usr/include/kerberos
 
-# Example configuration for various cross-compilation platforms
-
-#### sveasoft (e.g., for Linksys WRT54G) ######################################
-#CC=mipsel-uclibc-gcc
-#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc
-#CFLAGS += -Os
-#CPPFLAGS += -I../src/include -I../../src/router/openssl/include
-#LIBS += -L/opt/brcm/hndtools-mipsel-uclibc-0.9.19/lib -lssl
-###############################################################################
-
-#### openwrt (e.g., for Linksys WRT54G) #######################################
-#CC=mipsel-uclibc-gcc
-#CC=/opt/brcm/hndtools-mipsel-uclibc/bin/mipsel-uclibc-gcc
-#CFLAGS += -Os
-#CPPFLAGS=-I../src/include -I../openssl-0.9.7d/include \
-#      -I../WRT54GS/release/src/include
-#LIBS = -lssl
-###############################################################################
-
-
-# Driver interface for Host AP driver
-CONFIG_DRIVER_HOSTAP=y
-
-# Driver interface for Agere driver
-#CONFIG_DRIVER_HERMES=y
-# Change include directories to match with the local setup
-#CFLAGS += -I../../hcf -I../../include -I../../include/hcf
-#CFLAGS += -I../../include/wireless
-
-# Driver interface for madwifi driver
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_MADWIFI=y
-# Set include directory to the madwifi source tree
-#CFLAGS += -I../../madwifi
-
-# Driver interface for ndiswrapper
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_NDISWRAPPER=y
-
-# Driver interface for Atmel driver
-CONFIG_DRIVER_ATMEL=y
-
-# Driver interface for old Broadcom driver
-# Please note that the newer Broadcom driver ("hybrid Linux driver") supports
-# Linux wireless extensions and does not need (or even work) with the old
-# driver wrapper. Use CONFIG_DRIVER_WEXT=y with that driver.
-#CONFIG_DRIVER_BROADCOM=y
-# Example path for wlioctl.h; change to match your configuration
-#CFLAGS += -I/opt/WRT54GS/release/src/include
-
-# Driver interface for Intel ipw2100/2200 driver
-# Deprecated; use CONFIG_DRIVER_WEXT=y instead.
-#CONFIG_DRIVER_IPW=y
-
-# Driver interface for Ralink driver
-#CONFIG_DRIVER_RALINK=y
-
 # Driver interface for generic Linux wireless extensions
 # Note: WEXT is deprecated in the current Linux kernel version and no new
 # functionality is added to it. nl80211-based interface is the new
@@ -88,6 +31,19 @@ CONFIG_DRIVER_WEXT=y
 # Driver interface for Linux drivers using the nl80211 kernel interface
 CONFIG_DRIVER_NL80211=y
 
+# driver_nl80211.c requires libnl. If you are compiling it yourself
+# you may need to point hostapd to your version of libnl.
+#
+#CFLAGS += -I$<path to libnl include files>
+#LIBS += -L$<path to libnl library files>
+
+# Use libnl v2.0 (or 3.0) libraries.
+#CONFIG_LIBNL20=y
+
+# Use libnl 3.2 libraries (if this is selected, CONFIG_LIBNL20 is ignored)
+#CONFIG_LIBNL32=y
+
+
 # Driver interface for FreeBSD net80211 layer (e.g., Atheros driver)
 #CONFIG_DRIVER_BSD=y
 #CFLAGS += -I/usr/local/include
@@ -111,9 +67,6 @@ CONFIG_DRIVER_NL80211=y
 # wpa_supplicant.
 # CONFIG_USE_NDISUIO=y
 
-# Driver interface for development testing
-#CONFIG_DRIVER_TEST=y
-
 # Driver interface for wired Ethernet drivers
 CONFIG_DRIVER_WIRED=y
 
@@ -147,10 +100,9 @@ CONFIG_EAP_PEAP=y
 CONFIG_EAP_TTLS=y
 
 # EAP-FAST
-# Note: Default OpenSSL package does not include support for all the
-# functionality needed for EAP-FAST. If EAP-FAST is enabled with OpenSSL,
-# the OpenSSL library must be patched (openssl-0.9.8d-tls-extensions.patch)
-# to add the needed functions.
+# Note: If OpenSSL is used as the TLS library, OpenSSL 1.0 or newer is needed
+# for EAP-FAST support. Older OpenSSL releases would need to be patched, e.g.,
+# with openssl-0.9.8x-tls-extensions.patch, to add the needed functions.
 #CONFIG_EAP_FAST=y
 
 # EAP-GTC
@@ -197,8 +149,6 @@ CONFIG_EAP_LEAP=y
 
 # Wi-Fi Protected Setup (WPS)
 #CONFIG_WPS=y
-# Enable WSC 2.0 support
-#CONFIG_WPS2=y
 # Enable WPS external registrar functionality
 #CONFIG_WPS_ER=y
 # Disable credentials for an open network by default when acting as a WPS
@@ -210,6 +160,9 @@ CONFIG_EAP_LEAP=y
 # EAP-IKEv2
 #CONFIG_EAP_IKEV2=y
 
+# EAP-EKE
+#CONFIG_EAP_EKE=y
+
 # PKCS#12 (PFX) support (used to read private key and certificate file from
 # a file that usually has extension .p12 or .pfx)
 CONFIG_PKCS12=y
@@ -234,8 +187,10 @@ CONFIG_SMARTCARD=y
 # Select control interface backend for external programs, e.g, wpa_cli:
 # unix = UNIX domain sockets (default for Linux/*BSD)
 # udp = UDP sockets using localhost (127.0.0.1)
+# udp6 = UDP IPv6 sockets using localhost (::1)
 # named_pipe = Windows Named Pipe (default for Windows)
 # udp-remote = UDP sockets with remote access (only for tests systems/purpose)
+# udp6-remote = UDP IPv6 sockets with remote access (only for tests purpose)
 # y = use default (backwards compatibility)
 # If this option is commented out, control interface is not included in the
 # build.
@@ -295,7 +250,7 @@ CONFIG_BACKEND=file
 # main_none = Very basic example (development use only)
 #CONFIG_MAIN=main
 
-# Select wrapper for operatins system and C library specific functions
+# Select wrapper for operating system and C library specific functions
 # unix = UNIX/POSIX like systems (default)
 # win32 = Windows systems
 # none = Empty template
@@ -304,12 +259,14 @@ CONFIG_BACKEND=file
 # Select event loop implementation
 # eloop = select() loop (default)
 # eloop_win = Windows events and WaitForMultipleObject() loop
-# eloop_none = Empty template
 #CONFIG_ELOOP=eloop
 
 # Should we use poll instead of select? Select is used by default.
 #CONFIG_ELOOP_POLL=y
 
+# Should we use epoll instead of select? Select is used by default.
+#CONFIG_ELOOP_EPOLL=y
+
 # Select layer 2 packet implementation
 # linux = Linux packet socket (default)
 # pcap = libpcap/libdnet/WinPcap
@@ -481,6 +438,10 @@ CONFIG_PEERKEY=y
 # IEEE 802.11n (High Throughput) support (mainly for AP mode)
 #CONFIG_IEEE80211N=y
 
+# IEEE 802.11ac (Very High Throughput) support (mainly for AP mode)
+# (depends on CONFIG_IEEE80211N)
+#CONFIG_IEEE80211AC=y
+
 # Wireless Network Management (IEEE Std 802.11v-2011)
 # Note: This is experimental and not complete implementation.
 #CONFIG_WNM=y
@@ -512,9 +473,14 @@ CONFIG_PEERKEY=y
 # Enable TDLS support
 #CONFIG_TDLS=y
 
+# Wi-Fi Direct
+# This can be used to enable Wi-Fi Direct extensions for P2P using an external
+# program to control the additional information exchanges in the messages.
+#CONFIG_WIFI_DISPLAY=y
+
 # Autoscan
 # This can be used to enable automatic scan support in wpa_supplicant.
-# See wpa_supplicant.conf for more information on autoscan usage.
+# See wpa_supplicant.conf for more information on autoscan usage.
 #
 # Enabling directly a module will enable autoscan support.
 # For exponential module:
index aaeee2e..82f9de3 100644 (file)
@@ -7,6 +7,7 @@ FILES += wpa_passphrase
 FILES += wpa_priv
 FILES += wpa_supplicant.conf
 FILES += wpa_supplicant
+FILES += eapol_test
 
 man:
        for i in $(FILES); do docbook2man $$i.sgml; done
@@ -20,7 +21,7 @@ pdf:
 
 
 clean:
-       rm -f wpa_background.8 wpa_cli.8 wpa_gui.8 wpa_passphrase.8 wpa_priv.8 wpa_supplicant.8
+       rm -f wpa_background.8 wpa_cli.8 wpa_gui.8 wpa_passphrase.8 wpa_priv.8 wpa_supplicant.8 eapol_test.8
        rm -f wpa_supplicant.conf.5
        rm -f manpage.links manpage.refs
        rm -f $(FILES:%=%.pdf)
diff --git a/wpa_supplicant/doc/docbook/eapol_test.sgml b/wpa_supplicant/doc/docbook/eapol_test.sgml
new file mode 100644 (file)
index 0000000..e9af6d9
--- /dev/null
@@ -0,0 +1,205 @@
+<!doctype refentry PUBLIC "-//OASIS//DTD DocBook V4.1//EN">
+
+<refentry>
+  <refmeta>
+    <refentrytitle>eapol_test</refentrytitle>
+    <manvolnum>8</manvolnum>
+  </refmeta>
+  <refnamediv>
+    <refname>eapol_test</refname>
+
+    <refpurpose>EAP peer and RADIUS client testing</refpurpose>
+  </refnamediv>
+
+  <refsynopsisdiv>
+    <cmdsynopsis>
+      <command>eapol_test</command>
+      <arg>-nWS</arg>
+      <arg>-c<replaceable>config file</replaceable></arg>
+      <arg>-a<replaceable>server IP address</replaceable></arg>
+      <arg>-A<replaceable>client IP address</replaceable></arg>
+      <arg>-p<replaceable>UDP port</replaceable></arg>
+      <arg>-s<replaceable>shared secret</replaceable></arg>
+      <arg>-r<replaceable>re-authentications</replaceable></arg>
+      <arg>-t<replaceable>timeout</replaceable></arg>
+      <arg>-C<replaceable>Connect-Info</replaceable></arg>
+      <arg>-M<replaceable>MAC address</replaceable></arg>
+      <arg>-o<replaceable>file</replaceable></arg>
+      <arg>-N<replaceable>attr spec</replaceable></arg>
+    </cmdsynopsis>
+    <cmdsynopsis>
+      <command>eapol_test scard</command>
+    </cmdsynopsis>
+    <cmdsynopsis>
+      <command>eapol_test sim</command>
+      <arg>PIN</arg>
+      <arg>num triplets</arg>
+    </cmdsynopsis>
+  </refsynopsisdiv>
+
+  <refsect1>
+    <title>Overview</title>
+
+    <para>eapol_test is a program that links together the same EAP
+    peer implementation that wpa_supplicant is using and the RADIUS
+    authentication client code from hostapd. In addition, it has
+    minimal glue code to combine these two components in similar
+    ways to IEEE 802.1X/EAPOL Authenticator state machines. In other
+    words, it integrates IEEE 802.1X Authenticator (normally, an
+    access point) and IEEE 802.1X Supplicant (normally, a wireless
+    client) together to generate a single program that can be used to
+    test EAP methods without having to setup an access point and a
+    wireless client.</para>
+
+    <para>The main uses for eapol_test are in interoperability testing
+    of EAP methods against RADIUS servers and in development testing
+    for new EAP methods. It can be easily used to automate EAP testing
+    for interoperability and regression since the program can be run
+    from shell scripts without require additional test components apart
+    from a RADIUS server. For example, the automated EAP tests described
+    in eap_testing.txt are implemented with eapol_test. Similarly,
+    eapol_test could be used to implement an automated regression
+    test suite for a RADIUS authentication server.</para>
+
+
+    <para>As an example:</para>
+
+<blockquote><programlisting>
+eapol_test -ctest.conf -a127.0.0.1 -p1812 -ssecret -r1
+</programlisting></blockquote>
+
+    <para>tries to complete EAP authentication based on the network
+    configuration from test.conf against the RADIUS server running
+    on the local host. A re-authentication is triggered to test fast
+    re-authentication. The configuration file uses the same format for
+    network blocks as wpa_supplicant.</para>
+
+  </refsect1>
+  <refsect1>
+    <title>Command Arguments</title>
+    <variablelist>
+      <varlistentry>
+       <term>-c configuration file path</term>
+
+       <listitem><para>A configuration to use.  The configuration should
+       use the same format for network blocks as wpa_supplicant.
+       </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-a AS address</term>
+
+       <listitem><para>IP address of the authentication server.  The
+       default is '127.0.0.1'.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-A client address</term>
+
+       <listitem><para>IP address of the client.  The default is to
+       select an address automatically.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-p AS port</term>
+
+       <listitem><para>UDP port of the authentication server. The
+       default is '1812'.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-s AS secret</term>
+
+       <listitem><para>Shared secret with the authentication server.
+       The default is 'radius'.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-r count</term>
+
+       <listitem><para>Number of reauthentications.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-t timeout</term>
+
+       <listitem><para>Timeout in seconds. The default is 30.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-C info</term>
+
+       <listitem><para>RADIUS Connect-Info.  The default is
+       'CONNECT 11Mbps 802.11b'.</para></listitem>
+      </varlistentry>
+
+
+      <varlistentry>
+       <term>-M mac address</term>
+
+       <listitem><para>Client MAC address (Calling-Station-Id).  The
+       default is '02:00:00:00:00:01'.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-o file</term>
+
+       <listitem><para>Location to write out server certificate.
+       </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-N attr spec</term>
+
+       <listitem><para>Send arbitrary attribute specific by
+       attr_id:syntax:value, or attr_id alone.  attr_id should be the numeric
+       ID of the attribute, and syntax should be one of 's' (string),
+       'd' (integer), or 'x' (octet string). The value is the attribute value
+       to send.  When attr_id is given alone, NULL is used as the attribute
+       value.  Multiple attributes can be specified by using the option
+       several times.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-n</term>
+
+       <listitem><para>Indicates that no MPPE keys are expected.
+       </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-W</term>
+
+       <listitem><para>Wait for a control interface monitor before starting.
+       </para></listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-S</term>
+
+       <listitem><para>Save configuration after authentication.
+       </para></listitem>
+      </varlistentry>
+
+    </variablelist>
+  </refsect1>
+  <refsect1>
+    <title>See Also</title>
+    <para>
+      <citerefentry>
+       <refentrytitle>wpa_supplicant</refentrytitle>
+       <manvolnum>8</manvolnum>
+      </citerefentry>
+    </para>
+  </refsect1>
+  <refsect1>
+    <title>Legal</title>
+    <para>wpa_supplicant is copyright (c) 2003-2015,
+    Jouni Malinen <email>j@w1.fi</email> and
+    contributors.
+    All Rights Reserved.</para>
+
+    <para>This program is licensed under the BSD license (the one with
+    advertisement clause removed).</para>
+  </refsect1>
+</refentry>
index eb3a089..afb8c3b 100644 (file)
@@ -90,7 +90,7 @@
 
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2012,
+    <para>wpa_supplicant is copyright (c) 2003-2015,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
index c080c07..47947c1 100644 (file)
     <cmdsynopsis>
       <command>wpa_cli</command>
       <arg>-p <replaceable>path to ctrl sockets</replaceable></arg>
+      <arg>-g <replaceable>path to global ctrl_interface socket</replaceable></arg>
       <arg>-i <replaceable>ifname</replaceable></arg>
       <arg>-hvB</arg>
       <arg>-a <replaceable>action file</replaceable></arg>
       <arg>-P <replaceable>pid file</replaceable></arg>
+      <arg>-G <replaceable>ping interval</replaceable></arg>
       <arg><replaceable>command ...</replaceable></arg>
     </cmdsynopsis>
   </refsynopsisdiv>
@@ -111,6 +113,14 @@ CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar
       </varlistentry>
 
       <varlistentry>
+       <term>-g control socket path</term>
+
+       <listitem><para>Connect to the global control socket at the
+       indicated path rather than an interface-specific control
+       socket.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
        <term>-i ifname</term>
 
         <listitem><para>Specify the interface that is being
@@ -161,6 +171,13 @@ CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar
       </varlistentry>
 
       <varlistentry>
+       <term>-G ping interval</term>
+
+       <listitem><para>Set the interval (in seconds) at which
+       wpa_cli pings the supplicant.</para></listitem>
+      </varlistentry>
+
+      <varlistentry>
        <term>command</term>
 
        <listitem><para>Run a command.  The available commands are
@@ -328,7 +345,7 @@ CTRL-REQ-OTP-2:Challenge 1235663 needed for SSID foobar
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2012,
+    <para>wpa_supplicant is copyright (c) 2003-2015,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
index 0ab6419..84766db 100644 (file)
@@ -74,7 +74,7 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2012,
+    <para>wpa_supplicant is copyright (c) 2003-2015,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
index 336c03b..b381e40 100644 (file)
@@ -62,7 +62,7 @@
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2012,
+    <para>wpa_supplicant is copyright (c) 2003-2015,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
index eb907a8..d13a5db 100644 (file)
@@ -137,7 +137,7 @@ wpa_supplicant -i ath0 -c wpa_supplicant.conf
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2012,
+    <para>wpa_supplicant is copyright (c) 2003-2015,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
index aa20e57..46c21b5 100644 (file)
@@ -12,7 +12,7 @@
   <refsynopsisdiv>
     <cmdsynopsis>
       <command>wpa_supplicant</command>
-      <arg>-BddfhKLqqtuvW</arg>
+      <arg>-BddfhKLqqsTtuvW</arg>
       <arg>-i<replaceable>ifname</replaceable></arg>
       <arg>-c<replaceable>config file</replaceable></arg>
       <arg>-D<replaceable>driver</replaceable></arg>
       </varlistentry>
 
       <varlistentry>
+       <term>-e entropy file</term>
+       <listitem>
+         <para>File for <command>wpa_supplicant</command> to use to
+         maintain its internal entropy store in over restarts.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
        <term>-f output file</term>
        <listitem>
-         <para>Log output to specified file instead of stdout.</para>
+         <para>Log output to specified file instead of stdout. (This
+         is only available if <command>wpa_supplicant</command> was
+         built with the <literal>CONFIG_DEBUG_FILE</literal>
+         option.)</para>
        </listitem>
       </varlistentry>
 
       </varlistentry>
 
       <varlistentry>
+       <term>-o override driver</term>
+       <listitem>
+         <para>Override the driver parameter for new
+         interfaces.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-O override ctrl_interface</term>
+       <listitem>
+         <para>Override the ctrl_interface parameter for new
+         interfaces.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
        <term>-p</term>
        <listitem>
          <para>Driver parameters. (Per interface)</para>
       </varlistentry>
 
       <varlistentry>
+       <term>-s</term>
+       <listitem>
+         <para>Log output to syslog instead of stdout. (This is only
+         available if <command>wpa_supplicant</command> was built
+         with the <literal>CONFIG_DEBUG_SYSLOG</literal>
+         option.)</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-T</term>
+       <listitem>
+         <para>Log output to Linux tracing in addition to any other
+         destinations. (This is only available
+         if <command>wpa_supplicant</command> was built with
+         the <literal>CONFIG_DEBUG_LINUX_TRACING</literal>
+         option.)</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
+       <term>-t</term>
+       <listitem>
+         <para>Include timestamp in debug messages.</para>
+       </listitem>
+      </varlistentry>
+
+      <varlistentry>
        <term>-u</term>
        <listitem>
-         <para>Enabled DBus control interface. If enabled, interface
-         definitions may be omitted.</para>
+         <para>Enable DBus control interface. If enabled, interface
+         definitions may be omitted. (This is only available
+         if <command>wpa_supplicant</command> was built with
+         the <literal>CONFIG_DBUS</literal> option.)</para>
        </listitem>
       </varlistentry>
 
@@ -679,7 +736,7 @@ fi
   </refsect1>
   <refsect1>
     <title>Legal</title>
-    <para>wpa_supplicant is copyright (c) 2003-2012,
+    <para>wpa_supplicant is copyright (c) 2003-2015,
     Jouni Malinen <email>j@w1.fi</email> and
     contributors.
     All Rights Reserved.</para>
index 8036c07..65b430d 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant - Internal driver interface wrappers
- * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -65,9 +65,35 @@ static inline int wpa_drv_associate(struct wpa_supplicant *wpa_s,
        return -1;
 }
 
+static inline int wpa_drv_init_mesh(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->driver->init_mesh)
+               return wpa_s->driver->init_mesh(wpa_s->drv_priv);
+       return -1;
+}
+
+static inline int wpa_drv_join_mesh(struct wpa_supplicant *wpa_s,
+                                   struct wpa_driver_mesh_join_params *params)
+{
+       if (wpa_s->driver->join_mesh)
+               return wpa_s->driver->join_mesh(wpa_s->drv_priv, params);
+       return -1;
+}
+
+static inline int wpa_drv_leave_mesh(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->driver->leave_mesh)
+               return wpa_s->driver->leave_mesh(wpa_s->drv_priv);
+       return -1;
+}
+
 static inline int wpa_drv_scan(struct wpa_supplicant *wpa_s,
                               struct wpa_driver_scan_params *params)
 {
+#ifdef CONFIG_TESTING_OPTIONS
+       if (wpa_s->test_failure == WPAS_TEST_FAILURE_SCAN_TRIGGER)
+               return -EBUSY;
+#endif /* CONFIG_TESTING_OPTIONS */
        if (wpa_s->driver->scan2)
                return wpa_s->driver->scan2(wpa_s->drv_priv, params);
        return -1;
@@ -117,11 +143,16 @@ static inline int wpa_drv_get_ssid(struct wpa_supplicant *wpa_s, u8 *ssid)
 static inline int wpa_drv_set_key(struct wpa_supplicant *wpa_s,
                                  enum wpa_alg alg, const u8 *addr,
                                  int key_idx, int set_tx,
-                                  const u8 *seq, size_t seq_len,
-                                  const u8 *key, size_t key_len)
-{
+                                 const u8 *seq, size_t seq_len,
+                                 const u8 *key, size_t key_len)
+{
+       if (alg != WPA_ALG_NONE) {
+               if (key_idx >= 0 && key_idx <= 6)
+                       wpa_s->keys_cleared &= ~BIT(key_idx);
+               else
+                       wpa_s->keys_cleared = 0;
+       }
        if (wpa_s->driver->set_key) {
-               wpa_s->keys_cleared = 0;
                return wpa_s->driver->set_key(wpa_s->ifname, wpa_s->drv_priv,
                                              alg, addr, key_idx, set_tx,
                                              seq, seq_len, key, key_len);
@@ -133,7 +164,8 @@ static inline int wpa_drv_sta_deauth(struct wpa_supplicant *wpa_s,
                                     const u8 *addr, int reason_code)
 {
        if (wpa_s->driver->sta_deauth) {
-               return wpa_s->driver->sta_deauth(wpa_s->drv_priv, NULL, addr,
+               return wpa_s->driver->sta_deauth(wpa_s->drv_priv,
+                                                wpa_s->own_addr, addr,
                                                 reason_code);
        }
        return -1;
@@ -149,18 +181,6 @@ static inline int wpa_drv_deauthenticate(struct wpa_supplicant *wpa_s,
        return -1;
 }
 
-#ifdef TIZEN_EXT
-static inline int wpa_drv_disassociate(struct wpa_supplicant *wpa_s,
-                                      const u8 *addr, int reason_code)
-{
-       if (wpa_s->driver->disassociate) {
-               return wpa_s->driver->disassociate(wpa_s->drv_priv, addr,
-                                                  reason_code);
-       }
-       return -1;
-}
-#endif /* TIZEN_EXT */
-
 static inline int wpa_drv_add_pmkid(struct wpa_supplicant *wpa_s,
                                    const u8 *bssid, const u8 *pmkid)
 {
@@ -212,6 +232,14 @@ static inline const char * wpa_drv_get_ifname(struct wpa_supplicant *wpa_s)
        return NULL;
 }
 
+static inline const char *
+wpa_driver_get_radio_name(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->driver->get_radio_name)
+               return wpa_s->driver->get_radio_name(wpa_s->drv_priv);
+       return NULL;
+}
+
 static inline const u8 * wpa_drv_get_mac_addr(struct wpa_supplicant *wpa_s)
 {
        if (wpa_s->driver->get_mac_addr) {
@@ -220,16 +248,6 @@ static inline const u8 * wpa_drv_get_mac_addr(struct wpa_supplicant *wpa_s)
        return NULL;
 }
 
-static inline int wpa_drv_send_eapol(struct wpa_supplicant *wpa_s,
-                                    const u8 *dst, u16 proto,
-                                    const u8 *data, size_t data_len)
-{
-       if (wpa_s->driver->send_eapol)
-               return wpa_s->driver->send_eapol(wpa_s->drv_priv, dst, proto,
-                                                data, data_len);
-       return -1;
-}
-
 static inline int wpa_drv_set_operstate(struct wpa_supplicant *wpa_s,
                                        int state)
 {
@@ -286,16 +304,6 @@ static inline int wpa_drv_update_ft_ies(struct wpa_supplicant *wpa_s,
        return -1;
 }
 
-static inline int wpa_drv_send_ft_action(struct wpa_supplicant *wpa_s,
-                                        u8 action, const u8 *target_ap,
-                                        const u8 *ies, size_t ies_len)
-{
-       if (wpa_s->driver->send_ft_action)
-               return wpa_s->driver->send_ft_action(wpa_s->drv_priv, action,
-                                                    target_ap, ies, ies_len);
-       return -1;
-}
-
 static inline int wpa_drv_set_ap(struct wpa_supplicant *wpa_s,
                                 struct wpa_driver_ap_params *params)
 {
@@ -391,7 +399,7 @@ static inline int wpa_drv_if_add(struct wpa_supplicant *wpa_s,
        if (wpa_s->driver->if_add)
                return wpa_s->driver->if_add(wpa_s->drv_priv, type, ifname,
                                             addr, bss_ctx, NULL, force_ifname,
-                                            if_addr, bridge);
+                                            if_addr, bridge, 0);
        return -1;
 }
 
@@ -527,188 +535,359 @@ static inline int wpa_drv_ampdu(struct wpa_supplicant *wpa_s, int ampdu)
        return wpa_s->driver->ampdu(wpa_s->drv_priv, ampdu);
 }
 
-static inline int wpa_drv_p2p_find(struct wpa_supplicant *wpa_s,
-                                  unsigned int timeout, int type)
+static inline int wpa_drv_send_tdls_mgmt(struct wpa_supplicant *wpa_s,
+                                        const u8 *dst, u8 action_code,
+                                        u8 dialog_token, u16 status_code,
+                                        u32 peer_capab, int initiator,
+                                        const u8 *buf, size_t len)
+{
+       if (wpa_s->driver->send_tdls_mgmt) {
+               return wpa_s->driver->send_tdls_mgmt(wpa_s->drv_priv, dst,
+                                                    action_code, dialog_token,
+                                                    status_code, peer_capab,
+                                                    initiator, buf, len);
+       }
+       return -1;
+}
+
+static inline int wpa_drv_tdls_oper(struct wpa_supplicant *wpa_s,
+                                   enum tdls_oper oper, const u8 *peer)
+{
+       if (!wpa_s->driver->tdls_oper)
+               return -1;
+       return wpa_s->driver->tdls_oper(wpa_s->drv_priv, oper, peer);
+}
+
+#ifdef ANDROID
+static inline int wpa_drv_driver_cmd(struct wpa_supplicant *wpa_s,
+                                    char *cmd, char *buf, size_t buf_len)
+{
+       if (!wpa_s->driver->driver_cmd)
+               return -1;
+       return wpa_s->driver->driver_cmd(wpa_s->drv_priv, cmd, buf, buf_len);
+}
+#endif /* ANDROID */
+
+static inline void wpa_drv_set_rekey_info(struct wpa_supplicant *wpa_s,
+                                         const u8 *kek, size_t kek_len,
+                                         const u8 *kck, size_t kck_len,
+                                         const u8 *replay_ctr)
+{
+       if (!wpa_s->driver->set_rekey_info)
+               return;
+       wpa_s->driver->set_rekey_info(wpa_s->drv_priv, kek, kek_len,
+                                     kck, kck_len, replay_ctr);
+}
+
+static inline int wpa_drv_radio_disable(struct wpa_supplicant *wpa_s,
+                                       int disabled)
 {
-       if (!wpa_s->driver->p2p_find)
+       if (!wpa_s->driver->radio_disable)
                return -1;
-       return wpa_s->driver->p2p_find(wpa_s->drv_priv, timeout, type);
+       return wpa_s->driver->radio_disable(wpa_s->drv_priv, disabled);
 }
 
-static inline int wpa_drv_p2p_stop_find(struct wpa_supplicant *wpa_s)
+static inline int wpa_drv_switch_channel(struct wpa_supplicant *wpa_s,
+                                        struct csa_settings *settings)
 {
-       if (!wpa_s->driver->p2p_stop_find)
+       if (!wpa_s->driver->switch_channel)
                return -1;
-       return wpa_s->driver->p2p_stop_find(wpa_s->drv_priv);
+       return wpa_s->driver->switch_channel(wpa_s->drv_priv, settings);
 }
 
-static inline int wpa_drv_p2p_listen(struct wpa_supplicant *wpa_s,
-                                    unsigned int timeout)
+static inline int wpa_drv_add_ts(struct wpa_supplicant *wpa_s, u8 tsid,
+                                const u8 *address, u8 user_priority,
+                                u16 admitted_time)
 {
-       if (!wpa_s->driver->p2p_listen)
+       if (!wpa_s->driver->add_tx_ts)
                return -1;
-       return wpa_s->driver->p2p_listen(wpa_s->drv_priv, timeout);
+       return wpa_s->driver->add_tx_ts(wpa_s->drv_priv, tsid, address,
+                                       user_priority, admitted_time);
 }
 
-static inline int wpa_drv_p2p_connect(struct wpa_supplicant *wpa_s,
-                                     const u8 *peer_addr, int wps_method,
-                                     int go_intent,
-                                     const u8 *own_interface_addr,
-                                     unsigned int force_freq,
-                                     int persistent_group)
+static inline int wpa_drv_del_ts(struct wpa_supplicant *wpa_s, u8 tid,
+                                const u8 *address)
 {
-       if (!wpa_s->driver->p2p_connect)
+       if (!wpa_s->driver->del_tx_ts)
                return -1;
-       return wpa_s->driver->p2p_connect(wpa_s->drv_priv, peer_addr,
-                                         wps_method, go_intent,
-                                         own_interface_addr, force_freq,
-                                         persistent_group);
+       return wpa_s->driver->del_tx_ts(wpa_s->drv_priv, tid, address);
 }
 
-static inline int wpa_drv_wps_success_cb(struct wpa_supplicant *wpa_s,
-                                        const u8 *peer_addr)
+static inline int wpa_drv_tdls_enable_channel_switch(
+       struct wpa_supplicant *wpa_s, const u8 *addr, u8 oper_class,
+       const struct hostapd_freq_params *freq_params)
 {
-       if (!wpa_s->driver->wps_success_cb)
+       if (!wpa_s->driver->tdls_enable_channel_switch)
                return -1;
-       return wpa_s->driver->wps_success_cb(wpa_s->drv_priv, peer_addr);
+       return wpa_s->driver->tdls_enable_channel_switch(wpa_s->drv_priv, addr,
+                                                        oper_class,
+                                                        freq_params);
 }
 
 static inline int
-wpa_drv_p2p_group_formation_failed(struct wpa_supplicant *wpa_s)
+wpa_drv_tdls_disable_channel_switch(struct wpa_supplicant *wpa_s,
+                                   const u8 *addr)
 {
-       if (!wpa_s->driver->p2p_group_formation_failed)
+       if (!wpa_s->driver->tdls_disable_channel_switch)
                return -1;
-       return wpa_s->driver->p2p_group_formation_failed(wpa_s->drv_priv);
+       return wpa_s->driver->tdls_disable_channel_switch(wpa_s->drv_priv,
+                                                         addr);
 }
 
-static inline int wpa_drv_p2p_set_params(struct wpa_supplicant *wpa_s,
-                                        const struct p2p_params *params)
+static inline int wpa_drv_wnm_oper(struct wpa_supplicant *wpa_s,
+                                  enum wnm_oper oper, const u8 *peer,
+                                  u8 *buf, u16 *buf_len)
 {
-       if (!wpa_s->driver->p2p_set_params)
+       if (!wpa_s->driver->wnm_oper)
                return -1;
-       return wpa_s->driver->p2p_set_params(wpa_s->drv_priv, params);
+       return wpa_s->driver->wnm_oper(wpa_s->drv_priv, oper, peer, buf,
+                                      buf_len);
 }
 
-static inline int wpa_drv_p2p_prov_disc_req(struct wpa_supplicant *wpa_s,
-                                           const u8 *peer_addr,
-                                           u16 config_methods, int join)
+static inline int wpa_drv_status(struct wpa_supplicant *wpa_s,
+                                char *buf, size_t buflen)
 {
-       if (!wpa_s->driver->p2p_prov_disc_req)
+       if (!wpa_s->driver->status)
                return -1;
-       return wpa_s->driver->p2p_prov_disc_req(wpa_s->drv_priv, peer_addr,
-                                               config_methods, join);
+       return wpa_s->driver->status(wpa_s->drv_priv, buf, buflen);
 }
 
-static inline u64 wpa_drv_p2p_sd_request(struct wpa_supplicant *wpa_s,
-                                        const u8 *dst,
-                                        const struct wpabuf *tlvs)
+static inline int wpa_drv_set_qos_map(struct wpa_supplicant *wpa_s,
+                                     const u8 *qos_map_set, u8 qos_map_set_len)
 {
-       if (!wpa_s->driver->p2p_sd_request)
-               return 0;
-       return wpa_s->driver->p2p_sd_request(wpa_s->drv_priv, dst, tlvs);
+       if (!wpa_s->driver->set_qos_map)
+               return -1;
+       return wpa_s->driver->set_qos_map(wpa_s->drv_priv, qos_map_set,
+                                         qos_map_set_len);
 }
 
-static inline int wpa_drv_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s,
-                                               u64 req)
+static inline int wpa_drv_wowlan(struct wpa_supplicant *wpa_s,
+                                const struct wowlan_triggers *triggers)
 {
-       if (!wpa_s->driver->p2p_sd_cancel_request)
+       if (!wpa_s->driver->set_wowlan)
                return -1;
-       return wpa_s->driver->p2p_sd_cancel_request(wpa_s->drv_priv, req);
+       return wpa_s->driver->set_wowlan(wpa_s->drv_priv, triggers);
 }
 
-static inline int wpa_drv_p2p_sd_response(struct wpa_supplicant *wpa_s,
-                                         int freq, const u8 *dst,
-                                         u8 dialog_token,
-                                         const struct wpabuf *resp_tlvs)
+static inline int wpa_drv_vendor_cmd(struct wpa_supplicant *wpa_s,
+                                    int vendor_id, int subcmd, const u8 *data,
+                                    size_t data_len, struct wpabuf *buf)
 {
-       if (!wpa_s->driver->p2p_sd_response)
+       if (!wpa_s->driver->vendor_cmd)
                return -1;
-       return wpa_s->driver->p2p_sd_response(wpa_s->drv_priv, freq, dst,
-                                             dialog_token, resp_tlvs);
+       return wpa_s->driver->vendor_cmd(wpa_s->drv_priv, vendor_id, subcmd,
+                                        data, data_len, buf);
 }
 
-static inline int wpa_drv_p2p_service_update(struct wpa_supplicant *wpa_s)
+static inline int wpa_drv_roaming(struct wpa_supplicant *wpa_s, int allowed,
+                                 const u8 *bssid)
 {
-       if (!wpa_s->driver->p2p_service_update)
+       if (!wpa_s->driver->roaming)
                return -1;
-       return wpa_s->driver->p2p_service_update(wpa_s->drv_priv);
+       return wpa_s->driver->roaming(wpa_s->drv_priv, allowed, bssid);
 }
 
-static inline int wpa_drv_p2p_reject(struct wpa_supplicant *wpa_s,
-                                    const u8 *addr)
+static inline int wpa_drv_set_mac_addr(struct wpa_supplicant *wpa_s,
+                                      const u8 *addr)
 {
-       if (!wpa_s->driver->p2p_reject)
+       if (!wpa_s->driver->set_mac_addr)
                return -1;
-       return wpa_s->driver->p2p_reject(wpa_s->drv_priv, addr);
+       return wpa_s->driver->set_mac_addr(wpa_s->drv_priv, addr);
 }
 
-static inline int wpa_drv_p2p_invite(struct wpa_supplicant *wpa_s,
-                                    const u8 *peer, int role, const u8 *bssid,
-                                    const u8 *ssid, size_t ssid_len,
-                                    const u8 *go_dev_addr,
-                                    int persistent_group)
+
+#ifdef CONFIG_MACSEC
+
+static inline int wpa_drv_macsec_init(struct wpa_supplicant *wpa_s,
+                                     struct macsec_init_params *params)
 {
-       if (!wpa_s->driver->p2p_invite)
+       if (!wpa_s->driver->macsec_init)
                return -1;
-       return wpa_s->driver->p2p_invite(wpa_s->drv_priv, peer, role, bssid,
-                                        ssid, ssid_len, go_dev_addr,
-                                        persistent_group);
+       return wpa_s->driver->macsec_init(wpa_s->drv_priv, params);
 }
 
-static inline int wpa_drv_send_tdls_mgmt(struct wpa_supplicant *wpa_s,
-                                        const u8 *dst, u8 action_code,
-                                        u8 dialog_token, u16 status_code,
-                                        const u8 *buf, size_t len)
+static inline int wpa_drv_macsec_deinit(struct wpa_supplicant *wpa_s)
 {
-       if (wpa_s->driver->send_tdls_mgmt) {
-               return wpa_s->driver->send_tdls_mgmt(wpa_s->drv_priv, dst,
-                                                    action_code, dialog_token,
-                                                    status_code, buf, len);
-       }
-       return -1;
+       if (!wpa_s->driver->macsec_deinit)
+               return -1;
+       return wpa_s->driver->macsec_deinit(wpa_s->drv_priv);
 }
 
-static inline int wpa_drv_tdls_oper(struct wpa_supplicant *wpa_s,
-                                   enum tdls_oper oper, const u8 *peer)
+static inline int wpa_drv_enable_protect_frames(struct wpa_supplicant *wpa_s,
+                                               Boolean enabled)
 {
-       if (!wpa_s->driver->tdls_oper)
+       if (!wpa_s->driver->enable_protect_frames)
                return -1;
-       return wpa_s->driver->tdls_oper(wpa_s->drv_priv, oper, peer);
+       return wpa_s->driver->enable_protect_frames(wpa_s->drv_priv, enabled);
 }
 
-static inline void wpa_drv_set_rekey_info(struct wpa_supplicant *wpa_s,
-                                         const u8 *kek, const u8 *kck,
-                                         const u8 *replay_ctr)
+static inline int wpa_drv_set_replay_protect(struct wpa_supplicant *wpa_s,
+                                            Boolean enabled, u32 window)
 {
-       if (!wpa_s->driver->set_rekey_info)
-               return;
-       wpa_s->driver->set_rekey_info(wpa_s->drv_priv, kek, kck, replay_ctr);
+       if (!wpa_s->driver->set_replay_protect)
+               return -1;
+       return wpa_s->driver->set_replay_protect(wpa_s->drv_priv, enabled,
+                                                window);
 }
 
-static inline int wpa_drv_radio_disable(struct wpa_supplicant *wpa_s,
-                                       int disabled)
+static inline int wpa_drv_set_current_cipher_suite(struct wpa_supplicant *wpa_s,
+                                                  const u8 *cs, size_t cs_len)
 {
-       if (!wpa_s->driver->radio_disable)
+       if (!wpa_s->driver->set_current_cipher_suite)
                return -1;
-       return wpa_s->driver->radio_disable(wpa_s->drv_priv, disabled);
+       return wpa_s->driver->set_current_cipher_suite(wpa_s->drv_priv, cs,
+                                                      cs_len);
 }
 
-static inline int wpa_drv_switch_channel(struct wpa_supplicant *wpa_s,
-                                        unsigned int freq)
+static inline int wpa_drv_enable_controlled_port(struct wpa_supplicant *wpa_s,
+                                                Boolean enabled)
 {
-       if (!wpa_s->driver->switch_channel)
+       if (!wpa_s->driver->enable_controlled_port)
                return -1;
-       return wpa_s->driver->switch_channel(wpa_s->drv_priv, freq);
+       return wpa_s->driver->enable_controlled_port(wpa_s->drv_priv, enabled);
 }
 
-static inline int wpa_drv_wnm_oper(struct wpa_supplicant *wpa_s,
-                                  enum wnm_oper oper, const u8 *peer,
-                                  u8 *buf, u16 *buf_len)
+static inline int wpa_drv_get_receive_lowest_pn(struct wpa_supplicant *wpa_s,
+                                               u32 channel, u8 an,
+                                               u32 *lowest_pn)
 {
-       if (!wpa_s->driver->wnm_oper)
+       if (!wpa_s->driver->get_receive_lowest_pn)
                return -1;
-       return wpa_s->driver->wnm_oper(wpa_s->drv_priv, oper, peer, buf,
-                                      buf_len);
+       return wpa_s->driver->get_receive_lowest_pn(wpa_s->drv_priv, channel,
+                                                   an, lowest_pn);
+}
+
+static inline int wpa_drv_get_transmit_next_pn(struct wpa_supplicant *wpa_s,
+                                               u32 channel, u8 an,
+                                               u32 *next_pn)
+{
+       if (!wpa_s->driver->get_transmit_next_pn)
+               return -1;
+       return wpa_s->driver->get_transmit_next_pn(wpa_s->drv_priv, channel,
+                                                   an, next_pn);
+}
+
+static inline int wpa_drv_set_transmit_next_pn(struct wpa_supplicant *wpa_s,
+                                               u32 channel, u8 an,
+                                               u32 next_pn)
+{
+       if (!wpa_s->driver->set_transmit_next_pn)
+               return -1;
+       return wpa_s->driver->set_transmit_next_pn(wpa_s->drv_priv, channel,
+                                                   an, next_pn);
+}
+
+static inline int wpa_drv_get_available_receive_sc(struct wpa_supplicant *wpa_s,
+                                                  u32 *channel)
+{
+       if (!wpa_s->driver->get_available_receive_sc)
+               return -1;
+       return wpa_s->driver->get_available_receive_sc(wpa_s->drv_priv,
+                                                      channel);
+}
+
+static inline int
+wpa_drv_create_receive_sc(struct wpa_supplicant *wpa_s, u32 channel,
+                         const u8 *sci_addr, u16 sci_port,
+                         unsigned int conf_offset, int validation)
+{
+       if (!wpa_s->driver->create_receive_sc)
+               return -1;
+       return wpa_s->driver->create_receive_sc(wpa_s->drv_priv, channel,
+                                               sci_addr, sci_port, conf_offset,
+                                               validation);
+}
+
+static inline int wpa_drv_delete_receive_sc(struct wpa_supplicant *wpa_s,
+                                           u32 channel)
+{
+       if (!wpa_s->driver->delete_receive_sc)
+               return -1;
+       return wpa_s->driver->delete_receive_sc(wpa_s->drv_priv, channel);
+}
+
+static inline int wpa_drv_create_receive_sa(struct wpa_supplicant *wpa_s,
+                                           u32 channel, u8 an,
+                                           u32 lowest_pn, const u8 *sak)
+{
+       if (!wpa_s->driver->create_receive_sa)
+               return -1;
+       return wpa_s->driver->create_receive_sa(wpa_s->drv_priv, channel, an,
+                                               lowest_pn, sak);
+}
+
+static inline int wpa_drv_enable_receive_sa(struct wpa_supplicant *wpa_s,
+                                           u32 channel, u8 an)
+{
+       if (!wpa_s->driver->enable_receive_sa)
+               return -1;
+       return wpa_s->driver->enable_receive_sa(wpa_s->drv_priv, channel, an);
+}
+
+static inline int wpa_drv_disable_receive_sa(struct wpa_supplicant *wpa_s,
+                                            u32 channel, u8 an)
+{
+       if (!wpa_s->driver->disable_receive_sa)
+               return -1;
+       return wpa_s->driver->disable_receive_sa(wpa_s->drv_priv, channel, an);
+}
+
+static inline int
+wpa_drv_get_available_transmit_sc(struct wpa_supplicant *wpa_s, u32 *channel)
+{
+       if (!wpa_s->driver->get_available_transmit_sc)
+               return -1;
+       return wpa_s->driver->get_available_transmit_sc(wpa_s->drv_priv,
+                                                       channel);
+}
+
+static inline int
+wpa_drv_create_transmit_sc(struct wpa_supplicant *wpa_s, u32 channel,
+                          const u8 *sci_addr, u16 sci_port,
+                          unsigned int conf_offset)
+{
+       if (!wpa_s->driver->create_transmit_sc)
+               return -1;
+       return wpa_s->driver->create_transmit_sc(wpa_s->drv_priv, channel,
+                                                sci_addr, sci_port,
+                                                conf_offset);
+}
+
+static inline int wpa_drv_delete_transmit_sc(struct wpa_supplicant *wpa_s,
+                                            u32 channel)
+{
+       if (!wpa_s->driver->delete_transmit_sc)
+               return -1;
+       return wpa_s->driver->delete_transmit_sc(wpa_s->drv_priv, channel);
+}
+
+static inline int wpa_drv_create_transmit_sa(struct wpa_supplicant *wpa_s,
+                                            u32 channel, u8 an,
+                                            u32 next_pn,
+                                            Boolean confidentiality,
+                                            const u8 *sak)
+{
+       if (!wpa_s->driver->create_transmit_sa)
+               return -1;
+       return wpa_s->driver->create_transmit_sa(wpa_s->drv_priv, channel, an,
+                                                next_pn, confidentiality, sak);
+}
+
+static inline int wpa_drv_enable_transmit_sa(struct wpa_supplicant *wpa_s,
+                                            u32 channel, u8 an)
+{
+       if (!wpa_s->driver->enable_transmit_sa)
+               return -1;
+       return wpa_s->driver->enable_transmit_sa(wpa_s->drv_priv, channel, an);
+}
+
+static inline int wpa_drv_disable_transmit_sa(struct wpa_supplicant *wpa_s,
+                                             u32 channel, u8 an)
+{
+       if (!wpa_s->driver->disable_transmit_sa)
+               return -1;
+       return wpa_s->driver->disable_transmit_sa(wpa_s->drv_priv, channel, an);
 }
+#endif /* CONFIG_MACSEC */
 
 #endif /* DRIVER_I_H */
diff --git a/wpa_supplicant/eap_proxy_dummy.mak b/wpa_supplicant/eap_proxy_dummy.mak
new file mode 100644 (file)
index 0000000..e69de29
index d1eb4ff..ece5716 100644 (file)
@@ -40,6 +40,13 @@ int eap_register_methods(void)
                ret = eap_peer_unauth_tls_register();
 #endif /* EAP_UNAUTH_TLS */
 
+#ifdef EAP_TLS
+#ifdef CONFIG_HS20
+       if (ret == 0)
+               ret = eap_peer_wfa_unauth_tls_register();
+#endif /* CONFIG_HS20 */
+#endif /* EAP_TLS */
+
 #ifdef EAP_MSCHAPv2
        if (ret == 0)
                ret = eap_peer_mschapv2_register();
@@ -135,6 +142,11 @@ int eap_register_methods(void)
                ret = eap_peer_pwd_register();
 #endif /* EAP_PWD */
 
+#ifdef EAP_EKE
+       if (ret == 0)
+               ret = eap_peer_eke_register();
+#endif /* EAP_EKE */
+
 #ifdef EAP_SERVER_IDENTITY
        if (ret == 0)
                ret = eap_server_identity_register();
index dad2765..9b7af30 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - test code
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 #include "common/wpa_ctrl.h"
 #include "ctrl_iface.h"
 #include "pcsc_funcs.h"
+#include "wpas_glue.h"
 
 
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-
 struct wpa_driver_ops *wpa_drivers[] = { NULL };
 
 
@@ -48,6 +46,7 @@ struct eapol_test_data {
        int eapol_test_num_reauths;
        int no_mppe_keys;
        int num_mppe_ok, num_mppe_mismatch;
+       int req_eap_key_name;
 
        u8 radius_identifier;
        struct radius_msg *last_recv_radius;
@@ -60,6 +59,8 @@ struct eapol_test_data {
 
        u8 authenticator_pmk[PMK_LEN];
        size_t authenticator_pmk_len;
+       u8 authenticator_eap_key_name[256];
+       size_t authenticator_eap_key_name_len;
        int radius_access_accept_received;
        int radius_access_reject_received;
        int auth_timed_out;
@@ -72,6 +73,9 @@ struct eapol_test_data {
        struct extra_radius_attr *extra_attrs;
 
        FILE *server_cert_file;
+
+       const char *pcsc_reader;
+       const char *pcsc_pin;
 };
 
 static struct eapol_test_data eapol_test;
@@ -210,6 +214,13 @@ static void ieee802_1x_encapsulate_radius(struct eapol_test_data *e,
                goto fail;
        }
 
+       if (e->req_eap_key_name &&
+           !radius_msg_add_attr(msg, RADIUS_ATTR_EAP_KEY_NAME, (u8 *) "\0",
+                                1)) {
+               printf("Could not add EAP-Key-Name\n");
+               goto fail;
+       }
+
        if (!find_extra_attr(e->extra_attrs, RADIUS_ATTR_NAS_IP_ADDRESS) &&
            !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS,
                                 (u8 *) &e->own_ip_addr, 4)) {
@@ -335,6 +346,8 @@ static int eapol_test_compare_pmk(struct eapol_test_data *e)
 {
        u8 pmk[PMK_LEN];
        int ret = 1;
+       const u8 *sess_id;
+       size_t sess_id_len;
 
        if (eapol_sm_get_key(e->wpa_s->eapol, pmk, PMK_LEN) == 0) {
                wpa_hexdump(MSG_DEBUG, "PMK from EAPOL", pmk, PMK_LEN);
@@ -363,14 +376,37 @@ static int eapol_test_compare_pmk(struct eapol_test_data *e)
        else if (!e->no_mppe_keys)
                e->num_mppe_ok++;
 
+       sess_id = eapol_sm_get_session_id(e->wpa_s->eapol, &sess_id_len);
+       if (!sess_id)
+               return ret;
+       if (e->authenticator_eap_key_name_len == 0) {
+               wpa_printf(MSG_INFO, "No EAP-Key-Name received from server");
+               return ret;
+       }
+
+       if (e->authenticator_eap_key_name_len != sess_id_len ||
+           os_memcmp(e->authenticator_eap_key_name, sess_id, sess_id_len) != 0)
+       {
+               wpa_printf(MSG_INFO,
+                          "Locally derived EAP Session-Id does not match EAP-Key-Name from server");
+               wpa_hexdump(MSG_DEBUG, "EAP Session-Id", sess_id, sess_id_len);
+               wpa_hexdump(MSG_DEBUG, "EAP-Key-Name from server",
+                           e->authenticator_eap_key_name,
+                           e->authenticator_eap_key_name_len);
+       } else {
+               wpa_printf(MSG_INFO,
+                          "Locally derived EAP Session-Id matches EAP-Key-Name from server");
+       }
+
        return ret;
 }
 
 
-static void eapol_sm_cb(struct eapol_sm *eapol, int success, void *ctx)
+static void eapol_sm_cb(struct eapol_sm *eapol, enum eapol_supp_result result,
+                       void *ctx)
 {
        struct eapol_test_data *e = ctx;
-       printf("eapol_sm_cb: success=%d\n", success);
+       printf("eapol_sm_cb: result=%d\n", result);
        e->eapol_test_num_reauths--;
        if (e->eapol_test_num_reauths < 0)
                eloop_terminate();
@@ -395,7 +431,56 @@ static void eapol_test_write_cert(FILE *f, const char *subject,
 }
 
 
+#if defined(CONFIG_CTRL_IFACE) || !defined(CONFIG_NO_STDOUT_DEBUG)
+static void eapol_test_eap_param_needed(void *ctx, enum wpa_ctrl_req_type field,
+                                       const char *default_txt)
+{
+       struct eapol_test_data *e = ctx;
+       struct wpa_supplicant *wpa_s = e->wpa_s;
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+       const char *field_name, *txt = NULL;
+       char *buf;
+       size_t buflen;
+       int len;
+
+       if (ssid == NULL)
+               return;
+
+       field_name = wpa_supplicant_ctrl_req_to_string(field, default_txt,
+                                                      &txt);
+       if (field_name == NULL) {
+               wpa_printf(MSG_WARNING, "Unhandled EAP param %d needed",
+                          field);
+               return;
+       }
+
+       buflen = 100 + os_strlen(txt) + ssid->ssid_len;
+       buf = os_malloc(buflen);
+       if (buf == NULL)
+               return;
+       len = os_snprintf(buf, buflen,
+                         WPA_CTRL_REQ "%s-%d:%s needed for SSID ",
+                         field_name, ssid->id, txt);
+       if (os_snprintf_error(buflen, len)) {
+               os_free(buf);
+               return;
+       }
+       if (ssid->ssid && buflen > len + ssid->ssid_len) {
+               os_memcpy(buf + len, ssid->ssid, ssid->ssid_len);
+               len += ssid->ssid_len;
+               buf[len] = '\0';
+       }
+       buf[buflen - 1] = '\0';
+       wpa_msg(wpa_s, MSG_INFO, "%s", buf);
+       os_free(buf);
+}
+#else /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+#define eapol_test_eap_param_needed NULL
+#endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
+
+
 static void eapol_test_cert_cb(void *ctx, int depth, const char *subject,
+                              const char *altsubject[], int num_altsubject,
                               const char *cert_hash,
                               const struct wpabuf *cert)
 {
@@ -425,6 +510,14 @@ static void eapol_test_cert_cb(void *ctx, int depth, const char *subject,
                        eapol_test_write_cert(e->server_cert_file,
                                              subject, cert);
        }
+
+       if (altsubject) {
+               int i;
+
+               for (i = 0; i < num_altsubject; i++)
+                       wpa_msg(e->wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_ALT
+                               "depth=%d %s", depth, altsubject[i]);
+       }
 }
 
 
@@ -484,6 +577,8 @@ static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s,
        ctx->opensc_engine_path = wpa_s->conf->opensc_engine_path;
        ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
        ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
+       ctx->openssl_ciphers = wpa_s->conf->openssl_ciphers;
+       ctx->eap_param_needed = eapol_test_eap_param_needed;
        ctx->cert_cb = eapol_test_cert_cb;
        ctx->cert_in_cb = 1;
        ctx->set_anon_id = eapol_test_set_anon_id;
@@ -501,6 +596,7 @@ static int test_eapol(struct eapol_test_data *e, struct wpa_supplicant *wpa_s,
        eapol_conf.required_keys = 0;
        eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
        eapol_conf.workaround = ssid->eap_workaround;
+       eapol_conf.external_sim = wpa_s->conf->external_sim;
        eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
        eapol_sm_register_scard_ctx(wpa_s->eapol, wpa_s->scard);
 
@@ -700,6 +796,8 @@ static void ieee802_1x_get_keys(struct eapol_test_data *e,
                                size_t shared_secret_len)
 {
        struct radius_ms_mppe_keys *keys;
+       u8 *buf;
+       size_t len;
 
        keys = radius_msg_get_ms_keys(msg, req, shared_secret,
                                      shared_secret_len);
@@ -738,6 +836,14 @@ static void ieee802_1x_get_keys(struct eapol_test_data *e,
                os_free(keys->recv);
                os_free(keys);
        }
+
+       if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_EAP_KEY_NAME, &buf, &len,
+                                   NULL) == 0) {
+               os_memcpy(e->authenticator_eap_key_name, buf, len);
+               e->authenticator_eap_key_name_len = len;
+       } else {
+               e->authenticator_eap_key_name_len = 0;
+       }
 }
 
 
@@ -832,7 +938,11 @@ static void wpa_init_conf(struct eapol_test_data *e,
                *pos++ = a[3];
        }
 #else /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
-       inet_aton(authsrv, &as->addr.u.v4);
+       if (inet_aton(authsrv, &as->addr.u.v4) < 0) {
+               wpa_printf(MSG_ERROR, "Invalid IP address '%s'",
+                          authsrv);
+               assert(0);
+       }
 #endif /* CONFIG_NATIVE_WINDOWS or CONFIG_ANSI_C_EXTRA */
        as->addr.af = AF_INET;
        as->port = port;
@@ -861,7 +971,7 @@ static void wpa_init_conf(struct eapol_test_data *e,
 }
 
 
-static int scard_test(void)
+static int scard_test(struct eapol_test_data *e)
 {
        struct scard_data *scard;
        size_t len;
@@ -892,10 +1002,10 @@ static int scard_test(void)
        unsigned char aka_ik[IK_LEN];
        unsigned char aka_ck[CK_LEN];
 
-       scard = scard_init(SCARD_TRY_BOTH, NULL);
+       scard = scard_init(e->pcsc_reader);
        if (scard == NULL)
                return -1;
-       if (scard_set_pin(scard, "1234")) {
+       if (scard_set_pin(scard, e->pcsc_pin)) {
                wpa_printf(MSG_WARNING, "PIN validation failed");
                scard_deinit(scard);
                return -1;
@@ -970,7 +1080,7 @@ failed:
 }
 
 
-static int scard_get_triplets(int argc, char *argv[])
+static int scard_get_triplets(struct eapol_test_data *e, int argc, char *argv[])
 {
        struct scard_data *scard;
        size_t len;
@@ -992,7 +1102,7 @@ static int scard_get_triplets(int argc, char *argv[])
                wpa_debug_level = 99;
        }
 
-       scard = scard_init(SCARD_GSM_SIM_ONLY, NULL);
+       scard = scard_init(e->pcsc_reader);
        if (scard == NULL) {
                printf("Failed to open smartcard connection\n");
                return -1;
@@ -1046,11 +1156,12 @@ static void eapol_test_terminate(int sig, void *signal_ctx)
 static void usage(void)
 {
        printf("usage:\n"
-              "eapol_test [-nWS] -c<conf> [-a<AS IP>] [-p<AS port>] "
+              "eapol_test [-enWS] -c<conf> [-a<AS IP>] [-p<AS port>] "
               "[-s<AS secret>]\\\n"
               "           [-r<count>] [-t<timeout>] [-C<Connect-Info>] \\\n"
               "           [-M<client MAC address>] [-o<server cert file] \\\n"
-              "           [-N<attr spec>] \\\n"
+              "           [-N<attr spec>] [-R<PC/SC reader>] "
+              "[-P<PC/SC PIN>] \\\n"
               "           [-A<client IP>]\n"
               "eapol_test scard\n"
               "eapol_test sim <PIN> <num triplets> [debug]\n"
@@ -1066,6 +1177,7 @@ static void usage(void)
               "  -A<client IP> = IP address of the client, default: select "
               "automatically\n"
               "  -r<count> = number of re-authentications\n"
+              "  -e = Request EAP-Key-Name\n"
               "  -W = wait for a control interface monitor before starting\n"
               "  -S = save configuration after authentication\n"
               "  -n = no MPPE keys expected\n"
@@ -1094,6 +1206,7 @@ static void usage(void)
 
 int main(int argc, char *argv[])
 {
+       struct wpa_global global;
        struct wpa_supplicant wpa_s;
        int c, ret = 1, wait_for_monitor = 0, save_config = 0;
        char *as_addr = "127.0.0.1";
@@ -1113,12 +1226,13 @@ int main(int argc, char *argv[])
        os_memset(&eapol_test, 0, sizeof(eapol_test));
        eapol_test.connect_info = "CONNECT 11Mbps 802.11b";
        os_memcpy(eapol_test.own_addr, "\x02\x00\x00\x00\x00\x01", ETH_ALEN);
+       eapol_test.pcsc_pin = "1234";
 
        wpa_debug_level = 0;
        wpa_debug_show_keys = 1;
 
        for (;;) {
-               c = getopt(argc, argv, "a:A:c:C:M:nN:o:p:r:s:St:W");
+               c = getopt(argc, argv, "a:A:c:C:eM:nN:o:p:P:r:R:s:St:W");
                if (c < 0)
                        break;
                switch (c) {
@@ -1134,6 +1248,9 @@ int main(int argc, char *argv[])
                case 'C':
                        eapol_test.connect_info = optarg;
                        break;
+               case 'e':
+                       eapol_test.req_eap_key_name = 1;
+                       break;
                case 'M':
                        if (hwaddr_aton(optarg, eapol_test.own_addr)) {
                                usage();
@@ -1156,9 +1273,15 @@ int main(int argc, char *argv[])
                case 'p':
                        as_port = atoi(optarg);
                        break;
+               case 'P':
+                       eapol_test.pcsc_pin = optarg;
+                       break;
                case 'r':
                        eapol_test.eapol_test_num_reauths = atoi(optarg);
                        break;
+               case 'R':
+                       eapol_test.pcsc_reader = optarg;
+                       break;
                case 's':
                        as_secret = optarg;
                        break;
@@ -1206,11 +1329,11 @@ int main(int argc, char *argv[])
        }
 
        if (argc > optind && os_strcmp(argv[optind], "scard") == 0) {
-               return scard_test();
+               return scard_test(&eapol_test);
        }
 
        if (argc > optind && os_strcmp(argv[optind], "sim") == 0) {
-               return scard_get_triplets(argc - optind - 1,
+               return scard_get_triplets(&eapol_test, argc - optind - 1,
                                          &argv[optind + 1]);
        }
 
@@ -1230,8 +1353,12 @@ int main(int argc, char *argv[])
                return -1;
        }
 
+       os_memset(&global, 0, sizeof(global));
        os_memset(&wpa_s, 0, sizeof(wpa_s));
+       wpa_s.global = &global;
        eapol_test.wpa_s = &wpa_s;
+       dl_list_init(&wpa_s.bss);
+       dl_list_init(&wpa_s.bss_id);
        wpa_s.conf = wpa_config_read(conf, NULL);
        if (wpa_s.conf == NULL) {
                printf("Failed to parse configuration file '%s'.\n", conf);
@@ -1242,6 +1369,11 @@ int main(int argc, char *argv[])
                return -1;
        }
 
+       if (eapol_test.pcsc_reader) {
+               os_free(wpa_s.conf->pcsc_reader);
+               wpa_s.conf->pcsc_reader = os_strdup(eapol_test.pcsc_reader);
+       }
+
        wpa_init_conf(&eapol_test, &wpa_s, as_addr, as_port, as_secret,
                      cli_addr);
        wpa_s.ctrl_iface = wpa_supplicant_ctrl_iface_init(&wpa_s);
old mode 100644 (file)
new mode 100755 (executable)
index 0f4d283..d275ca4
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - Driver event processing
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 #include "scan.h"
 #include "offchannel.h"
 #include "interworking.h"
+#include "mesh.h"
+#include "mesh_mpm.h"
+#include "wmm_ac.h"
 
 
 #ifndef CONFIG_NO_SCAN_PROCESSING
 static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
-                                             int new_scan);
+                                             int new_scan, int own_request);
 #endif /* CONFIG_NO_SCAN_PROCESSING */
 
 
 static int wpas_temp_disabled(struct wpa_supplicant *wpa_s,
                              struct wpa_ssid *ssid)
 {
-       struct os_time now;
+       struct os_reltime now;
 
        if (ssid == NULL || ssid->disabled_until.sec == 0)
                return 0;
 
-       os_get_time(&now);
+       os_get_reltime(&now);
        if (ssid->disabled_until.sec > now.sec)
                return ssid->disabled_until.sec - now.sec;
 
@@ -68,13 +71,46 @@ static int wpas_temp_disabled(struct wpa_supplicant *wpa_s,
 }
 
 
+static struct wpa_bss * wpa_supplicant_get_new_bss(
+       struct wpa_supplicant *wpa_s, const u8 *bssid)
+{
+       struct wpa_bss *bss = NULL;
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+       if (ssid->ssid_len > 0)
+               bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
+       if (!bss)
+               bss = wpa_bss_get_bssid(wpa_s, bssid);
+
+       return bss;
+}
+
+
+static void wpa_supplicant_update_current_bss(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_bss *bss = wpa_supplicant_get_new_bss(wpa_s, wpa_s->bssid);
+
+       if (!bss) {
+               wpa_supplicant_update_scan_results(wpa_s);
+
+               /* Get the BSS from the new scan results */
+               bss = wpa_supplicant_get_new_bss(wpa_s, wpa_s->bssid);
+       }
+
+       if (bss)
+               wpa_s->current_bss = bss;
+}
+
+
 static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
 {
        struct wpa_ssid *ssid, *old_ssid;
        int res;
 
-       if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid)
+       if (wpa_s->conf->ap_scan == 1 && wpa_s->current_ssid) {
+               wpa_supplicant_update_current_bss(wpa_s);
                return 0;
+       }
 
        wpa_dbg(wpa_s, MSG_DEBUG, "Select network based on association "
                "information");
@@ -119,6 +155,9 @@ static int wpa_supplicant_select_config(struct wpa_supplicant *wpa_s)
                eapol_sm_invalidate_cached_session(wpa_s->eapol);
        old_ssid = wpa_s->current_ssid;
        wpa_s->current_ssid = ssid;
+
+       wpa_supplicant_update_current_bss(wpa_s);
+
        wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
        wpa_supplicant_initiate_eapol(wpa_s);
        if (old_ssid != wpa_s->current_ssid)
@@ -136,6 +175,15 @@ void wpa_supplicant_stop_countermeasures(void *eloop_ctx, void *sock_ctx)
                wpa_s->countermeasures = 0;
                wpa_drv_set_countermeasures(wpa_s, 0);
                wpa_msg(wpa_s, MSG_INFO, "WPA: TKIP countermeasures stopped");
+
+               /*
+                * It is possible that the device is sched scanning, which means
+                * that a connection attempt will be done only when we receive
+                * scan results. However, in this case, it would be preferable
+                * to scan and connect immediately, so cancel the sched_scan and
+                * issue a regular scan flow.
+                */
+               wpa_supplicant_cancel_sched_scan(wpa_s);
                wpa_supplicant_req_scan(wpa_s, 0, 0);
        }
 }
@@ -163,20 +211,12 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
        bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
        os_memset(wpa_s->bssid, 0, ETH_ALEN);
        os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
-#ifdef CONFIG_SME
-       wpa_s->sme.prev_bssid_set = 0;
-#endif /* CONFIG_SME */
+       sme_clear_on_disassoc(wpa_s);
 #ifdef CONFIG_P2P
        os_memset(wpa_s->go_dev_addr, 0, ETH_ALEN);
 #endif /* CONFIG_P2P */
        wpa_s->current_bss = NULL;
        wpa_s->assoc_freq = 0;
-#ifdef CONFIG_IEEE80211R
-#ifdef CONFIG_SME
-       if (wpa_s->sme.ft_ies)
-               sme_update_ft_ies(wpa_s, NULL, NULL, 0);
-#endif /* CONFIG_SME */
-#endif /* CONFIG_IEEE80211R */
 
        if (bssid_changed)
                wpas_notify_bssid_changed(wpa_s);
@@ -187,7 +227,10 @@ void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s)
                eapol_sm_notify_eap_success(wpa_s->eapol, FALSE);
        wpa_s->ap_ies_from_associnfo = 0;
        wpa_s->current_ssid = NULL;
+       eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
        wpa_s->key_mgmt = 0;
+
+       wpas_rrm_reset(wpa_s);
 }
 
 
@@ -206,7 +249,7 @@ static void wpa_find_assoc_pmkid(struct wpa_supplicant *wpa_s)
                                                    ie.pmkid + i * PMKID_LEN,
                                                    NULL, NULL, 0);
                if (pmksa_set == 0) {
-                       eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1);
+                       eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
                        break;
                }
        }
@@ -272,12 +315,13 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
 {
 #ifdef IEEE8021X_EAPOL
 #ifdef PCSC_FUNCS
-       int aka = 0, sim = 0, type;
+       int aka = 0, sim = 0;
 
-       if (ssid->eap.pcsc == NULL || wpa_s->scard != NULL)
+       if ((ssid != NULL && ssid->eap.pcsc == NULL) ||
+           wpa_s->scard != NULL || wpa_s->conf->external_sim)
                return 0;
 
-       if (ssid->eap.eap_methods == NULL) {
+       if (ssid == NULL || ssid->eap.eap_methods == NULL) {
                sim = 1;
                aka = 1;
        } else {
@@ -311,14 +355,8 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
 
        wpa_dbg(wpa_s, MSG_DEBUG, "Selected network is configured to use SIM "
                "(sim=%d aka=%d) - initialize PCSC", sim, aka);
-       if (sim && aka)
-               type = SCARD_TRY_BOTH;
-       else if (aka)
-               type = SCARD_USIM_ONLY;
-       else
-               type = SCARD_GSM_SIM_ONLY;
 
-       wpa_s->scard = scard_init(type, NULL);
+       wpa_s->scard = scard_init(wpa_s->conf->pcsc_reader);
        if (wpa_s->scard == NULL) {
                wpa_msg(wpa_s, MSG_WARNING, "Failed to initialize SIM "
                        "(pcsc-lite)");
@@ -334,10 +372,24 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
 
 
 #ifndef CONFIG_NO_SCAN_PROCESSING
+
+static int has_wep_key(struct wpa_ssid *ssid)
+{
+       int i;
+
+       for (i = 0; i < NUM_WEP_KEYS; i++) {
+               if (ssid->wep_key_len[i])
+                       return 1;
+       }
+
+       return 0;
+}
+
+
 static int wpa_supplicant_match_privacy(struct wpa_bss *bss,
                                        struct wpa_ssid *ssid)
 {
-       int i, privacy = 0;
+       int privacy = 0;
 
        if (ssid->mixed_cell)
                return 1;
@@ -347,12 +399,9 @@ static int wpa_supplicant_match_privacy(struct wpa_bss *bss,
                return 1;
 #endif /* CONFIG_WPS */
 
-       for (i = 0; i < NUM_WEP_KEYS; i++) {
-               if (ssid->wep_key_len[i]) {
-                       privacy = 1;
-                       break;
-               }
-       }
+       if (has_wep_key(ssid))
+               privacy = 1;
+
 #ifdef IEEE8021X_EAPOL
        if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
            ssid->eapol_flags & (EAPOL_FLAG_REQUIRE_KEY_UNICAST |
@@ -363,6 +412,9 @@ static int wpa_supplicant_match_privacy(struct wpa_bss *bss,
        if (wpa_key_mgmt_wpa(ssid->key_mgmt))
                privacy = 1;
 
+       if (ssid->key_mgmt & WPA_KEY_MGMT_OSEN)
+               privacy = 1;
+
        if (bss->caps & IEEE80211_CAP_PRIVACY)
                return privacy;
        return !privacy;
@@ -433,8 +485,7 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
 
 #ifdef CONFIG_IEEE80211W
                if (!(ie.capabilities & WPA_CAPABILITY_MFPC) &&
-                   (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
-                    wpa_s->conf->pmf : ssid->ieee80211w) ==
+                   wpas_get_ssid_pmf(wpa_s, ssid) ==
                    MGMT_FRAME_PROTECTION_REQUIRED) {
                        wpa_dbg(wpa_s, MSG_DEBUG, "   skip RSN IE - no mgmt "
                                "frame protection");
@@ -504,6 +555,12 @@ static int wpa_supplicant_ssid_bss_match(struct wpa_supplicant *wpa_s,
                return 0;
        }
 
+       if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) &&
+           wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE)) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "   allow in OSEN");
+               return 1;
+       }
+
        if (!wpa_key_mgmt_wpa(ssid->key_mgmt)) {
                wpa_dbg(wpa_s, MSG_DEBUG, "   allow in non-WPA/WPA2");
                return 1;
@@ -530,24 +587,6 @@ static int freq_allowed(int *freqs, int freq)
 }
 
 
-static int ht_supported(const struct hostapd_hw_modes *mode)
-{
-       if (!(mode->flags & HOSTAPD_MODE_FLAG_HT_INFO_KNOWN)) {
-               /*
-                * The driver did not indicate whether it supports HT. Assume
-                * it does to avoid connection issues.
-                */
-               return 1;
-       }
-
-       /*
-        * IEEE Std 802.11n-2009 20.1.1:
-        * An HT non-AP STA shall support all EQM rates for one spatial stream.
-        */
-       return mode->mcs_set[0] == 0xff;
-}
-
-
 static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 {
        const struct hostapd_hw_modes *mode = NULL, *modes;
@@ -613,6 +652,18 @@ static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
                                continue;
                        }
 
+                       /* There's also a VHT selector for 802.11ac */
+                       if (flagged && ((rate_ie[j] & 0x7f) ==
+                                       BSS_MEMBERSHIP_SELECTOR_VHT_PHY)) {
+                               if (!vht_supported(mode)) {
+                                       wpa_dbg(wpa_s, MSG_DEBUG,
+                                               "   hardware does not support "
+                                               "VHT PHY");
+                                       return 0;
+                               }
+                               continue;
+                       }
+
                        if (!flagged)
                                continue;
 
@@ -627,9 +678,10 @@ static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
                                 * order to join a BSS all required rates
                                 * have to be supported by the hardware.
                                 */
-                               wpa_dbg(wpa_s, MSG_DEBUG, "   hardware does "
-                                       "not support required rate %d.%d Mbps",
-                                       r / 10, r % 10);
+                               wpa_dbg(wpa_s, MSG_DEBUG,
+                                       "   hardware does not support required rate %d.%d Mbps (freq=%d mode==%d num_rates=%d)",
+                                       r / 10, r % 10,
+                                       bss->freq, mode->mode, mode->num_rates);
                                return 0;
                        }
                }
@@ -639,12 +691,6 @@ static int rate_match(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 }
 
 
-static int bss_is_dmg(struct wpa_bss *bss)
-{
-       return bss->freq > 45000;
-}
-
-
 /*
  * Test whether BSS is in an ESS.
  * This is done differently in DMG (60 GHz) and non-DMG bands
@@ -661,15 +707,44 @@ static int bss_is_ess(struct wpa_bss *bss)
 }
 
 
+static int match_mac_mask(const u8 *addr_a, const u8 *addr_b, const u8 *mask)
+{
+       size_t i;
+
+       for (i = 0; i < ETH_ALEN; i++) {
+               if ((addr_a[i] & mask[i]) != (addr_b[i] & mask[i]))
+                       return 0;
+       }
+       return 1;
+}
+
+
+static int addr_in_list(const u8 *addr, const u8 *list, size_t num)
+{
+       size_t i;
+
+       for (i = 0; i < num; i++) {
+               const u8 *a = list + i * ETH_ALEN * 2;
+               const u8 *m = a + ETH_ALEN;
+
+               if (match_mac_mask(a, addr, m))
+                       return 1;
+       }
+       return 0;
+}
+
+
 static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
                                            int i, struct wpa_bss *bss,
-                                           struct wpa_ssid *group)
+                                           struct wpa_ssid *group,
+                                           int only_first_ssid)
 {
        u8 wpa_ie_len, rsn_ie_len;
        int wpa;
        struct wpa_blacklist *e;
        const u8 *ie;
        struct wpa_ssid *ssid;
+       int osen;
 
        ie = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
        wpa_ie_len = ie ? ie[1] : 0;
@@ -677,11 +752,18 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
        ie = wpa_bss_get_ie(bss, WLAN_EID_RSN);
        rsn_ie_len = ie ? ie[1] : 0;
 
+       ie = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
+       osen = ie != NULL;
+
        wpa_dbg(wpa_s, MSG_DEBUG, "%d: " MACSTR " ssid='%s' "
-               "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s",
+               "wpa_ie_len=%u rsn_ie_len=%u caps=0x%x level=%d%s%s%s",
                i, MAC2STR(bss->bssid), wpa_ssid_txt(bss->ssid, bss->ssid_len),
                wpa_ie_len, rsn_ie_len, bss->caps, bss->level,
-               wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "");
+               wpa_bss_get_vendor_ie(bss, WPS_IE_VENDOR_TYPE) ? " wps" : "",
+               (wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) ||
+                wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) ?
+               " p2p" : "",
+               osen ? " osen=1" : "");
 
        e = wpa_blacklist_get(wpa_s, bss->bssid);
        if (e) {
@@ -721,7 +803,7 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
 
        wpa = wpa_ie_len > 0 || rsn_ie_len > 0;
 
-       for (ssid = group; ssid; ssid = ssid->pnext) {
+       for (ssid = group; ssid; ssid = only_first_ssid ? NULL : ssid->pnext) {
                int check_ssid = wpa ? 1 : (ssid->ssid_len != 0);
                int res;
 
@@ -776,10 +858,28 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
                        continue;
                }
 
+               /* check blacklist */
+               if (ssid->num_bssid_blacklist &&
+                   addr_in_list(bss->bssid, ssid->bssid_blacklist,
+                                ssid->num_bssid_blacklist)) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "   skip - BSSID blacklisted");
+                       continue;
+               }
+
+               /* if there is a whitelist, only accept those APs */
+               if (ssid->num_bssid_whitelist &&
+                   !addr_in_list(bss->bssid, ssid->bssid_whitelist,
+                                 ssid->num_bssid_whitelist)) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "   skip - BSSID not in whitelist");
+                       continue;
+               }
+
                if (!wpa_supplicant_ssid_bss_match(wpa_s, ssid, bss))
                        continue;
 
-               if (!wpa &&
+               if (!osen && !wpa &&
                    !(ssid->key_mgmt & WPA_KEY_MGMT_NONE) &&
                    !(ssid->key_mgmt & WPA_KEY_MGMT_WPS) &&
                    !(ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) {
@@ -788,6 +888,18 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
                        continue;
                }
 
+               if (wpa && !wpa_key_mgmt_wpa(ssid->key_mgmt) &&
+                   has_wep_key(ssid)) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "   skip - ignore WPA/WPA2 AP for WEP network block");
+                       continue;
+               }
+
+               if ((ssid->key_mgmt & WPA_KEY_MGMT_OSEN) && !osen) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "   skip - non-OSEN network "
+                               "not allowed");
+                       continue;
+               }
+
                if (!wpa_supplicant_match_privacy(bss, ssid)) {
                        wpa_dbg(wpa_s, MSG_DEBUG, "   skip - privacy "
                                "mismatch");
@@ -812,6 +924,39 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
                }
 
 #ifdef CONFIG_P2P
+               if (ssid->p2p_group &&
+                   !wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
+                   !wpa_bss_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "   skip - no P2P IE seen");
+                       continue;
+               }
+
+               if (!is_zero_ether_addr(ssid->go_p2p_dev_addr)) {
+                       struct wpabuf *p2p_ie;
+                       u8 dev_addr[ETH_ALEN];
+
+                       ie = wpa_bss_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE);
+                       if (ie == NULL) {
+                               wpa_dbg(wpa_s, MSG_DEBUG, "   skip - no P2P element");
+                               continue;
+                       }
+                       p2p_ie = wpa_bss_get_vendor_ie_multi(
+                               bss, P2P_IE_VENDOR_TYPE);
+                       if (p2p_ie == NULL) {
+                               wpa_dbg(wpa_s, MSG_DEBUG, "   skip - could not fetch P2P element");
+                               continue;
+                       }
+
+                       if (p2p_parse_dev_addr_in_p2p_ie(p2p_ie, dev_addr) < 0
+                           || os_memcmp(dev_addr, ssid->go_p2p_dev_addr,
+                                        ETH_ALEN) != 0) {
+                               wpa_dbg(wpa_s, MSG_DEBUG, "   skip - no matching GO P2P Device Address in P2P element");
+                               wpabuf_free(p2p_ie);
+                               continue;
+                       }
+                       wpabuf_free(p2p_ie);
+               }
+
                /*
                 * TODO: skip the AP if its P2P IE has Group Formation
                 * bit set in the P2P Group Capability Bitmap and we
@@ -831,16 +976,22 @@ static struct wpa_ssid * wpa_scan_res_match(struct wpa_supplicant *wpa_s,
 static struct wpa_bss *
 wpa_supplicant_select_bss(struct wpa_supplicant *wpa_s,
                          struct wpa_ssid *group,
-                         struct wpa_ssid **selected_ssid)
+                         struct wpa_ssid **selected_ssid,
+                         int only_first_ssid)
 {
        unsigned int i;
 
-       wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d",
-               group->priority);
+       if (only_first_ssid)
+               wpa_dbg(wpa_s, MSG_DEBUG, "Try to find BSS matching pre-selected network id=%d",
+                       group->id);
+       else
+               wpa_dbg(wpa_s, MSG_DEBUG, "Selecting BSS from priority group %d",
+                       group->priority);
 
        for (i = 0; i < wpa_s->last_scan_res_used; i++) {
                struct wpa_bss *bss = wpa_s->last_scan_res[i];
-               *selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group);
+               *selected_ssid = wpa_scan_res_match(wpa_s, i, bss, group,
+                                                   only_first_ssid);
                if (!*selected_ssid)
                        continue;
                wpa_dbg(wpa_s, MSG_DEBUG, "   selected BSS " MACSTR
@@ -859,16 +1010,36 @@ struct wpa_bss * wpa_supplicant_pick_network(struct wpa_supplicant *wpa_s,
 {
        struct wpa_bss *selected = NULL;
        int prio;
+       struct wpa_ssid *next_ssid = NULL;
 
        if (wpa_s->last_scan_res == NULL ||
            wpa_s->last_scan_res_used == 0)
                return NULL; /* no scan results from last update */
 
+       if (wpa_s->next_ssid) {
+               struct wpa_ssid *ssid;
+
+               /* check that next_ssid is still valid */
+               for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+                       if (ssid == wpa_s->next_ssid)
+                               break;
+               }
+               next_ssid = ssid;
+               wpa_s->next_ssid = NULL;
+       }
+
        while (selected == NULL) {
                for (prio = 0; prio < wpa_s->conf->num_prio; prio++) {
+                       if (next_ssid && next_ssid->priority ==
+                           wpa_s->conf->pssid[prio]->priority) {
+                               selected = wpa_supplicant_select_bss(
+                                       wpa_s, next_ssid, selected_ssid, 1);
+                               if (selected)
+                                       break;
+                       }
                        selected = wpa_supplicant_select_bss(
                                wpa_s, wpa_s->conf->pssid[prio],
-                               selected_ssid);
+                               selected_ssid, 0);
                        if (selected)
                                break;
                }
@@ -899,9 +1070,6 @@ static void wpa_supplicant_req_new_scan(struct wpa_supplicant *wpa_s,
                wpa_dbg(wpa_s, MSG_DEBUG, "Short-circuit new scan request "
                        "since there are no enabled networks");
                wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
-#ifdef CONFIG_P2P
-               wpa_s->sta_scan_pending = 0;
-#endif /* CONFIG_P2P */
                return;
        }
 
@@ -918,8 +1086,12 @@ int wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
                wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_OVERLAP
                        "PBC session overlap");
 #ifdef CONFIG_P2P
-               if (wpas_p2p_notif_pbc_overlap(wpa_s) == 1)
+               if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT ||
+                   wpa_s->p2p_in_provisioning) {
+                       eloop_register_timeout(0, 0, wpas_p2p_pbc_overlap_cb,
+                                              wpa_s, NULL);
                        return -1;
+               }
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_WPS
@@ -979,7 +1151,8 @@ wpa_supplicant_pick_new_network(struct wpa_supplicant *wpa_s)
                        if (wpas_network_disabled(wpa_s, ssid))
                                continue;
                        if (ssid->mode == IEEE80211_MODE_IBSS ||
-                           ssid->mode == IEEE80211_MODE_AP)
+                           ssid->mode == IEEE80211_MODE_AP ||
+                           ssid->mode == IEEE80211_MODE_MESH)
                                return ssid;
                }
        }
@@ -1051,10 +1224,14 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
 
 #ifndef CONFIG_NO_ROAMING
        wpa_dbg(wpa_s, MSG_DEBUG, "Considering within-ESS reassociation");
-       wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR " level=%d",
-               MAC2STR(current_bss->bssid), current_bss->level);
-       wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS: " MACSTR " level=%d",
-               MAC2STR(selected->bssid), selected->level);
+       wpa_dbg(wpa_s, MSG_DEBUG, "Current BSS: " MACSTR
+               " level=%d snr=%d est_throughput=%u",
+               MAC2STR(current_bss->bssid), current_bss->level,
+               current_bss->snr, current_bss->est_throughput);
+       wpa_dbg(wpa_s, MSG_DEBUG, "Selected BSS: " MACSTR
+               " level=%d snr=%d est_throughput=%u",
+               MAC2STR(selected->bssid), selected->level,
+               selected->snr, selected->est_throughput);
 
        if (wpa_s->current_ssid->bssid_set &&
            os_memcmp(selected->bssid, wpa_s->current_ssid->bssid, ETH_ALEN) ==
@@ -1064,6 +1241,12 @@ static int wpa_supplicant_need_to_roam(struct wpa_supplicant *wpa_s,
                return 1;
        }
 
+       if (selected->est_throughput > current_bss->est_throughput + 5000) {
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "Allow reassociation - selected BSS has better estimated throughput");
+               return 1;
+       }
+
        if (current_bss->level < 0 && current_bss->level > selected->level) {
                wpa_dbg(wpa_s, MSG_DEBUG, "Skip roam - Current BSS has better "
                        "signal level");
@@ -1102,7 +1285,8 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
                                              union wpa_event_data *data,
                                              int own_request)
 {
-       struct wpa_scan_results *scan_res;
+       struct wpa_scan_results *scan_res = NULL;
+       int ret = 0;
        int ap = 0;
 #ifndef CONFIG_NO_RANDOM_POOL
        size_t i, num;
@@ -1115,23 +1299,6 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
 
        wpa_supplicant_notify_scanning(wpa_s, 0);
 
-#ifdef CONFIG_P2P
-       if (own_request && wpa_s->global->p2p_cb_on_scan_complete &&
-           !wpa_s->global->p2p_disabled &&
-           wpa_s->global->p2p != NULL && !wpa_s->sta_scan_pending &&
-           !wpa_s->scan_res_handler) {
-               wpa_s->global->p2p_cb_on_scan_complete = 0;
-               if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) {
-                       wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation "
-                               "stopped scan processing");
-                       wpa_s->sta_scan_pending = 1;
-                       wpa_supplicant_req_scan(wpa_s, 5, 0);
-                       return -1;
-               }
-       }
-       wpa_s->sta_scan_pending = 0;
-#endif /* CONFIG_P2P */
-
        scan_res = wpa_supplicant_get_scan_results(wpa_s,
                                                   data ? &data->scan_info :
                                                   NULL, 1);
@@ -1144,7 +1311,8 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
                wpa_dbg(wpa_s, MSG_DEBUG, "Failed to get scan results - try "
                        "scanning again");
                wpa_supplicant_req_new_scan(wpa_s, 1, 0);
-               return -1;
+               ret = -1;
+               goto scan_work_done;
        }
 
 #ifndef CONFIG_NO_RANDOM_POOL
@@ -1163,16 +1331,16 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
        }
 #endif /* CONFIG_NO_RANDOM_POOL */
 
-       if (own_request && wpa_s->scan_res_handler) {
+       if (own_request && wpa_s->scan_res_handler &&
+           (wpa_s->own_scan_running || !wpa_s->radio->external_scan_running)) {
                void (*scan_res_handler)(struct wpa_supplicant *wpa_s,
                                         struct wpa_scan_results *scan_res);
 
                scan_res_handler = wpa_s->scan_res_handler;
                wpa_s->scan_res_handler = NULL;
                scan_res_handler(wpa_s, scan_res);
-
-               wpa_scan_results_free(scan_res);
-               return -2;
+               ret = -2;
+               goto scan_work_done;
        }
 
        if (ap) {
@@ -1181,57 +1349,82 @@ static int _wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
                if (wpa_s->ap_iface->scan_cb)
                        wpa_s->ap_iface->scan_cb(wpa_s->ap_iface);
 #endif /* CONFIG_AP */
-               wpa_scan_results_free(scan_res);
-               return 0;
+               goto scan_work_done;
        }
 
-       wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available");
-       wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
+       wpa_dbg(wpa_s, MSG_DEBUG, "New scan results available (own=%u ext=%u)",
+               wpa_s->own_scan_running, wpa_s->radio->external_scan_running);
+       if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+           wpa_s->manual_scan_use_id && wpa_s->own_scan_running) {
+               wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u",
+                            wpa_s->manual_scan_id);
+               wpa_s->manual_scan_use_id = 0;
+       } else {
+               wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
+       }
        wpas_notify_scan_results(wpa_s);
 
        wpas_notify_scan_done(wpa_s, 1);
 
-       if (sme_proc_obss_scan(wpa_s) > 0) {
+       if (!wpa_s->own_scan_running && wpa_s->radio->external_scan_running) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Do not use results from externally requested scan operation for network selection");
                wpa_scan_results_free(scan_res);
                return 0;
        }
 
-       if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s))) {
-               wpa_scan_results_free(scan_res);
-               return 0;
-       }
+       if (wnm_scan_process(wpa_s, 1) > 0)
+               goto scan_work_done;
 
-       if (autoscan_notify_scan(wpa_s, scan_res)) {
-               wpa_scan_results_free(scan_res);
-               return 0;
-       }
+       if (sme_proc_obss_scan(wpa_s) > 0)
+               goto scan_work_done;
+
+       if ((wpa_s->conf->ap_scan == 2 && !wpas_wps_searching(wpa_s)))
+               goto scan_work_done;
+
+       if (autoscan_notify_scan(wpa_s, scan_res))
+               goto scan_work_done;
 
        if (wpa_s->disconnected) {
                wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
-               wpa_scan_results_free(scan_res);
-               return 0;
+               goto scan_work_done;
        }
 
        if (!wpas_driver_bss_selection(wpa_s) &&
-           bgscan_notify_scan(wpa_s, scan_res) == 1) {
-               wpa_scan_results_free(scan_res);
-               return 0;
-       }
+           bgscan_notify_scan(wpa_s, scan_res) == 1)
+               goto scan_work_done;
 
        wpas_wps_update_ap_info(wpa_s, scan_res);
 
        wpa_scan_results_free(scan_res);
 
-       return wpas_select_network_from_last_scan(wpa_s, 1);
+       if (wpa_s->scan_work) {
+               struct wpa_radio_work *work = wpa_s->scan_work;
+               wpa_s->scan_work = NULL;
+               radio_work_done(work);
+       }
+
+       return wpas_select_network_from_last_scan(wpa_s, 1, own_request);
+
+scan_work_done:
+       wpa_scan_results_free(scan_res);
+       if (wpa_s->scan_work) {
+               struct wpa_radio_work *work = wpa_s->scan_work;
+               wpa_s->scan_work = NULL;
+               radio_work_done(work);
+       }
+       return ret;
 }
 
 
 static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
-                                             int new_scan)
+                                             int new_scan, int own_request)
 {
        struct wpa_bss *selected;
        struct wpa_ssid *ssid = NULL;
 
+       if (wpa_s->p2p_mgmt)
+               return 0; /* no normal connection on p2p_mgmt interface */
+
        selected = wpa_supplicant_pick_network(wpa_s, &ssid);
 
        if (selected) {
@@ -1255,6 +1448,13 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
                 */
                return 1;
        } else {
+#ifdef CONFIG_MESH
+               if (wpa_s->ifmsh) {
+                       wpa_msg(wpa_s, MSG_INFO,
+                               "Avoiding join because we already joined a mesh group");
+                       return 0;
+               }
+#endif /* CONFIG_MESH */
                wpa_dbg(wpa_s, MSG_DEBUG, "No suitable network found");
                ssid = wpa_supplicant_pick_new_network(wpa_s);
                if (ssid) {
@@ -1262,17 +1462,30 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
                        wpa_supplicant_associate(wpa_s, NULL, ssid);
                        if (new_scan)
                                wpa_supplicant_rsn_preauth_scan_results(wpa_s);
-               } else {
+               } else if (own_request) {
+                       /*
+                        * No SSID found. If SCAN results are as a result of
+                        * own scan request and not due to a scan request on
+                        * another shared interface, try another scan.
+                        */
                        int timeout_sec = wpa_s->scan_interval;
                        int timeout_usec = 0;
 #ifdef CONFIG_P2P
-                       if (wpas_p2p_scan_no_go_seen(wpa_s) == 1)
+                       int res;
+
+                       res = wpas_p2p_scan_no_go_seen(wpa_s);
+                       if (res == 2)
+                               return 2;
+                       if (res == 1)
                                return 0;
 
-                       if (wpa_s->p2p_in_provisioning) {
+                       if (wpa_s->p2p_in_provisioning ||
+                           wpa_s->show_group_started ||
+                           wpa_s->p2p_in_invitation) {
                                /*
                                 * Use shorter wait during P2P Provisioning
-                                * state to speed up group formation.
+                                * state and during P2P join-a-group operation
+                                * to speed up group formation.
                                 */
                                timeout_sec = 0;
                                timeout_usec = 250000;
@@ -1294,6 +1507,16 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
                                return 1;
                        }
 #endif /* CONFIG_INTERWORKING */
+#ifdef CONFIG_WPS
+                       if (wpa_s->after_wps > 0 || wpas_wps_searching(wpa_s)) {
+                               wpa_dbg(wpa_s, MSG_DEBUG, "Use shorter wait during WPS processing");
+                               timeout_sec = 0;
+                               timeout_usec = 500000;
+                               wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
+                                                           timeout_usec);
+                               return 0;
+                       }
+#endif /* CONFIG_WPS */
                        if (wpa_supplicant_req_sched_scan(wpa_s))
                                wpa_supplicant_req_new_scan(wpa_s, timeout_sec,
                                                            timeout_usec);
@@ -1303,13 +1526,21 @@ static int wpas_select_network_from_last_scan(struct wpa_supplicant *wpa_s,
 }
 
 
-static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
-                                             union wpa_event_data *data)
+static int wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
+                                            union wpa_event_data *data)
 {
-       const char *rn, *rn2;
        struct wpa_supplicant *ifs;
+       int res;
 
-       if (_wpa_supplicant_event_scan_results(wpa_s, data, 1) != 0) {
+       res = _wpa_supplicant_event_scan_results(wpa_s, data, 1);
+       if (res == 2) {
+               /*
+                * Interface may have been removed, so must not dereference
+                * wpa_s after this.
+                */
+               return 1;
+       }
+       if (res != 0) {
                /*
                 * If no scan results could be fetched, then no need to
                 * notify those interfaces that did not actually request
@@ -1317,34 +1548,23 @@ static void wpa_supplicant_event_scan_results(struct wpa_supplicant *wpa_s,
                 * interface, do not notify other interfaces to avoid concurrent
                 * operations during a connection attempt.
                 */
-               return;
+               return 0;
        }
 
        /*
-        * Check other interfaces to see if they have the same radio-name. If
+        * Check other interfaces to see if they share the same radio. If
         * so, they get updated with this same scan info.
         */
-       if (!wpa_s->driver->get_radio_name)
-               return;
-
-       rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv);
-       if (rn == NULL || rn[0] == '\0')
-               return;
-
-       wpa_dbg(wpa_s, MSG_DEBUG, "Checking for other virtual interfaces "
-               "sharing same radio (%s) in event_scan_results", rn);
-
-       for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
-               if (ifs == wpa_s || !ifs->driver->get_radio_name)
-                       continue;
-
-               rn2 = ifs->driver->get_radio_name(ifs->drv_priv);
-               if (rn2 && os_strcmp(rn, rn2) == 0) {
+       dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+                        radio_list) {
+               if (ifs != wpa_s) {
                        wpa_printf(MSG_DEBUG, "%s: Updating scan results from "
                                   "sibling", ifs->ifname);
                        _wpa_supplicant_event_scan_results(ifs, data, 0);
                }
        }
+
+       return 0;
 }
 
 #endif /* CONFIG_NO_SCAN_PROCESSING */
@@ -1355,18 +1575,18 @@ int wpa_supplicant_fast_associate(struct wpa_supplicant *wpa_s)
 #ifdef CONFIG_NO_SCAN_PROCESSING
        return -1;
 #else /* CONFIG_NO_SCAN_PROCESSING */
-       struct os_time now;
+       struct os_reltime now;
 
        if (wpa_s->last_scan_res_used <= 0)
                return -1;
 
-       os_get_time(&now);
-       if (now.sec - wpa_s->last_scan.sec > 5) {
+       os_get_reltime(&now);
+       if (os_reltime_expired(&now, &wpa_s->last_scan, 5)) {
                wpa_printf(MSG_DEBUG, "Fast associate: Old scan results");
                return -1;
        }
 
-       return wpas_select_network_from_last_scan(wpa_s, 0);
+       return wpas_select_network_from_last_scan(wpa_s, 0, 1);
 #endif /* CONFIG_NO_SCAN_PROCESSING */
 }
 
@@ -1450,6 +1670,43 @@ void wnm_bss_keep_alive_deinit(struct wpa_supplicant *wpa_s)
 }
 
 
+#ifdef CONFIG_INTERWORKING
+
+static int wpas_qos_map_set(struct wpa_supplicant *wpa_s, const u8 *qos_map,
+                           size_t len)
+{
+       int res;
+
+       wpa_hexdump(MSG_DEBUG, "Interworking: QoS Map Set", qos_map, len);
+       res = wpa_drv_set_qos_map(wpa_s, qos_map, len);
+       if (res) {
+               wpa_printf(MSG_DEBUG, "Interworking: Failed to configure QoS Map Set to the driver");
+       }
+
+       return res;
+}
+
+
+static void interworking_process_assoc_resp(struct wpa_supplicant *wpa_s,
+                                           const u8 *ies, size_t ies_len)
+{
+       struct ieee802_11_elems elems;
+
+       if (ies == NULL)
+               return;
+
+       if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed)
+               return;
+
+       if (elems.qos_map_set) {
+               wpas_qos_map_set(wpa_s, elems.qos_map_set,
+                                elems.qos_map_set_len);
+       }
+}
+
+#endif /* CONFIG_INTERWORKING */
+
+
 static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
                                          union wpa_event_data *data)
 {
@@ -1474,6 +1731,10 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
                wnm_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
                                       data->assoc_info.resp_ies_len);
 #endif /* CONFIG_WNM */
+#ifdef CONFIG_INTERWORKING
+               interworking_process_assoc_resp(wpa_s, data->assoc_info.resp_ies,
+                                               data->assoc_info.resp_ies_len);
+#endif /* CONFIG_INTERWORKING */
        }
        if (data->assoc_info.beacon_ies)
                wpa_hexdump(MSG_DEBUG, "beacon_ies",
@@ -1642,21 +1903,6 @@ static int wpa_supplicant_event_associnfo(struct wpa_supplicant *wpa_s,
 }
 
 
-static struct wpa_bss * wpa_supplicant_get_new_bss(
-       struct wpa_supplicant *wpa_s, const u8 *bssid)
-{
-       struct wpa_bss *bss = NULL;
-       struct wpa_ssid *ssid = wpa_s->current_ssid;
-
-       if (ssid->ssid_len > 0)
-               bss = wpa_bss_get(wpa_s, bssid, ssid->ssid, ssid->ssid_len);
-       if (!bss)
-               bss = wpa_bss_get_bssid(wpa_s, bssid);
-
-       return bss;
-}
-
-
 static int wpa_supplicant_assoc_update_ie(struct wpa_supplicant *wpa_s)
 {
        const u8 *bss_wpa = NULL, *bss_rsn = NULL;
@@ -1689,6 +1935,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
 
 #ifdef CONFIG_AP
        if (wpa_s->ap_iface) {
+               if (!data)
+                       return;
                hostapd_notif_assoc(wpa_s->ap_iface->bss[0],
                                    data->assoc_info.addr,
                                    data->assoc_info.req_ies,
@@ -1726,20 +1974,6 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
                                wpa_s, WLAN_REASON_DEAUTH_LEAVING);
                        return;
                }
-               if (wpa_s->current_ssid) {
-                       struct wpa_bss *bss = NULL;
-
-                       bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
-                       if (!bss) {
-                               wpa_supplicant_update_scan_results(wpa_s);
-
-                               /* Get the BSS from the new scan results */
-                               bss = wpa_supplicant_get_new_bss(wpa_s, bssid);
-                       }
-
-                       if (bss)
-                               wpa_s->current_bss = bss;
-               }
 
                if (wpa_s->conf->ap_scan == 1 &&
                    wpa_s->drv_flags & WPA_DRIVER_FLAGS_BSS_SELECTION) {
@@ -1752,6 +1986,7 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
 #ifdef CONFIG_SME
        os_memcpy(wpa_s->sme.prev_bssid, bssid, ETH_ALEN);
        wpa_s->sme.prev_bssid_set = 1;
+       wpa_s->sme.last_unprot_disconnect.sec = 0;
 #endif /* CONFIG_SME */
 
        wpa_msg(wpa_s, MSG_INFO, "Associated with " MACSTR, MAC2STR(bssid));
@@ -1787,7 +2022,8 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
            wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE ||
            (wpa_s->current_ssid &&
             wpa_s->current_ssid->mode == IEEE80211_MODE_IBSS)) {
-               if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE &&
+               if (wpa_s->current_ssid &&
+                   wpa_s->key_mgmt == WPA_KEY_MGMT_WPA_NONE &&
                    (wpa_s->drv_flags &
                     WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE)) {
                        /*
@@ -1837,9 +2073,9 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
        wpa_s->last_eapol_matches_bssid = 0;
 
        if (wpa_s->pending_eapol_rx) {
-               struct os_time now, age;
-               os_get_time(&now);
-               os_time_sub(&now, &wpa_s->pending_eapol_rx_time, &age);
+               struct os_reltime now, age;
+               os_get_reltime(&now);
+               os_reltime_sub(&now, &wpa_s->pending_eapol_rx_time, &age);
                if (age.sec == 0 && age.usec < 100000 &&
                    os_memcmp(wpa_s->pending_eapol_rx_src, bssid, ETH_ALEN) ==
                    0) {
@@ -1882,6 +2118,15 @@ static void wpa_supplicant_event_assoc(struct wpa_supplicant *wpa_s,
 #endif /* CONFIG_IBSS_RSN */
 
        wpas_wps_notify_assoc(wpa_s, bssid);
+
+       if (data) {
+               wmm_ac_notify_assoc(wpa_s, data->assoc_info.resp_ies,
+                                   data->assoc_info.resp_ies_len,
+                                   &data->assoc_info.wmm_params);
+
+               if (wpa_s->reassoc_same_bss)
+                       wmm_ac_restore_tspecs(wpa_s);
+       }
 }
 
 
@@ -1943,6 +2188,7 @@ static int could_be_psk_mismatch(struct wpa_supplicant *wpa_s, u16 reason_code,
        return 1;
 }
 
+
 static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
                                                 u16 reason_code,
                                                 int locally_generated)
@@ -1951,9 +2197,7 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
        int authenticating;
        u8 prev_pending_bssid[ETH_ALEN];
        struct wpa_bss *fast_reconnect = NULL;
-#ifndef CONFIG_NO_SCAN_PROCESSING
        struct wpa_ssid *fast_reconnect_ssid = NULL;
-#endif /* CONFIG_NO_SCAN_PROCESSING */
        struct wpa_ssid *last_ssid;
 
        authenticating = wpa_s->wpa_state == WPA_AUTHENTICATING;
@@ -1973,14 +2217,18 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
        if (could_be_psk_mismatch(wpa_s, reason_code, locally_generated)) {
                wpa_msg(wpa_s, MSG_INFO, "WPA: 4-Way Handshake failed - "
                        "pre-shared key may be incorrect");
-               wpas_auth_failed(wpa_s);
+               if (wpas_p2p_4way_hs_failed(wpa_s) > 0)
+                       return; /* P2P group removed */
+               wpas_auth_failed(wpa_s, "WRONG_KEY");
        }
        if (!wpa_s->disconnected &&
            (!wpa_s->auto_reconnect_disabled ||
-            wpa_s->key_mgmt == WPA_KEY_MGMT_WPS)) {
+            wpa_s->key_mgmt == WPA_KEY_MGMT_WPS ||
+            wpas_wps_searching(wpa_s))) {
                wpa_dbg(wpa_s, MSG_DEBUG, "Auto connect enabled: try to "
-                       "reconnect (wps=%d wpa_state=%d)",
+                       "reconnect (wps=%d/%d wpa_state=%d)",
                        wpa_s->key_mgmt == WPA_KEY_MGMT_WPS,
+                       wpas_wps_searching(wpa_s),
                        wpa_s->wpa_state);
                if (wpa_s->wpa_state == WPA_COMPLETED &&
                    wpa_s->current_ssid &&
@@ -1994,9 +2242,7 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
                         * time for some common cases.
                         */
                        fast_reconnect = wpa_s->current_bss;
-#ifndef CONFIG_NO_SCAN_PROCESSING
                        fast_reconnect_ssid = wpa_s->current_ssid;
-#endif /* CONFIG_NO_SCAN_PROCESSING */
                } else if (wpa_s->wpa_state >= WPA_ASSOCIATING)
                        wpa_supplicant_req_scan(wpa_s, 0, 100000);
                else
@@ -2022,7 +2268,6 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
        wpas_notify_disconnect_reason(wpa_s);
        if (wpa_supplicant_dynamic_keys(wpa_s)) {
                wpa_dbg(wpa_s, MSG_DEBUG, "Disconnect event - remove keys");
-               wpa_s->keys_cleared = 0;
                wpa_clear_keys(wpa_s, wpa_s->bssid);
        }
        last_ssid = wpa_s->current_ssid;
@@ -2033,7 +2278,12 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
                wpa_s->current_ssid = last_ssid;
        }
 
-       if (fast_reconnect) {
+       if (fast_reconnect &&
+           !wpas_network_disabled(wpa_s, fast_reconnect_ssid) &&
+           !disallowed_bssid(wpa_s, fast_reconnect->bssid) &&
+           !disallowed_ssid(wpa_s, fast_reconnect->ssid,
+                            fast_reconnect->ssid_len) &&
+           !wpas_temp_disabled(wpa_s, fast_reconnect_ssid)) {
 #ifndef CONFIG_NO_SCAN_PROCESSING
                wpa_dbg(wpa_s, MSG_DEBUG, "Try to reconnect to the same BSS");
                if (wpa_supplicant_connect(wpa_s, fast_reconnect,
@@ -2042,6 +2292,14 @@ static void wpa_supplicant_event_disassoc_finish(struct wpa_supplicant *wpa_s,
                        wpa_supplicant_req_scan(wpa_s, 0, 100000);
                }
 #endif /* CONFIG_NO_SCAN_PROCESSING */
+       } else if (fast_reconnect) {
+               /*
+                * Could not reconnect to the same BSS due to network being
+                * disabled. Use a new scan to match the alternative behavior
+                * above, i.e., to continue automatic reconnection attempt in a
+                * way that enforces disabled network rules.
+                */
+               wpa_supplicant_req_scan(wpa_s, 0, 100000);
        }
 }
 
@@ -2066,13 +2324,13 @@ wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s,
                                         union wpa_event_data *data)
 {
        int pairwise;
-       struct os_time t;
+       struct os_reltime t;
 
        wpa_msg(wpa_s, MSG_WARNING, "Michael MIC failure detected");
        pairwise = (data && data->michael_mic_failure.unicast);
-       os_get_time(&t);
-       if ((wpa_s->last_michael_mic_error &&
-            t.sec - wpa_s->last_michael_mic_error <= 60) ||
+       os_get_reltime(&t);
+       if ((wpa_s->last_michael_mic_error.sec &&
+            !os_reltime_expired(&t, &wpa_s->last_michael_mic_error, 60)) ||
            wpa_s->pending_mic_error_report) {
                if (wpa_s->pending_mic_error_report) {
                        /*
@@ -2150,7 +2408,7 @@ wpa_supplicant_event_michael_mic_failure(struct wpa_supplicant *wpa_s,
                wpa_sm_key_request(wpa_s->wpa, 1, pairwise);
 #endif /* CONFIG_DELAYED_MIC_ERROR_REPORT */
        }
-       wpa_s->last_michael_mic_error = t.sec;
+       wpa_s->last_michael_mic_error = t;
        wpa_s->mic_errors_seen++;
 }
 
@@ -2185,7 +2443,6 @@ wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s,
                        wpa_msg(wpa_s, MSG_INFO, "Failed to initialize the "
                                "driver after interface was added");
                }
-               wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
                break;
        case EVENT_INTERFACE_REMOVED:
                wpa_dbg(wpa_s, MSG_DEBUG, "Configured interface was removed");
@@ -2194,10 +2451,6 @@ wpa_supplicant_event_interface_status(struct wpa_supplicant *wpa_s,
                wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
                l2_packet_deinit(wpa_s->l2);
                wpa_s->l2 = NULL;
-#ifdef CONFIG_IBSS_RSN
-               ibss_rsn_deinit(wpa_s->ibss_rsn);
-               wpa_s->ibss_rsn = NULL;
-#endif /* CONFIG_IBSS_RSN */
 #ifdef CONFIG_TERMINATE_ONLASTIF
                /* check if last interface */
                if (!any_interfaces(wpa_s->global->ifaces))
@@ -2235,8 +2488,16 @@ static void wpa_supplicant_event_tdls(struct wpa_supplicant *wpa_s,
                        wpa_drv_tdls_oper(wpa_s, TDLS_SETUP, data->tdls.peer);
                break;
        case TDLS_REQUEST_TEARDOWN:
-               wpa_tdls_teardown_link(wpa_s->wpa, data->tdls.peer,
-                                      data->tdls.reason_code);
+               if (wpa_tdls_is_external_setup(wpa_s->wpa))
+                       wpa_tdls_teardown_link(wpa_s->wpa, data->tdls.peer,
+                                              data->tdls.reason_code);
+               else
+                       wpa_drv_tdls_oper(wpa_s, TDLS_TEARDOWN,
+                                         data->tdls.peer);
+               break;
+       case TDLS_REQUEST_DISCOVER:
+                       wpa_tdls_send_discovery_request(wpa_s->wpa,
+                                                       data->tdls.peer);
                break;
        }
 }
@@ -2299,6 +2560,23 @@ static void wpa_supplicant_event_ibss_rsn_start(struct wpa_supplicant *wpa_s,
 
        ibss_rsn_start(wpa_s->ibss_rsn, data->ibss_rsn_start.peer);
 }
+
+
+static void wpa_supplicant_event_ibss_auth(struct wpa_supplicant *wpa_s,
+                                          union wpa_event_data *data)
+{
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+       if (ssid == NULL)
+               return;
+
+       /* check if the ssid is correctly configured as IBSS/RSN */
+       if (ssid->mode != WPAS_MODE_IBSS || !wpa_key_mgmt_wpa(ssid->key_mgmt))
+               return;
+
+       ibss_rsn_handle_auth(wpa_s->ibss_rsn, data->rx_mgmt.frame,
+                            data->rx_mgmt.frame_len);
+}
 #endif /* CONFIG_IBSS_RSN */
 
 
@@ -2382,12 +2660,393 @@ static void wpa_supplicant_event_unprot_disassoc(struct wpa_supplicant *wpa_s,
 }
 
 
+static void wpas_event_disconnect(struct wpa_supplicant *wpa_s, const u8 *addr,
+                                 u16 reason_code, int locally_generated,
+                                 const u8 *ie, size_t ie_len, int deauth)
+{
+#ifdef CONFIG_AP
+       if (wpa_s->ap_iface && addr) {
+               hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], addr);
+               return;
+       }
+
+       if (wpa_s->ap_iface) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Ignore deauth event in AP mode");
+               return;
+       }
+#endif /* CONFIG_AP */
+
+       if (!locally_generated)
+               wpa_s->own_disconnect_req = 0;
+
+       wpa_supplicant_event_disassoc(wpa_s, reason_code, locally_generated);
+
+       if (((reason_code == WLAN_REASON_IEEE_802_1X_AUTH_FAILED ||
+             ((wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
+               (wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) &&
+              eapol_sm_failed(wpa_s->eapol))) &&
+            !wpa_s->eap_expected_failure))
+               wpas_auth_failed(wpa_s, "AUTH_FAILED");
+
+#ifdef CONFIG_P2P
+       if (deauth && reason_code > 0) {
+               if (wpas_p2p_deauth_notif(wpa_s, addr, reason_code, ie, ie_len,
+                                         locally_generated) > 0) {
+                       /*
+                        * The interface was removed, so cannot continue
+                        * processing any additional operations after this.
+                        */
+                       return;
+               }
+       }
+#endif /* CONFIG_P2P */
+
+       wpa_supplicant_event_disassoc_finish(wpa_s, reason_code,
+                                            locally_generated);
+}
+
+
+static void wpas_event_disassoc(struct wpa_supplicant *wpa_s,
+                               struct disassoc_info *info)
+{
+       u16 reason_code = 0;
+       int locally_generated = 0;
+       const u8 *addr = NULL;
+       const u8 *ie = NULL;
+       size_t ie_len = 0;
+
+       wpa_dbg(wpa_s, MSG_DEBUG, "Disassociation notification");
+
+       if (info) {
+               addr = info->addr;
+               ie = info->ie;
+               ie_len = info->ie_len;
+               reason_code = info->reason_code;
+               locally_generated = info->locally_generated;
+               wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s", reason_code,
+                       locally_generated ? " (locally generated)" : "");
+               if (addr)
+                       wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR,
+                               MAC2STR(addr));
+               wpa_hexdump(MSG_DEBUG, "Disassociation frame IE(s)",
+                           ie, ie_len);
+       }
+
+#ifdef CONFIG_AP
+       if (wpa_s->ap_iface && info && info->addr) {
+               hostapd_notif_disassoc(wpa_s->ap_iface->bss[0], info->addr);
+               return;
+       }
+
+       if (wpa_s->ap_iface) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Ignore disassoc event in AP mode");
+               return;
+       }
+#endif /* CONFIG_AP */
+
+#ifdef CONFIG_P2P
+       if (info) {
+               wpas_p2p_disassoc_notif(
+                       wpa_s, info->addr, reason_code, info->ie, info->ie_len,
+                       locally_generated);
+       }
+#endif /* CONFIG_P2P */
+
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
+               sme_event_disassoc(wpa_s, info);
+
+       wpas_event_disconnect(wpa_s, addr, reason_code, locally_generated,
+                             ie, ie_len, 0);
+}
+
+
+static void wpas_event_deauth(struct wpa_supplicant *wpa_s,
+                             struct deauth_info *info)
+{
+       u16 reason_code = 0;
+       int locally_generated = 0;
+       const u8 *addr = NULL;
+       const u8 *ie = NULL;
+       size_t ie_len = 0;
+
+       wpa_dbg(wpa_s, MSG_DEBUG, "Deauthentication notification");
+
+       if (info) {
+               addr = info->addr;
+               ie = info->ie;
+               ie_len = info->ie_len;
+               reason_code = info->reason_code;
+               locally_generated = info->locally_generated;
+               wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s",
+                       reason_code,
+                       locally_generated ? " (locally generated)" : "");
+               if (addr) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR,
+                               MAC2STR(addr));
+               }
+               wpa_hexdump(MSG_DEBUG, "Deauthentication frame IE(s)",
+                           ie, ie_len);
+       }
+
+       wpa_reset_ft_completed(wpa_s->wpa);
+
+       wpas_event_disconnect(wpa_s, addr, reason_code,
+                             locally_generated, ie, ie_len, 1);
+}
+
+
+static const char * reg_init_str(enum reg_change_initiator init)
+{
+       switch (init) {
+       case REGDOM_SET_BY_CORE:
+               return "CORE";
+       case REGDOM_SET_BY_USER:
+               return "USER";
+       case REGDOM_SET_BY_DRIVER:
+               return "DRIVER";
+       case REGDOM_SET_BY_COUNTRY_IE:
+               return "COUNTRY_IE";
+       case REGDOM_BEACON_HINT:
+               return "BEACON_HINT";
+       }
+       return "?";
+}
+
+
+static const char * reg_type_str(enum reg_type type)
+{
+       switch (type) {
+       case REGDOM_TYPE_UNKNOWN:
+               return "UNKNOWN";
+       case REGDOM_TYPE_COUNTRY:
+               return "COUNTRY";
+       case REGDOM_TYPE_WORLD:
+               return "WORLD";
+       case REGDOM_TYPE_CUSTOM_WORLD:
+               return "CUSTOM_WORLD";
+       case REGDOM_TYPE_INTERSECTION:
+               return "INTERSECTION";
+       }
+       return "?";
+}
+
+
+static void wpa_supplicant_update_channel_list(
+       struct wpa_supplicant *wpa_s, struct channel_list_changed *info)
+{
+       struct wpa_supplicant *ifs;
+
+       wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_REGDOM_CHANGE "init=%s type=%s%s%s",
+               reg_init_str(info->initiator), reg_type_str(info->type),
+               info->alpha2[0] ? " alpha2=" : "",
+               info->alpha2[0] ? info->alpha2 : "");
+
+       if (wpa_s->drv_priv == NULL)
+               return; /* Ignore event during drv initialization */
+
+       free_hw_features(wpa_s);
+       wpa_s->hw.modes = wpa_drv_get_hw_feature_data(
+               wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags);
+
+       wpas_p2p_update_channel_list(wpa_s);
+
+       /*
+        * Check other interfaces to see if they share the same radio. If
+        * so, they get updated with this same hw mode info.
+        */
+       dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+                        radio_list) {
+               if (ifs != wpa_s) {
+                       wpa_printf(MSG_DEBUG, "%s: Updating hw mode",
+                                  ifs->ifname);
+                       free_hw_features(ifs);
+                       ifs->hw.modes = wpa_drv_get_hw_feature_data(
+                               ifs, &ifs->hw.num_modes, &ifs->hw.flags);
+               }
+       }
+}
+
+
+static void wpas_event_rx_mgmt_action(struct wpa_supplicant *wpa_s,
+                                     const u8 *frame, size_t len, int freq,
+                                     int rssi)
+{
+       const struct ieee80211_mgmt *mgmt;
+       const u8 *payload;
+       size_t plen;
+       u8 category;
+
+       if (len < IEEE80211_HDRLEN + 2)
+               return;
+
+       mgmt = (const struct ieee80211_mgmt *) frame;
+       payload = frame + IEEE80211_HDRLEN;
+       category = *payload++;
+       plen = len - IEEE80211_HDRLEN - 1;
+
+       wpa_dbg(wpa_s, MSG_DEBUG, "Received Action frame: SA=" MACSTR
+               " Category=%u DataLen=%d freq=%d MHz",
+               MAC2STR(mgmt->sa), category, (int) plen, freq);
+
+       if (category == WLAN_ACTION_WMM) {
+               wmm_ac_rx_action(wpa_s, mgmt->da, mgmt->sa, payload, plen);
+               return;
+       }
+
+#ifdef CONFIG_IEEE80211R
+       if (category == WLAN_ACTION_FT) {
+               ft_rx_action(wpa_s, payload, plen);
+               return;
+       }
+#endif /* CONFIG_IEEE80211R */
+
+#ifdef CONFIG_IEEE80211W
+#ifdef CONFIG_SME
+       if (category == WLAN_ACTION_SA_QUERY) {
+               sme_sa_query_rx(wpa_s, mgmt->sa, payload, plen);
+               return;
+       }
+#endif /* CONFIG_SME */
+#endif /* CONFIG_IEEE80211W */
+
+#ifdef CONFIG_WNM
+       if (mgmt->u.action.category == WLAN_ACTION_WNM) {
+               ieee802_11_rx_wnm_action(wpa_s, mgmt, len);
+               return;
+       }
+#endif /* CONFIG_WNM */
+
+#ifdef CONFIG_GAS
+       if ((mgmt->u.action.category == WLAN_ACTION_PUBLIC ||
+            mgmt->u.action.category == WLAN_ACTION_PROTECTED_DUAL) &&
+           gas_query_rx(wpa_s->gas, mgmt->da, mgmt->sa, mgmt->bssid,
+                        mgmt->u.action.category,
+                        payload, plen, freq) == 0)
+               return;
+#endif /* CONFIG_GAS */
+
+#ifdef CONFIG_TDLS
+       if (category == WLAN_ACTION_PUBLIC && plen >= 4 &&
+           payload[0] == WLAN_TDLS_DISCOVERY_RESPONSE) {
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "TDLS: Received Discovery Response from " MACSTR,
+                       MAC2STR(mgmt->sa));
+               return;
+       }
+#endif /* CONFIG_TDLS */
+
+#ifdef CONFIG_INTERWORKING
+       if (category == WLAN_ACTION_QOS && plen >= 1 &&
+           payload[0] == QOS_QOS_MAP_CONFIG) {
+               const u8 *pos = payload + 1;
+               size_t qlen = plen - 1;
+               wpa_dbg(wpa_s, MSG_DEBUG, "Interworking: Received QoS Map Configure frame from "
+                       MACSTR, MAC2STR(mgmt->sa));
+               if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) == 0 &&
+                   qlen > 2 && pos[0] == WLAN_EID_QOS_MAP_SET &&
+                   pos[1] <= qlen - 2 && pos[1] >= 16)
+                       wpas_qos_map_set(wpa_s, pos + 2, pos[1]);
+               return;
+       }
+#endif /* CONFIG_INTERWORKING */
+
+       if (category == WLAN_ACTION_RADIO_MEASUREMENT &&
+           payload[0] == WLAN_RRM_NEIGHBOR_REPORT_RESPONSE) {
+               wpas_rrm_process_neighbor_rep(wpa_s, payload + 1, plen - 1);
+               return;
+       }
+
+       if (category == WLAN_ACTION_RADIO_MEASUREMENT &&
+           payload[0] == WLAN_RRM_LINK_MEASUREMENT_REQUEST) {
+               wpas_rrm_handle_link_measurement_request(wpa_s, mgmt->sa,
+                                                        payload + 1, plen - 1,
+                                                        rssi);
+               return;
+       }
+
+       wpas_p2p_rx_action(wpa_s, mgmt->da, mgmt->sa, mgmt->bssid,
+                          category, payload, plen, freq);
+       if (wpa_s->ifmsh)
+               mesh_mpm_action_rx(wpa_s, mgmt, len);
+}
+
+
+static void wpa_supplicant_notify_avoid_freq(struct wpa_supplicant *wpa_s,
+                                            union wpa_event_data *event)
+{
+#ifdef CONFIG_P2P
+       struct wpa_supplicant *ifs;
+#endif /* CONFIG_P2P */
+       struct wpa_freq_range_list *list;
+       char *str = NULL;
+
+       list = &event->freq_range;
+
+       if (list->num)
+               str = freq_range_list_str(list);
+       wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_AVOID_FREQ "ranges=%s",
+               str ? str : "");
+
+#ifdef CONFIG_P2P
+       if (freq_range_list_parse(&wpa_s->global->p2p_go_avoid_freq, str)) {
+               wpa_dbg(wpa_s, MSG_ERROR, "%s: Failed to parse freq range",
+                       __func__);
+       } else {
+               wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Update channel list based on frequency avoid event");
+               wpas_p2p_update_channel_list(wpa_s);
+       }
+
+       for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
+               int freq;
+               if (!ifs->current_ssid ||
+                   !ifs->current_ssid->p2p_group ||
+                   (ifs->current_ssid->mode != WPAS_MODE_P2P_GO &&
+                    ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION))
+                       continue;
+
+               freq = ifs->current_ssid->frequency;
+               if (!freq_range_list_includes(list, freq)) {
+                       wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating frequency %d MHz in safe range",
+                               freq);
+                       continue;
+               }
+
+               wpa_dbg(ifs, MSG_DEBUG, "P2P GO operating in unsafe frequency %d MHz",
+                       freq);
+               /* TODO: Consider using CSA or removing the group within
+                * wpa_supplicant */
+               wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
+       }
+#endif /* CONFIG_P2P */
+
+       os_free(str);
+}
+
+
+static void wpa_supplicant_event_assoc_auth(struct wpa_supplicant *wpa_s,
+                                           union wpa_event_data *data)
+{
+       wpa_dbg(wpa_s, MSG_DEBUG,
+               "Connection authorized by device, previous state %d",
+               wpa_s->wpa_state);
+       if (wpa_s->wpa_state == WPA_ASSOCIATED) {
+               wpa_supplicant_cancel_auth_timeout(wpa_s);
+               wpa_supplicant_set_state(wpa_s, WPA_COMPLETED);
+               eapol_sm_notify_portValid(wpa_s->eapol, TRUE);
+               eapol_sm_notify_eap_success(wpa_s->eapol, TRUE);
+       }
+       wpa_sm_set_rx_replay_ctr(wpa_s->wpa, data->assoc_info.key_replay_ctr);
+       wpa_sm_set_ptk_kck_kek(wpa_s->wpa, data->assoc_info.ptk_kck,
+                              data->assoc_info.ptk_kck_len,
+                              data->assoc_info.ptk_kek,
+                              data->assoc_info.ptk_kek_len);
+}
+
+
 void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                          union wpa_event_data *data)
 {
        struct wpa_supplicant *wpa_s = ctx;
-       u16 reason_code = 0;
-       int locally_generated = 0;
 
        if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED &&
            event != EVENT_INTERFACE_ENABLED &&
@@ -2424,121 +3083,62 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                break;
        case EVENT_ASSOC:
                wpa_supplicant_event_assoc(wpa_s, data);
+               if (data && data->assoc_info.authorized)
+                       wpa_supplicant_event_assoc_auth(wpa_s, data);
                break;
        case EVENT_DISASSOC:
-               wpa_dbg(wpa_s, MSG_DEBUG, "Disassociation notification");
-               if (data) {
-                       wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s",
-                               data->disassoc_info.reason_code,
-                               data->disassoc_info.locally_generated ?
-                               " (locally generated)" : "");
-                       if (data->disassoc_info.addr)
-                               wpa_dbg(wpa_s, MSG_DEBUG, " * address " MACSTR,
-                                       MAC2STR(data->disassoc_info.addr));
-               }
-#ifdef CONFIG_AP
-               if (wpa_s->ap_iface && data && data->disassoc_info.addr) {
-                       hostapd_notif_disassoc(wpa_s->ap_iface->bss[0],
-                                              data->disassoc_info.addr);
-                       break;
-               }
-               if (wpa_s->ap_iface) {
-                       wpa_dbg(wpa_s, MSG_DEBUG, "Ignore disassoc event in "
-                               "AP mode");
-                       break;
-               }
-#endif /* CONFIG_AP */
-               if (data) {
-                       reason_code = data->disassoc_info.reason_code;
-                       locally_generated =
-                               data->disassoc_info.locally_generated;
-                       wpa_hexdump(MSG_DEBUG, "Disassociation frame IE(s)",
-                                   data->disassoc_info.ie,
-                                   data->disassoc_info.ie_len);
-#ifdef CONFIG_P2P
-                       wpas_p2p_disassoc_notif(
-                               wpa_s, data->disassoc_info.addr, reason_code,
-                               data->disassoc_info.ie,
-                               data->disassoc_info.ie_len,
-                               locally_generated);
-#endif /* CONFIG_P2P */
-               }
-               if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
-                       sme_event_disassoc(wpa_s, data);
-               /* fall through */
+               wpas_event_disassoc(wpa_s,
+                                   data ? &data->disassoc_info : NULL);
+               break;
        case EVENT_DEAUTH:
-               if (event == EVENT_DEAUTH) {
-                       wpa_dbg(wpa_s, MSG_DEBUG,
-                               "Deauthentication notification");
-                       if (data) {
-                               reason_code = data->deauth_info.reason_code;
-                               locally_generated =
-                                       data->deauth_info.locally_generated;
-                               wpa_dbg(wpa_s, MSG_DEBUG, " * reason %u%s",
-                                       data->deauth_info.reason_code,
-                                       data->deauth_info.locally_generated ?
-                                       " (locally generated)" : "");
-                               if (data->deauth_info.addr) {
-                                       wpa_dbg(wpa_s, MSG_DEBUG, " * address "
-                                               MACSTR,
-                                               MAC2STR(data->deauth_info.
-                                                       addr));
-                               }
-                               wpa_hexdump(MSG_DEBUG,
-                                           "Deauthentication frame IE(s)",
-                                           data->deauth_info.ie,
-                                           data->deauth_info.ie_len);
-                       }
-                       wpa_reset_ft_completed(wpa_s->wpa);
-               }
-#ifdef CONFIG_AP
-               if (wpa_s->ap_iface && data && data->deauth_info.addr) {
-                       hostapd_notif_disassoc(wpa_s->ap_iface->bss[0],
-                                              data->deauth_info.addr);
-                       break;
-               }
-               if (wpa_s->ap_iface) {
-                       wpa_dbg(wpa_s, MSG_DEBUG, "Ignore deauth event in "
-                               "AP mode");
-                       break;
-               }
-#endif /* CONFIG_AP */
-               wpa_supplicant_event_disassoc(wpa_s, reason_code,
-                                             locally_generated);
-               if (reason_code == WLAN_REASON_IEEE_802_1X_AUTH_FAILED ||
-                   ((wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) ||
-                     (wpa_s->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA)) &&
-                    eapol_sm_failed(wpa_s->eapol)))
-                       wpas_auth_failed(wpa_s);
-#ifdef CONFIG_P2P
-               if (event == EVENT_DEAUTH && data) {
-                       if (wpas_p2p_deauth_notif(wpa_s,
-                                                 data->deauth_info.addr,
-                                                 reason_code,
-                                                 data->deauth_info.ie,
-                                                 data->deauth_info.ie_len,
-                                                 locally_generated) > 0) {
-                               /*
-                                * The interface was removed, so cannot
-                                * continue processing any additional
-                                * operations after this.
-                                */
-                               break;
-                       }
-               }
-#endif /* CONFIG_P2P */
-               wpa_supplicant_event_disassoc_finish(wpa_s, reason_code,
-                                                    locally_generated);
+               wpas_event_deauth(wpa_s,
+                                 data ? &data->deauth_info : NULL);
                break;
        case EVENT_MICHAEL_MIC_FAILURE:
                wpa_supplicant_event_michael_mic_failure(wpa_s, data);
                break;
 #ifndef CONFIG_NO_SCAN_PROCESSING
+       case EVENT_SCAN_STARTED:
+               os_get_reltime(&wpa_s->scan_start_time);
+               if (wpa_s->own_scan_requested) {
+                       struct os_reltime diff;
+
+                       os_reltime_sub(&wpa_s->scan_start_time,
+                                      &wpa_s->scan_trigger_time, &diff);
+                       wpa_dbg(wpa_s, MSG_DEBUG, "Own scan request started a scan in %ld.%06ld seconds",
+                               diff.sec, diff.usec);
+                       wpa_s->own_scan_requested = 0;
+                       wpa_s->own_scan_running = 1;
+                       if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+                           wpa_s->manual_scan_use_id) {
+                               wpa_msg_ctrl(wpa_s, MSG_INFO,
+                                            WPA_EVENT_SCAN_STARTED "id=%u",
+                                            wpa_s->manual_scan_id);
+                       } else {
+                               wpa_msg_ctrl(wpa_s, MSG_INFO,
+                                            WPA_EVENT_SCAN_STARTED);
+                       }
+               } else {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "External program started a scan");
+                       wpa_s->radio->external_scan_running = 1;
+                       wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_STARTED);
+               }
+               break;
        case EVENT_SCAN_RESULTS:
-               wpa_supplicant_event_scan_results(wpa_s, data);
-               if (wpa_s->wpa_state != WPA_AUTHENTICATING &&
-                   wpa_s->wpa_state != WPA_ASSOCIATING)
-                       wpas_p2p_continue_after_scan(wpa_s);
+               if (os_reltime_initialized(&wpa_s->scan_start_time)) {
+                       struct os_reltime now, diff;
+                       os_get_reltime(&now);
+                       os_reltime_sub(&now, &wpa_s->scan_start_time, &diff);
+                       wpa_s->scan_start_time.sec = 0;
+                       wpa_s->scan_start_time.usec = 0;
+                       wpa_dbg(wpa_s, MSG_DEBUG, "Scan completed in %ld.%06ld seconds",
+                               diff.sec, diff.usec);
+               }
+               if (wpa_supplicant_event_scan_results(wpa_s, data))
+                       break; /* interface may have been removed */
+               wpa_s->own_scan_running = 0;
+               wpa_s->radio->external_scan_running = 0;
+               radio_work_check_next(wpa_s);
                break;
 #endif /* CONFIG_NO_SCAN_PROCESSING */
        case EVENT_ASSOCINFO:
@@ -2594,20 +3194,26 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                        wpas_connection_failed(wpa_s, bssid);
                        wpa_supplicant_mark_disassoc(wpa_s);
                }
-#if defined TIZEN_EXT
-               wpa_supplicant_cancel_auth_timeout(wpa_s);
-               /* Clear the states */
-               wpa_sm_notify_disassoc(wpa_s->wpa);
-               wpa_supplicant_disassociate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
-               wpa_s->reassociate = 1;
-               wpa_supplicant_req_scan(wpa_s, 1, 0);
-#endif
                break;
        case EVENT_AUTH_TIMED_OUT:
+               /* It is possible to get this event from earlier connection */
+               if (wpa_s->current_ssid &&
+                   wpa_s->current_ssid->mode == WPAS_MODE_MESH) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "Ignore AUTH_TIMED_OUT in mesh configuration");
+                       break;
+               }
                if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
                        sme_event_auth_timed_out(wpa_s, data);
                break;
        case EVENT_ASSOC_TIMED_OUT:
+               /* It is possible to get this event from earlier connection */
+               if (wpa_s->current_ssid &&
+                   wpa_s->current_ssid->mode == WPAS_MODE_MESH) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "Ignore ASSOC_TIMED_OUT in mesh configuration");
+                       break;
+               }
                if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_SME)
                        sme_event_assoc_timed_out(wpa_s, data);
                break;
@@ -2695,22 +3301,66 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                        break;
                }
 
-#ifdef CONFIG_AP
                wpas_ap_ch_switch(wpa_s, data->ch_switch.freq,
                                  data->ch_switch.ht_enabled,
-                                 data->ch_switch.ch_offset);
-#endif /* CONFIG_AP */
+                                 data->ch_switch.ch_offset,
+                                 data->ch_switch.ch_width,
+                                 data->ch_switch.cf1,
+                                 data->ch_switch.cf2);
+               break;
+#ifdef NEED_AP_MLME
+       case EVENT_DFS_RADAR_DETECTED:
+               if (data)
+                       wpas_event_dfs_radar_detected(wpa_s, &data->dfs_event);
                break;
+       case EVENT_DFS_CAC_STARTED:
+               if (data)
+                       wpas_event_dfs_cac_started(wpa_s, &data->dfs_event);
+               break;
+       case EVENT_DFS_CAC_FINISHED:
+               if (data)
+                       wpas_event_dfs_cac_finished(wpa_s, &data->dfs_event);
+               break;
+       case EVENT_DFS_CAC_ABORTED:
+               if (data)
+                       wpas_event_dfs_cac_aborted(wpa_s, &data->dfs_event);
+               break;
+       case EVENT_DFS_NOP_FINISHED:
+               if (data)
+                       wpas_event_dfs_cac_nop_finished(wpa_s,
+                                                       &data->dfs_event);
+               break;
+#endif /* NEED_AP_MLME */
+#endif /* CONFIG_AP */
        case EVENT_RX_MGMT: {
                u16 fc, stype;
                const struct ieee80211_mgmt *mgmt;
 
+#ifdef CONFIG_TESTING_OPTIONS
+               if (wpa_s->ext_mgmt_frame_handling) {
+                       struct rx_mgmt *rx = &data->rx_mgmt;
+                       size_t hex_len = 2 * rx->frame_len + 1;
+                       char *hex = os_malloc(hex_len);
+                       if (hex) {
+                               wpa_snprintf_hex(hex, hex_len,
+                                                rx->frame, rx->frame_len);
+                               wpa_msg(wpa_s, MSG_INFO, "MGMT-RX freq=%d datarate=%u ssi_signal=%d %s",
+                                       rx->freq, rx->datarate, rx->ssi_signal,
+                                       hex);
+                               os_free(hex);
+                       }
+                       break;
+               }
+#endif /* CONFIG_TESTING_OPTIONS */
+
                mgmt = (const struct ieee80211_mgmt *)
                        data->rx_mgmt.frame;
                fc = le_to_host16(mgmt->frame_control);
                stype = WLAN_FC_GET_STYPE(fc);
 
+#ifdef CONFIG_AP
                if (wpa_s->ap_iface == NULL) {
+#endif /* CONFIG_AP */
 #ifdef CONFIG_P2P
                        if (stype == WLAN_FC_STYPE_PROBE_REQ &&
                            data->rx_mgmt.frame_len > 24) {
@@ -2719,25 +3369,6 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                                size_t ie_len = data->rx_mgmt.frame_len -
                                        (mgmt->u.probe_req.variable -
                                         data->rx_mgmt.frame);
-#ifdef TIZEN_EXT_P2P
-                               wpa_printf(MSG_DEBUG, "Non-AP: Probe request frame ");
-                       {
-                               /* If we are Go or client, we need not reply the probe reqest on eth0 interface */
-                               struct wpa_supplicant* ifs;
-                               int ignore = 0;
-                               for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
-                                       if ( (ifs->p2p_group_interface == P2P_GROUP_INTERFACE_GO ) ||(ifs->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT )) {
-                                               wpa_printf(MSG_DEBUG, "Non-AP: NEERAJKG Ignoring Probe request");
-                                               ignore = 1;
-                                               break;
-                                       }
-                               }
-                               if(ignore)
-                                       break;
-                               else
-                                       wpa_printf(MSG_DEBUG, "Non-AP: Couln't Ignore Probe request %d", wpa_s->p2p_group_interface);
-                       }
-#endif
                                wpas_p2p_probe_req_rx(
                                        wpa_s, src, mgmt->da,
                                        mgmt->bssid, ie, ie_len,
@@ -2745,9 +3376,34 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                                break;
                        }
 #endif /* CONFIG_P2P */
+#ifdef CONFIG_IBSS_RSN
+                       if (wpa_s->current_ssid &&
+                           wpa_s->current_ssid->mode == WPAS_MODE_IBSS &&
+                           stype == WLAN_FC_STYPE_AUTH &&
+                           data->rx_mgmt.frame_len >= 30) {
+                               wpa_supplicant_event_ibss_auth(wpa_s, data);
+                               break;
+                       }
+#endif /* CONFIG_IBSS_RSN */
+
+                       if (stype == WLAN_FC_STYPE_ACTION) {
+                               wpas_event_rx_mgmt_action(
+                                       wpa_s, data->rx_mgmt.frame,
+                                       data->rx_mgmt.frame_len,
+                                       data->rx_mgmt.freq,
+                                       data->rx_mgmt.ssi_signal);
+                               break;
+                       }
+
+                       if (wpa_s->ifmsh) {
+                               mesh_mpm_mgmt_rx(wpa_s, &data->rx_mgmt);
+                               break;
+                       }
+
                        wpa_dbg(wpa_s, MSG_DEBUG, "AP: ignore received "
                                "management frame in non-AP mode");
                        break;
+#ifdef CONFIG_AP
                }
 
                if (stype == WLAN_FC_STYPE_PROBE_REQ &&
@@ -2763,65 +3419,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                }
 
                ap_mgmt_rx(wpa_s, &data->rx_mgmt);
-               break;
-               }
 #endif /* CONFIG_AP */
-       case EVENT_RX_ACTION:
-               wpa_dbg(wpa_s, MSG_DEBUG, "Received Action frame: SA=" MACSTR
-                       " Category=%u DataLen=%d freq=%d MHz",
-                       MAC2STR(data->rx_action.sa),
-                       data->rx_action.category, (int) data->rx_action.len,
-                       data->rx_action.freq);
-#ifdef CONFIG_IEEE80211R
-               if (data->rx_action.category == WLAN_ACTION_FT) {
-                       ft_rx_action(wpa_s, data->rx_action.data,
-                                    data->rx_action.len);
-                       break;
-               }
-#endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_IEEE80211W
-#ifdef CONFIG_SME
-               if (data->rx_action.category == WLAN_ACTION_SA_QUERY) {
-                       sme_sa_query_rx(wpa_s, data->rx_action.sa,
-                                       data->rx_action.data,
-                                       data->rx_action.len);
-                       break;
-               }
-#endif /* CONFIG_SME */
-#endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_WNM
-               if (data->rx_action.category == WLAN_ACTION_WNM) {
-                       ieee802_11_rx_wnm_action(wpa_s, &data->rx_action);
-                       break;
-               }
-#endif /* CONFIG_WNM */
-#ifdef CONFIG_GAS
-               if (data->rx_action.category == WLAN_ACTION_PUBLIC &&
-                   gas_query_rx(wpa_s->gas, data->rx_action.da,
-                                data->rx_action.sa, data->rx_action.bssid,
-                                data->rx_action.data, data->rx_action.len,
-                                data->rx_action.freq) == 0)
-                       break;
-#endif /* CONFIG_GAS */
-#ifdef CONFIG_TDLS
-               if (data->rx_action.category == WLAN_ACTION_PUBLIC &&
-                   data->rx_action.len >= 4 &&
-                   data->rx_action.data[0] == WLAN_TDLS_DISCOVERY_RESPONSE) {
-                       wpa_dbg(wpa_s, MSG_DEBUG, "TDLS: Received Discovery "
-                               "Response from " MACSTR,
-                               MAC2STR(data->rx_action.sa));
-                       break;
-               }
-#endif /* CONFIG_TDLS */
-#ifdef CONFIG_P2P
-               wpas_p2p_rx_action(wpa_s, data->rx_action.da,
-                                  data->rx_action.sa,
-                                  data->rx_action.bssid,
-                                  data->rx_action.category,
-                                  data->rx_action.data,
-                                  data->rx_action.len, data->rx_action.freq);
-#endif /* CONFIG_P2P */
                break;
+               }
        case EVENT_RX_PROBE_REQ:
                if (data->rx_probe_req.sa == NULL ||
                    data->rx_probe_req.ie == NULL)
@@ -2838,14 +3438,12 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                        break;
                }
 #endif /* CONFIG_AP */
-#ifdef CONFIG_P2P
                wpas_p2p_probe_req_rx(wpa_s, data->rx_probe_req.sa,
                                      data->rx_probe_req.da,
                                      data->rx_probe_req.bssid,
                                      data->rx_probe_req.ie,
                                      data->rx_probe_req.ie_len,
                                      data->rx_probe_req.ssi_signal);
-#endif /* CONFIG_P2P */
                break;
        case EVENT_REMAIN_ON_CHANNEL:
 #ifdef CONFIG_OFFCHANNEL
@@ -2853,93 +3451,32 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                        wpa_s, data->remain_on_channel.freq,
                        data->remain_on_channel.duration);
 #endif /* CONFIG_OFFCHANNEL */
-#ifdef CONFIG_P2P
                wpas_p2p_remain_on_channel_cb(
                        wpa_s, data->remain_on_channel.freq,
                        data->remain_on_channel.duration);
-#endif /* CONFIG_P2P */
                break;
        case EVENT_CANCEL_REMAIN_ON_CHANNEL:
 #ifdef CONFIG_OFFCHANNEL
                offchannel_cancel_remain_on_channel_cb(
                        wpa_s, data->remain_on_channel.freq);
 #endif /* CONFIG_OFFCHANNEL */
-#ifdef CONFIG_P2P
                wpas_p2p_cancel_remain_on_channel_cb(
                        wpa_s, data->remain_on_channel.freq);
-#endif /* CONFIG_P2P */
-               break;
-#ifdef CONFIG_P2P
-       case EVENT_P2P_DEV_FOUND: {
-               struct p2p_peer_info peer_info;
-
-               os_memset(&peer_info, 0, sizeof(peer_info));
-               if (data->p2p_dev_found.dev_addr)
-                       os_memcpy(peer_info.p2p_device_addr,
-                                 data->p2p_dev_found.dev_addr, ETH_ALEN);
-               if (data->p2p_dev_found.pri_dev_type)
-                       os_memcpy(peer_info.pri_dev_type,
-                                 data->p2p_dev_found.pri_dev_type,
-                                 sizeof(peer_info.pri_dev_type));
-               if (data->p2p_dev_found.dev_name)
-                       os_strlcpy(peer_info.device_name,
-                                  data->p2p_dev_found.dev_name,
-                                  sizeof(peer_info.device_name));
-               peer_info.config_methods = data->p2p_dev_found.config_methods;
-               peer_info.dev_capab = data->p2p_dev_found.dev_capab;
-               peer_info.group_capab = data->p2p_dev_found.group_capab;
-
-               /*
-                * FIX: new_device=1 is not necessarily correct. We should
-                * maintain a P2P peer database in wpa_supplicant and update
-                * this information based on whether the peer is truly new.
-                */
-               wpas_dev_found(wpa_s, data->p2p_dev_found.addr, &peer_info, 1);
-               break;
-               }
-       case EVENT_P2P_GO_NEG_REQ_RX:
-               wpas_go_neg_req_rx(wpa_s, data->p2p_go_neg_req_rx.src,
-                                  data->p2p_go_neg_req_rx.dev_passwd_id);
-               break;
-       case EVENT_P2P_GO_NEG_COMPLETED:
-               wpas_go_neg_completed(wpa_s, data->p2p_go_neg_completed.res);
-               break;
-       case EVENT_P2P_PROV_DISC_REQUEST:
-               wpas_prov_disc_req(wpa_s, data->p2p_prov_disc_req.peer,
-                                  data->p2p_prov_disc_req.config_methods,
-                                  data->p2p_prov_disc_req.dev_addr,
-                                  data->p2p_prov_disc_req.pri_dev_type,
-                                  data->p2p_prov_disc_req.dev_name,
-                                  data->p2p_prov_disc_req.supp_config_methods,
-                                  data->p2p_prov_disc_req.dev_capab,
-                                  data->p2p_prov_disc_req.group_capab,
-                                  NULL, 0);
                break;
-       case EVENT_P2P_PROV_DISC_RESPONSE:
-               wpas_prov_disc_resp(wpa_s, data->p2p_prov_disc_resp.peer,
-                                   data->p2p_prov_disc_resp.config_methods);
-               break;
-       case EVENT_P2P_SD_REQUEST:
-               wpas_sd_request(wpa_s, data->p2p_sd_req.freq,
-                               data->p2p_sd_req.sa,
-                               data->p2p_sd_req.dialog_token,
-                               data->p2p_sd_req.update_indic,
-                               data->p2p_sd_req.tlvs,
-                               data->p2p_sd_req.tlvs_len);
-               break;
-       case EVENT_P2P_SD_RESPONSE:
-               wpas_sd_response(wpa_s, data->p2p_sd_resp.sa,
-                                data->p2p_sd_resp.update_indic,
-                                data->p2p_sd_resp.tlvs,
-                                data->p2p_sd_resp.tlvs_len);
-               break;
-#endif /* CONFIG_P2P */
        case EVENT_EAPOL_RX:
                wpa_supplicant_rx_eapol(wpa_s, data->eapol_rx.src,
                                        data->eapol_rx.data,
                                        data->eapol_rx.data_len);
                break;
        case EVENT_SIGNAL_CHANGE:
+               wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SIGNAL_CHANGE
+                       "above=%d signal=%d noise=%d txrate=%d",
+                       data->signal_change.above_threshold,
+                       data->signal_change.current_signal,
+                       data->signal_change.current_noise,
+                       data->signal_change.current_txrate);
+               wpa_bss_update_level(wpa_s->current_bss,
+                                    data->signal_change.current_signal);
                bgscan_notify_signal_change(
                        wpa_s, data->signal_change.above_threshold,
                        data->signal_change.current_signal,
@@ -2950,10 +3487,17 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                wpa_dbg(wpa_s, MSG_DEBUG, "Interface was enabled");
                if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
                        wpa_supplicant_update_mac_addr(wpa_s);
+                       if (wpa_s->p2p_mgmt) {
+                               wpa_supplicant_set_state(wpa_s,
+                                                        WPA_DISCONNECTED);
+                               break;
+                       }
+
 #ifdef CONFIG_AP
                        if (!wpa_s->ap_iface) {
                                wpa_supplicant_set_state(wpa_s,
                                                         WPA_DISCONNECTED);
+                               wpa_s->scan_req = NORMAL_SCAN_REQ;
                                wpa_supplicant_req_scan(wpa_s, 0, 0);
                        } else
                                wpa_supplicant_set_state(wpa_s,
@@ -2966,25 +3510,59 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                break;
        case EVENT_INTERFACE_DISABLED:
                wpa_dbg(wpa_s, MSG_DEBUG, "Interface was disabled");
+#ifdef CONFIG_P2P
+               if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO ||
+                   (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group &&
+                    wpa_s->current_ssid->mode == WPAS_MODE_P2P_GO)) {
+                       /*
+                        * Mark interface disabled if this happens to end up not
+                        * being removed as a separate P2P group interface.
+                        */
+                       wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
+                       /*
+                        * The interface was externally disabled. Remove
+                        * it assuming an external entity will start a
+                        * new session if needed.
+                        */
+                       if (wpa_s->current_ssid &&
+                           wpa_s->current_ssid->p2p_group)
+                               wpas_p2p_interface_unavailable(wpa_s);
+                       else
+                               wpas_p2p_disconnect(wpa_s);
+                       /*
+                        * wpa_s instance may have been freed, so must not use
+                        * it here anymore.
+                        */
+                       break;
+               }
+               if (wpa_s->p2p_scan_work && wpa_s->global->p2p &&
+                   p2p_in_progress(wpa_s->global->p2p) > 1) {
+                       /* This radio work will be cancelled, so clear P2P
+                        * state as well.
+                        */
+                       p2p_stop_find(wpa_s->global->p2p);
+               }
+#endif /* CONFIG_P2P */
+
+               if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
+                       /*
+                        * Indicate disconnection to keep ctrl_iface events
+                        * consistent.
+                        */
+                       wpa_supplicant_event_disassoc(
+                               wpa_s, WLAN_REASON_DEAUTH_LEAVING, 1);
+               }
                wpa_supplicant_mark_disassoc(wpa_s);
+               radio_remove_works(wpa_s, NULL, 0);
+
                wpa_supplicant_set_state(wpa_s, WPA_INTERFACE_DISABLED);
                break;
        case EVENT_CHANNEL_LIST_CHANGED:
-               if (wpa_s->drv_priv == NULL)
-                       break; /* Ignore event during drv initialization */
-
-               free_hw_features(wpa_s);
-               wpa_s->hw.modes = wpa_drv_get_hw_feature_data(
-                       wpa_s, &wpa_s->hw.num_modes, &wpa_s->hw.flags);
-
-#ifdef CONFIG_P2P
-               wpas_p2p_update_channel_list(wpa_s);
-#endif /* CONFIG_P2P */
+               wpa_supplicant_update_channel_list(
+                       wpa_s, &data->channel_list_changed);
                break;
        case EVENT_INTERFACE_UNAVAILABLE:
-#ifdef CONFIG_P2P
                wpas_p2p_interface_unavailable(wpa_s);
-#endif /* CONFIG_P2P */
                break;
        case EVENT_BEST_CHANNEL:
                wpa_dbg(wpa_s, MSG_DEBUG, "Best channel event received "
@@ -2994,11 +3572,9 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                wpa_s->best_24_freq = data->best_chan.freq_24;
                wpa_s->best_5_freq = data->best_chan.freq_5;
                wpa_s->best_overall_freq = data->best_chan.freq_overall;
-#ifdef CONFIG_P2P
                wpas_p2p_update_best_channels(wpa_s, data->best_chan.freq_24,
                                              data->best_chan.freq_5,
                                              data->best_chan.freq_overall);
-#endif /* CONFIG_P2P */
                break;
        case EVENT_UNPROT_DEAUTH:
                wpa_supplicant_event_unprot_deauth(wpa_s,
@@ -3016,7 +3592,8 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 #endif /* CONFIG_AP */
 #ifdef CONFIG_TDLS
                if (data)
-                       wpa_tdls_disable_link(wpa_s->wpa, data->low_ack.addr);
+                       wpa_tdls_disable_unreachable_link(wpa_s->wpa,
+                                                         data->low_ack.addr);
 #endif /* CONFIG_TDLS */
                break;
        case EVENT_IBSS_PEER_LOST:
@@ -3034,6 +3611,7 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                                         data->driver_gtk_rekey.replay_ctr);
                break;
        case EVENT_SCHED_SCAN_STOPPED:
+               wpa_s->pno = 0;
                wpa_s->sched_scanning = 0;
                wpa_supplicant_notify_scanning(wpa_s, 0);
 
@@ -3041,17 +3619,25 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                        break;
 
                /*
-                * If we timed out, start a new sched scan to continue
-                * searching for more SSIDs.
+                * Start a new sched scan to continue searching for more SSIDs
+                * either if timed out or PNO schedule scan is pending.
                 */
-               if (wpa_s->sched_scan_timed_out)
+               if (wpa_s->sched_scan_timed_out) {
                        wpa_supplicant_req_sched_scan(wpa_s);
+               } else if (wpa_s->pno_sched_pending) {
+                       wpa_s->pno_sched_pending = 0;
+                       wpas_start_pno(wpa_s);
+               }
+
                break;
        case EVENT_WPS_BUTTON_PUSHED:
 #ifdef CONFIG_WPS
                wpas_wps_start_pbc(wpa_s, NULL, 0);
 #endif /* CONFIG_WPS */
                break;
+       case EVENT_AVOID_FREQUENCIES:
+               wpa_supplicant_notify_avoid_freq(wpa_s, data);
+               break;
        case EVENT_CONNECT_FAILED_REASON:
 #ifdef CONFIG_AP
                if (!wpa_s->ap_iface || !data)
@@ -3062,6 +3648,15 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
                        data->connect_failed_reason.code);
 #endif /* CONFIG_AP */
                break;
+       case EVENT_NEW_PEER_CANDIDATE:
+#ifdef CONFIG_MESH
+               if (!wpa_s->ifmsh || !data)
+                       break;
+               wpa_mesh_notify_peer(wpa_s, data->mesh_peer.peer,
+                                    data->mesh_peer.ies,
+                                    data->mesh_peer.ie_len);
+#endif /* CONFIG_MESH */
+               break;
        default:
                wpa_msg(wpa_s, MSG_INFO, "Unknown event %d", event);
                break;
index 8759f54..797d43a 100755 (executable)
@@ -34,13 +34,26 @@ if [ "$CMD" = "P2P-GROUP-STARTED" ]; then
            # start with -z to avoid that
            dnsmasq -x /var/run/dnsmasq.pid-$GIFNAME \
                -i $GIFNAME \
-               -F192.168.42.11,192.168.42.99 --listen-address 192.168.42.1 -z
+               -F192.168.42.11,192.168.42.99 --listen-address 192.168.42.1 -z -p 0
        fi
     fi
     if [ "$4" = "client" ]; then
        kill_daemon dhclient /var/run/dhclient-$GIFNAME.pid
        rm /var/run/dhclient.leases-$GIFNAME
        kill_daemon dnsmasq /var/run/dnsmasq.pid-$GIFNAME
+       ipaddr=`echo "$*" | sed 's/.* ip_addr=\([^ ]*\).*/\1/'`
+       ipmask=`echo "$*" | sed 's/.* ip_mask=\([^ ]*\).*/\1/'`
+       goipaddr=`echo "$*" | sed 's/.* go_ip_addr=\([^ ]*\).*/\1/'`
+       if echo "$ipaddr$ipmask$goipaddr" | grep -q ' '; then
+           ipaddr=""
+           ipmask=""
+           goipaddr=""
+       fi
+       if [ -n "$ipaddr" ]; then
+           sudo ifconfig $GIFNAME "$ipaddr" netmask "$ipmask"
+           sudo ip ro re default via "$goipaddr"
+           exit 0
+       fi
        dhclient -pf /var/run/dhclient-$GIFNAME.pid \
            -lf /var/run/dhclient.leases-$GIFNAME \
            -nw \
diff --git a/wpa_supplicant/examples/p2p-nfc.py b/wpa_supplicant/examples/p2p-nfc.py
new file mode 100755 (executable)
index 0000000..91eba28
--- /dev/null
@@ -0,0 +1,654 @@
+#!/usr/bin/python
+#
+# Example nfcpy to wpa_supplicant wrapper for P2P NFC operations
+# Copyright (c) 2012-2013, Jouni Malinen <j@w1.fi>
+#
+# This software may be distributed under the terms of the BSD license.
+# See README for more details.
+
+import os
+import sys
+import time
+import random
+import threading
+import argparse
+
+import nfc
+import nfc.ndef
+import nfc.llcp
+import nfc.handover
+
+import logging
+
+import wpaspy
+
+wpas_ctrl = '/var/run/wpa_supplicant'
+ifname = None
+init_on_touch = False
+in_raw_mode = False
+prev_tcgetattr = 0
+include_wps_req = True
+include_p2p_req = True
+no_input = False
+srv = None
+continue_loop = True
+terminate_now = False
+summary_file = None
+success_file = None
+
+def summary(txt):
+    print txt
+    if summary_file:
+        with open(summary_file, 'a') as f:
+            f.write(txt + "\n")
+
+def success_report(txt):
+    summary(txt)
+    if success_file:
+        with open(success_file, 'a') as f:
+            f.write(txt + "\n")
+
+def wpas_connect():
+    ifaces = []
+    if os.path.isdir(wpas_ctrl):
+        try:
+            ifaces = [os.path.join(wpas_ctrl, i) for i in os.listdir(wpas_ctrl)]
+        except OSError, error:
+            print "Could not find wpa_supplicant: ", error
+            return None
+
+    if len(ifaces) < 1:
+        print "No wpa_supplicant control interface found"
+        return None
+
+    for ctrl in ifaces:
+        if ifname:
+            if ifname not in ctrl:
+                continue
+        try:
+            print "Trying to use control interface " + ctrl
+            wpas = wpaspy.Ctrl(ctrl)
+            return wpas
+        except Exception, e:
+            pass
+    return None
+
+
+def wpas_tag_read(message):
+    wpas = wpas_connect()
+    if (wpas == None):
+        return False
+    cmd = "WPS_NFC_TAG_READ " + str(message).encode("hex")
+    global force_freq
+    if force_freq:
+        cmd = cmd + " freq=" + force_freq
+    if "FAIL" in wpas.request(cmd):
+        return False
+    return True
+
+
+def wpas_get_handover_req():
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    res = wpas.request("NFC_GET_HANDOVER_REQ NDEF P2P-CR").rstrip()
+    if "FAIL" in res:
+        return None
+    return res.decode("hex")
+
+def wpas_get_handover_req_wps():
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    res = wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip()
+    if "FAIL" in res:
+        return None
+    return res.decode("hex")
+
+
+def wpas_get_handover_sel(tag=False):
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    if tag:
+        res = wpas.request("NFC_GET_HANDOVER_SEL NDEF P2P-CR-TAG").rstrip()
+    else:
+       res = wpas.request("NFC_GET_HANDOVER_SEL NDEF P2P-CR").rstrip()
+    if "FAIL" in res:
+        return None
+    return res.decode("hex")
+
+
+def wpas_get_handover_sel_wps():
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR");
+    if "FAIL" in res:
+        return None
+    return res.rstrip().decode("hex")
+
+
+def wpas_report_handover(req, sel, type):
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    cmd = "NFC_REPORT_HANDOVER " + type + " P2P " + str(req).encode("hex") + " " + str(sel).encode("hex")
+    global force_freq
+    if force_freq:
+        cmd = cmd + " freq=" + force_freq
+    return wpas.request(cmd)
+
+
+def wpas_report_handover_wsc(req, sel, type):
+    wpas = wpas_connect()
+    if (wpas == None):
+        return None
+    cmd = "NFC_REPORT_HANDOVER " + type + " WPS " + str(req).encode("hex") + " " + str(sel).encode("hex")
+    if force_freq:
+        cmd = cmd + " freq=" + force_freq
+    return wpas.request(cmd)
+
+
+def p2p_handover_client(llc):
+    message = nfc.ndef.HandoverRequestMessage(version="1.2")
+    message.nonce = random.randint(0, 0xffff)
+
+    global include_p2p_req
+    if include_p2p_req:
+        data = wpas_get_handover_req()
+        if (data == None):
+            summary("Could not get handover request carrier record from wpa_supplicant")
+            return
+        print "Handover request carrier record from wpa_supplicant: " + data.encode("hex")
+        datamsg = nfc.ndef.Message(data)
+        message.add_carrier(datamsg[0], "active", datamsg[1:])
+
+    global include_wps_req
+    if include_wps_req:
+        print "Handover request (pre-WPS):"
+        try:
+            print message.pretty()
+        except Exception, e:
+            print e
+
+        data = wpas_get_handover_req_wps()
+        if data:
+            print "Add WPS request in addition to P2P"
+            datamsg = nfc.ndef.Message(data)
+            message.add_carrier(datamsg[0], "active", datamsg[1:])
+
+    print "Handover request:"
+    try:
+        print message.pretty()
+    except Exception, e:
+        print e
+    print str(message).encode("hex")
+
+    client = nfc.handover.HandoverClient(llc)
+    try:
+        summary("Trying to initiate NFC connection handover")
+        client.connect()
+        summary("Connected for handover")
+    except nfc.llcp.ConnectRefused:
+        summary("Handover connection refused")
+        client.close()
+        return
+    except Exception, e:
+        summary("Other exception: " + str(e))
+        client.close()
+        return
+
+    summary("Sending handover request")
+
+    if not client.send(message):
+        summary("Failed to send handover request")
+        client.close()
+        return
+
+    summary("Receiving handover response")
+    message = client._recv()
+    if message is None:
+        summary("No response received")
+        client.close()
+        return
+    if message.type != "urn:nfc:wkt:Hs":
+        summary("Response was not Hs - received: " + message.type)
+        client.close()
+        return
+
+    print "Received message"
+    try:
+        print message.pretty()
+    except Exception, e:
+        print e
+    print str(message).encode("hex")
+    message = nfc.ndef.HandoverSelectMessage(message)
+    summary("Handover select received")
+    try:
+        print message.pretty()
+    except Exception, e:
+        print e
+
+    for carrier in message.carriers:
+        print "Remote carrier type: " + carrier.type
+        if carrier.type == "application/vnd.wfa.p2p":
+            print "P2P carrier type match - send to wpa_supplicant"
+            if "OK" in wpas_report_handover(data, carrier.record, "INIT"):
+                success_report("P2P handover reported successfully (initiator)")
+            else:
+                summary("P2P handover report rejected")
+            break
+
+    print "Remove peer"
+    client.close()
+    print "Done with handover"
+    global only_one
+    if only_one:
+        print "only_one -> stop loop"
+        global continue_loop
+        continue_loop = False
+
+    global no_wait
+    if no_wait:
+        print "Trying to exit.."
+        global terminate_now
+        terminate_now = True
+
+
+class HandoverServer(nfc.handover.HandoverServer):
+    def __init__(self, llc):
+        super(HandoverServer, self).__init__(llc)
+        self.sent_carrier = None
+        self.ho_server_processing = False
+        self.success = False
+
+    # override to avoid parser error in request/response.pretty() in nfcpy
+    # due to new WSC handover format
+    def _process_request(self, request):
+        summary("received handover request {}".format(request.type))
+        response = nfc.ndef.Message("\xd1\x02\x01Hs\x12")
+        if not request.type == 'urn:nfc:wkt:Hr':
+            summary("not a handover request")
+        else:
+            try:
+                request = nfc.ndef.HandoverRequestMessage(request)
+            except nfc.ndef.DecodeError as e:
+                summary("error decoding 'Hr' message: {}".format(e))
+            else:
+                response = self.process_request(request)
+        summary("send handover response {}".format(response.type))
+        return response
+
+    def process_request(self, request):
+        self.ho_server_processing = True
+        clear_raw_mode()
+        print "HandoverServer - request received"
+        try:
+            print "Parsed handover request: " + request.pretty()
+        except Exception, e:
+            print e
+
+        sel = nfc.ndef.HandoverSelectMessage(version="1.2")
+
+        found = False
+
+        for carrier in request.carriers:
+            print "Remote carrier type: " + carrier.type
+            if carrier.type == "application/vnd.wfa.p2p":
+                print "P2P carrier type match - add P2P carrier record"
+                found = True
+                self.received_carrier = carrier.record
+                print "Carrier record:"
+                try:
+                    print carrier.record.pretty()
+                except Exception, e:
+                    print e
+                data = wpas_get_handover_sel()
+                if data is None:
+                    print "Could not get handover select carrier record from wpa_supplicant"
+                    continue
+                print "Handover select carrier record from wpa_supplicant:"
+                print data.encode("hex")
+                self.sent_carrier = data
+                if "OK" in wpas_report_handover(self.received_carrier, self.sent_carrier, "RESP"):
+                    success_report("P2P handover reported successfully (responder)")
+                else:
+                    summary("P2P handover report rejected")
+                    break
+
+                message = nfc.ndef.Message(data);
+                sel.add_carrier(message[0], "active", message[1:])
+                break
+
+        for carrier in request.carriers:
+            if found:
+                break
+            print "Remote carrier type: " + carrier.type
+            if carrier.type == "application/vnd.wfa.wsc":
+                print "WSC carrier type match - add WSC carrier record"
+                found = True
+                self.received_carrier = carrier.record
+                print "Carrier record:"
+                try:
+                    print carrier.record.pretty()
+                except Exception, e:
+                    print e
+                data = wpas_get_handover_sel_wps()
+                if data is None:
+                    print "Could not get handover select carrier record from wpa_supplicant"
+                    continue
+                print "Handover select carrier record from wpa_supplicant:"
+                print data.encode("hex")
+                self.sent_carrier = data
+                if "OK" in wpas_report_handover_wsc(self.received_carrier, self.sent_carrier, "RESP"):
+                    success_report("WSC handover reported successfully")
+                else:
+                    summary("WSC handover report rejected")
+                    break
+
+                message = nfc.ndef.Message(data);
+                sel.add_carrier(message[0], "active", message[1:])
+                found = True
+                break
+
+        print "Handover select:"
+        try:
+            print sel.pretty()
+        except Exception, e:
+            print e
+        print str(sel).encode("hex")
+
+        summary("Sending handover select")
+        self.success = True
+        return sel
+
+
+def clear_raw_mode():
+    import sys, tty, termios
+    global prev_tcgetattr, in_raw_mode
+    if not in_raw_mode:
+        return
+    fd = sys.stdin.fileno()
+    termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr)
+    in_raw_mode = False
+
+
+def getch():
+    import sys, tty, termios, select
+    global prev_tcgetattr, in_raw_mode
+    fd = sys.stdin.fileno()
+    prev_tcgetattr = termios.tcgetattr(fd)
+    ch = None
+    try:
+        tty.setraw(fd)
+        in_raw_mode = True
+        [i, o, e] = select.select([fd], [], [], 0.05)
+        if i:
+            ch = sys.stdin.read(1)
+    finally:
+        termios.tcsetattr(fd, termios.TCSADRAIN, prev_tcgetattr)
+        in_raw_mode = False
+    return ch
+
+
+def p2p_tag_read(tag):
+    success = False
+    if len(tag.ndef.message):
+        for record in tag.ndef.message:
+            print "record type " + record.type
+            if record.type == "application/vnd.wfa.wsc":
+                summary("WPS tag - send to wpa_supplicant")
+                success = wpas_tag_read(tag.ndef.message)
+                break
+            if record.type == "application/vnd.wfa.p2p":
+                summary("P2P tag - send to wpa_supplicant")
+                success = wpas_tag_read(tag.ndef.message)
+                break
+    else:
+        summary("Empty tag")
+
+    if success:
+        success_report("Tag read succeeded")
+
+    return success
+
+
+def rdwr_connected_p2p_write(tag):
+    summary("Tag found - writing - " + str(tag))
+    global p2p_sel_data
+    tag.ndef.message = str(p2p_sel_data)
+    success_report("Tag write succeeded")
+    print "Done - remove tag"
+    global only_one
+    if only_one:
+        global continue_loop
+        continue_loop = False
+    global p2p_sel_wait_remove
+    return p2p_sel_wait_remove
+
+def wps_write_p2p_handover_sel(clf, wait_remove=True):
+    print "Write P2P handover select"
+    data = wpas_get_handover_sel(tag=True)
+    if (data == None):
+        summary("Could not get P2P handover select from wpa_supplicant")
+        return
+
+    global p2p_sel_wait_remove
+    p2p_sel_wait_remove = wait_remove
+    global p2p_sel_data
+    p2p_sel_data = nfc.ndef.HandoverSelectMessage(version="1.2")
+    message = nfc.ndef.Message(data);
+    p2p_sel_data.add_carrier(message[0], "active", message[1:])
+    print "Handover select:"
+    try:
+        print p2p_sel_data.pretty()
+    except Exception, e:
+        print e
+    print str(p2p_sel_data).encode("hex")
+
+    print "Touch an NFC tag"
+    clf.connect(rdwr={'on-connect': rdwr_connected_p2p_write})
+
+
+def rdwr_connected(tag):
+    global only_one, no_wait
+    summary("Tag connected: " + str(tag))
+
+    if tag.ndef:
+        print "NDEF tag: " + tag.type
+        try:
+            print tag.ndef.message.pretty()
+        except Exception, e:
+            print e
+        success = p2p_tag_read(tag)
+        if only_one and success:
+            global continue_loop
+            continue_loop = False
+    else:
+        summary("Not an NDEF tag - remove tag")
+        return True
+
+    return not no_wait
+
+
+def llcp_worker(llc):
+    global init_on_touch
+    if init_on_touch:
+            print "Starting handover client"
+            p2p_handover_client(llc)
+            return
+
+    global no_input
+    if no_input:
+        print "Wait for handover to complete"
+    else:
+        print "Wait for handover to complete - press 'i' to initiate ('w' for WPS only, 'p' for P2P only)"
+    global srv
+    global wait_connection
+    while not wait_connection and srv.sent_carrier is None:
+        if srv.ho_server_processing:
+            time.sleep(0.025)
+        elif no_input:
+            time.sleep(0.5)
+        else:
+            global include_wps_req, include_p2p_req
+            res = getch()
+            if res == 'i':
+                include_wps_req = True
+                include_p2p_req = True
+            elif res == 'p':
+                include_wps_req = False
+                include_p2p_req = True
+            elif res == 'w':
+                include_wps_req = True
+                include_p2p_req = False
+            else:
+                continue
+            clear_raw_mode()
+            print "Starting handover client"
+            p2p_handover_client(llc)
+            return
+            
+    clear_raw_mode()
+    print "Exiting llcp_worker thread"
+
+def llcp_startup(clf, llc):
+    print "Start LLCP server"
+    global srv
+    srv = HandoverServer(llc)
+    return llc
+
+def llcp_connected(llc):
+    print "P2P LLCP connected"
+    global wait_connection
+    wait_connection = False
+    global init_on_touch
+    if not init_on_touch:
+        global srv
+        srv.start()
+    if init_on_touch or not no_input:
+        threading.Thread(target=llcp_worker, args=(llc,)).start()
+    return True
+
+def terminate_loop():
+    global terminate_now
+    return terminate_now
+
+def main():
+    clf = nfc.ContactlessFrontend()
+
+    parser = argparse.ArgumentParser(description='nfcpy to wpa_supplicant integration for P2P and WPS NFC operations')
+    parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
+                        action='store_const', dest='loglevel',
+                        help='verbose debug output')
+    parser.add_argument('-q', const=logging.WARNING, action='store_const',
+                        dest='loglevel', help='be quiet')
+    parser.add_argument('--only-one', '-1', action='store_true',
+                        help='run only one operation and exit')
+    parser.add_argument('--init-on-touch', '-I', action='store_true',
+                        help='initiate handover on touch')
+    parser.add_argument('--no-wait', action='store_true',
+                        help='do not wait for tag to be removed before exiting')
+    parser.add_argument('--ifname', '-i',
+                        help='network interface name')
+    parser.add_argument('--no-wps-req', '-N', action='store_true',
+                        help='do not include WPS carrier record in request')
+    parser.add_argument('--no-input', '-a', action='store_true',
+                        help='do not use stdout input to initiate handover')
+    parser.add_argument('--tag-read-only', '-t', action='store_true',
+                        help='tag read only (do not allow connection handover)')
+    parser.add_argument('--handover-only', action='store_true',
+                        help='connection handover only (do not allow tag read)')
+    parser.add_argument('--freq', '-f',
+                        help='forced frequency of operating channel in MHz')
+    parser.add_argument('--summary',
+                        help='summary file for writing status updates')
+    parser.add_argument('--success',
+                        help='success file for writing success update')
+    parser.add_argument('command', choices=['write-p2p-sel'],
+                        nargs='?')
+    args = parser.parse_args()
+
+    global only_one
+    only_one = args.only_one
+
+    global no_wait
+    no_wait = args.no_wait
+
+    global force_freq
+    force_freq = args.freq
+
+    logging.basicConfig(level=args.loglevel)
+
+    global init_on_touch
+    init_on_touch = args.init_on_touch
+
+    if args.ifname:
+        global ifname
+        ifname = args.ifname
+        print "Selected ifname " + ifname
+
+    if args.no_wps_req:
+        global include_wps_req
+        include_wps_req = False
+
+    if args.summary:
+        global summary_file
+        summary_file = args.summary
+
+    if args.success:
+        global success_file
+        success_file = args.success
+
+    if args.no_input:
+        global no_input
+        no_input = True
+
+    clf = nfc.ContactlessFrontend()
+    global wait_connection
+
+    try:
+        if not clf.open("usb"):
+            print "Could not open connection with an NFC device"
+            raise SystemExit
+
+        if args.command == "write-p2p-sel":
+            wps_write_p2p_handover_sel(clf, wait_remove=not args.no_wait)
+            raise SystemExit
+
+        global continue_loop
+        while continue_loop:
+            print "Waiting for a tag or peer to be touched"
+            wait_connection = True
+            try:
+                if args.tag_read_only:
+                    if not clf.connect(rdwr={'on-connect': rdwr_connected}):
+                        break
+                elif args.handover_only:
+                    if not clf.connect(llcp={'on-startup': llcp_startup,
+                                             'on-connect': llcp_connected},
+                                       terminate=terminate_loop):
+                        break
+                else:
+                    if not clf.connect(rdwr={'on-connect': rdwr_connected},
+                                       llcp={'on-startup': llcp_startup,
+                                             'on-connect': llcp_connected},
+                                       terminate=terminate_loop):
+                        break
+            except Exception, e:
+                print "clf.connect failed"
+
+            global srv
+            if only_one and srv and srv.success:
+                raise SystemExit
+
+    except KeyboardInterrupt:
+        raise SystemExit
+    finally:
+        clf.close()
+
+    raise SystemExit
+
+if __name__ == '__main__':
+    main()
index 7c6b0aa..cc2cff2 100755 (executable)
@@ -14,11 +14,13 @@ pbc()
 enter_pin()
 {
        echo "Enter a PIN from a station to be enrolled to the network."
-       read -p "Enrollee PIN: " pin
+       echo -n "Enrollee PIN: "
+       read pin
        cpin=`$CLI wps_check_pin "$pin" | tail -1`
        if [ "$cpin" = "FAIL-CHECKSUM" ]; then
                echo "Checksum digit is not valid"
-               read -p "Do you want to use this PIN (y/n)? " resp
+               echo -n "Do you want to use this PIN (y/n)? "
+               read resp
                case "$resp" in
                        y*)
                                cpin=`echo "$pin" | sed "s/[^1234567890]//g"`
@@ -50,7 +52,8 @@ main_menu()
        echo "3: Show current configuration"
        echo "0: Exit wps-ap-cli"
 
-       read -p "Command: " cmd
+       echo -n "Command: "
+       read cmd
 
        case "$cmd" in
                1)
index d6dec85..7459eb9 100755 (executable)
@@ -10,7 +10,8 @@ import os
 import sys
 import time
 import random
-import StringIO
+import threading
+import argparse
 
 import nfc
 import nfc.ndef
@@ -18,11 +19,27 @@ import nfc.llcp
 import nfc.handover
 
 import logging
-logging.basicConfig()
 
 import wpaspy
 
 wpas_ctrl = '/var/run/wpa_supplicant'
+srv = None
+continue_loop = True
+terminate_now = False
+summary_file = None
+success_file = None
+
+def summary(txt):
+    print txt
+    if summary_file:
+        with open(summary_file, 'a') as f:
+            f.write(txt + "\n")
+
+def success_report(txt):
+    summary(txt)
+    if success_file:
+        with open(success_file, 'a') as f:
+            f.write(txt + "\n")
 
 def wpas_connect():
     ifaces = []
@@ -50,7 +67,7 @@ def wpas_tag_read(message):
     wpas = wpas_connect()
     if (wpas == None):
         return False
-    if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + message.encode("hex")):
+    if "FAIL" in wpas.request("WPS_NFC_TAG_READ " + str(message).encode("hex")):
         return False
     return True
 
@@ -71,21 +88,29 @@ def wpas_get_er_config_token(uuid):
     wpas = wpas_connect()
     if (wpas == None):
         return None
-    return wpas.request("WPS_ER_NFC_CONFIG_TOKEN NDEF " + uuid).rstrip().decode("hex")
+    ret = wpas.request("WPS_ER_NFC_CONFIG_TOKEN NDEF " + uuid)
+    if "FAIL" in ret:
+        return None
+    return ret.rstrip().decode("hex")
 
 
 def wpas_get_password_token():
     wpas = wpas_connect()
     if (wpas == None):
         return None
-    return wpas.request("WPS_NFC_TOKEN NDEF").rstrip().decode("hex")
-
+    ret = wpas.request("WPS_NFC_TOKEN NDEF")
+    if "FAIL" in ret:
+        return None
+    return ret.rstrip().decode("hex")
 
 def wpas_get_handover_req():
     wpas = wpas_connect()
     if (wpas == None):
         return None
-    return wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS-CR").rstrip().decode("hex")
+    ret = wpas.request("NFC_GET_HANDOVER_REQ NDEF WPS-CR")
+    if "FAIL" in ret:
+        return None
+    return ret.rstrip().decode("hex")
 
 
 def wpas_get_handover_sel(uuid):
@@ -93,8 +118,12 @@ def wpas_get_handover_sel(uuid):
     if (wpas == None):
         return None
     if uuid is None:
-        return wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip().decode("hex")
-    return wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR " + uuid).rstrip().decode("hex")
+        res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR").rstrip()
+    else:
+       res = wpas.request("NFC_GET_HANDOVER_SEL NDEF WPS-CR " + uuid).rstrip()
+    if "FAIL" in res:
+       return None
+    return res.decode("hex")
 
 
 def wpas_report_handover(req, sel, type):
@@ -107,159 +136,176 @@ def wpas_report_handover(req, sel, type):
 
 
 class HandoverServer(nfc.handover.HandoverServer):
-    def __init__(self):
-        super(HandoverServer, self).__init__()
+    def __init__(self, llc):
+        super(HandoverServer, self).__init__(llc)
+        self.sent_carrier = None
+        self.ho_server_processing = False
+        self.success = False
+
+    # override to avoid parser error in request/response.pretty() in nfcpy
+    # due to new WSC handover format
+    def _process_request(self, request):
+        summary("received handover request {}".format(request.type))
+        response = nfc.ndef.Message("\xd1\x02\x01Hs\x12")
+        if not request.type == 'urn:nfc:wkt:Hr':
+            summary("not a handover request")
+        else:
+            try:
+                request = nfc.ndef.HandoverRequestMessage(request)
+            except nfc.ndef.DecodeError as e:
+                summary("error decoding 'Hr' message: {}".format(e))
+            else:
+                response = self.process_request(request)
+        summary("send handover response {}".format(response.type))
+        return response
 
     def process_request(self, request):
-        print "HandoverServer - request received"
-        print "Parsed handover request: " + request.pretty()
+        self.ho_server_processing = True
+        summary("HandoverServer - request received")
+        try:
+            print "Parsed handover request: " + request.pretty()
+        except Exception, e:
+            print e
 
         sel = nfc.ndef.HandoverSelectMessage(version="1.2")
 
         for carrier in request.carriers:
             print "Remote carrier type: " + carrier.type
             if carrier.type == "application/vnd.wfa.wsc":
-                print "WPS carrier type match - add WPS carrier record"
-                self.received_carrier = carrier.record
+                summary("WPS carrier type match - add WPS carrier record")
                 data = wpas_get_handover_sel(self.uuid)
                 if data is None:
-                    print "Could not get handover select carrier record from wpa_supplicant"
+                    summary("Could not get handover select carrier record from wpa_supplicant")
                     continue
                 print "Handover select carrier record from wpa_supplicant:"
                 print data.encode("hex")
                 self.sent_carrier = data
+                if "OK" in wpas_report_handover(carrier.record, self.sent_carrier, "RESP"):
+                    success_report("Handover reported successfully (responder)")
+                else:
+                    summary("Handover report rejected (responder)")
 
                 message = nfc.ndef.Message(data);
                 sel.add_carrier(message[0], "active", message[1:])
 
         print "Handover select:"
-        print sel.pretty()
+        try:
+            print sel.pretty()
+        except Exception, e:
+            print e
         print str(sel).encode("hex")
 
-        print "Sending handover select"
+        summary("Sending handover select")
+        self.success = True
         return sel
 
 
-def wps_handover_resp(peer, uuid):
-    if uuid is None:
-        print "Trying to handle WPS handover"
-    else:
-        print "Trying to handle WPS handover with AP " + uuid
-
-    srv = HandoverServer()
-    srv.sent_carrier = None
-    srv.uuid = uuid
-
-    nfc.llcp.activate(peer);
-
-    try:
-        print "Trying handover";
-        srv.start()
-        print "Wait for disconnect"
-        while nfc.llcp.connected():
-            time.sleep(0.1)
-        print "Disconnected after handover"
-    except nfc.llcp.ConnectRefused:
-        print "Handover connection refused"
-        nfc.llcp.shutdown()
-        return
-
-    if srv.sent_carrier:
-        wpas_report_handover(srv.received_carrier, srv.sent_carrier, "RESP")
-
-    print "Remove peer"
-    nfc.llcp.shutdown()
-    print "Done with handover"
-    time.sleep(1)
-
-
-def wps_handover_init(peer):
-    print "Trying to initiate WPS handover"
+def wps_handover_init(llc):
+    summary("Trying to initiate WPS handover")
 
     data = wpas_get_handover_req()
     if (data == None):
-        print "Could not get handover request carrier record from wpa_supplicant"
+        summary("Could not get handover request carrier record from wpa_supplicant")
         return
     print "Handover request carrier record from wpa_supplicant: " + data.encode("hex")
-    record = nfc.ndef.Record()
-    f = StringIO.StringIO(data)
-    record._read(f)
-    record = nfc.ndef.HandoverCarrierRecord(record)
-    print "Parsed handover request carrier record:"
-    print record.pretty()
 
     message = nfc.ndef.HandoverRequestMessage(version="1.2")
     message.nonce = random.randint(0, 0xffff)
-    message.add_carrier(record, "active")
+    datamsg = nfc.ndef.Message(data)
+    message.add_carrier(datamsg[0], "active", datamsg[1:])
 
     print "Handover request:"
-    print message.pretty()
-
-    nfc.llcp.activate(peer);
+    try:
+        print message.pretty()
+    except Exception, e:
+        print e
+    print str(message).encode("hex")
 
-    client = nfc.handover.HandoverClient()
+    client = nfc.handover.HandoverClient(llc)
     try:
-        print "Trying handover";
+        summary("Trying to initiate NFC connection handover")
         client.connect()
-        print "Connected for handover"
+        summary("Connected for handover")
     except nfc.llcp.ConnectRefused:
-        print "Handover connection refused"
-        nfc.llcp.shutdown()
+        summary("Handover connection refused")
+        client.close()
+        return
+    except Exception, e:
+        summary("Other exception: " + str(e))
         client.close()
         return
 
-    print "Sending handover request"
+    summary("Sending handover request")
 
     if not client.send(message):
-        print "Failed to send handover request"
+        summary("Failed to send handover request")
+        client.close()
+        return
 
-    print "Receiving handover response"
+    summary("Receiving handover response")
     message = client._recv()
     if message is None:
-        print "No response received"
-        nfc.llcp.shutdown()
+        summary("No response received")
         client.close()
         return
     if message.type != "urn:nfc:wkt:Hs":
-        print "Response was not Hs - received: " + message.type
-        nfc.llcp.shutdown()
+        summary("Response was not Hs - received: " + message.type)
         client.close()
         return
 
     print "Received message"
-    print message.pretty()
+    try:
+        print message.pretty()
+    except Exception, e:
+        print e
+    print str(message).encode("hex")
     message = nfc.ndef.HandoverSelectMessage(message)
-    print "Handover select received"
-    print message.pretty()
+    summary("Handover select received")
+    try:
+        print message.pretty()
+    except Exception, e:
+        print e
 
     for carrier in message.carriers:
         print "Remote carrier type: " + carrier.type
         if carrier.type == "application/vnd.wfa.wsc":
             print "WPS carrier type match - send to wpa_supplicant"
-            wpas_report_handover(data, carrier.record, "INIT")
-            wifi = nfc.ndef.WifiConfigRecord(carrier.record)
-            print wifi.pretty()
+            if "OK" in wpas_report_handover(data, carrier.record, "INIT"):
+                success_report("Handover reported successfully (initiator)")
+            else:
+                summary("Handover report rejected (initiator)")
+            # nfcpy does not support the new format..
+            #wifi = nfc.ndef.WifiConfigRecord(carrier.record)
+            #print wifi.pretty()
 
     print "Remove peer"
-    nfc.llcp.shutdown()
     client.close()
     print "Done with handover"
+    global only_one
+    if only_one:
+        global continue_loop
+        continue_loop = False
 
+    global no_wait
+    if no_wait:
+        print "Trying to exit.."
+        global terminate_now
+        terminate_now = True
 
 def wps_tag_read(tag, wait_remove=True):
     success = False
     if len(tag.ndef.message):
-        message = nfc.ndef.Message(tag.ndef.message)
-        print "message type " + message.type
-
-        for record in message:
+        for record in tag.ndef.message:
             print "record type " + record.type
             if record.type == "application/vnd.wfa.wsc":
-                print "WPS tag - send to wpa_supplicant"
+                summary("WPS tag - send to wpa_supplicant")
                 success = wpas_tag_read(tag.ndef.message)
                 break
     else:
-        print "Empty tag"
+        summary("Empty tag")
+
+    if success:
+        success_report("Tag read succeeded")
 
     if wait_remove:
         print "Remove tag"
@@ -269,166 +315,204 @@ def wps_tag_read(tag, wait_remove=True):
     return success
 
 
+def rdwr_connected_write(tag):
+    summary("Tag found - writing - " + str(tag))
+    global write_data
+    tag.ndef.message = str(write_data)
+    success_report("Tag write succeeded")
+    print "Done - remove tag"
+    global only_one
+    if only_one:
+        global continue_loop
+        continue_loop = False
+    global write_wait_remove
+    while write_wait_remove and tag.is_present:
+        time.sleep(0.1)
+
 def wps_write_config_tag(clf, id=None, wait_remove=True):
     print "Write WPS config token"
-    data = wpas_get_config_token(id)
-    if (data == None):
+    global write_data, write_wait_remove
+    write_wait_remove = wait_remove
+    write_data = wpas_get_config_token(id)
+    if write_data == None:
         print "Could not get WPS config token from wpa_supplicant"
         sys.exit(1)
         return
-
     print "Touch an NFC tag"
-    while True:
-        tag = clf.poll()
-        if tag == None:
-            time.sleep(0.1)
-            continue
-        break
-
-    print "Tag found - writing"
-    tag.ndef.message = data
-    print "Done - remove tag"
-    while wait_remove and tag.is_present:
-        time.sleep(0.1)
+    clf.connect(rdwr={'on-connect': rdwr_connected_write})
 
 
-def wps_write_er_config_tag(clf, uuid):
+def wps_write_er_config_tag(clf, uuid, wait_remove=True):
     print "Write WPS ER config token"
-    data = wpas_get_er_config_token(uuid)
-    if (data == None):
+    global write_data, write_wait_remove
+    write_wait_remove = wait_remove
+    write_data = wpas_get_er_config_token(uuid)
+    if write_data == None:
         print "Could not get WPS config token from wpa_supplicant"
         return
 
     print "Touch an NFC tag"
-    while True:
-        tag = clf.poll()
-        if tag == None:
-            time.sleep(0.1)
-            continue
-        break
-
-    print "Tag found - writing"
-    tag.ndef.message = data
-    print "Done - remove tag"
-    while tag.is_present:
-        time.sleep(0.1)
+    clf.connect(rdwr={'on-connect': rdwr_connected_write})
 
 
 def wps_write_password_tag(clf, wait_remove=True):
     print "Write WPS password token"
-    data = wpas_get_password_token()
-    if (data == None):
+    global write_data, write_wait_remove
+    write_wait_remove = wait_remove
+    write_data = wpas_get_password_token()
+    if write_data == None:
         print "Could not get WPS password token from wpa_supplicant"
         return
 
     print "Touch an NFC tag"
-    while True:
-        tag = clf.poll()
-        if tag == None:
-            time.sleep(0.1)
-            continue
-        break
+    clf.connect(rdwr={'on-connect': rdwr_connected_write})
 
-    print "Tag found - writing"
-    tag.ndef.message = data
-    print "Done - remove tag"
-    while wait_remove and tag.is_present:
-        time.sleep(0.1)
 
+def rdwr_connected(tag):
+    global only_one, no_wait
+    summary("Tag connected: " + str(tag))
 
-def find_peer(clf):
-    while True:
-        if nfc.llcp.connected():
-            print "LLCP connected"
-        general_bytes = nfc.llcp.startup({})
-        peer = clf.listen(ord(os.urandom(1)) + 250, general_bytes)
-        if isinstance(peer, nfc.DEP):
-            print "listen -> DEP";
-            if peer.general_bytes.startswith("Ffm"):
-                print "Found DEP"
-                return peer
-            print "mismatch in general_bytes"
-            print peer.general_bytes
-
-        peer = clf.poll(general_bytes)
-        if isinstance(peer, nfc.DEP):
-            print "poll -> DEP";
-            if peer.general_bytes.startswith("Ffm"):
-                print "Found DEP"
-                return peer
-            print "mismatch in general_bytes"
-            print peer.general_bytes
-
-        if peer:
-            print "Found tag"
-            return peer
+    if tag.ndef:
+        print "NDEF tag: " + tag.type
+        try:
+            print tag.ndef.message.pretty()
+        except Exception, e:
+            print e
+        success = wps_tag_read(tag, not only_one)
+        if only_one and success:
+            global continue_loop
+            continue_loop = False
+    else:
+        summary("Not an NDEF tag - remove tag")
+        return True
 
+    return not no_wait
 
-def main():
-    clf = nfc.ContactlessFrontend()
 
-    try:
-        arg_uuid = None
-        if len(sys.argv) > 1 and sys.argv[1] != '-1':
-            arg_uuid = sys.argv[1]
+def llcp_worker(llc):
+    global arg_uuid
+    if arg_uuid is None:
+        wps_handover_init(llc)
+        print "Exiting llcp_worker thread"
+        return
 
-        if len(sys.argv) > 1 and sys.argv[1] == '-1':
-            only_one = True
+    global srv
+    global wait_connection
+    while not wait_connection and srv.sent_carrier is None:
+        if srv.ho_server_processing:
+            time.sleep(0.025)
+
+def llcp_startup(clf, llc):
+    global arg_uuid
+    if arg_uuid:
+        print "Start LLCP server"
+        global srv
+        srv = HandoverServer(llc)
+        if arg_uuid is "ap":
+            print "Trying to handle WPS handover"
+            srv.uuid = None
         else:
-            only_one = False
+            print "Trying to handle WPS handover with AP " + arg_uuid
+            srv.uuid = arg_uuid
+    return llc
+
+def llcp_connected(llc):
+    print "P2P LLCP connected"
+    global wait_connection
+    wait_connection = False
+    global arg_uuid
+    if arg_uuid:
+        global srv
+        srv.start()
+    else:
+        threading.Thread(target=llcp_worker, args=(llc,)).start()
+    print "llcp_connected returning"
+    return True
 
-        if len(sys.argv) > 1 and sys.argv[1] == "write-config":
-            wps_write_config_tag(clf)
-            raise SystemExit
 
-        if len(sys.argv) > 1 and sys.argv[1] == "write-config-no-wait":
-            wps_write_config_tag(clf, wait_remove=False)
-            raise SystemExit
+def terminate_loop():
+    global terminate_now
+    return terminate_now
+
+def main():
+    clf = nfc.ContactlessFrontend()
+
+    parser = argparse.ArgumentParser(description='nfcpy to wpa_supplicant integration for WPS NFC operations')
+    parser.add_argument('-d', const=logging.DEBUG, default=logging.INFO,
+                        action='store_const', dest='loglevel',
+                        help='verbose debug output')
+    parser.add_argument('-q', const=logging.WARNING, action='store_const',
+                        dest='loglevel', help='be quiet')
+    parser.add_argument('--only-one', '-1', action='store_true',
+                        help='run only one operation and exit')
+    parser.add_argument('--no-wait', action='store_true',
+                        help='do not wait for tag to be removed before exiting')
+    parser.add_argument('--uuid',
+                        help='UUID of an AP (used for WPS ER operations)')
+    parser.add_argument('--id',
+                        help='network id (used for WPS ER operations)')
+    parser.add_argument('--summary',
+                        help='summary file for writing status updates')
+    parser.add_argument('--success',
+                        help='success file for writing success update')
+    parser.add_argument('command', choices=['write-config',
+                                            'write-er-config',
+                                            'write-password'],
+                        nargs='?')
+    args = parser.parse_args()
+
+    global arg_uuid
+    arg_uuid = args.uuid
+
+    global only_one
+    only_one = args.only_one
+
+    global no_wait
+    no_wait = args.no_wait
+
+    if args.summary:
+        global summary_file
+        summary_file = args.summary
+
+    if args.success:
+        global success_file
+        success_file = args.success
+
+    logging.basicConfig(level=args.loglevel)
 
-        if len(sys.argv) > 2 and sys.argv[1] == "write-config-id":
-            wps_write_config_tag(clf, sys.argv[2])
+    try:
+        if not clf.open("usb"):
+            print "Could not open connection with an NFC device"
             raise SystemExit
 
-        if len(sys.argv) > 2 and sys.argv[1] == "write-er-config":
-            wps_write_er_config_tag(clf, sys.argv[2])
+        if args.command == "write-config":
+            wps_write_config_tag(clf, id=args.id, wait_remove=not args.no_wait)
             raise SystemExit
 
-        if len(sys.argv) > 1 and sys.argv[1] == "write-password":
-            wps_write_password_tag(clf)
+        if args.command == "write-er-config":
+            wps_write_er_config_tag(clf, args.uuid, wait_remove=not args.no_wait)
             raise SystemExit
 
-        if len(sys.argv) > 1 and sys.argv[1] == "write-password-no-wait":
-            wps_write_password_tag(clf, wait_remove=False)
+        if args.command == "write-password":
+            wps_write_password_tag(clf, wait_remove=not args.no_wait)
             raise SystemExit
 
-        while True:
+        global continue_loop
+        while continue_loop:
             print "Waiting for a tag or peer to be touched"
-
-            tag = find_peer(clf)
-            if isinstance(tag, nfc.DEP):
-                if arg_uuid is None:
-                    wps_handover_init(tag)
-                elif arg_uuid is "ap":
-                    wps_handover_resp(tag, None)
-                else:
-                    wps_handover_resp(tag, arg_uuid)
-                if only_one:
-                    break
-                continue
-
-            if tag.ndef:
-                success = wps_tag_read(tag, not only_one)
-                if only_one:
-                    if not success:
-                        sys.exit(1)
+            wait_connection = True
+            try:
+                if not clf.connect(rdwr={'on-connect': rdwr_connected},
+                                   llcp={'on-startup': llcp_startup,
+                                         'on-connect': llcp_connected},
+                                   terminate=terminate_loop):
                     break
-                continue
+            except Exception, e:
+                print "clf.connect failed"
 
-            print "Not an NDEF tag - remove tag"
-            if only_one:
-                sys.exit(1)
-            while tag.is_present:
-                time.sleep(0.1)
+            global srv
+            if only_one and srv and srv.success:
+                raise SystemExit
 
     except KeyboardInterrupt:
         raise SystemExit
old mode 100644 (file)
new mode 100755 (executable)
index 06a97d3..10ecce7
@@ -1,7 +1,8 @@
 /*
  * Generic advertisement service (GAS) query
  * Copyright (c) 2009, Atheros Communications
- * Copyright (c) 2011, Qualcomm Atheros
+ * Copyright (c) 2011-2014, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -13,6 +14,8 @@
 #include "utils/eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "common/gas.h"
+#include "common/wpa_ctrl.h"
+#include "rsn_supp/wpa.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
 #include "offchannel.h"
@@ -28,6 +31,7 @@
  */
 struct gas_query_pending {
        struct dl_list list;
+       struct gas_query *gas;
        u8 addr[ETH_ALEN];
        u8 dialog_token;
        u8 next_frag_id;
@@ -35,8 +39,10 @@ struct gas_query_pending {
        unsigned int offchannel_tx_started:1;
        int freq;
        u16 status_code;
+       struct wpabuf *req;
        struct wpabuf *adv_proto;
        struct wpabuf *resp;
+       struct os_reltime last_oper;
        void (*cb)(void *ctx, const u8 *dst, u8 dialog_token,
                   enum gas_query_result result,
                   const struct wpabuf *adv_proto,
@@ -50,6 +56,8 @@ struct gas_query_pending {
 struct gas_query {
        struct wpa_supplicant *wpa_s;
        struct dl_list pending; /* struct gas_query_pending */
+       struct gas_query_pending *current;
+       struct wpa_radio_work *work;
 };
 
 
@@ -57,6 +65,16 @@ static void gas_query_tx_comeback_timeout(void *eloop_data, void *user_ctx);
 static void gas_query_timeout(void *eloop_data, void *user_ctx);
 
 
+static int ms_from_time(struct os_reltime *last)
+{
+       struct os_reltime now, res;
+
+       os_get_reltime(&now);
+       os_reltime_sub(&now, last, &res);
+       return res.sec * 1000 + res.usec / 1000;
+}
+
+
 /**
  * gas_query_init - Initialize GAS query component
  * @wpa_s: Pointer to wpa_supplicant data
@@ -77,10 +95,58 @@ struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s)
 }
 
 
+static const char * gas_result_txt(enum gas_query_result result)
+{
+       switch (result) {
+       case GAS_QUERY_SUCCESS:
+               return "SUCCESS";
+       case GAS_QUERY_FAILURE:
+               return "FAILURE";
+       case GAS_QUERY_TIMEOUT:
+               return "TIMEOUT";
+       case GAS_QUERY_PEER_ERROR:
+               return "PEER_ERROR";
+       case GAS_QUERY_INTERNAL_ERROR:
+               return "INTERNAL_ERROR";
+       case GAS_QUERY_CANCELLED:
+               return "CANCELLED";
+       case GAS_QUERY_DELETED_AT_DEINIT:
+               return "DELETED_AT_DEINIT";
+       }
+
+       return "N/A";
+}
+
+
+static void gas_query_free(struct gas_query_pending *query, int del_list)
+{
+       struct gas_query *gas = query->gas;
+
+       if (del_list)
+               dl_list_del(&query->list);
+
+       if (gas->work && gas->work->ctx == query) {
+               radio_work_done(gas->work);
+               gas->work = NULL;
+       }
+
+       wpabuf_free(query->req);
+       wpabuf_free(query->adv_proto);
+       wpabuf_free(query->resp);
+       os_free(query);
+}
+
+
 static void gas_query_done(struct gas_query *gas,
                           struct gas_query_pending *query,
                           enum gas_query_result result)
 {
+       wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_DONE "addr=" MACSTR
+               " dialog_token=%u freq=%d status_code=%u result=%s",
+               MAC2STR(query->addr), query->dialog_token, query->freq,
+               query->status_code, gas_result_txt(result));
+       if (gas->current == query)
+               gas->current = NULL;
        if (query->offchannel_tx_started)
                offchannel_send_action_done(gas->wpa_s);
        eloop_cancel_timeout(gas_query_tx_comeback_timeout, gas, query);
@@ -88,9 +154,7 @@ static void gas_query_done(struct gas_query *gas,
        dl_list_del(&query->list);
        query->cb(query->ctx, query->addr, query->dialog_token, result,
                  query->adv_proto, query->resp, query->status_code);
-       wpabuf_free(query->adv_proto);
-       wpabuf_free(query->resp);
-       os_free(query);
+       gas_query_free(query, 0);
 }
 
 
@@ -138,17 +202,79 @@ static int gas_query_append(struct gas_query_pending *query, const u8 *data,
 }
 
 
+static void gas_query_tx_status(struct wpa_supplicant *wpa_s,
+                               unsigned int freq, const u8 *dst,
+                               const u8 *src, const u8 *bssid,
+                               const u8 *data, size_t data_len,
+                               enum offchannel_send_action_result result)
+{
+       struct gas_query_pending *query;
+       struct gas_query *gas = wpa_s->gas;
+       int dur;
+
+       if (gas->current == NULL) {
+               wpa_printf(MSG_DEBUG, "GAS: Unexpected TX status: freq=%u dst="
+                          MACSTR " result=%d - no query in progress",
+                          freq, MAC2STR(dst), result);
+               return;
+       }
+
+       query = gas->current;
+
+       dur = ms_from_time(&query->last_oper);
+       wpa_printf(MSG_DEBUG, "GAS: TX status: freq=%u dst=" MACSTR
+                  " result=%d query=%p dialog_token=%u dur=%d ms",
+                  freq, MAC2STR(dst), result, query, query->dialog_token, dur);
+       if (os_memcmp(dst, query->addr, ETH_ALEN) != 0) {
+               wpa_printf(MSG_DEBUG, "GAS: TX status for unexpected destination");
+               return;
+       }
+       os_get_reltime(&query->last_oper);
+
+       if (result == OFFCHANNEL_SEND_ACTION_SUCCESS) {
+               eloop_cancel_timeout(gas_query_timeout, gas, query);
+               eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+                                      gas_query_timeout, gas, query);
+       }
+       if (result == OFFCHANNEL_SEND_ACTION_FAILED) {
+               eloop_cancel_timeout(gas_query_timeout, gas, query);
+               eloop_register_timeout(0, 0, gas_query_timeout, gas, query);
+       }
+}
+
+
+static int pmf_in_use(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+       if (wpa_s->current_ssid == NULL ||
+           wpa_s->wpa_state < WPA_4WAY_HANDSHAKE ||
+           os_memcmp(addr, wpa_s->bssid, ETH_ALEN) != 0)
+               return 0;
+       return wpa_sm_pmf_enabled(wpa_s->wpa);
+}
+
+
 static int gas_query_tx(struct gas_query *gas, struct gas_query_pending *query,
                        struct wpabuf *req)
 {
-       int res;
+       unsigned int wait_time;
+       int res, prot = pmf_in_use(gas->wpa_s, query->addr);
+
        wpa_printf(MSG_DEBUG, "GAS: Send action frame to " MACSTR " len=%u "
-                  "freq=%d", MAC2STR(query->addr),
-                  (unsigned int) wpabuf_len(req), query->freq);
+                  "freq=%d prot=%d", MAC2STR(query->addr),
+                  (unsigned int) wpabuf_len(req), query->freq, prot);
+       if (prot) {
+               u8 *categ = wpabuf_mhead_u8(req);
+               *categ = WLAN_ACTION_PROTECTED_DUAL;
+       }
+       os_get_reltime(&query->last_oper);
+       wait_time = 1000;
+       if (gas->wpa_s->max_remain_on_chan &&
+           wait_time > gas->wpa_s->max_remain_on_chan)
+               wait_time = gas->wpa_s->max_remain_on_chan;
        res = offchannel_send_action(gas->wpa_s, query->freq, query->addr,
                                     gas->wpa_s->own_addr, query->addr,
-                                    wpabuf_head(req), wpabuf_len(req), 1000,
-                                    NULL, 0);
+                                    wpabuf_head(req), wpabuf_len(req),
+                                    wait_time, gas_query_tx_status, 0);
        if (res == 0)
                query->offchannel_tx_started = 1;
        return res;
@@ -296,27 +422,42 @@ static void gas_query_rx_comeback(struct gas_query *gas,
 
 
 /**
- * gas_query_rx - Indicate reception of a Public Action frame
+ * gas_query_rx - Indicate reception of a Public Action or Protected Dual frame
  * @gas: GAS query data from gas_query_init()
  * @da: Destination MAC address of the Action frame
  * @sa: Source MAC address of the Action frame
  * @bssid: BSSID of the Action frame
+ * @categ: Category of the Action frame
  * @data: Payload of the Action frame
  * @len: Length of @data
  * @freq: Frequency (in MHz) on which the frame was received
  * Returns: 0 if the Public Action frame was a GAS frame or -1 if not
  */
 int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
-                const u8 *bssid, const u8 *data, size_t len, int freq)
+                const u8 *bssid, u8 categ, const u8 *data, size_t len,
+                int freq)
 {
        struct gas_query_pending *query;
        u8 action, dialog_token, frag_id = 0, more_frags = 0;
        u16 comeback_delay, resp_len;
        const u8 *pos, *adv_proto;
+       int prot, pmf;
+       unsigned int left;
 
        if (gas == NULL || len < 4)
                return -1;
 
+       prot = categ == WLAN_ACTION_PROTECTED_DUAL;
+       pmf = pmf_in_use(gas->wpa_s, bssid);
+       if (prot && !pmf) {
+               wpa_printf(MSG_DEBUG, "GAS: Drop unexpected protected GAS frame when PMF is disabled");
+               return 0;
+       }
+       if (!prot && pmf) {
+               wpa_printf(MSG_DEBUG, "GAS: Drop unexpected unprotected GAS frame when PMF is enabled");
+               return 0;
+       }
+
        pos = data;
        action = *pos++;
        dialog_token = *pos++;
@@ -332,6 +473,9 @@ int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
                return -1;
        }
 
+       wpa_printf(MSG_DEBUG, "GAS: Response in %d ms from " MACSTR,
+                  ms_from_time(&query->last_oper), MAC2STR(sa));
+
        if (query->wait_comeback && action == WLAN_PA_GAS_INITIAL_RESP) {
                wpa_printf(MSG_DEBUG, "GAS: Unexpected initial response from "
                           MACSTR " dialog token %u when waiting for comeback "
@@ -349,7 +493,10 @@ int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
        query->status_code = WPA_GET_LE16(pos);
        pos += 2;
 
-       if (query->status_code != WLAN_STATUS_SUCCESS) {
+       if (query->status_code == WLAN_STATUS_QUERY_RESP_OUTSTANDING &&
+           action == WLAN_PA_GAS_COMEBACK_RESP) {
+               wpa_printf(MSG_DEBUG, "GAS: Allow non-zero status for outstanding comeback response");
+       } else if (query->status_code != WLAN_STATUS_SUCCESS) {
                wpa_printf(MSG_DEBUG, "GAS: Query to " MACSTR " dialog token "
                           "%u failed - status code %u",
                           MAC2STR(sa), dialog_token, query->status_code);
@@ -397,17 +544,17 @@ int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
        resp_len = WPA_GET_LE16(pos);
        pos += 2;
 
-       if (pos + resp_len > data + len) {
+       left = data + len - pos;
+       if (resp_len > left) {
                wpa_printf(MSG_DEBUG, "GAS: Truncated Query Response in "
                           "response from " MACSTR, MAC2STR(sa));
                return 0;
        }
 
-       if (pos + resp_len < data + len) {
+       if (resp_len < left) {
                wpa_printf(MSG_DEBUG, "GAS: Ignore %u octets of extra data "
                           "after Query Response from " MACSTR,
-                          (unsigned int) (data + len - pos - resp_len),
-                          MAC2STR(sa));
+                          left - resp_len, MAC2STR(sa));
        }
 
        if (action == WLAN_PA_GAS_COMEBACK_RESP)
@@ -426,8 +573,9 @@ static void gas_query_timeout(void *eloop_data, void *user_ctx)
        struct gas_query *gas = eloop_data;
        struct gas_query_pending *query = user_ctx;
 
-       wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR,
-                  MAC2STR(query->addr));
+       wpa_printf(MSG_DEBUG, "GAS: No response received for query to " MACSTR
+                  " dialog token %u",
+                  MAC2STR(query->addr), query->dialog_token);
        gas_query_done(gas, query, GAS_QUERY_TIMEOUT);
 }
 
@@ -446,12 +594,56 @@ static int gas_query_dialog_token_available(struct gas_query *gas,
 }
 
 
+static void gas_query_start_cb(struct wpa_radio_work *work, int deinit)
+{
+       struct gas_query_pending *query = work->ctx;
+       struct gas_query *gas = query->gas;
+       struct wpa_supplicant *wpa_s = gas->wpa_s;
+
+       if (deinit) {
+               if (work->started) {
+                       gas->work = NULL;
+                       gas_query_done(gas, query, GAS_QUERY_DELETED_AT_DEINIT);
+                       return;
+               }
+
+               gas_query_free(query, 1);
+               return;
+       }
+
+       if (wpas_update_random_addr_disassoc(wpa_s) < 0) {
+               wpa_msg(wpa_s, MSG_INFO,
+                       "Failed to assign random MAC address for GAS");
+               gas_query_free(query, 1);
+               radio_work_done(work);
+               return;
+       }
+
+       gas->work = work;
+
+       if (gas_query_tx(gas, query, query->req) < 0) {
+               wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
+                          MACSTR, MAC2STR(query->addr));
+               gas_query_free(query, 1);
+               return;
+       }
+       gas->current = query;
+
+       wpa_printf(MSG_DEBUG, "GAS: Starting query timeout for dialog token %u",
+                  query->dialog_token);
+       eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0,
+                              gas_query_timeout, gas, query);
+
+}
+
+
 /**
  * gas_query_req - Request a GAS query
  * @gas: GAS query data from gas_query_init()
  * @dst: Destination MAC address for the query
  * @freq: Frequency (in MHz) for the channel on which to send the query
- * @req: GAS query payload
+ * @req: GAS query payload (to be freed by gas_query module in case of success
+ *     return)
  * @cb: Callback function for reporting GAS query result and response
  * @ctx: Context pointer to use with the @cb call
  * Returns: dialog token (>= 0) on success or -1 on failure
@@ -485,28 +677,27 @@ int gas_query_req(struct gas_query *gas, const u8 *dst, int freq,
        if (query == NULL)
                return -1;
 
+       query->gas = gas;
        os_memcpy(query->addr, dst, ETH_ALEN);
        query->dialog_token = dialog_token;
        query->freq = freq;
        query->cb = cb;
        query->ctx = ctx;
+       query->req = req;
        dl_list_add(&gas->pending, &query->list);
 
        *(wpabuf_mhead_u8(req) + 2) = dialog_token;
 
-       wpa_printf(MSG_DEBUG, "GAS: Starting request for " MACSTR
-                  " dialog_token %u", MAC2STR(dst), dialog_token);
-       if (gas_query_tx(gas, query, req) < 0) {
-               wpa_printf(MSG_DEBUG, "GAS: Failed to send Action frame to "
-                          MACSTR, MAC2STR(query->addr));
-               dl_list_del(&query->list);
-               os_free(query);
+       wpa_msg(gas->wpa_s, MSG_INFO, GAS_QUERY_START "addr=" MACSTR
+               " dialog_token=%u freq=%d",
+               MAC2STR(query->addr), query->dialog_token, query->freq);
+
+       if (radio_add_work(gas->wpa_s, freq, "gas-query", 0, gas_query_start_cb,
+                          query) < 0) {
+               gas_query_free(query, 1);
                return -1;
        }
 
-       eloop_register_timeout(GAS_QUERY_TIMEOUT_PERIOD, 0, gas_query_timeout,
-                              gas, query);
-
        return dialog_token;
 }
 
old mode 100644 (file)
new mode 100755 (executable)
index 5c3d161..ad13490
@@ -17,7 +17,8 @@ struct gas_query;
 struct gas_query * gas_query_init(struct wpa_supplicant *wpa_s);
 void gas_query_deinit(struct gas_query *gas);
 int gas_query_rx(struct gas_query *gas, const u8 *da, const u8 *sa,
-                const u8 *bssid, const u8 *data, size_t len, int freq);
+                const u8 *bssid, u8 categ, const u8 *data, size_t len,
+                int freq);
 
 /**
  * enum gas_query_result - GAS query result
old mode 100644 (file)
new mode 100755 (executable)
index 4048cf7..b9cd681
@@ -1,12 +1,13 @@
 /*
  * Copyright (c) 2009, Atheros Communications, Inc.
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
  */
 
 #include "includes.h"
+#include <sys/stat.h>
 
 #include "common.h"
 #include "eloop.h"
 #include "common/ieee802_11_defs.h"
 #include "common/gas.h"
 #include "common/wpa_ctrl.h"
+#include "rsn_supp/wpa.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
 #include "config.h"
+#include "scan.h"
 #include "bss.h"
+#include "blacklist.h"
 #include "gas_query.h"
 #include "interworking.h"
 #include "hs20_supplicant.h"
 
 
-void wpas_hs20_add_indication(struct wpabuf *buf)
+#define OSU_MAX_ITEMS 10
+
+struct osu_lang_string {
+       char lang[4];
+       char text[253];
+};
+
+struct osu_icon {
+       u16 width;
+       u16 height;
+       char lang[4];
+       char icon_type[256];
+       char filename[256];
+       unsigned int id;
+       unsigned int failed:1;
+};
+
+struct osu_provider {
+       u8 bssid[ETH_ALEN];
+       u8 osu_ssid[32];
+       u8 osu_ssid_len;
+       char server_uri[256];
+       u32 osu_methods; /* bit 0 = OMA-DM, bit 1 = SOAP-XML SPP */
+       char osu_nai[256];
+       struct osu_lang_string friendly_name[OSU_MAX_ITEMS];
+       size_t friendly_name_count;
+       struct osu_lang_string serv_desc[OSU_MAX_ITEMS];
+       size_t serv_desc_count;
+       struct osu_icon icon[OSU_MAX_ITEMS];
+       size_t icon_count;
+};
+
+
+void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id)
 {
+       u8 conf;
+
        wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
-       wpabuf_put_u8(buf, 5);
+       wpabuf_put_u8(buf, pps_mo_id >= 0 ? 7 : 5);
        wpabuf_put_be24(buf, OUI_WFA);
        wpabuf_put_u8(buf, HS20_INDICATION_OUI_TYPE);
-       wpabuf_put_u8(buf, 0x00); /* Hotspot Configuration */
+       conf = HS20_VERSION;
+       if (pps_mo_id >= 0)
+               conf |= HS20_PPS_MO_ID_PRESENT;
+       wpabuf_put_u8(buf, conf);
+       if (pps_mo_id >= 0)
+               wpabuf_put_le16(buf, pps_mo_id);
 }
 
 
@@ -62,15 +106,35 @@ int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
 }
 
 
-struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload,
-                                   size_t payload_len)
+int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+       struct wpa_cred *cred;
+
+       if (ssid == NULL)
+               return 0;
+
+       if (ssid->update_identifier)
+               return ssid->update_identifier;
+
+       if (ssid->parent_cred == NULL)
+               return 0;
+
+       for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+               if (ssid->parent_cred == cred)
+                       return cred->update_identifier;
+       }
+
+       return 0;
+}
+
+
+void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len,
+                      struct wpabuf *buf)
 {
-       struct wpabuf *buf;
        u8 *len_pos;
 
-       buf = gas_anqp_build_initial_req(0, 100 + payload_len);
        if (buf == NULL)
-               return NULL;
+               return;
 
        len_pos = gas_anqp_add_element(buf, ANQP_VENDOR_SPECIFIC);
        wpabuf_put_be24(buf, OUI_WFA);
@@ -80,6 +144,11 @@ struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload,
                wpabuf_put_u8(buf, 0); /* Reserved */
                if (payload)
                        wpabuf_put_data(buf, payload, payload_len);
+       } else if (stypes == BIT(HS20_STYPE_ICON_REQUEST)) {
+               wpabuf_put_u8(buf, HS20_STYPE_ICON_REQUEST);
+               wpabuf_put_u8(buf, 0); /* Reserved */
+               if (payload)
+                       wpabuf_put_data(buf, payload, payload_len);
        } else {
                u8 i;
                wpabuf_put_u8(buf, HS20_STYPE_QUERY_LIST);
@@ -92,6 +161,19 @@ struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload,
        gas_anqp_set_element_len(buf, len_pos);
 
        gas_anqp_set_len(buf);
+}
+
+
+struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload,
+                                   size_t payload_len)
+{
+       struct wpabuf *buf;
+
+       buf = gas_anqp_build_initial_req(0, 100 + payload_len);
+       if (buf == NULL)
+               return NULL;
+
+       hs20_put_anqp_req(stypes, payload, payload_len, buf);
 
        return buf;
 }
@@ -125,23 +207,161 @@ int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes,
        res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
        if (res < 0) {
                wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+               wpabuf_free(buf);
                ret = -1;
        } else
                wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
                           "%u", res);
 
-       wpabuf_free(buf);
        return ret;
 }
 
 
+static void hs20_set_osu_access_permission(const char *osu_dir,
+                                          const char *fname)
+{
+       struct stat statbuf;
+
+       /* Get OSU directory information */
+       if (stat(osu_dir, &statbuf) < 0) {
+               wpa_printf(MSG_WARNING, "Cannot stat the OSU directory %s",
+                          osu_dir);
+               return;
+       }
+
+       if (chmod(fname, statbuf.st_mode) < 0) {
+               wpa_printf(MSG_WARNING,
+                          "Cannot change the permissions for %s", fname);
+               return;
+       }
+
+       if (chown(fname, statbuf.st_uid, statbuf.st_gid) < 0) {
+               wpa_printf(MSG_WARNING, "Cannot change the ownership for %s",
+                          fname);
+       }
+}
+
+static int hs20_process_icon_binary_file(struct wpa_supplicant *wpa_s,
+                                        const u8 *sa, const u8 *pos,
+                                        size_t slen)
+{
+       char fname[256];
+       int png;
+       FILE *f;
+       u16 data_len;
+
+       wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR " Icon Binary File",
+               MAC2STR(sa));
+
+       if (slen < 4) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+                       "value from " MACSTR, MAC2STR(sa));
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "HS 2.0: Download Status Code %u", *pos);
+       if (*pos != 0)
+               return -1;
+       pos++;
+       slen--;
+
+       if ((size_t) 1 + pos[0] > slen) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+                       "value from " MACSTR, MAC2STR(sa));
+               return -1;
+       }
+       wpa_hexdump_ascii(MSG_DEBUG, "Icon Type", pos + 1, pos[0]);
+       png = os_strncasecmp((char *) pos + 1, "image/png", 9) == 0;
+       slen -= 1 + pos[0];
+       pos += 1 + pos[0];
+
+       if (slen < 2) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+                       "value from " MACSTR, MAC2STR(sa));
+               return -1;
+       }
+       data_len = WPA_GET_LE16(pos);
+       pos += 2;
+       slen -= 2;
+
+       if (data_len > slen) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: Too short Icon Binary File "
+                       "value from " MACSTR, MAC2STR(sa));
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "Icon Binary Data: %u bytes", data_len);
+       if (wpa_s->conf->osu_dir == NULL)
+               return -1;
+
+       wpa_s->osu_icon_id++;
+       if (wpa_s->osu_icon_id == 0)
+               wpa_s->osu_icon_id++;
+       snprintf(fname, sizeof(fname), "%s/osu-icon-%u.%s",
+                wpa_s->conf->osu_dir, wpa_s->osu_icon_id,
+                png ? "png" : "icon");
+       f = fopen(fname, "wb");
+       if (f == NULL)
+               return -1;
+
+       hs20_set_osu_access_permission(wpa_s->conf->osu_dir, fname);
+
+       if (fwrite(pos, slen, 1, f) != 1) {
+               fclose(f);
+               unlink(fname);
+               return -1;
+       }
+       fclose(f);
+
+       wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP-ICON %s", fname);
+       return 0;
+}
+
+
+static void hs20_continue_icon_fetch(void *eloop_ctx, void *sock_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       if (wpa_s->fetch_osu_icon_in_progress)
+               hs20_next_osu_icon(wpa_s);
+}
+
+
+static void hs20_osu_icon_fetch_result(struct wpa_supplicant *wpa_s, int res)
+{
+       size_t i, j;
+       struct os_reltime now, tmp;
+       int dur;
+
+       os_get_reltime(&now);
+       os_reltime_sub(&now, &wpa_s->osu_icon_fetch_start, &tmp);
+       dur = tmp.sec * 1000 + tmp.usec / 1000;
+       wpa_printf(MSG_DEBUG, "HS 2.0: Icon fetch dur=%d ms res=%d",
+                  dur, res);
+
+       for (i = 0; i < wpa_s->osu_prov_count; i++) {
+               struct osu_provider *osu = &wpa_s->osu_prov[i];
+               for (j = 0; j < osu->icon_count; j++) {
+                       struct osu_icon *icon = &osu->icon[j];
+                       if (icon->id || icon->failed)
+                               continue;
+                       if (res < 0)
+                               icon->failed = 1;
+                       else
+                               icon->id = wpa_s->osu_icon_id;
+                       return;
+               }
+       }
+}
+
+
 void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
-                                 const u8 *sa, const u8 *data, size_t slen)
+                                 struct wpa_bss *bss, const u8 *sa,
+                                 const u8 *data, size_t slen)
 {
        const u8 *pos = data;
        u8 subtype;
-       struct wpa_bss *bss = wpa_bss_get_bssid(wpa_s, sa);
        struct wpa_bss_anqp *anqp = NULL;
+       int ret;
 
        if (slen < 2)
                return;
@@ -160,6 +380,11 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
                wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
                        " HS Capability List", MAC2STR(sa));
                wpa_hexdump_ascii(MSG_DEBUG, "HS Capability List", pos, slen);
+               if (anqp) {
+                       wpabuf_free(anqp->hs20_capability_list);
+                       anqp->hs20_capability_list =
+                               wpabuf_alloc_copy(pos, slen);
+               }
                break;
        case HS20_STYPE_OPERATOR_FRIENDLY_NAME:
                wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
@@ -207,8 +432,576 @@ void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
                                wpabuf_alloc_copy(pos, slen);
                }
                break;
+       case HS20_STYPE_OSU_PROVIDERS_LIST:
+               wpa_msg(wpa_s, MSG_INFO, "RX-HS20-ANQP " MACSTR
+                       " OSU Providers list", MAC2STR(sa));
+               wpa_s->num_prov_found++;
+               if (anqp) {
+                       wpabuf_free(anqp->hs20_osu_providers_list);
+                       anqp->hs20_osu_providers_list =
+                               wpabuf_alloc_copy(pos, slen);
+               }
+               break;
+       case HS20_STYPE_ICON_BINARY_FILE:
+               ret = hs20_process_icon_binary_file(wpa_s, sa, pos, slen);
+               if (wpa_s->fetch_osu_icon_in_progress) {
+                       hs20_osu_icon_fetch_result(wpa_s, ret);
+                       eloop_cancel_timeout(hs20_continue_icon_fetch,
+                                            wpa_s, NULL);
+                       eloop_register_timeout(0, 0, hs20_continue_icon_fetch,
+                                              wpa_s, NULL);
+               }
+               break;
        default:
                wpa_printf(MSG_DEBUG, "HS20: Unsupported subtype %u", subtype);
                break;
        }
 }
+
+
+void hs20_notify_parse_done(struct wpa_supplicant *wpa_s)
+{
+       if (!wpa_s->fetch_osu_icon_in_progress)
+               return;
+       if (eloop_is_timeout_registered(hs20_continue_icon_fetch, wpa_s, NULL))
+               return;
+       /*
+        * We are going through icon fetch, but no icon response was received.
+        * Assume this means the current AP could not provide an answer to avoid
+        * getting stuck in fetch iteration.
+        */
+       hs20_icon_fetch_failed(wpa_s);
+}
+
+
+static void hs20_free_osu_prov_entry(struct osu_provider *prov)
+{
+}
+
+
+void hs20_free_osu_prov(struct wpa_supplicant *wpa_s)
+{
+       size_t i;
+       for (i = 0; i < wpa_s->osu_prov_count; i++)
+               hs20_free_osu_prov_entry(&wpa_s->osu_prov[i]);
+       os_free(wpa_s->osu_prov);
+       wpa_s->osu_prov = NULL;
+       wpa_s->osu_prov_count = 0;
+}
+
+
+static void hs20_osu_fetch_done(struct wpa_supplicant *wpa_s)
+{
+       char fname[256];
+       FILE *f;
+       size_t i, j;
+
+       wpa_s->fetch_osu_info = 0;
+       wpa_s->fetch_osu_icon_in_progress = 0;
+
+       if (wpa_s->conf->osu_dir == NULL) {
+               hs20_free_osu_prov(wpa_s);
+               wpa_s->fetch_anqp_in_progress = 0;
+               return;
+       }
+
+       snprintf(fname, sizeof(fname), "%s/osu-providers.txt",
+                wpa_s->conf->osu_dir);
+       f = fopen(fname, "w");
+       if (f == NULL) {
+               hs20_free_osu_prov(wpa_s);
+               return;
+       }
+
+       hs20_set_osu_access_permission(wpa_s->conf->osu_dir, fname);
+
+       for (i = 0; i < wpa_s->osu_prov_count; i++) {
+               struct osu_provider *osu = &wpa_s->osu_prov[i];
+               if (i > 0)
+                       fprintf(f, "\n");
+               fprintf(f, "OSU-PROVIDER " MACSTR "\n"
+                       "uri=%s\n"
+                       "methods=%08x\n",
+                       MAC2STR(osu->bssid), osu->server_uri, osu->osu_methods);
+               if (osu->osu_ssid_len) {
+                       fprintf(f, "osu_ssid=%s\n",
+                               wpa_ssid_txt(osu->osu_ssid,
+                                            osu->osu_ssid_len));
+               }
+               if (osu->osu_nai[0])
+                       fprintf(f, "osu_nai=%s\n", osu->osu_nai);
+               for (j = 0; j < osu->friendly_name_count; j++) {
+                       fprintf(f, "friendly_name=%s:%s\n",
+                               osu->friendly_name[j].lang,
+                               osu->friendly_name[j].text);
+               }
+               for (j = 0; j < osu->serv_desc_count; j++) {
+                       fprintf(f, "desc=%s:%s\n",
+                               osu->serv_desc[j].lang,
+                               osu->serv_desc[j].text);
+               }
+               for (j = 0; j < osu->icon_count; j++) {
+                       struct osu_icon *icon = &osu->icon[j];
+                       if (icon->failed)
+                               continue; /* could not fetch icon */
+                       fprintf(f, "icon=%u:%u:%u:%s:%s:%s\n",
+                               icon->id, icon->width, icon->height, icon->lang,
+                               icon->icon_type, icon->filename);
+               }
+       }
+       fclose(f);
+       hs20_free_osu_prov(wpa_s);
+
+       wpa_msg(wpa_s, MSG_INFO, "OSU provider fetch completed");
+       wpa_s->fetch_anqp_in_progress = 0;
+}
+
+
+void hs20_next_osu_icon(struct wpa_supplicant *wpa_s)
+{
+       size_t i, j;
+
+       wpa_printf(MSG_DEBUG, "HS 2.0: Ready to fetch next icon");
+
+       for (i = 0; i < wpa_s->osu_prov_count; i++) {
+               struct osu_provider *osu = &wpa_s->osu_prov[i];
+               for (j = 0; j < osu->icon_count; j++) {
+                       struct osu_icon *icon = &osu->icon[j];
+                       if (icon->id || icon->failed)
+                               continue;
+
+                       wpa_printf(MSG_DEBUG, "HS 2.0: Try to fetch icon '%s' "
+                                  "from " MACSTR, icon->filename,
+                                  MAC2STR(osu->bssid));
+                       os_get_reltime(&wpa_s->osu_icon_fetch_start);
+                       if (hs20_anqp_send_req(wpa_s, osu->bssid,
+                                              BIT(HS20_STYPE_ICON_REQUEST),
+                                              (u8 *) icon->filename,
+                                              os_strlen(icon->filename)) < 0) {
+                               icon->failed = 1;
+                               continue;
+                       }
+                       return;
+               }
+       }
+
+       wpa_printf(MSG_DEBUG, "HS 2.0: No more icons to fetch");
+       hs20_osu_fetch_done(wpa_s);
+}
+
+
+static void hs20_osu_add_prov(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+                             const u8 *osu_ssid, u8 osu_ssid_len,
+                             const u8 *pos, size_t len)
+{
+       struct osu_provider *prov;
+       const u8 *end = pos + len;
+       u16 len2;
+       const u8 *pos2;
+       u8 uri_len, osu_method_len, osu_nai_len;
+
+       wpa_hexdump(MSG_DEBUG, "HS 2.0: Parsing OSU Provider", pos, len);
+       prov = os_realloc_array(wpa_s->osu_prov,
+                               wpa_s->osu_prov_count + 1,
+                               sizeof(*prov));
+       if (prov == NULL)
+               return;
+       wpa_s->osu_prov = prov;
+       prov = &prov[wpa_s->osu_prov_count];
+       os_memset(prov, 0, sizeof(*prov));
+
+       os_memcpy(prov->bssid, bss->bssid, ETH_ALEN);
+       os_memcpy(prov->osu_ssid, osu_ssid, osu_ssid_len);
+       prov->osu_ssid_len = osu_ssid_len;
+
+       /* OSU Friendly Name Length */
+       if (pos + 2 > end) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+                          "Friendly Name Length");
+               return;
+       }
+       len2 = WPA_GET_LE16(pos);
+       pos += 2;
+       if (len2 > end - pos) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+                          "Friendly Name Duples");
+               return;
+       }
+       pos2 = pos;
+       pos += len2;
+
+       /* OSU Friendly Name Duples */
+       while (pos2 + 4 <= pos && prov->friendly_name_count < OSU_MAX_ITEMS) {
+               struct osu_lang_string *f;
+               if (pos2 + 1 + pos2[0] > pos || pos2[0] < 3) {
+                       wpa_printf(MSG_DEBUG, "Invalid OSU Friendly Name");
+                       break;
+               }
+               f = &prov->friendly_name[prov->friendly_name_count++];
+               os_memcpy(f->lang, pos2 + 1, 3);
+               os_memcpy(f->text, pos2 + 1 + 3, pos2[0] - 3);
+               pos2 += 1 + pos2[0];
+       }
+
+       /* OSU Server URI */
+       if (pos + 1 > end) {
+               wpa_printf(MSG_DEBUG,
+                          "HS 2.0: Not enough room for OSU Server URI length");
+               return;
+       }
+       uri_len = *pos++;
+       if (uri_len > end - pos) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Server "
+                          "URI");
+               return;
+       }
+       os_memcpy(prov->server_uri, pos, uri_len);
+       pos += uri_len;
+
+       /* OSU Method list */
+       if (pos + 1 > end) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method "
+                          "list length");
+               return;
+       }
+       osu_method_len = pos[0];
+       if (osu_method_len > end - pos - 1) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU Method "
+                          "list");
+               return;
+       }
+       pos2 = pos + 1;
+       pos += 1 + osu_method_len;
+       while (pos2 < pos) {
+               if (*pos2 < 32)
+                       prov->osu_methods |= BIT(*pos2);
+               pos2++;
+       }
+
+       /* Icons Available Length */
+       if (pos + 2 > end) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons "
+                          "Available Length");
+               return;
+       }
+       len2 = WPA_GET_LE16(pos);
+       pos += 2;
+       if (len2 > end - pos) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for Icons "
+                          "Available");
+               return;
+       }
+       pos2 = pos;
+       pos += len2;
+
+       /* Icons Available */
+       while (pos2 < pos) {
+               struct osu_icon *icon = &prov->icon[prov->icon_count];
+               u8 flen;
+
+               if (pos2 + 2 + 2 + 3 + 1 + 1 > pos) {
+                       wpa_printf(MSG_DEBUG, "HS 2.0: Invalid Icon Metadata");
+                       break;
+               }
+
+               icon->width = WPA_GET_LE16(pos2);
+               pos2 += 2;
+               icon->height = WPA_GET_LE16(pos2);
+               pos2 += 2;
+               os_memcpy(icon->lang, pos2, 3);
+               pos2 += 3;
+
+               flen = pos2[0];
+               if (flen > pos - pos2 - 1) {
+                       wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon Type");
+                       break;
+               }
+               os_memcpy(icon->icon_type, pos2 + 1, flen);
+               pos2 += 1 + flen;
+
+               if (pos2 + 1 > pos) {
+                       wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon "
+                                  "Filename length");
+                       break;
+               }
+               flen = pos2[0];
+               if (flen > pos - pos2 - 1) {
+                       wpa_printf(MSG_DEBUG, "HS 2.0: Not room for Icon "
+                                  "Filename");
+                       break;
+               }
+               os_memcpy(icon->filename, pos2 + 1, flen);
+               pos2 += 1 + flen;
+
+               prov->icon_count++;
+       }
+
+       /* OSU_NAI */
+       if (pos + 1 > end) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI");
+               return;
+       }
+       osu_nai_len = pos[0];
+       if (osu_nai_len > end - pos - 1) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU_NAI");
+               return;
+       }
+       os_memcpy(prov->osu_nai, pos + 1, osu_nai_len);
+       pos += 1 + osu_nai_len;
+
+       /* OSU Service Description Length */
+       if (pos + 2 > end) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+                          "Service Description Length");
+               return;
+       }
+       len2 = WPA_GET_LE16(pos);
+       pos += 2;
+       if (len2 > end - pos) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for OSU "
+                          "Service Description Duples");
+               return;
+       }
+       pos2 = pos;
+       pos += len2;
+
+       /* OSU Service Description Duples */
+       while (pos2 + 4 <= pos && prov->serv_desc_count < OSU_MAX_ITEMS) {
+               struct osu_lang_string *f;
+               u8 descr_len;
+
+               descr_len = pos2[0];
+               if (descr_len > pos - pos2 - 1 || descr_len < 3) {
+                       wpa_printf(MSG_DEBUG, "Invalid OSU Service "
+                                  "Description");
+                       break;
+               }
+               f = &prov->serv_desc[prov->serv_desc_count++];
+               os_memcpy(f->lang, pos2 + 1, 3);
+               os_memcpy(f->text, pos2 + 1 + 3, descr_len - 3);
+               pos2 += 1 + descr_len;
+       }
+
+       wpa_printf(MSG_DEBUG, "HS 2.0: Added OSU Provider through " MACSTR,
+                  MAC2STR(bss->bssid));
+       wpa_s->osu_prov_count++;
+}
+
+
+void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_bss *bss;
+       struct wpabuf *prov_anqp;
+       const u8 *pos, *end;
+       u16 len;
+       const u8 *osu_ssid;
+       u8 osu_ssid_len;
+       u8 num_providers;
+
+       hs20_free_osu_prov(wpa_s);
+
+       dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+               if (bss->anqp == NULL)
+                       continue;
+               prov_anqp = bss->anqp->hs20_osu_providers_list;
+               if (prov_anqp == NULL)
+                       continue;
+               wpa_printf(MSG_DEBUG, "HS 2.0: Parsing OSU Providers list from "
+                          MACSTR, MAC2STR(bss->bssid));
+               wpa_hexdump_buf(MSG_DEBUG, "HS 2.0: OSU Providers list",
+                               prov_anqp);
+               pos = wpabuf_head(prov_anqp);
+               end = pos + wpabuf_len(prov_anqp);
+
+               /* OSU SSID */
+               if (pos + 1 > end)
+                       continue;
+               if (pos + 1 + pos[0] > end) {
+                       wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for "
+                                  "OSU SSID");
+                       continue;
+               }
+               osu_ssid_len = *pos++;
+               if (osu_ssid_len > 32) {
+                       wpa_printf(MSG_DEBUG, "HS 2.0: Invalid OSU SSID "
+                                  "Length %u", osu_ssid_len);
+                       continue;
+               }
+               osu_ssid = pos;
+               pos += osu_ssid_len;
+
+               if (pos + 1 > end) {
+                       wpa_printf(MSG_DEBUG, "HS 2.0: Not enough room for "
+                                  "Number of OSU Providers");
+                       continue;
+               }
+               num_providers = *pos++;
+               wpa_printf(MSG_DEBUG, "HS 2.0: Number of OSU Providers: %u",
+                          num_providers);
+
+               /* OSU Providers */
+               while (pos + 2 < end && num_providers > 0) {
+                       num_providers--;
+                       len = WPA_GET_LE16(pos);
+                       pos += 2;
+                       if (len > (unsigned int) (end - pos))
+                               break;
+                       hs20_osu_add_prov(wpa_s, bss, osu_ssid,
+                                         osu_ssid_len, pos, len);
+                       pos += len;
+               }
+
+               if (pos != end) {
+                       wpa_printf(MSG_DEBUG, "HS 2.0: Ignored %d bytes of "
+                                  "extra data after OSU Providers",
+                                  (int) (end - pos));
+               }
+       }
+
+       wpa_s->fetch_osu_icon_in_progress = 1;
+       hs20_next_osu_icon(wpa_s);
+}
+
+
+static void hs20_osu_scan_res_handler(struct wpa_supplicant *wpa_s,
+                                     struct wpa_scan_results *scan_res)
+{
+       wpa_printf(MSG_DEBUG, "OSU provisioning fetch scan completed");
+       if (!wpa_s->fetch_osu_waiting_scan) {
+               wpa_printf(MSG_DEBUG, "OSU fetch have been canceled");
+               return;
+       }
+       wpa_s->network_select = 0;
+       wpa_s->fetch_all_anqp = 1;
+       wpa_s->fetch_osu_info = 1;
+       wpa_s->fetch_osu_icon_in_progress = 0;
+
+       interworking_start_fetch_anqp(wpa_s);
+}
+
+
+int hs20_fetch_osu(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+                          "interface disabled");
+               return -1;
+       }
+
+       if (wpa_s->scanning) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+                          "scanning");
+               return -1;
+       }
+
+       if (wpa_s->conf->osu_dir == NULL) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+                          "osu_dir not configured");
+               return -1;
+       }
+
+       if (wpa_s->fetch_anqp_in_progress || wpa_s->network_select) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Cannot start fetch_osu - "
+                          "fetch in progress (%d, %d)",
+                          wpa_s->fetch_anqp_in_progress,
+                          wpa_s->network_select);
+               return -1;
+       }
+
+       wpa_msg(wpa_s, MSG_INFO, "Starting OSU provisioning information fetch");
+       wpa_s->num_osu_scans = 0;
+       wpa_s->num_prov_found = 0;
+       hs20_start_osu_scan(wpa_s);
+
+       return 0;
+}
+
+
+void hs20_start_osu_scan(struct wpa_supplicant *wpa_s)
+{
+       wpa_s->fetch_osu_waiting_scan = 1;
+       wpa_s->num_osu_scans++;
+       wpa_s->scan_req = MANUAL_SCAN_REQ;
+       wpa_s->scan_res_handler = hs20_osu_scan_res_handler;
+       wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s)
+{
+       wpa_printf(MSG_DEBUG, "Cancel OSU fetch");
+       interworking_stop_fetch_anqp(wpa_s);
+       wpa_s->fetch_osu_waiting_scan = 0;
+       wpa_s->network_select = 0;
+       wpa_s->fetch_osu_info = 0;
+       wpa_s->fetch_osu_icon_in_progress = 0;
+}
+
+
+void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s)
+{
+       hs20_osu_icon_fetch_result(wpa_s, -1);
+       eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL);
+       eloop_register_timeout(0, 0, hs20_continue_icon_fetch, wpa_s, NULL);
+}
+
+
+void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s,
+                                     const char *url, u8 osu_method)
+{
+       if (url)
+               wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION "%u %s",
+                       osu_method, url);
+       else
+               wpa_msg(wpa_s, MSG_INFO, HS20_SUBSCRIPTION_REMEDIATION);
+}
+
+
+void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code,
+                                   u16 reauth_delay, const char *url)
+{
+       if (!wpa_sm_pmf_enabled(wpa_s->wpa)) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Ignore deauthentication imminent notice since PMF was not enabled");
+               return;
+       }
+
+       wpa_msg(wpa_s, MSG_INFO, HS20_DEAUTH_IMMINENT_NOTICE "%u %u %s",
+               code, reauth_delay, url);
+
+       if (code == HS20_DEAUTH_REASON_CODE_BSS) {
+               wpa_printf(MSG_DEBUG, "HS 2.0: Add BSS to blacklist");
+               wpa_blacklist_add(wpa_s, wpa_s->bssid);
+               /* TODO: For now, disable full ESS since some drivers may not
+                * support disabling per BSS. */
+               if (wpa_s->current_ssid) {
+                       struct os_reltime now;
+                       os_get_reltime(&now);
+                       if (now.sec + reauth_delay <=
+                           wpa_s->current_ssid->disabled_until.sec)
+                               return;
+                       wpa_printf(MSG_DEBUG, "HS 2.0: Disable network for %u seconds (BSS)",
+                                  reauth_delay);
+                       wpa_s->current_ssid->disabled_until.sec =
+                               now.sec + reauth_delay;
+               }
+       }
+
+       if (code == HS20_DEAUTH_REASON_CODE_ESS && wpa_s->current_ssid) {
+               struct os_reltime now;
+               os_get_reltime(&now);
+               if (now.sec + reauth_delay <=
+                   wpa_s->current_ssid->disabled_until.sec)
+                       return;
+               wpa_printf(MSG_DEBUG, "HS 2.0: Disable network for %u seconds",
+                          reauth_delay);
+               wpa_s->current_ssid->disabled_until.sec =
+                       now.sec + reauth_delay;
+       }
+}
+
+
+void hs20_deinit(struct wpa_supplicant *wpa_s)
+{
+       eloop_cancel_timeout(hs20_continue_icon_fetch, wpa_s, NULL);
+       hs20_free_osu_prov(wpa_s);
+}
old mode 100644 (file)
new mode 100755 (executable)
index 1c8481b..85b5120
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -8,15 +8,34 @@
 #ifndef HS20_SUPPLICANT_H
 #define HS20_SUPPLICANT_H
 
-void wpas_hs20_add_indication(struct wpabuf *buf);
+void wpas_hs20_add_indication(struct wpabuf *buf, int pps_mo_id);
 
 int hs20_anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst, u32 stypes,
                       const u8 *payload, size_t payload_len);
 struct wpabuf * hs20_build_anqp_req(u32 stypes, const u8 *payload,
                                    size_t payload_len);
+void hs20_put_anqp_req(u32 stypes, const u8 *payload, size_t payload_len,
+                      struct wpabuf *buf);
 void hs20_parse_rx_hs20_anqp_resp(struct wpa_supplicant *wpa_s,
-                                 const u8 *sa, const u8 *data, size_t slen);
+                                 struct wpa_bss *bss, const u8 *sa,
+                                 const u8 *data, size_t slen);
 int is_hs20_network(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
                    struct wpa_bss *bss);
+int hs20_get_pps_mo_id(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+void hs20_notify_parse_done(struct wpa_supplicant *wpa_s);
+
+void hs20_rx_subscription_remediation(struct wpa_supplicant *wpa_s,
+                                     const char *url, u8 osu_method);
+void hs20_rx_deauth_imminent_notice(struct wpa_supplicant *wpa_s, u8 code,
+                                   u16 reauth_delay, const char *url);
+
+void hs20_free_osu_prov(struct wpa_supplicant *wpa_s);
+void hs20_next_osu_icon(struct wpa_supplicant *wpa_s);
+void hs20_osu_icon_fetch(struct wpa_supplicant *wpa_s);
+int hs20_fetch_osu(struct wpa_supplicant *wpa_s);
+void hs20_cancel_fetch_osu(struct wpa_supplicant *wpa_s);
+void hs20_icon_fetch_failed(struct wpa_supplicant *wpa_s);
+void hs20_start_osu_scan(struct wpa_supplicant *wpa_s);
+void hs20_deinit(struct wpa_supplicant *wpa_s);
 
 #endif /* HS20_SUPPLICANT_H */
old mode 100644 (file)
new mode 100755 (executable)
index 687c042..d0ae135
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant - IBSS RSN
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2013, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -9,15 +9,21 @@
 #include "includes.h"
 
 #include "common.h"
+#include "common/wpa_ctrl.h"
+#include "utils/eloop.h"
 #include "l2_packet/l2_packet.h"
 #include "rsn_supp/wpa.h"
 #include "rsn_supp/wpa_ie.h"
 #include "ap/wpa_auth.h"
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
+#include "common/ieee802_11_defs.h"
 #include "ibss_rsn.h"
 
 
+static void ibss_rsn_auth_timeout(void *eloop_ctx, void *timeout_ctx);
+
+
 static struct ibss_rsn_peer * ibss_rsn_get_peer(struct ibss_rsn *ibss_rsn,
                                                const u8 *addr)
 {
@@ -32,6 +38,7 @@ static struct ibss_rsn_peer * ibss_rsn_get_peer(struct ibss_rsn *ibss_rsn,
 
 static void ibss_rsn_free(struct ibss_rsn_peer *peer)
 {
+       eloop_cancel_timeout(ibss_rsn_auth_timeout, peer, NULL);
        wpa_auth_sta_deinit(peer->auth);
        wpa_sm_deinit(peer->supp);
        os_free(peer);
@@ -65,7 +72,7 @@ static int supp_ether_send(void *ctx, const u8 *dest, u16 proto, const u8 *buf,
        if (wpa_s->l2)
                return l2_packet_send(wpa_s->l2, dest, proto, buf, len);
 
-       return wpa_drv_send_eapol(wpa_s, dest, proto, buf, len);
+       return -1;
 }
 
 
@@ -113,6 +120,22 @@ static int supp_get_beacon_ie(void *ctx)
 }
 
 
+static void ibss_check_rsn_completed(struct ibss_rsn_peer *peer)
+{
+       struct wpa_supplicant *wpa_s = peer->ibss_rsn->wpa_s;
+
+       if ((peer->authentication_status &
+            (IBSS_RSN_SET_PTK_SUPP | IBSS_RSN_SET_PTK_AUTH)) !=
+           (IBSS_RSN_SET_PTK_SUPP | IBSS_RSN_SET_PTK_AUTH))
+               return;
+       if (peer->authentication_status & IBSS_RSN_REPORTED_PTK)
+               return;
+       peer->authentication_status |= IBSS_RSN_REPORTED_PTK;
+       wpa_msg(wpa_s, MSG_INFO, IBSS_RSN_COMPLETED MACSTR,
+               MAC2STR(peer->addr));
+}
+
+
 static int supp_set_key(void *ctx, enum wpa_alg alg,
                        const u8 *addr, int key_idx, int set_tx,
                        const u8 *seq, size_t seq_len,
@@ -127,6 +150,8 @@ static int supp_set_key(void *ctx, enum wpa_alg alg,
        wpa_hexdump_key(MSG_DEBUG, "SUPP: set_key - key", key, key_len);
 
        if (key_idx == 0) {
+               peer->authentication_status |= IBSS_RSN_SET_PTK_SUPP;
+               ibss_check_rsn_completed(peer);
                /*
                 * In IBSS RSN, the pairwise key from the 4-way handshake
                 * initiated by the peer with highest MAC address is used.
@@ -205,7 +230,7 @@ static int ibss_rsn_supp_init(struct ibss_rsn_peer *peer, const u8 *own_addr,
        wpa_sm_set_param(peer->supp, WPA_PARAM_PAIRWISE, WPA_CIPHER_CCMP);
        wpa_sm_set_param(peer->supp, WPA_PARAM_GROUP, WPA_CIPHER_CCMP);
        wpa_sm_set_param(peer->supp, WPA_PARAM_KEY_MGMT, WPA_KEY_MGMT_PSK);
-       wpa_sm_set_pmk(peer->supp, psk, PMK_LEN);
+       wpa_sm_set_pmk(peer->supp, psk, PMK_LEN, NULL);
 
        peer->supp_ie_len = sizeof(peer->supp_ie);
        if (wpa_sm_set_assoc_wpa_ie_default(peer->supp, peer->supp_ie,
@@ -232,7 +257,8 @@ static void auth_logger(void *ctx, const u8 *addr, logger_level level,
 }
 
 
-static const u8 * auth_get_psk(void *ctx, const u8 *addr, const u8 *prev_psk)
+static const u8 * auth_get_psk(void *ctx, const u8 *addr,
+                              const u8 *p2p_dev_addr, const u8 *prev_psk)
 {
        struct ibss_rsn *ibss_rsn = ctx;
        wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
@@ -257,7 +283,7 @@ static int auth_send_eapol(void *ctx, const u8 *addr, const u8 *data,
                return l2_packet_send(wpa_s->l2, addr, ETH_P_EAPOL, data,
                                      data_len);
 
-       return wpa_drv_send_eapol(wpa_s, addr, ETH_P_EAPOL, data, data_len);
+       return -1;
 }
 
 
@@ -280,6 +306,15 @@ static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
        wpa_hexdump_key(MSG_DEBUG, "AUTH: set_key - key", key, key_len);
 
        if (idx == 0) {
+               if (addr) {
+                       struct ibss_rsn_peer *peer;
+                       peer = ibss_rsn_get_peer(ibss_rsn, addr);
+                       if (peer) {
+                               peer->authentication_status |=
+                                       IBSS_RSN_SET_PTK_AUTH;
+                               ibss_check_rsn_completed(peer);
+                       }
+               }
                /*
                 * In IBSS RSN, the pairwise key from the 4-way handshake
                 * initiated by the peer with highest MAC address is used.
@@ -410,7 +445,7 @@ static int ibss_rsn_auth_init_group(struct ibss_rsn *ibss_rsn,
 static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn,
                              struct ibss_rsn_peer *peer)
 {
-       peer->auth = wpa_auth_sta_init(ibss_rsn->auth_group, peer->addr);
+       peer->auth = wpa_auth_sta_init(ibss_rsn->auth_group, peer->addr, NULL);
        if (peer->auth == NULL) {
                wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed");
                return -1;
@@ -438,45 +473,152 @@ static int ibss_rsn_auth_init(struct ibss_rsn *ibss_rsn,
 }
 
 
-int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr)
+static int ibss_rsn_send_auth(struct ibss_rsn *ibss_rsn, const u8 *da, int seq)
 {
-       struct ibss_rsn_peer *peer;
+       struct ieee80211_mgmt auth;
+       const size_t auth_length = IEEE80211_HDRLEN + sizeof(auth.u.auth);
+       struct wpa_supplicant *wpa_s = ibss_rsn->wpa_s;
 
-       if (ibss_rsn == NULL)
+       if (wpa_s->driver->send_frame == NULL)
                return -1;
 
-       if (ibss_rsn_get_peer(ibss_rsn, addr)) {
-               wpa_printf(MSG_DEBUG, "RSN: IBSS Authenticator and Supplicant "
-                          "for peer " MACSTR " already running",
-                          MAC2STR(addr));
-               return 0;
+       os_memset(&auth, 0, sizeof(auth));
+
+       auth.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+                                         WLAN_FC_STYPE_AUTH);
+       os_memcpy(auth.da, da, ETH_ALEN);
+       os_memcpy(auth.sa, wpa_s->own_addr, ETH_ALEN);
+       os_memcpy(auth.bssid, wpa_s->bssid, ETH_ALEN);
+
+       auth.u.auth.auth_alg = host_to_le16(WLAN_AUTH_OPEN);
+       auth.u.auth.auth_transaction = host_to_le16(seq);
+       auth.u.auth.status_code = host_to_le16(WLAN_STATUS_SUCCESS);
+
+       wpa_printf(MSG_DEBUG, "RSN: IBSS TX Auth frame (SEQ %d) to " MACSTR,
+                  seq, MAC2STR(da));
+
+       return wpa_s->driver->send_frame(wpa_s->drv_priv, (u8 *) &auth,
+                                        auth_length, 0);
+}
+
+
+static int ibss_rsn_is_auth_started(struct ibss_rsn_peer * peer)
+{
+       return peer->authentication_status &
+              (IBSS_RSN_AUTH_BY_US | IBSS_RSN_AUTH_EAPOL_BY_US);
+}
+
+
+static struct ibss_rsn_peer *
+ibss_rsn_peer_init(struct ibss_rsn *ibss_rsn, const u8 *addr)
+{
+       struct ibss_rsn_peer *peer;
+       if (ibss_rsn == NULL)
+               return NULL;
+
+       peer = ibss_rsn_get_peer(ibss_rsn, addr);
+       if (peer) {
+               wpa_printf(MSG_DEBUG, "RSN: IBSS Supplicant for peer "MACSTR
+                          " already running", MAC2STR(addr));
+               return peer;
        }
 
-       wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Authenticator and "
-                  "Supplicant for peer " MACSTR, MAC2STR(addr));
+       wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Supplicant for peer "MACSTR,
+                  MAC2STR(addr));
 
        peer = os_zalloc(sizeof(*peer));
-       if (peer == NULL)
-               return -1;
+       if (peer == NULL) {
+               wpa_printf(MSG_DEBUG, "RSN: Could not allocate memory.");
+               return NULL;
+       }
 
        peer->ibss_rsn = ibss_rsn;
        os_memcpy(peer->addr, addr, ETH_ALEN);
+       peer->authentication_status = IBSS_RSN_AUTH_NOT_AUTHENTICATED;
 
-       if (ibss_rsn_supp_init(peer, ibss_rsn->wpa_s->own_addr, ibss_rsn->psk)
-           < 0) {
+       if (ibss_rsn_supp_init(peer, ibss_rsn->wpa_s->own_addr,
+                              ibss_rsn->psk) < 0) {
                ibss_rsn_free(peer);
+               return NULL;
+       }
+
+       peer->next = ibss_rsn->peers;
+       ibss_rsn->peers = peer;
+
+       return peer;
+}
+
+
+static void ibss_rsn_auth_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct ibss_rsn_peer *peer = eloop_ctx;
+
+       /*
+        * Assume peer does not support Authentication exchange or the frame was
+        * lost somewhere - start EAPOL Authenticator.
+        */
+       wpa_printf(MSG_DEBUG,
+                  "RSN: Timeout on waiting Authentication frame response from "
+                  MACSTR " - start authenticator", MAC2STR(peer->addr));
+
+       peer->authentication_status |= IBSS_RSN_AUTH_BY_US;
+       ibss_rsn_auth_init(peer->ibss_rsn, peer);
+}
+
+
+int ibss_rsn_start(struct ibss_rsn *ibss_rsn, const u8 *addr)
+{
+       struct ibss_rsn_peer *peer;
+       int res;
+
+       /* if the peer already exists, exit immediately */
+       peer = ibss_rsn_get_peer(ibss_rsn, addr);
+       if (peer)
+               return 0;
+
+       peer = ibss_rsn_peer_init(ibss_rsn, addr);
+       if (peer == NULL)
                return -1;
+
+       /* Open Authentication: send first Authentication frame */
+       res = ibss_rsn_send_auth(ibss_rsn, addr, 1);
+       if (res) {
+               /*
+                * The driver may not support Authentication frame exchange in
+                * IBSS. Ignore authentication and go through EAPOL exchange.
+                */
+               peer->authentication_status |= IBSS_RSN_AUTH_BY_US;
+               return ibss_rsn_auth_init(ibss_rsn, peer);
+       } else {
+               os_get_reltime(&peer->own_auth_tx);
+               eloop_register_timeout(1, 0, ibss_rsn_auth_timeout, peer, NULL);
        }
 
-       if (ibss_rsn_auth_init(ibss_rsn, peer) < 0) {
-               ibss_rsn_free(peer);
+       return 0;
+}
+
+
+static int ibss_rsn_peer_authenticated(struct ibss_rsn *ibss_rsn,
+                                      struct ibss_rsn_peer *peer, int reason)
+{
+       int already_started;
+
+       if (ibss_rsn == NULL || peer == NULL)
                return -1;
+
+       already_started = ibss_rsn_is_auth_started(peer);
+       peer->authentication_status |= reason;
+
+       if (already_started) {
+               wpa_printf(MSG_DEBUG, "RSN: IBSS Authenticator already "
+                          "started for peer " MACSTR, MAC2STR(peer->addr));
+               return 0;
        }
 
-       peer->next = ibss_rsn->peers;
-       ibss_rsn->peers = peer;
+       wpa_printf(MSG_DEBUG, "RSN: Starting IBSS Authenticator "
+                  "for now-authenticated peer " MACSTR, MAC2STR(peer->addr));
 
-       return 0;
+       return ibss_rsn_auth_init(ibss_rsn, peer);
 }
 
 
@@ -617,10 +759,21 @@ static int ibss_rsn_process_rx_eapol(struct ibss_rsn *ibss_rsn,
                return -1;
        os_memcpy(tmp, buf, len);
        if (supp) {
-               wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Supplicant");
+               peer->authentication_status |= IBSS_RSN_AUTH_EAPOL_BY_PEER;
+               wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Supplicant from "
+                          MACSTR, MAC2STR(peer->addr));
                wpa_sm_rx_eapol(peer->supp, peer->addr, tmp, len);
        } else {
-               wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Authenticator");
+               if (ibss_rsn_is_auth_started(peer) == 0) {
+                       wpa_printf(MSG_DEBUG, "RSN: IBSS EAPOL for "
+                                  "Authenticator dropped as " MACSTR " is not "
+                                  "authenticated", MAC2STR(peer->addr));
+                       os_free(tmp);
+                       return -1;
+               }
+
+               wpa_printf(MSG_DEBUG, "RSN: IBSS RX EAPOL for Authenticator "
+                          "from "MACSTR, MAC2STR(peer->addr));
                wpa_receive(ibss_rsn->auth_group, peer->auth, tmp, len);
        }
        os_free(tmp);
@@ -646,8 +799,16 @@ int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
                 * Create new IBSS peer based on an EAPOL message from the peer
                 * Authenticator.
                 */
-               if (ibss_rsn_start(ibss_rsn, src_addr) < 0)
+               peer = ibss_rsn_peer_init(ibss_rsn, src_addr);
+               if (peer == NULL)
                        return -1;
+
+               /* assume the peer is authenticated already */
+               wpa_printf(MSG_DEBUG, "RSN: IBSS Not using IBSS Auth for peer "
+                          MACSTR, MAC2STR(src_addr));
+               ibss_rsn_peer_authenticated(ibss_rsn, peer,
+                                           IBSS_RSN_AUTH_EAPOL_BY_US);
+
                return ibss_rsn_process_rx_eapol(ibss_rsn, ibss_rsn->peers,
                                                 buf, len);
        }
@@ -655,10 +816,101 @@ int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
        return 0;
 }
 
-
 void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk)
 {
        if (ibss_rsn == NULL)
                return;
        os_memcpy(ibss_rsn->psk, psk, PMK_LEN);
 }
+
+
+static void ibss_rsn_handle_auth_1_of_2(struct ibss_rsn *ibss_rsn,
+                                       struct ibss_rsn_peer *peer,
+                                       const u8* addr)
+{
+       wpa_printf(MSG_DEBUG, "RSN: IBSS RX Auth frame (SEQ 1) from " MACSTR,
+                  MAC2STR(addr));
+
+       if (peer &&
+           peer->authentication_status & IBSS_RSN_AUTH_EAPOL_BY_PEER) {
+               if (peer->own_auth_tx.sec) {
+                       struct os_reltime now, diff;
+                       os_get_reltime(&now);
+                       os_reltime_sub(&now, &peer->own_auth_tx, &diff);
+                       if (diff.sec == 0 && diff.usec < 500000) {
+                               wpa_printf(MSG_DEBUG, "RSN: Skip IBSS reinit since only %u usec from own Auth frame TX",
+                                          (int) diff.usec);
+                               goto skip_reinit;
+                       }
+               }
+               /*
+                * A peer sent us an Authentication frame even though it already
+                * started an EAPOL session. We should reinit state machines
+                * here, but it's much more complicated than just deleting and
+                * recreating the state machine
+                */
+               wpa_printf(MSG_DEBUG, "RSN: IBSS Reinitializing station "
+                          MACSTR, MAC2STR(addr));
+
+               ibss_rsn_stop(ibss_rsn, addr);
+               peer = NULL;
+       }
+
+       if (!peer) {
+               peer = ibss_rsn_peer_init(ibss_rsn, addr);
+               if (!peer)
+                       return;
+
+               wpa_printf(MSG_DEBUG, "RSN: IBSS Auth started by peer " MACSTR,
+                          MAC2STR(addr));
+       }
+
+skip_reinit:
+       /* reply with an Authentication frame now, before sending an EAPOL */
+       ibss_rsn_send_auth(ibss_rsn, addr, 2);
+       /* no need to start another AUTH challenge in the other way.. */
+       ibss_rsn_peer_authenticated(ibss_rsn, peer, IBSS_RSN_AUTH_EAPOL_BY_US);
+}
+
+
+void ibss_rsn_handle_auth(struct ibss_rsn *ibss_rsn, const u8 *auth_frame,
+                         size_t len)
+{
+       const struct ieee80211_mgmt *header;
+       struct ibss_rsn_peer *peer;
+       size_t auth_length;
+
+       header = (const struct ieee80211_mgmt *) auth_frame;
+       auth_length = IEEE80211_HDRLEN + sizeof(header->u.auth);
+
+       if (ibss_rsn == NULL || len < auth_length)
+               return;
+
+       if (le_to_host16(header->u.auth.auth_alg) != WLAN_AUTH_OPEN ||
+           le_to_host16(header->u.auth.status_code) != WLAN_STATUS_SUCCESS)
+               return;
+
+       peer = ibss_rsn_get_peer(ibss_rsn, header->sa);
+
+       switch (le_to_host16(header->u.auth.auth_transaction)) {
+       case 1:
+               ibss_rsn_handle_auth_1_of_2(ibss_rsn, peer, header->sa);
+               break;
+       case 2:
+               wpa_printf(MSG_DEBUG, "RSN: IBSS RX Auth frame (SEQ 2) from "
+                          MACSTR, MAC2STR(header->sa));
+               if (!peer) {
+                       wpa_printf(MSG_DEBUG, "RSN: Received Auth seq 2 from "
+                                  "unknown STA " MACSTR, MAC2STR(header->sa));
+                       break;
+               }
+
+               /* authentication has been completed */
+               eloop_cancel_timeout(ibss_rsn_auth_timeout, peer, NULL);
+               wpa_printf(MSG_DEBUG, "RSN: IBSS Auth completed with " MACSTR,
+                          MAC2STR(header->sa));
+               ibss_rsn_peer_authenticated(ibss_rsn, peer,
+                                           IBSS_RSN_AUTH_BY_US);
+               break;
+       }
+}
old mode 100644 (file)
new mode 100755 (executable)
index 1da94ab..67fae2d
 
 struct ibss_rsn;
 
+/* not authenticated */
+#define IBSS_RSN_AUTH_NOT_AUTHENTICATED        0x00
+/* remote peer sent an EAPOL message */
+#define IBSS_RSN_AUTH_EAPOL_BY_PEER    0x01
+/* we sent an AUTH message with seq 1 */
+#define IBSS_RSN_AUTH_BY_US            0x02
+/* we sent an EAPOL message */
+#define IBSS_RSN_AUTH_EAPOL_BY_US      0x04
+/* PTK derived as supplicant */
+#define IBSS_RSN_SET_PTK_SUPP          0x08
+/* PTK derived as authenticator */
+#define IBSS_RSN_SET_PTK_AUTH          0x10
+/* PTK completion reported */
+#define IBSS_RSN_REPORTED_PTK          0x20
+
 struct ibss_rsn_peer {
        struct ibss_rsn_peer *next;
        struct ibss_rsn *ibss_rsn;
@@ -23,6 +38,9 @@ struct ibss_rsn_peer {
        size_t supp_ie_len;
 
        struct wpa_state_machine *auth;
+       int authentication_status;
+
+       struct os_reltime own_auth_tx;
 };
 
 struct ibss_rsn {
@@ -40,5 +58,7 @@ void ibss_rsn_stop(struct ibss_rsn *ibss_rsn, const u8 *peermac);
 int ibss_rsn_rx_eapol(struct ibss_rsn *ibss_rsn, const u8 *src_addr,
                      const u8 *buf, size_t len);
 void ibss_rsn_set_psk(struct ibss_rsn *ibss_rsn, const u8 *psk);
+void ibss_rsn_handle_auth(struct ibss_rsn *ibss_rsn, const u8 *auth_frame,
+                         size_t len);
 
 #endif /* IBSS_RSN_H */
old mode 100644 (file)
new mode 100755 (executable)
index 2f35240..4a39665
@@ -1,6 +1,7 @@
 /*
  * Interworking (IEEE 802.11u)
- * Copyright (c) 2011-2012, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2013, Qualcomm Atheros, Inc.
+ * Copyright (c) 2011-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 #include "eap_common/eap_defs.h"
 #include "eap_peer/eap.h"
 #include "eap_peer/eap_methods.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "rsn_supp/wpa.h"
 #include "wpa_supplicant_i.h"
 #include "config.h"
 #include "config_ssid.h"
 #include "bss.h"
 #include "scan.h"
 #include "notify.h"
+#include "driver_i.h"
 #include "gas_query.h"
 #include "hs20_supplicant.h"
 #include "interworking.h"
 
 static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s);
 static struct wpa_cred * interworking_credentials_available_realm(
-       struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
+       struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+       int *excluded);
 static struct wpa_cred * interworking_credentials_available_3gpp(
-       struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
+       struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+       int *excluded);
+
+
+static int cred_prio_cmp(const struct wpa_cred *a, const struct wpa_cred *b)
+{
+       if (a->priority > b->priority)
+               return 1;
+       if (a->priority < b->priority)
+               return -1;
+       if (a->provisioning_sp == NULL || b->provisioning_sp == NULL ||
+           os_strcmp(a->provisioning_sp, b->provisioning_sp) != 0)
+               return 0;
+       if (a->sp_priority < b->sp_priority)
+               return 1;
+       if (a->sp_priority > b->sp_priority)
+               return -1;
+       return 0;
+}
 
 
 static void interworking_reconnect(struct wpa_supplicant *wpa_s)
 {
+       unsigned int tried;
+
        if (wpa_s->wpa_state >= WPA_AUTHENTICATING) {
                wpa_supplicant_cancel_sched_scan(wpa_s);
+               wpa_s->own_disconnect_req = 1;
                wpa_supplicant_deauthenticate(wpa_s,
                                              WLAN_REASON_DEAUTH_LEAVING);
        }
        wpa_s->disconnected = 0;
        wpa_s->reassociate = 1;
+       tried = wpa_s->interworking_fast_assoc_tried;
+       wpa_s->interworking_fast_assoc_tried = 1;
 
-       if (wpa_supplicant_fast_associate(wpa_s) >= 0)
+       if (!tried && wpa_supplicant_fast_associate(wpa_s) >= 0)
                return;
 
+       wpa_s->interworking_fast_assoc_tried = 0;
        wpa_supplicant_req_scan(wpa_s, 0, 0);
 }
 
@@ -99,6 +128,9 @@ static void interworking_anqp_resp_cb(void *ctx, const u8 *dst,
 {
        struct wpa_supplicant *wpa_s = ctx;
 
+       wpa_printf(MSG_DEBUG, "ANQP: Response callback dst=" MACSTR
+                  " dialog_token=%u result=%d status_code=%u",
+                  MAC2STR(dst), dialog_token, result, status_code);
        anqp_resp_cb(wpa_s, dst, dialog_token, result, adv_proto, resp,
                     status_code);
        interworking_next_anqp_fetch(wpa_s);
@@ -112,6 +144,8 @@ static int cred_with_roaming_consortium(struct wpa_supplicant *wpa_s)
        for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
                if (cred->roaming_consortium_len)
                        return 1;
+               if (cred->required_roaming_consortium_len)
+                       return 1;
        }
        return 0;
 }
@@ -150,13 +184,45 @@ static int cred_with_domain(struct wpa_supplicant *wpa_s)
        struct wpa_cred *cred;
 
        for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
-               if (cred->domain || cred->pcsc || cred->imsi)
+               if (cred->domain || cred->pcsc || cred->imsi ||
+                   cred->roaming_partner)
+                       return 1;
+       }
+       return 0;
+}
+
+
+#ifdef CONFIG_HS20
+
+static int cred_with_min_backhaul(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_cred *cred;
+
+       for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+               if (cred->min_dl_bandwidth_home ||
+                   cred->min_ul_bandwidth_home ||
+                   cred->min_dl_bandwidth_roaming ||
+                   cred->min_ul_bandwidth_roaming)
                        return 1;
        }
        return 0;
 }
 
 
+static int cred_with_conn_capab(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_cred *cred;
+
+       for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
+               if (cred->num_req_conn_capab)
+                       return 1;
+       }
+       return 0;
+}
+
+#endif /* CONFIG_HS20 */
+
+
 static int additional_roaming_consortiums(struct wpa_bss *bss)
 {
        const u8 *ie;
@@ -185,8 +251,8 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
        struct wpabuf *extra = NULL;
        int all = wpa_s->fetch_all_anqp;
 
-       wpa_printf(MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
-                  MAC2STR(bss->bssid));
+       wpa_msg(wpa_s, MSG_DEBUG, "Interworking: ANQP Query Request to " MACSTR,
+               MAC2STR(bss->bssid));
        wpa_s->interworking_gas_bss = bss;
 
        info_ids[num_info_ids++] = ANQP_CAPABILITY_LIST;
@@ -201,8 +267,10 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
                info_ids[num_info_ids++] = ANQP_IP_ADDR_TYPE_AVAILABILITY;
        if (all || cred_with_nai_realm(wpa_s))
                info_ids[num_info_ids++] = ANQP_NAI_REALM;
-       if (all || cred_with_3gpp(wpa_s))
+       if (all || cred_with_3gpp(wpa_s)) {
                info_ids[num_info_ids++] = ANQP_3GPP_CELLULAR_NETWORK;
+               wpa_supplicant_scard_init(wpa_s, NULL);
+       }
        if (all || cred_with_domain(wpa_s))
                info_ids[num_info_ids++] = ANQP_DOMAIN_NAME;
        wpa_hexdump(MSG_DEBUG, "Interworking: ANQP Query info",
@@ -222,13 +290,17 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
                wpabuf_put_u8(extra, HS20_STYPE_QUERY_LIST);
                wpabuf_put_u8(extra, 0); /* Reserved */
                wpabuf_put_u8(extra, HS20_STYPE_CAPABILITY_LIST);
-               if (all) {
+               if (all)
                        wpabuf_put_u8(extra,
                                      HS20_STYPE_OPERATOR_FRIENDLY_NAME);
+               if (all || cred_with_min_backhaul(wpa_s))
                        wpabuf_put_u8(extra, HS20_STYPE_WAN_METRICS);
+               if (all || cred_with_conn_capab(wpa_s))
                        wpabuf_put_u8(extra, HS20_STYPE_CONNECTION_CAPABILITY);
+               if (all)
                        wpabuf_put_u8(extra, HS20_STYPE_OPERATING_CLASS);
-               }
+               if (all)
+                       wpabuf_put_u8(extra, HS20_STYPE_OSU_PROVIDERS_LIST);
                gas_anqp_set_element_len(extra, len_pos);
        }
 #endif /* CONFIG_HS20 */
@@ -241,15 +313,15 @@ static int interworking_anqp_send_req(struct wpa_supplicant *wpa_s,
        res = gas_query_req(wpa_s->gas, bss->bssid, bss->freq, buf,
                            interworking_anqp_resp_cb, wpa_s);
        if (res < 0) {
-               wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+               wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request");
+               wpabuf_free(buf);
                ret = -1;
                eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s,
                                       NULL);
        } else
-               wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
-                          "%u", res);
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "ANQP: Query started with dialog token %u", res);
 
-       wpabuf_free(buf);
        return ret;
 }
 
@@ -442,20 +514,25 @@ static struct nai_realm * nai_realm_parse(struct wpabuf *anqp, u16 *count)
        struct nai_realm *realm;
        const u8 *pos, *end;
        u16 i, num;
+       size_t left;
 
-       if (anqp == NULL || wpabuf_len(anqp) < 2)
+       if (anqp == NULL)
+               return NULL;
+       left = wpabuf_len(anqp);
+       if (left < 2)
                return NULL;
 
        pos = wpabuf_head_u8(anqp);
-       end = pos + wpabuf_len(anqp);
+       end = pos + left;
        num = WPA_GET_LE16(pos);
        wpa_printf(MSG_DEBUG, "NAI Realm Count: %u", num);
        pos += 2;
+       left -= 2;
 
-       if (num * 5 > end - pos) {
+       if (num > left / 5) {
                wpa_printf(MSG_DEBUG, "Invalid NAI Realm Count %u - not "
                           "enough data (%u octets) for that many realms",
-                          num, (unsigned int) (end - pos));
+                          num, (unsigned int) left);
                return NULL;
        }
 
@@ -511,55 +588,91 @@ static int nai_realm_match(struct nai_realm *realm, const char *home_realm)
 }
 
 
-static int nai_realm_cred_username(struct nai_realm_eap *eap)
+static int nai_realm_cred_username(struct wpa_supplicant *wpa_s,
+                                  struct nai_realm_eap *eap)
 {
-       if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL)
+       if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL) {
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "nai-realm-cred-username: EAP method not supported: %d",
+                       eap->method);
                return 0; /* method not supported */
+       }
 
-       if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP) {
+       if (eap->method != EAP_TYPE_TTLS && eap->method != EAP_TYPE_PEAP &&
+           eap->method != EAP_TYPE_FAST) {
                /* Only tunneled methods with username/password supported */
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "nai-realm-cred-username: Method: %d is not TTLS, PEAP, or FAST",
+                       eap->method);
                return 0;
        }
 
-       if (eap->method == EAP_TYPE_PEAP) {
+       if (eap->method == EAP_TYPE_PEAP || eap->method == EAP_TYPE_FAST) {
                if (eap->inner_method &&
-                   eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
+                   eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) {
+                       wpa_msg(wpa_s, MSG_DEBUG,
+                               "nai-realm-cred-username: PEAP/FAST: Inner method not supported: %d",
+                               eap->inner_method);
                        return 0;
+               }
                if (!eap->inner_method &&
-                   eap_get_name(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2) == NULL)
+                   eap_get_name(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2) == NULL) {
+                       wpa_msg(wpa_s, MSG_DEBUG,
+                               "nai-realm-cred-username: MSCHAPv2 not supported");
                        return 0;
+               }
        }
 
        if (eap->method == EAP_TYPE_TTLS) {
                if (eap->inner_method == 0 && eap->inner_non_eap == 0)
                        return 1; /* Assume TTLS/MSCHAPv2 is used */
                if (eap->inner_method &&
-                   eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL)
+                   eap_get_name(EAP_VENDOR_IETF, eap->inner_method) == NULL) {
+                       wpa_msg(wpa_s, MSG_DEBUG,
+                               "nai-realm-cred-username: TTLS, but inner not supported: %d",
+                               eap->inner_method);
                        return 0;
+               }
                if (eap->inner_non_eap &&
                    eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_PAP &&
                    eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_CHAP &&
                    eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAP &&
-                   eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAPV2)
+                   eap->inner_non_eap != NAI_REALM_INNER_NON_EAP_MSCHAPV2) {
+                       wpa_msg(wpa_s, MSG_DEBUG,
+                               "nai-realm-cred-username: TTLS, inner-non-eap not supported: %d",
+                               eap->inner_non_eap);
                        return 0;
+               }
        }
 
        if (eap->inner_method &&
            eap->inner_method != EAP_TYPE_GTC &&
-           eap->inner_method != EAP_TYPE_MSCHAPV2)
+           eap->inner_method != EAP_TYPE_MSCHAPV2) {
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "nai-realm-cred-username: inner-method not GTC or MSCHAPv2: %d",
+                       eap->inner_method);
                return 0;
+       }
 
        return 1;
 }
 
 
-static int nai_realm_cred_cert(struct nai_realm_eap *eap)
+static int nai_realm_cred_cert(struct wpa_supplicant *wpa_s,
+                              struct nai_realm_eap *eap)
 {
-       if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL)
+       if (eap_get_name(EAP_VENDOR_IETF, eap->method) == NULL) {
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "nai-realm-cred-cert: Method not supported: %d",
+                       eap->method);
                return 0; /* method not supported */
+       }
 
        if (eap->method != EAP_TYPE_TLS) {
                /* Only EAP-TLS supported for credential authentication */
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "nai-realm-cred-cert: Method not TLS: %d",
+                       eap->method);
                return 0;
        }
 
@@ -567,27 +680,33 @@ static int nai_realm_cred_cert(struct nai_realm_eap *eap)
 }
 
 
-static struct nai_realm_eap * nai_realm_find_eap(struct wpa_cred *cred,
+static struct nai_realm_eap * nai_realm_find_eap(struct wpa_supplicant *wpa_s,
+                                                struct wpa_cred *cred,
                                                 struct nai_realm *realm)
 {
        u8 e;
 
-       if (cred == NULL ||
-           cred->username == NULL ||
+       if (cred->username == NULL ||
            cred->username[0] == '\0' ||
            ((cred->password == NULL ||
              cred->password[0] == '\0') &&
             (cred->private_key == NULL ||
-             cred->private_key[0] == '\0')))
+             cred->private_key[0] == '\0'))) {
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "nai-realm-find-eap: incomplete cred info: username: %s  password: %s private_key: %s",
+                       cred->username ? cred->username : "NULL",
+                       cred->password ? cred->password : "NULL",
+                       cred->private_key ? cred->private_key : "NULL");
                return NULL;
+       }
 
        for (e = 0; e < realm->eap_count; e++) {
                struct nai_realm_eap *eap = &realm->eap[e];
                if (cred->password && cred->password[0] &&
-                   nai_realm_cred_username(eap))
+                   nai_realm_cred_username(wpa_s, eap))
                        return eap;
                if (cred->private_key && cred->private_key[0] &&
-                   nai_realm_cred_cert(eap))
+                   nai_realm_cred_cert(wpa_s, eap))
                        return eap;
        }
 
@@ -599,19 +718,29 @@ static struct nai_realm_eap * nai_realm_find_eap(struct wpa_cred *cred,
 
 static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len)
 {
-       u8 plmn[3];
+       u8 plmn[3], plmn2[3];
        const u8 *pos, *end;
        u8 udhl;
 
-       /* See Annex A of 3GPP TS 24.234 v8.1.0 for description */
+       /*
+        * See Annex A of 3GPP TS 24.234 v8.1.0 for description. The network
+        * operator is allowed to include only two digits of the MNC, so allow
+        * matches based on both two and three digit MNC assumptions. Since some
+        * SIM/USIM cards may not expose MNC length conveniently, we may be
+        * provided the default MNC length 3 here and as such, checking with MNC
+        * length 2 is justifiable even though 3GPP TS 24.234 does not mention
+        * that case. Anyway, MCC/MNC pair where both 2 and 3 digit MNC is used
+        * with otherwise matching values would not be good idea in general, so
+        * this should not result in selecting incorrect networks.
+        */
+       /* Match with 3 digit MNC */
        plmn[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4);
-       plmn[1] = imsi[2] - '0';
-       /* default to MNC length 3 if unknown */
-       if (mnc_len != 2)
-               plmn[1] |= (imsi[5] - '0') << 4;
-       else
-               plmn[1] |= 0xf0;
+       plmn[1] = (imsi[2] - '0') | ((imsi[5] - '0') << 4);
        plmn[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4);
+       /* Match with 2 digit MNC */
+       plmn2[0] = (imsi[0] - '0') | ((imsi[1] - '0') << 4);
+       plmn2[1] = (imsi[2] - '0') | 0xf0;
+       plmn2[2] = (imsi[3] - '0') | ((imsi[4] - '0') << 4);
 
        if (anqp == NULL)
                return 0;
@@ -631,6 +760,10 @@ static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len)
        }
        end = pos + udhl;
 
+       wpa_printf(MSG_DEBUG, "Interworking: Matching against MCC/MNC alternatives: %02x:%02x:%02x or %02x:%02x:%02x (IMSI %s, MNC length %d)",
+                  plmn[0], plmn[1], plmn[2], plmn2[0], plmn2[1], plmn2[2],
+                  imsi, mnc_len);
+
        while (pos + 2 <= end) {
                u8 iei, len;
                const u8 *l_end;
@@ -643,14 +776,20 @@ static int plmn_id_match(struct wpabuf *anqp, const char *imsi, int mnc_len)
                if (iei == 0 && len > 0) {
                        /* PLMN List */
                        u8 num, i;
+                       wpa_hexdump(MSG_DEBUG, "Interworking: PLMN List information element",
+                                   pos, len);
                        num = *pos++;
                        for (i = 0; i < num; i++) {
-                               if (pos + 3 > end)
+                               if (pos + 3 > l_end)
                                        break;
-                               if (os_memcmp(pos, plmn, 3) == 0)
+                               if (os_memcmp(pos, plmn, 3) == 0 ||
+                                   os_memcmp(pos, plmn2, 3) == 0)
                                        return 1; /* Found matching PLMN */
                                pos += 3;
                        }
+               } else {
+                       wpa_hexdump(MSG_DEBUG, "Interworking: Unrecognized 3GPP information element",
+                                   pos, len);
                }
 
                pos = l_end;
@@ -709,8 +848,8 @@ static int build_root_nai(char *nai, size_t nai_len, const char *imsi,
                *pos++ = imsi[4];
                *pos++ = imsi[5];
        }
-       pos += os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org",
-                          imsi[0], imsi[1], imsi[2]);
+       os_snprintf(pos, end - pos, ".mcc%c%c%c.3gppnetwork.org",
+                   imsi[0], imsi[1], imsi[2]);
 
        return 0;
 }
@@ -727,12 +866,86 @@ static int set_root_nai(struct wpa_ssid *ssid, const char *imsi, char prefix)
 #endif /* INTERWORKING_3GPP */
 
 
+static int already_connected(struct wpa_supplicant *wpa_s,
+                            struct wpa_cred *cred, struct wpa_bss *bss)
+{
+       struct wpa_ssid *ssid, *sel_ssid;
+       struct wpa_bss *selected;
+
+       if (wpa_s->wpa_state < WPA_ASSOCIATED || wpa_s->current_ssid == NULL)
+               return 0;
+
+       ssid = wpa_s->current_ssid;
+       if (ssid->parent_cred != cred)
+               return 0;
+
+       if (ssid->ssid_len != bss->ssid_len ||
+           os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) != 0)
+               return 0;
+
+       sel_ssid = NULL;
+       selected = wpa_supplicant_pick_network(wpa_s, &sel_ssid);
+       if (selected && sel_ssid && sel_ssid->priority > ssid->priority)
+               return 0; /* higher priority network in scan results */
+
+       return 1;
+}
+
+
+static void remove_duplicate_network(struct wpa_supplicant *wpa_s,
+                                    struct wpa_cred *cred,
+                                    struct wpa_bss *bss)
+{
+       struct wpa_ssid *ssid;
+
+       for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+               if (ssid->parent_cred != cred)
+                       continue;
+               if (ssid->ssid_len != bss->ssid_len ||
+                   os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) != 0)
+                       continue;
+
+               break;
+       }
+
+       if (ssid == NULL)
+               return;
+
+       wpa_printf(MSG_DEBUG, "Interworking: Remove duplicate network entry for the same credential");
+
+       if (ssid == wpa_s->current_ssid) {
+               wpa_sm_set_config(wpa_s->wpa, NULL);
+               eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+               wpa_s->own_disconnect_req = 1;
+               wpa_supplicant_deauthenticate(wpa_s,
+                                             WLAN_REASON_DEAUTH_LEAVING);
+       }
+
+       wpas_notify_network_removed(wpa_s, ssid);
+       wpa_config_remove_network(wpa_s->conf, ssid->id);
+}
+
+
 static int interworking_set_hs20_params(struct wpa_supplicant *wpa_s,
                                        struct wpa_ssid *ssid)
 {
-       if (wpa_config_set(ssid, "key_mgmt",
-                          wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ?
-                          "WPA-EAP WPA-EAP-SHA256" : "WPA-EAP", 0) < 0)
+       const char *key_mgmt = NULL;
+#ifdef CONFIG_IEEE80211R
+       int res;
+       struct wpa_driver_capa capa;
+
+       res = wpa_drv_get_capa(wpa_s, &capa);
+       if (res == 0 && capa.key_mgmt & WPA_DRIVER_CAPA_KEY_MGMT_FT) {
+               key_mgmt = wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ?
+                       "WPA-EAP WPA-EAP-SHA256 FT-EAP" :
+                       "WPA-EAP FT-EAP";
+       }
+#endif /* CONFIG_IEEE80211R */
+
+       if (!key_mgmt)
+               key_mgmt = wpa_s->conf->pmf != NO_MGMT_FRAME_PROTECTION ?
+                       "WPA-EAP WPA-EAP-SHA256" : "WPA-EAP";
+       if (wpa_config_set(ssid, "key_mgmt", key_mgmt, 0) < 0)
                return -1;
        if (wpa_config_set(ssid, "proto", "RSN", 0) < 0)
                return -1;
@@ -744,11 +957,10 @@ static int interworking_set_hs20_params(struct wpa_supplicant *wpa_s,
 
 static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
                                     struct wpa_cred *cred,
-                                    struct wpa_bss *bss)
+                                    struct wpa_bss *bss, int only_add)
 {
 #ifdef INTERWORKING_3GPP
        struct wpa_ssid *ssid;
-       const u8 *ie;
        int eap_type;
        int res;
        char prefix;
@@ -756,11 +968,16 @@ static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
        if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
                return -1;
 
-       ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
-       if (ie == NULL)
-               return -1;
-       wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " (3GPP)",
-                  MAC2STR(bss->bssid));
+       wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR
+               " (3GPP)", MAC2STR(bss->bssid));
+
+       if (already_connected(wpa_s, cred, bss)) {
+               wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
+                       MAC2STR(bss->bssid));
+               return wpa_s->current_ssid->id;
+       }
+
+       remove_duplicate_network(wpa_s, cred, bss);
 
        ssid = wpa_config_add_network(wpa_s->conf);
        if (ssid == NULL)
@@ -771,11 +988,12 @@ static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
        wpa_config_set_network_defaults(ssid);
        ssid->priority = cred->priority;
        ssid->temporary = 1;
-       ssid->ssid = os_zalloc(ie[1] + 1);
+       ssid->ssid = os_zalloc(bss->ssid_len + 1);
        if (ssid->ssid == NULL)
                goto fail;
-       os_memcpy(ssid->ssid, ie + 2, ie[1]);
-       ssid->ssid_len = ie[1];
+       os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
+       ssid->ssid_len = bss->ssid_len;
+       ssid->eap.sim_num = cred->sim_num;
 
        if (interworking_set_hs20_params(wpa_s, ssid) < 0)
                goto fail;
@@ -808,13 +1026,13 @@ static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
                break;
        }
        if (res < 0) {
-               wpa_printf(MSG_DEBUG, "Selected EAP method (%d) not supported",
-                          eap_type);
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "Selected EAP method (%d) not supported", eap_type);
                goto fail;
        }
 
        if (!cred->pcsc && set_root_nai(ssid, cred->imsi, prefix) < 0) {
-               wpa_printf(MSG_DEBUG, "Failed to set Root NAI");
+               wpa_msg(wpa_s, MSG_DEBUG, "Failed to set Root NAI");
                goto fail;
        }
 
@@ -831,14 +1049,12 @@ static int interworking_connect_3gpp(struct wpa_supplicant *wpa_s,
                        goto fail;
        }
 
-       if (cred->password && cred->password[0] &&
-           wpa_config_set_quoted(ssid, "password", cred->password) < 0)
-               goto fail;
-
+       wpa_s->next_ssid = ssid;
        wpa_config_update_prio_list(wpa_s->conf);
-       interworking_reconnect(wpa_s);
+       if (!only_add)
+               interworking_reconnect(wpa_s);
 
-       return 0;
+       return ssid->id;
 
 fail:
        wpas_notify_network_removed(wpa_s, ssid);
@@ -924,6 +1140,27 @@ static int roaming_consortium_match(const u8 *ie, const struct wpabuf *anqp,
 }
 
 
+static int cred_no_required_oi_match(struct wpa_cred *cred, struct wpa_bss *bss)
+{
+       const u8 *ie;
+
+       if (cred->required_roaming_consortium_len == 0)
+               return 0;
+
+       ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
+
+       if (ie == NULL &&
+           (bss->anqp == NULL || bss->anqp->roaming_consortium == NULL))
+               return 1;
+
+       return !roaming_consortium_match(ie,
+                                        bss->anqp ?
+                                        bss->anqp->roaming_consortium : NULL,
+                                        cred->required_roaming_consortium,
+                                        cred->required_roaming_consortium_len);
+}
+
+
 static int cred_excluded_ssid(struct wpa_cred *cred, struct wpa_bss *bss)
 {
        size_t i;
@@ -942,11 +1179,164 @@ static int cred_excluded_ssid(struct wpa_cred *cred, struct wpa_bss *bss)
 }
 
 
+static int cred_below_min_backhaul(struct wpa_supplicant *wpa_s,
+                                  struct wpa_cred *cred, struct wpa_bss *bss)
+{
+       int res;
+       unsigned int dl_bandwidth, ul_bandwidth;
+       const u8 *wan;
+       u8 wan_info, dl_load, ul_load;
+       u16 lmd;
+       u32 ul_speed, dl_speed;
+
+       if (!cred->min_dl_bandwidth_home &&
+           !cred->min_ul_bandwidth_home &&
+           !cred->min_dl_bandwidth_roaming &&
+           !cred->min_ul_bandwidth_roaming)
+               return 0; /* No bandwidth constraint specified */
+
+       if (bss->anqp == NULL || bss->anqp->hs20_wan_metrics == NULL)
+               return 0; /* No WAN Metrics known - ignore constraint */
+
+       wan = wpabuf_head(bss->anqp->hs20_wan_metrics);
+       wan_info = wan[0];
+       if (wan_info & BIT(3))
+               return 1; /* WAN link at capacity */
+       lmd = WPA_GET_LE16(wan + 11);
+       if (lmd == 0)
+               return 0; /* Downlink/Uplink Load was not measured */
+       dl_speed = WPA_GET_LE32(wan + 1);
+       ul_speed = WPA_GET_LE32(wan + 5);
+       dl_load = wan[9];
+       ul_load = wan[10];
+
+       if (dl_speed >= 0xffffff)
+               dl_bandwidth = dl_speed / 255 * (255 - dl_load);
+       else
+               dl_bandwidth = dl_speed * (255 - dl_load) / 255;
+
+       if (ul_speed >= 0xffffff)
+               ul_bandwidth = ul_speed / 255 * (255 - ul_load);
+       else
+               ul_bandwidth = ul_speed * (255 - ul_load) / 255;
+
+       res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
+                                       bss->anqp->domain_name : NULL);
+       if (res > 0) {
+               if (cred->min_dl_bandwidth_home > dl_bandwidth)
+                       return 1;
+               if (cred->min_ul_bandwidth_home > ul_bandwidth)
+                       return 1;
+       } else {
+               if (cred->min_dl_bandwidth_roaming > dl_bandwidth)
+                       return 1;
+               if (cred->min_ul_bandwidth_roaming > ul_bandwidth)
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+static int cred_over_max_bss_load(struct wpa_supplicant *wpa_s,
+                                 struct wpa_cred *cred, struct wpa_bss *bss)
+{
+       const u8 *ie;
+       int res;
+
+       if (!cred->max_bss_load)
+               return 0; /* No BSS Load constraint specified */
+
+       ie = wpa_bss_get_ie(bss, WLAN_EID_BSS_LOAD);
+       if (ie == NULL || ie[1] < 3)
+               return 0; /* No BSS Load advertised */
+
+       res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
+                                       bss->anqp->domain_name : NULL);
+       if (res <= 0)
+               return 0; /* Not a home network */
+
+       return ie[4] > cred->max_bss_load;
+}
+
+
+static int has_proto_match(const u8 *pos, const u8 *end, u8 proto)
+{
+       while (pos + 4 <= end) {
+               if (pos[0] == proto && pos[3] == 1 /* Open */)
+                       return 1;
+               pos += 4;
+       }
+
+       return 0;
+}
+
+
+static int has_proto_port_match(const u8 *pos, const u8 *end, u8 proto,
+                               u16 port)
+{
+       while (pos + 4 <= end) {
+               if (pos[0] == proto && WPA_GET_LE16(&pos[1]) == port &&
+                   pos[3] == 1 /* Open */)
+                       return 1;
+               pos += 4;
+       }
+
+       return 0;
+}
+
+
+static int cred_conn_capab_missing(struct wpa_supplicant *wpa_s,
+                                  struct wpa_cred *cred, struct wpa_bss *bss)
+{
+       int res;
+       const u8 *capab, *end;
+       unsigned int i, j;
+       int *ports;
+
+       if (!cred->num_req_conn_capab)
+               return 0; /* No connection capability constraint specified */
+
+       if (bss->anqp == NULL || bss->anqp->hs20_connection_capability == NULL)
+               return 0; /* No Connection Capability known - ignore constraint
+                          */
+
+       res = interworking_home_sp_cred(wpa_s, cred, bss->anqp ?
+                                       bss->anqp->domain_name : NULL);
+       if (res > 0)
+               return 0; /* No constraint in home network */
+
+       capab = wpabuf_head(bss->anqp->hs20_connection_capability);
+       end = capab + wpabuf_len(bss->anqp->hs20_connection_capability);
+
+       for (i = 0; i < cred->num_req_conn_capab; i++) {
+               ports = cred->req_conn_capab_port[i];
+               if (!ports) {
+                       if (!has_proto_match(capab, end,
+                                            cred->req_conn_capab_proto[i]))
+                               return 1;
+               } else {
+                       for (j = 0; ports[j] > -1; j++) {
+                               if (!has_proto_port_match(
+                                           capab, end,
+                                           cred->req_conn_capab_proto[i],
+                                           ports[j]))
+                                       return 1;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+
 static struct wpa_cred * interworking_credentials_available_roaming_consortium(
-       struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+       struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+       int *excluded)
 {
        struct wpa_cred *cred, *selected = NULL;
        const u8 *ie;
+       int is_excluded = 0;
 
        ie = wpa_bss_get_ie(bss, WLAN_EID_ROAMING_CONSORTIUM);
 
@@ -969,14 +1359,33 @@ static struct wpa_cred * interworking_credentials_available_roaming_consortium(
                                              cred->roaming_consortium_len))
                        continue;
 
-               if (cred_excluded_ssid(cred, bss))
+               if (cred_no_required_oi_match(cred, bss))
                        continue;
-
-               if (selected == NULL ||
-                   selected->priority < cred->priority)
-                       selected = cred;
+               if (!ignore_bw && cred_below_min_backhaul(wpa_s, cred, bss))
+                       continue;
+               if (!ignore_bw && cred_over_max_bss_load(wpa_s, cred, bss))
+                       continue;
+               if (!ignore_bw && cred_conn_capab_missing(wpa_s, cred, bss))
+                       continue;
+               if (cred_excluded_ssid(cred, bss)) {
+                       if (excluded == NULL)
+                               continue;
+                       if (selected == NULL) {
+                               selected = cred;
+                               is_excluded = 1;
+                       }
+               } else {
+                       if (selected == NULL || is_excluded ||
+                           cred_prio_cmp(selected, cred) < 0) {
+                               selected = cred;
+                               is_excluded = 0;
+                       }
+               }
        }
 
+       if (excluded)
+               *excluded = is_excluded;
+
        return selected;
 }
 
@@ -1080,18 +1489,33 @@ static int interworking_set_eap_params(struct wpa_ssid *ssid,
            wpa_config_set_quoted(ssid, "ca_cert", cred->ca_cert) < 0)
                return -1;
 
+       if (cred->domain_suffix_match && cred->domain_suffix_match[0] &&
+           wpa_config_set_quoted(ssid, "domain_suffix_match",
+                                 cred->domain_suffix_match) < 0)
+               return -1;
+
+       ssid->eap.ocsp = cred->ocsp;
+
        return 0;
 }
 
 
 static int interworking_connect_roaming_consortium(
        struct wpa_supplicant *wpa_s, struct wpa_cred *cred,
-       struct wpa_bss *bss, const u8 *ssid_ie)
+       struct wpa_bss *bss, int only_add)
 {
        struct wpa_ssid *ssid;
 
-       wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR " based on "
-                  "roaming consortium match", MAC2STR(bss->bssid));
+       wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR
+               " based on roaming consortium match", MAC2STR(bss->bssid));
+
+       if (already_connected(wpa_s, cred, bss)) {
+               wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
+                       MAC2STR(bss->bssid));
+               return wpa_s->current_ssid->id;
+       }
+
+       remove_duplicate_network(wpa_s, cred, bss);
 
        ssid = wpa_config_add_network(wpa_s->conf);
        if (ssid == NULL)
@@ -1101,18 +1525,18 @@ static int interworking_connect_roaming_consortium(
        wpa_config_set_network_defaults(ssid);
        ssid->priority = cred->priority;
        ssid->temporary = 1;
-       ssid->ssid = os_zalloc(ssid_ie[1] + 1);
+       ssid->ssid = os_zalloc(bss->ssid_len + 1);
        if (ssid->ssid == NULL)
                goto fail;
-       os_memcpy(ssid->ssid, ssid_ie + 2, ssid_ie[1]);
-       ssid->ssid_len = ssid_ie[1];
+       os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
+       ssid->ssid_len = bss->ssid_len;
 
        if (interworking_set_hs20_params(wpa_s, ssid) < 0)
                goto fail;
 
        if (cred->eap_method == NULL) {
-               wpa_printf(MSG_DEBUG, "Interworking: No EAP method set for "
-                          "credential using roaming consortium");
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "Interworking: No EAP method set for credential using roaming consortium");
                goto fail;
        }
 
@@ -1122,10 +1546,12 @@ static int interworking_connect_roaming_consortium(
                    cred->eap_method->method == EAP_TYPE_TTLS) < 0)
                goto fail;
 
+       wpa_s->next_ssid = ssid;
        wpa_config_update_prio_list(wpa_s->conf);
-       interworking_reconnect(wpa_s);
+       if (!only_add)
+               interworking_reconnect(wpa_s);
 
-       return 0;
+       return ssid->id;
 
 fail:
        wpas_notify_network_removed(wpa_s, ssid);
@@ -1134,7 +1560,9 @@ fail:
 }
 
 
-int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+static int interworking_connect_helper(struct wpa_supplicant *wpa_s,
+                                      struct wpa_bss *bss, int allow_excluded,
+                                      int only_add)
 {
        struct wpa_cred *cred, *cred_rc, *cred_3gpp;
        struct wpa_ssid *ssid;
@@ -1142,91 +1570,151 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
        struct nai_realm_eap *eap = NULL;
        u16 count, i;
        char buf[100];
-       const u8 *ie;
+       int excluded = 0, *excl = allow_excluded ? &excluded : NULL;
+       const char *name;
 
        if (wpa_s->conf->cred == NULL || bss == NULL)
                return -1;
-       ie = wpa_bss_get_ie(bss, WLAN_EID_SSID);
-       if (ie == NULL || ie[1] == 0) {
-               wpa_printf(MSG_DEBUG, "Interworking: No SSID known for "
-                          MACSTR, MAC2STR(bss->bssid));
+       if (disallowed_bssid(wpa_s, bss->bssid) ||
+           disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "Interworking: Reject connection to disallowed BSS "
+                       MACSTR, MAC2STR(bss->bssid));
                return -1;
        }
 
+       wpa_printf(MSG_DEBUG, "Interworking: Considering BSS " MACSTR
+                  " for connection (allow_excluded=%d)",
+                  MAC2STR(bss->bssid), allow_excluded);
+
        if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
                /*
                 * We currently support only HS 2.0 networks and those are
                 * required to use WPA2-Enterprise.
                 */
-               wpa_printf(MSG_DEBUG, "Interworking: Network does not use "
-                          "RSN");
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "Interworking: Network does not use RSN");
                return -1;
        }
 
-       cred_rc = interworking_credentials_available_roaming_consortium(wpa_s,
-                                                                       bss);
+       cred_rc = interworking_credentials_available_roaming_consortium(
+               wpa_s, bss, 0, excl);
        if (cred_rc) {
-               wpa_printf(MSG_DEBUG, "Interworking: Highest roaming "
-                          "consortium matching credential priority %d",
-                          cred_rc->priority);
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "Interworking: Highest roaming consortium matching credential priority %d sp_priority %d",
+                       cred_rc->priority, cred_rc->sp_priority);
+               if (allow_excluded && excl && !(*excl))
+                       excl = NULL;
        }
 
-       cred = interworking_credentials_available_realm(wpa_s, bss);
+       cred = interworking_credentials_available_realm(wpa_s, bss, 0, excl);
        if (cred) {
-               wpa_printf(MSG_DEBUG, "Interworking: Highest NAI Realm list "
-                          "matching credential priority %d",
-                          cred->priority);
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "Interworking: Highest NAI Realm list matching credential priority %d sp_priority %d",
+                       cred->priority, cred->sp_priority);
+               if (allow_excluded && excl && !(*excl))
+                       excl = NULL;
        }
 
-       cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss);
+       cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss, 0,
+                                                           excl);
        if (cred_3gpp) {
-               wpa_printf(MSG_DEBUG, "Interworking: Highest 3GPP matching "
-                          "credential priority %d", cred_3gpp->priority);
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "Interworking: Highest 3GPP matching credential priority %d sp_priority %d",
+                       cred_3gpp->priority, cred_3gpp->sp_priority);
+               if (allow_excluded && excl && !(*excl))
+                       excl = NULL;
+       }
+
+       if (!cred_rc && !cred && !cred_3gpp) {
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "Interworking: No full credential matches - consider options without BW(etc.) limits");
+               cred_rc = interworking_credentials_available_roaming_consortium(
+                       wpa_s, bss, 1, excl);
+               if (cred_rc) {
+                       wpa_msg(wpa_s, MSG_DEBUG,
+                               "Interworking: Highest roaming consortium matching credential priority %d sp_priority %d (ignore BW)",
+                               cred_rc->priority, cred_rc->sp_priority);
+                       if (allow_excluded && excl && !(*excl))
+                               excl = NULL;
+               }
+
+               cred = interworking_credentials_available_realm(wpa_s, bss, 1,
+                                                               excl);
+               if (cred) {
+                       wpa_msg(wpa_s, MSG_DEBUG,
+                               "Interworking: Highest NAI Realm list matching credential priority %d sp_priority %d (ignore BW)",
+                               cred->priority, cred->sp_priority);
+                       if (allow_excluded && excl && !(*excl))
+                               excl = NULL;
+               }
+
+               cred_3gpp = interworking_credentials_available_3gpp(wpa_s, bss,
+                                                                   1, excl);
+               if (cred_3gpp) {
+                       wpa_msg(wpa_s, MSG_DEBUG,
+                               "Interworking: Highest 3GPP matching credential priority %d sp_priority %d (ignore BW)",
+                               cred_3gpp->priority, cred_3gpp->sp_priority);
+                       if (allow_excluded && excl && !(*excl))
+                               excl = NULL;
+               }
        }
 
        if (cred_rc &&
-           (cred == NULL || cred_rc->priority >= cred->priority) &&
-           (cred_3gpp == NULL || cred_rc->priority >= cred_3gpp->priority))
+           (cred == NULL || cred_prio_cmp(cred_rc, cred) >= 0) &&
+           (cred_3gpp == NULL || cred_prio_cmp(cred_rc, cred_3gpp) >= 0))
                return interworking_connect_roaming_consortium(wpa_s, cred_rc,
-                                                              bss, ie);
+                                                              bss, only_add);
 
        if (cred_3gpp &&
-           (cred == NULL || cred_3gpp->priority >= cred->priority)) {
-               return interworking_connect_3gpp(wpa_s, cred_3gpp, bss);
+           (cred == NULL || cred_prio_cmp(cred_3gpp, cred) >= 0)) {
+               return interworking_connect_3gpp(wpa_s, cred_3gpp, bss,
+                                                only_add);
        }
 
        if (cred == NULL) {
-               wpa_printf(MSG_DEBUG, "Interworking: No matching credentials "
-                          "found for " MACSTR, MAC2STR(bss->bssid));
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "Interworking: No matching credentials found for "
+                       MACSTR, MAC2STR(bss->bssid));
                return -1;
        }
 
        realm = nai_realm_parse(bss->anqp ? bss->anqp->nai_realm : NULL,
                                &count);
        if (realm == NULL) {
-               wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
-                          "Realm list from " MACSTR, MAC2STR(bss->bssid));
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "Interworking: Could not parse NAI Realm list from "
+                       MACSTR, MAC2STR(bss->bssid));
                return -1;
        }
 
        for (i = 0; i < count; i++) {
                if (!nai_realm_match(&realm[i], cred->realm))
                        continue;
-               eap = nai_realm_find_eap(cred, &realm[i]);
+               eap = nai_realm_find_eap(wpa_s, cred, &realm[i]);
                if (eap)
                        break;
        }
 
        if (!eap) {
-               wpa_printf(MSG_DEBUG, "Interworking: No matching credentials "
-                          "and EAP method found for " MACSTR,
-                          MAC2STR(bss->bssid));
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "Interworking: No matching credentials and EAP method found for "
+                       MACSTR, MAC2STR(bss->bssid));
                nai_realm_free(realm, count);
                return -1;
        }
 
-       wpa_printf(MSG_DEBUG, "Interworking: Connect with " MACSTR,
-                  MAC2STR(bss->bssid));
+       wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Connect with " MACSTR,
+               MAC2STR(bss->bssid));
+
+       if (already_connected(wpa_s, cred, bss)) {
+               wpa_msg(wpa_s, MSG_INFO, INTERWORKING_ALREADY_CONNECTED MACSTR,
+                       MAC2STR(bss->bssid));
+               nai_realm_free(realm, count);
+               return 0;
+       }
+
+       remove_duplicate_network(wpa_s, cred, bss);
 
        ssid = wpa_config_add_network(wpa_s->conf);
        if (ssid == NULL) {
@@ -1238,11 +1726,11 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
        wpa_config_set_network_defaults(ssid);
        ssid->priority = cred->priority;
        ssid->temporary = 1;
-       ssid->ssid = os_zalloc(ie[1] + 1);
+       ssid->ssid = os_zalloc(bss->ssid_len + 1);
        if (ssid->ssid == NULL)
                goto fail;
-       os_memcpy(ssid->ssid, ie + 2, ie[1]);
-       ssid->ssid_len = ie[1];
+       os_memcpy(ssid->ssid, bss->ssid, bss->ssid_len);
+       ssid->ssid_len = bss->ssid_len;
 
        if (interworking_set_hs20_params(wpa_s, ssid) < 0)
                goto fail;
@@ -1291,11 +1779,19 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
                }
                break;
        case EAP_TYPE_PEAP:
-               os_snprintf(buf, sizeof(buf), "\"auth=%s\"",
-                           eap_get_name(EAP_VENDOR_IETF,
-                                        eap->inner_method ?
-                                        eap->inner_method :
-                                        EAP_TYPE_MSCHAPV2));
+       case EAP_TYPE_FAST:
+               if (wpa_config_set(ssid, "phase1", "\"fast_provisioning=2\"",
+                                  0) < 0)
+                       goto fail;
+               if (wpa_config_set(ssid, "pac_file",
+                                  "\"blob://pac_interworking\"", 0) < 0)
+                       goto fail;
+               name = eap_get_name(EAP_VENDOR_IETF,
+                                   eap->inner_method ? eap->inner_method :
+                                   EAP_TYPE_MSCHAPV2);
+               if (name == NULL)
+                       goto fail;
+               os_snprintf(buf, sizeof(buf), "\"auth=%s\"", name);
                if (wpa_config_set(ssid, "phase2", buf, 0) < 0)
                        goto fail;
                break;
@@ -1309,10 +1805,12 @@ int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
 
        nai_realm_free(realm, count);
 
+       wpa_s->next_ssid = ssid;
        wpa_config_update_prio_list(wpa_s->conf);
-       interworking_reconnect(wpa_s);
+       if (!only_add)
+               interworking_reconnect(wpa_s);
 
-       return 0;
+       return ssid->id;
 
 fail:
        wpas_notify_network_removed(wpa_s, ssid);
@@ -1322,25 +1820,86 @@ fail:
 }
 
 
+int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+                        int only_add)
+{
+       return interworking_connect_helper(wpa_s, bss, 1, only_add);
+}
+
+
+#ifdef PCSC_FUNCS
+static int interworking_pcsc_read_imsi(struct wpa_supplicant *wpa_s)
+{
+       size_t len;
+
+       if (wpa_s->imsi[0] && wpa_s->mnc_len)
+               return 0;
+
+       len = sizeof(wpa_s->imsi) - 1;
+       if (scard_get_imsi(wpa_s->scard, wpa_s->imsi, &len)) {
+               scard_deinit(wpa_s->scard);
+               wpa_s->scard = NULL;
+               wpa_msg(wpa_s, MSG_ERROR, "Could not read IMSI");
+               return -1;
+       }
+       wpa_s->imsi[len] = '\0';
+       wpa_s->mnc_len = scard_get_mnc_len(wpa_s->scard);
+       wpa_printf(MSG_DEBUG, "SCARD: IMSI %s (MNC length %d)",
+                  wpa_s->imsi, wpa_s->mnc_len);
+
+       return 0;
+}
+#endif /* PCSC_FUNCS */
+
+
 static struct wpa_cred * interworking_credentials_available_3gpp(
-       struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+       struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+       int *excluded)
 {
        struct wpa_cred *selected = NULL;
 #ifdef INTERWORKING_3GPP
        struct wpa_cred *cred;
        int ret;
+       int is_excluded = 0;
 
-       if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL)
+       if (bss->anqp == NULL || bss->anqp->anqp_3gpp == NULL) {
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "interworking-avail-3gpp: not avail, anqp: %p  anqp_3gpp: %p",
+                       bss->anqp, bss->anqp ? bss->anqp->anqp_3gpp : NULL);
                return NULL;
+       }
+
+#ifdef CONFIG_EAP_PROXY
+       if (!wpa_s->imsi[0]) {
+               size_t len;
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "Interworking: IMSI not available - try to read again through eap_proxy");
+               wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol,
+                                                            wpa_s->imsi,
+                                                            &len);
+               if (wpa_s->mnc_len > 0) {
+                       wpa_s->imsi[len] = '\0';
+                       wpa_msg(wpa_s, MSG_DEBUG,
+                               "eap_proxy: IMSI %s (MNC length %d)",
+                               wpa_s->imsi, wpa_s->mnc_len);
+               } else {
+                       wpa_msg(wpa_s, MSG_DEBUG,
+                               "eap_proxy: IMSI not available");
+               }
+       }
+#endif /* CONFIG_EAP_PROXY */
 
        for (cred = wpa_s->conf->cred; cred; cred = cred->next) {
                char *sep;
                const char *imsi;
                int mnc_len;
+               char imsi_buf[16];
+               size_t msin_len;
 
 #ifdef PCSC_FUNCS
-               if (cred->pcsc && wpa_s->conf->pcsc_reader && wpa_s->scard &&
-                   wpa_s->imsi[0]) {
+               if (cred->pcsc && wpa_s->scard) {
+                       if (interworking_pcsc_read_imsi(wpa_s) < 0)
+                               continue;
                        imsi = wpa_s->imsi;
                        mnc_len = wpa_s->mnc_len;
                        goto compare;
@@ -1355,7 +1914,8 @@ static struct wpa_cred * interworking_credentials_available_3gpp(
 #endif /* CONFIG_EAP_PROXY */
 
                if (cred->imsi == NULL || !cred->imsi[0] ||
-                   cred->milenage == NULL || !cred->milenage[0])
+                   (!wpa_s->conf->external_sim &&
+                    (cred->milenage == NULL || !cred->milenage[0])))
                        continue;
 
                sep = os_strchr(cred->imsi, '-');
@@ -1363,34 +1923,68 @@ static struct wpa_cred * interworking_credentials_available_3gpp(
                    (sep - cred->imsi != 5 && sep - cred->imsi != 6))
                        continue;
                mnc_len = sep - cred->imsi - 3;
-               imsi = cred->imsi;
+               os_memcpy(imsi_buf, cred->imsi, 3 + mnc_len);
+               sep++;
+               msin_len = os_strlen(cred->imsi);
+               if (3 + mnc_len + msin_len >= sizeof(imsi_buf) - 1)
+                       msin_len = sizeof(imsi_buf) - 3 - mnc_len - 1;
+               os_memcpy(&imsi_buf[3 + mnc_len], sep, msin_len);
+               imsi_buf[3 + mnc_len + msin_len] = '\0';
+               imsi = imsi_buf;
 
 #if defined(PCSC_FUNCS) || defined(CONFIG_EAP_PROXY)
        compare:
 #endif /* PCSC_FUNCS || CONFIG_EAP_PROXY */
-               wpa_printf(MSG_DEBUG, "Interworking: Parsing 3GPP info from "
-                          MACSTR, MAC2STR(bss->bssid));
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "Interworking: Parsing 3GPP info from " MACSTR,
+                       MAC2STR(bss->bssid));
                ret = plmn_id_match(bss->anqp->anqp_3gpp, imsi, mnc_len);
-               wpa_printf(MSG_DEBUG, "PLMN match %sfound", ret ? "" : "not ");
+               wpa_msg(wpa_s, MSG_DEBUG, "PLMN match %sfound",
+                       ret ? "" : "not ");
                if (ret) {
-                       if (cred_excluded_ssid(cred, bss))
+                       if (cred_no_required_oi_match(cred, bss))
                                continue;
-                       if (selected == NULL ||
-                           selected->priority < cred->priority)
-                               selected = cred;
+                       if (!ignore_bw &&
+                           cred_below_min_backhaul(wpa_s, cred, bss))
+                               continue;
+                       if (!ignore_bw &&
+                           cred_over_max_bss_load(wpa_s, cred, bss))
+                               continue;
+                       if (!ignore_bw &&
+                           cred_conn_capab_missing(wpa_s, cred, bss))
+                               continue;
+                       if (cred_excluded_ssid(cred, bss)) {
+                               if (excluded == NULL)
+                                       continue;
+                               if (selected == NULL) {
+                                       selected = cred;
+                                       is_excluded = 1;
+                               }
+                       } else {
+                               if (selected == NULL || is_excluded ||
+                                   cred_prio_cmp(selected, cred) < 0) {
+                                       selected = cred;
+                                       is_excluded = 0;
+                               }
+                       }
                }
        }
+
+       if (excluded)
+               *excluded = is_excluded;
 #endif /* INTERWORKING_3GPP */
        return selected;
 }
 
 
 static struct wpa_cred * interworking_credentials_available_realm(
-       struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+       struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+       int *excluded)
 {
        struct wpa_cred *cred, *selected = NULL;
        struct nai_realm *realm;
        u16 count, i;
+       int is_excluded = 0;
 
        if (bss->anqp == NULL || bss->anqp->nai_realm == NULL)
                return NULL;
@@ -1398,12 +1992,13 @@ static struct wpa_cred * interworking_credentials_available_realm(
        if (wpa_s->conf->cred == NULL)
                return NULL;
 
-       wpa_printf(MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
-                  MACSTR, MAC2STR(bss->bssid));
+       wpa_msg(wpa_s, MSG_DEBUG, "Interworking: Parsing NAI Realm list from "
+               MACSTR, MAC2STR(bss->bssid));
        realm = nai_realm_parse(bss->anqp->nai_realm, &count);
        if (realm == NULL) {
-               wpa_printf(MSG_DEBUG, "Interworking: Could not parse NAI "
-                          "Realm list from " MACSTR, MAC2STR(bss->bssid));
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "Interworking: Could not parse NAI Realm list from "
+                       MACSTR, MAC2STR(bss->bssid));
                return NULL;
        }
 
@@ -1414,48 +2009,114 @@ static struct wpa_cred * interworking_credentials_available_realm(
                for (i = 0; i < count; i++) {
                        if (!nai_realm_match(&realm[i], cred->realm))
                                continue;
-                       if (nai_realm_find_eap(cred, &realm[i])) {
-                               if (cred_excluded_ssid(cred, bss))
+                       if (nai_realm_find_eap(wpa_s, cred, &realm[i])) {
+                               if (cred_no_required_oi_match(cred, bss))
                                        continue;
-                               if (selected == NULL ||
-                                   selected->priority < cred->priority)
-                                       selected = cred;
+                               if (!ignore_bw &&
+                                   cred_below_min_backhaul(wpa_s, cred, bss))
+                                       continue;
+                               if (!ignore_bw &&
+                                   cred_over_max_bss_load(wpa_s, cred, bss))
+                                       continue;
+                               if (!ignore_bw &&
+                                   cred_conn_capab_missing(wpa_s, cred, bss))
+                                       continue;
+                               if (cred_excluded_ssid(cred, bss)) {
+                                       if (excluded == NULL)
+                                               continue;
+                                       if (selected == NULL) {
+                                               selected = cred;
+                                               is_excluded = 1;
+                                       }
+                               } else {
+                                       if (selected == NULL || is_excluded ||
+                                           cred_prio_cmp(selected, cred) < 0)
+                                       {
+                                               selected = cred;
+                                               is_excluded = 0;
+                                       }
+                               }
                                break;
+                       } else {
+                               wpa_msg(wpa_s, MSG_DEBUG,
+                                       "Interworking: realm-find-eap returned false");
                        }
                }
        }
 
        nai_realm_free(realm, count);
 
+       if (excluded)
+               *excluded = is_excluded;
+
        return selected;
 }
 
 
-static struct wpa_cred * interworking_credentials_available(
-       struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
+static struct wpa_cred * interworking_credentials_available_helper(
+       struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int ignore_bw,
+       int *excluded)
 {
        struct wpa_cred *cred, *cred2;
+       int excluded1, excluded2;
+
+       if (disallowed_bssid(wpa_s, bss->bssid) ||
+           disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len)) {
+               wpa_printf(MSG_DEBUG, "Interworking: Ignore disallowed BSS "
+                          MACSTR, MAC2STR(bss->bssid));
+               return NULL;
+       }
 
-       cred = interworking_credentials_available_realm(wpa_s, bss);
-       cred2 = interworking_credentials_available_3gpp(wpa_s, bss);
-       if (cred && cred2 && cred2->priority >= cred->priority)
+       cred = interworking_credentials_available_realm(wpa_s, bss, ignore_bw,
+                                                       &excluded1);
+       cred2 = interworking_credentials_available_3gpp(wpa_s, bss, ignore_bw,
+                                                       &excluded2);
+       if (cred && cred2 &&
+           (cred_prio_cmp(cred2, cred) >= 0 || (!excluded2 && excluded1))) {
                cred = cred2;
-       if (!cred)
+               excluded1 = excluded2;
+       }
+       if (!cred) {
                cred = cred2;
+               excluded1 = excluded2;
+       }
 
-       cred2 = interworking_credentials_available_roaming_consortium(wpa_s,
-                                                                     bss);
-       if (cred && cred2 && cred2->priority >= cred->priority)
+       cred2 = interworking_credentials_available_roaming_consortium(
+               wpa_s, bss, ignore_bw, &excluded2);
+       if (cred && cred2 &&
+           (cred_prio_cmp(cred2, cred) >= 0 || (!excluded2 && excluded1))) {
                cred = cred2;
-       if (!cred)
+               excluded1 = excluded2;
+       }
+       if (!cred) {
                cred = cred2;
+               excluded1 = excluded2;
+       }
 
+       if (excluded)
+               *excluded = excluded1;
        return cred;
 }
 
 
-static int domain_name_list_contains(struct wpabuf *domain_names,
-                                    const char *domain)
+static struct wpa_cred * interworking_credentials_available(
+       struct wpa_supplicant *wpa_s, struct wpa_bss *bss, int *excluded)
+{
+       struct wpa_cred *cred;
+
+       if (excluded)
+               *excluded = 0;
+       cred = interworking_credentials_available_helper(wpa_s, bss, 0,
+                                                        excluded);
+       if (cred)
+               return cred;
+       return interworking_credentials_available_helper(wpa_s, bss, 1,
+                                                        excluded);
+}
+
+
+int domain_name_list_contains(struct wpabuf *domain_names,
+                             const char *domain, int exact_match)
 {
        const u8 *pos, *end;
        size_t len;
@@ -1473,6 +2134,12 @@ static int domain_name_list_contains(struct wpabuf *domain_names,
                if (pos[0] == len &&
                    os_strncasecmp(domain, (const char *) (pos + 1), len) == 0)
                        return 1;
+               if (!exact_match && pos[0] > len && pos[pos[0] - len] == '.') {
+                       const char *ap = (const char *) (pos + 1);
+                       int offset = pos[0] - len;
+                       if (os_strncasecmp(domain, ap + offset, len) == 0)
+                               return 1;
+               }
 
                pos += 1 + pos[0];
        }
@@ -1485,6 +2152,8 @@ int interworking_home_sp_cred(struct wpa_supplicant *wpa_s,
                              struct wpa_cred *cred,
                              struct wpabuf *domain_names)
 {
+       size_t i;
+       int ret = -1;
 #ifdef INTERWORKING_3GPP
        char nai[100], *realm;
 
@@ -1492,33 +2161,46 @@ int interworking_home_sp_cred(struct wpa_supplicant *wpa_s,
        int mnc_len = 0;
        if (cred->imsi)
                imsi = cred->imsi;
-#ifdef CONFIG_PCSC
-       else if (cred->pcsc && wpa_s->conf->pcsc_reader &&
-                wpa_s->scard && wpa_s->imsi[0]) {
+#ifdef PCSC_FUNCS
+       else if (cred->pcsc && wpa_s->scard) {
+               if (interworking_pcsc_read_imsi(wpa_s) < 0)
+                       return -1;
                imsi = wpa_s->imsi;
                mnc_len = wpa_s->mnc_len;
        }
-#endif /* CONFIG_PCSC */
+#endif /* PCSC_FUNCS */
+#ifdef CONFIG_EAP_PROXY
+       else if (cred->pcsc && wpa_s->mnc_len > 0 && wpa_s->imsi[0]) {
+               imsi = wpa_s->imsi;
+               mnc_len = wpa_s->mnc_len;
+       }
+#endif /* CONFIG_EAP_PROXY */
        if (domain_names &&
            imsi && build_root_nai(nai, sizeof(nai), imsi, mnc_len, 0) == 0) {
                realm = os_strchr(nai, '@');
                if (realm)
                        realm++;
-               wpa_printf(MSG_DEBUG, "Interworking: Search for match "
-                          "with SIM/USIM domain %s", realm);
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "Interworking: Search for match with SIM/USIM domain %s",
+                       realm);
                if (realm &&
-                   domain_name_list_contains(domain_names, realm))
+                   domain_name_list_contains(domain_names, realm, 1))
                        return 1;
+               if (realm)
+                       ret = 0;
        }
 #endif /* INTERWORKING_3GPP */
 
        if (domain_names == NULL || cred->domain == NULL)
-               return 0;
+               return ret;
 
-       wpa_printf(MSG_DEBUG, "Interworking: Search for match with "
-                  "home SP FQDN %s", cred->domain);
-       if (domain_name_list_contains(domain_names, cred->domain))
-               return 1;
+       for (i = 0; i < cred->num_domain; i++) {
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "Interworking: Search for match with home SP FQDN %s",
+                       cred->domain[i]);
+               if (domain_name_list_contains(domain_names, cred->domain[i], 1))
+                       return 1;
+       }
 
        return 0;
 }
@@ -1568,32 +2250,143 @@ static int interworking_find_network_match(struct wpa_supplicant *wpa_s)
 }
 
 
+static int roaming_partner_match(struct wpa_supplicant *wpa_s,
+                                struct roaming_partner *partner,
+                                struct wpabuf *domain_names)
+{
+       wpa_printf(MSG_DEBUG, "Interworking: Comparing roaming_partner info fqdn='%s' exact_match=%d priority=%u country='%s'",
+                  partner->fqdn, partner->exact_match, partner->priority,
+                  partner->country);
+       wpa_hexdump_ascii(MSG_DEBUG, "Interworking: Domain names",
+                         wpabuf_head(domain_names),
+                         wpabuf_len(domain_names));
+       if (!domain_name_list_contains(domain_names, partner->fqdn,
+                                      partner->exact_match))
+               return 0;
+       /* TODO: match Country */
+       return 1;
+}
+
+
+static u8 roaming_prio(struct wpa_supplicant *wpa_s, struct wpa_cred *cred,
+                      struct wpa_bss *bss)
+{
+       size_t i;
+
+       if (bss->anqp == NULL || bss->anqp->domain_name == NULL) {
+               wpa_printf(MSG_DEBUG, "Interworking: No ANQP domain name info -> use default roaming partner priority 128");
+               return 128; /* cannot check preference with domain name */
+       }
+
+       if (interworking_home_sp_cred(wpa_s, cred, bss->anqp->domain_name) > 0)
+       {
+               wpa_printf(MSG_DEBUG, "Interworking: Determined to be home SP -> use maximum preference 0 as roaming partner priority");
+               return 0; /* max preference for home SP network */
+       }
+
+       for (i = 0; i < cred->num_roaming_partner; i++) {
+               if (roaming_partner_match(wpa_s, &cred->roaming_partner[i],
+                                         bss->anqp->domain_name)) {
+                       wpa_printf(MSG_DEBUG, "Interworking: Roaming partner preference match - priority %u",
+                                  cred->roaming_partner[i].priority);
+                       return cred->roaming_partner[i].priority;
+               }
+       }
+
+       wpa_printf(MSG_DEBUG, "Interworking: No roaming partner preference match - use default roaming partner priority 128");
+       return 128;
+}
+
+
+static struct wpa_bss * pick_best_roaming_partner(struct wpa_supplicant *wpa_s,
+                                                 struct wpa_bss *selected,
+                                                 struct wpa_cred *cred)
+{
+       struct wpa_bss *bss;
+       u8 best_prio, prio;
+       struct wpa_cred *cred2;
+
+       /*
+        * Check if any other BSS is operated by a more preferred roaming
+        * partner.
+        */
+
+       best_prio = roaming_prio(wpa_s, cred, selected);
+       wpa_printf(MSG_DEBUG, "Interworking: roaming_prio=%u for selected BSS "
+                  MACSTR " (cred=%d)", best_prio, MAC2STR(selected->bssid),
+                  cred->id);
+
+       dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+               if (bss == selected)
+                       continue;
+               cred2 = interworking_credentials_available(wpa_s, bss, NULL);
+               if (!cred2)
+                       continue;
+               if (!wpa_bss_get_ie(bss, WLAN_EID_RSN))
+                       continue;
+               prio = roaming_prio(wpa_s, cred2, bss);
+               wpa_printf(MSG_DEBUG, "Interworking: roaming_prio=%u for BSS "
+                          MACSTR " (cred=%d)", prio, MAC2STR(bss->bssid),
+                          cred2->id);
+               if (prio < best_prio) {
+                       int bh1, bh2, load1, load2, conn1, conn2;
+                       bh1 = cred_below_min_backhaul(wpa_s, cred, selected);
+                       load1 = cred_over_max_bss_load(wpa_s, cred, selected);
+                       conn1 = cred_conn_capab_missing(wpa_s, cred, selected);
+                       bh2 = cred_below_min_backhaul(wpa_s, cred2, bss);
+                       load2 = cred_over_max_bss_load(wpa_s, cred2, bss);
+                       conn2 = cred_conn_capab_missing(wpa_s, cred2, bss);
+                       wpa_printf(MSG_DEBUG, "Interworking: old: %d %d %d  new: %d %d %d",
+                                  bh1, load1, conn1, bh2, load2, conn2);
+                       if (bh1 || load1 || conn1 || !(bh2 || load2 || conn2)) {
+                               wpa_printf(MSG_DEBUG, "Interworking: Better roaming partner " MACSTR " selected", MAC2STR(bss->bssid));
+                               best_prio = prio;
+                               selected = bss;
+                       }
+               }
+       }
+
+       return selected;
+}
+
+
 static void interworking_select_network(struct wpa_supplicant *wpa_s)
 {
        struct wpa_bss *bss, *selected = NULL, *selected_home = NULL;
-       int selected_prio = -999999, selected_home_prio = -999999;
+       struct wpa_bss *selected2 = NULL, *selected2_home = NULL;
        unsigned int count = 0;
        const char *type;
        int res;
-       struct wpa_cred *cred;
+       struct wpa_cred *cred, *selected_cred = NULL;
+       struct wpa_cred *selected_home_cred = NULL;
+       struct wpa_cred *selected2_cred = NULL;
+       struct wpa_cred *selected2_home_cred = NULL;
 
        wpa_s->network_select = 0;
 
+       wpa_printf(MSG_DEBUG, "Interworking: Select network (auto_select=%d)",
+                  wpa_s->auto_select);
        dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
-               cred = interworking_credentials_available(wpa_s, bss);
+               int excluded = 0;
+               int bh, bss_load, conn_capab;
+               cred = interworking_credentials_available(wpa_s, bss,
+                                                         &excluded);
                if (!cred)
                        continue;
+
                if (!wpa_bss_get_ie(bss, WLAN_EID_RSN)) {
                        /*
                         * We currently support only HS 2.0 networks and those
                         * are required to use WPA2-Enterprise.
                         */
-                       wpa_printf(MSG_DEBUG, "Interworking: Credential match "
-                                  "with " MACSTR " but network does not use "
-                                  "RSN", MAC2STR(bss->bssid));
+                       wpa_msg(wpa_s, MSG_DEBUG,
+                               "Interworking: Credential match with " MACSTR
+                               " but network does not use RSN",
+                               MAC2STR(bss->bssid));
                        continue;
                }
-               count++;
+               if (!excluded)
+                       count++;
                res = interworking_home_sp(wpa_s, bss->anqp ?
                                           bss->anqp->domain_name : NULL);
                if (res > 0)
@@ -1602,29 +2395,75 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s)
                        type = "roaming";
                else
                        type = "unknown";
-               wpa_msg(wpa_s, MSG_INFO, INTERWORKING_AP MACSTR " type=%s",
-                       MAC2STR(bss->bssid), type);
+               bh = cred_below_min_backhaul(wpa_s, cred, bss);
+               bss_load = cred_over_max_bss_load(wpa_s, cred, bss);
+               conn_capab = cred_conn_capab_missing(wpa_s, cred, bss);
+               wpa_msg(wpa_s, MSG_INFO, "%s" MACSTR " type=%s%s%s%s id=%d priority=%d sp_priority=%d",
+                       excluded ? INTERWORKING_BLACKLISTED : INTERWORKING_AP,
+                       MAC2STR(bss->bssid), type,
+                       bh ? " below_min_backhaul=1" : "",
+                       bss_load ? " over_max_bss_load=1" : "",
+                       conn_capab ? " conn_capab_missing=1" : "",
+                       cred->id, cred->priority, cred->sp_priority);
+               if (excluded)
+                       continue;
                if (wpa_s->auto_select ||
                    (wpa_s->conf->auto_interworking &&
                     wpa_s->auto_network_select)) {
-                       if (selected == NULL ||
-                           cred->priority > selected_prio) {
-                               selected = bss;
-                               selected_prio = cred->priority;
-                       }
-                       if (res > 0 &&
-                           (selected_home == NULL ||
-                            cred->priority > selected_home_prio)) {
-                               selected_home = bss;
-                               selected_home_prio = cred->priority;
+                       if (bh || bss_load || conn_capab) {
+                               if (selected2_cred == NULL ||
+                                   cred_prio_cmp(cred, selected2_cred) > 0) {
+                                       wpa_printf(MSG_DEBUG, "Interworking: Mark as selected2");
+                                       selected2 = bss;
+                                       selected2_cred = cred;
+                               }
+                               if (res > 0 &&
+                                   (selected2_home_cred == NULL ||
+                                    cred_prio_cmp(cred, selected2_home_cred) >
+                                    0)) {
+                                       wpa_printf(MSG_DEBUG, "Interworking: Mark as selected2_home");
+                                       selected2_home = bss;
+                                       selected2_home_cred = cred;
+                               }
+                       } else {
+                               if (selected_cred == NULL ||
+                                   cred_prio_cmp(cred, selected_cred) > 0) {
+                                       wpa_printf(MSG_DEBUG, "Interworking: Mark as selected");
+                                       selected = bss;
+                                       selected_cred = cred;
+                               }
+                               if (res > 0 &&
+                                   (selected_home_cred == NULL ||
+                                    cred_prio_cmp(cred, selected_home_cred) >
+                                    0)) {
+                                       wpa_printf(MSG_DEBUG, "Interworking: Mark as selected_home");
+                                       selected_home = bss;
+                                       selected_home_cred = cred;
+                               }
                        }
                }
        }
 
        if (selected_home && selected_home != selected &&
-           selected_home_prio >= selected_prio) {
+           selected_home_cred &&
+           (selected_cred == NULL ||
+            cred_prio_cmp(selected_home_cred, selected_cred) >= 0)) {
                /* Prefer network operated by the Home SP */
+               wpa_printf(MSG_DEBUG, "Interworking: Overrided selected with selected_home");
                selected = selected_home;
+               selected_cred = selected_home_cred;
+       }
+
+       if (!selected) {
+               if (selected2_home) {
+                       wpa_printf(MSG_DEBUG, "Interworking: Use home BSS with BW limit mismatch since no other network could be selected");
+                       selected = selected2_home;
+                       selected_cred = selected2_home_cred;
+               } else if (selected2) {
+                       wpa_printf(MSG_DEBUG, "Interworking: Use visited BSS with BW limit mismatch since no other network could be selected");
+                       selected = selected2;
+                       selected_cred = selected2_cred;
+               }
        }
 
        if (count == 0) {
@@ -1634,16 +2473,17 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s)
                 * have matching APs.
                 */
                if (interworking_find_network_match(wpa_s)) {
-                       wpa_printf(MSG_DEBUG, "Interworking: Possible BSS "
-                                  "match for enabled network configurations");
-                       if (wpa_s->auto_select)
+                       wpa_msg(wpa_s, MSG_DEBUG,
+                               "Interworking: Possible BSS match for enabled network configurations");
+                       if (wpa_s->auto_select) {
                                interworking_reconnect(wpa_s);
-                       return;
+                               return;
+                       }
                }
 
                if (wpa_s->auto_network_select) {
-                       wpa_printf(MSG_DEBUG, "Interworking: Continue "
-                                  "scanning after ANQP fetch");
+                       wpa_msg(wpa_s, MSG_DEBUG,
+                               "Interworking: Continue scanning after ANQP fetch");
                        wpa_supplicant_req_scan(wpa_s, wpa_s->scan_interval,
                                                0);
                        return;
@@ -1651,10 +2491,22 @@ static void interworking_select_network(struct wpa_supplicant *wpa_s)
 
                wpa_msg(wpa_s, MSG_INFO, INTERWORKING_NO_MATCH "No network "
                        "with matching credentials found");
+               if (wpa_s->wpa_state == WPA_SCANNING)
+                       wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+       }
+
+       if (selected) {
+               wpa_printf(MSG_DEBUG, "Interworking: Selected " MACSTR,
+                          MAC2STR(selected->bssid));
+               selected = pick_best_roaming_partner(wpa_s, selected,
+                                                    selected_cred);
+               wpa_printf(MSG_DEBUG, "Interworking: Selected " MACSTR
+                          " (after best roaming partner selection)",
+                          MAC2STR(selected->bssid));
+               wpa_msg(wpa_s, MSG_INFO, INTERWORKING_SELECTED MACSTR,
+                       MAC2STR(selected->bssid));
+               interworking_connect(wpa_s, selected, 0);
        }
-
-       if (selected)
-               interworking_connect(wpa_s, selected);
 }
 
 
@@ -1684,9 +2536,10 @@ interworking_match_anqp_info(struct wpa_supplicant *wpa_s, struct wpa_bss *bss)
                    os_memcmp(bss->ssid, other->ssid, bss->ssid_len) != 0)
                        continue;
 
-               wpa_printf(MSG_DEBUG, "Interworking: Share ANQP data with "
-                          "already fetched BSSID " MACSTR " and " MACSTR,
-                          MAC2STR(other->bssid), MAC2STR(bss->bssid));
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "Interworking: Share ANQP data with already fetched BSSID "
+                       MACSTR " and " MACSTR,
+                       MAC2STR(other->bssid), MAC2STR(bss->bssid));
                other->anqp->users++;
                return other->anqp;
        }
@@ -1701,8 +2554,21 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
        int found = 0;
        const u8 *ie;
 
-       if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress)
+       wpa_printf(MSG_DEBUG, "Interworking: next_anqp_fetch - "
+                  "fetch_anqp_in_progress=%d fetch_osu_icon_in_progress=%d",
+                  wpa_s->fetch_anqp_in_progress,
+                  wpa_s->fetch_osu_icon_in_progress);
+
+       if (eloop_terminated() || !wpa_s->fetch_anqp_in_progress) {
+               wpa_printf(MSG_DEBUG, "Interworking: Stop next-ANQP-fetch");
                return;
+       }
+
+       if (wpa_s->fetch_osu_icon_in_progress) {
+               wpa_printf(MSG_DEBUG, "Interworking: Next icon (in progress)");
+               hs20_next_osu_icon(wpa_s);
+               return;
+       }
 
        dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
                if (!(bss->caps & IEEE80211_CAP_ESS))
@@ -1710,6 +2576,9 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
                ie = wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB);
                if (ie == NULL || ie[1] < 4 || !(ie[5] & 0x80))
                        continue; /* AP does not support Interworking */
+               if (disallowed_bssid(wpa_s, bss->bssid) ||
+                   disallowed_ssid(wpa_s, bss->ssid, bss->ssid_len))
+                       continue; /* Disallowed BSS */
 
                if (!(bss->flags & WPA_BSS_ANQP_FETCH_TRIED)) {
                        if (bss->anqp == NULL) {
@@ -1733,6 +2602,18 @@ static void interworking_next_anqp_fetch(struct wpa_supplicant *wpa_s)
        }
 
        if (found == 0) {
+               if (wpa_s->fetch_osu_info) {
+                       if (wpa_s->num_prov_found == 0 &&
+                           wpa_s->fetch_osu_waiting_scan &&
+                           wpa_s->num_osu_scans < 3) {
+                               wpa_printf(MSG_DEBUG, "HS 2.0: No OSU providers seen - try to scan again");
+                               hs20_start_osu_scan(wpa_s);
+                               return;
+                       }
+                       wpa_printf(MSG_DEBUG, "Interworking: Next icon");
+                       hs20_osu_icon_fetch(wpa_s);
+                       return;
+               }
                wpa_msg(wpa_s, MSG_INFO, "ANQP fetch completed");
                wpa_s->fetch_anqp_in_progress = 0;
                if (wpa_s->network_select)
@@ -1749,7 +2630,12 @@ void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s)
                bss->flags &= ~WPA_BSS_ANQP_FETCH_TRIED;
 
        wpa_s->fetch_anqp_in_progress = 1;
-       interworking_next_anqp_fetch(wpa_s);
+
+       /*
+        * Start actual ANQP operation from eloop call to make sure the loop
+        * does not end up using excessive recursion.
+        */
+       eloop_register_timeout(0, 0, interworking_continue_anqp, wpa_s, NULL);
 }
 
 
@@ -1760,6 +2646,7 @@ int interworking_fetch_anqp(struct wpa_supplicant *wpa_s)
 
        wpa_s->network_select = 0;
        wpa_s->fetch_all_anqp = 1;
+       wpa_s->fetch_osu_info = 0;
 
        interworking_start_fetch_anqp(wpa_s);
 
@@ -1777,9 +2664,10 @@ void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s)
 
 
 int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
-                 u16 info_ids[], size_t num_ids)
+                 u16 info_ids[], size_t num_ids, u32 subtypes)
 {
        struct wpabuf *buf;
+       struct wpabuf *hs20_buf = NULL;
        int ret = 0;
        int freq;
        struct wpa_bss *bss;
@@ -1794,22 +2682,34 @@ int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
        if (freq <= 0)
                return -1;
 
-       wpa_printf(MSG_DEBUG, "ANQP: Query Request to " MACSTR " for %u id(s)",
-                  MAC2STR(dst), (unsigned int) num_ids);
+       wpa_msg(wpa_s, MSG_DEBUG,
+               "ANQP: Query Request to " MACSTR " for %u id(s)",
+               MAC2STR(dst), (unsigned int) num_ids);
+
+#ifdef CONFIG_HS20
+       if (subtypes != 0) {
+               hs20_buf = wpabuf_alloc(100);
+               if (hs20_buf == NULL)
+                       return -1;
+               hs20_put_anqp_req(subtypes, NULL, 0, hs20_buf);
+       }
+#endif /* CONFIG_HS20 */
 
-       buf = anqp_build_req(info_ids, num_ids, NULL);
+       buf = anqp_build_req(info_ids, num_ids, hs20_buf);
+       wpabuf_free(hs20_buf);
        if (buf == NULL)
                return -1;
 
        res = gas_query_req(wpa_s->gas, dst, freq, buf, anqp_resp_cb, wpa_s);
        if (res < 0) {
-               wpa_printf(MSG_DEBUG, "ANQP: Failed to send Query Request");
+               wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Failed to send Query Request");
+               wpabuf_free(buf);
                ret = -1;
-       } else
-               wpa_printf(MSG_DEBUG, "ANQP: Query started with dialog token "
-                          "%u", res);
+       } else {
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "ANQP: Query started with dialog token %u", res);
+       }
 
-       wpabuf_free(buf);
        return ret;
 }
 
@@ -1832,6 +2732,12 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
        case ANQP_CAPABILITY_LIST:
                wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
                        " ANQP Capability list", MAC2STR(sa));
+               wpa_hexdump_ascii(MSG_DEBUG, "ANQP: Capability list",
+                                 pos, slen);
+               if (anqp) {
+                       wpabuf_free(anqp->capability_list);
+                       anqp->capability_list = wpabuf_alloc_copy(pos, slen);
+               }
                break;
        case ANQP_VENUE_NAME:
                wpa_msg(wpa_s, MSG_INFO, "RX-ANQP " MACSTR
@@ -1920,26 +2826,27 @@ static void interworking_parse_rx_anqp_resp(struct wpa_supplicant *wpa_s,
 
                        switch (type) {
                        case HS20_ANQP_OUI_TYPE:
-                               hs20_parse_rx_hs20_anqp_resp(wpa_s, sa, pos,
-                                                            slen);
+                               hs20_parse_rx_hs20_anqp_resp(wpa_s, bss, sa,
+                                                            pos, slen);
                                break;
                        default:
-                               wpa_printf(MSG_DEBUG, "HS20: Unsupported ANQP "
-                                          "vendor type %u", type);
+                               wpa_msg(wpa_s, MSG_DEBUG,
+                                       "HS20: Unsupported ANQP vendor type %u",
+                                       type);
                                break;
                        }
                        break;
 #endif /* CONFIG_HS20 */
                default:
-                       wpa_printf(MSG_DEBUG, "Interworking: Unsupported "
-                                  "vendor-specific ANQP OUI %06x",
-                                  WPA_GET_BE24(pos));
+                       wpa_msg(wpa_s, MSG_DEBUG,
+                               "Interworking: Unsupported vendor-specific ANQP OUI %06x",
+                               WPA_GET_BE24(pos));
                        return;
                }
                break;
        default:
-               wpa_printf(MSG_DEBUG, "Interworking: Unsupported ANQP Info ID "
-                          "%u", info_id);
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "Interworking: Unsupported ANQP Info ID %u", info_id);
                break;
        }
 }
@@ -1956,16 +2863,27 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
        u16 info_id;
        u16 slen;
        struct wpa_bss *bss = NULL, *tmp;
+       const char *anqp_result = "SUCCESS";
 
-       if (result != GAS_QUERY_SUCCESS)
-               return;
+       wpa_printf(MSG_DEBUG, "Interworking: anqp_resp_cb dst=" MACSTR
+                  " dialog_token=%u result=%d status_code=%u",
+                  MAC2STR(dst), dialog_token, result, status_code);
+       if (result != GAS_QUERY_SUCCESS) {
+               if (wpa_s->fetch_osu_icon_in_progress)
+                       hs20_icon_fetch_failed(wpa_s);
+               anqp_result = "FAILURE";
+               goto out;
+       }
 
        pos = wpabuf_head(adv_proto);
        if (wpabuf_len(adv_proto) < 4 || pos[0] != WLAN_EID_ADV_PROTO ||
            pos[1] < 2 || pos[3] != ACCESS_NETWORK_QUERY_PROTOCOL) {
-               wpa_printf(MSG_DEBUG, "ANQP: Unexpected Advertisement "
-                          "Protocol in response");
-               return;
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "ANQP: Unexpected Advertisement Protocol in response");
+               if (wpa_s->fetch_osu_icon_in_progress)
+                       hs20_icon_fetch_failed(wpa_s);
+               anqp_result = "INVALID_FRAME";
+               goto out;
        }
 
        /*
@@ -1987,46 +2905,65 @@ void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
        end = pos + wpabuf_len(resp);
 
        while (pos < end) {
-               if (pos + 4 > end) {
-                       wpa_printf(MSG_DEBUG, "ANQP: Invalid element");
-                       break;
+               unsigned int left = end - pos;
+
+               if (left < 4) {
+                       wpa_msg(wpa_s, MSG_DEBUG, "ANQP: Invalid element");
+                       anqp_result = "INVALID_FRAME";
+                       goto out_parse_done;
                }
                info_id = WPA_GET_LE16(pos);
                pos += 2;
                slen = WPA_GET_LE16(pos);
                pos += 2;
-               if (pos + slen > end) {
-                       wpa_printf(MSG_DEBUG, "ANQP: Invalid element length "
-                                  "for Info ID %u", info_id);
-                       break;
+               left -= 4;
+               if (left < slen) {
+                       wpa_msg(wpa_s, MSG_DEBUG,
+                               "ANQP: Invalid element length for Info ID %u",
+                               info_id);
+                       anqp_result = "INVALID_FRAME";
+                       goto out_parse_done;
                }
                interworking_parse_rx_anqp_resp(wpa_s, bss, dst, info_id, pos,
                                                slen);
                pos += slen;
        }
+
+out_parse_done:
+       hs20_notify_parse_done(wpa_s);
+out:
+       wpa_msg(wpa_s, MSG_INFO, ANQP_QUERY_DONE "addr=" MACSTR " result=%s",
+               MAC2STR(dst), anqp_result);
 }
 
 
 static void interworking_scan_res_handler(struct wpa_supplicant *wpa_s,
                                          struct wpa_scan_results *scan_res)
 {
-       wpa_printf(MSG_DEBUG, "Interworking: Scan results available - start "
-                  "ANQP fetch");
+       wpa_msg(wpa_s, MSG_DEBUG,
+               "Interworking: Scan results available - start ANQP fetch");
        interworking_start_fetch_anqp(wpa_s);
 }
 
 
-int interworking_select(struct wpa_supplicant *wpa_s, int auto_select)
+int interworking_select(struct wpa_supplicant *wpa_s, int auto_select,
+                       int *freqs)
 {
        interworking_stop_fetch_anqp(wpa_s);
        wpa_s->network_select = 1;
        wpa_s->auto_network_select = 0;
        wpa_s->auto_select = !!auto_select;
        wpa_s->fetch_all_anqp = 0;
-       wpa_printf(MSG_DEBUG, "Interworking: Start scan for network "
-                  "selection");
+       wpa_s->fetch_osu_info = 0;
+       wpa_msg(wpa_s, MSG_DEBUG,
+               "Interworking: Start scan for network selection");
        wpa_s->scan_res_handler = interworking_scan_res_handler;
+       wpa_s->normal_scans = 0;
        wpa_s->scan_req = MANUAL_SCAN_REQ;
+       os_free(wpa_s->manual_scan_freqs);
+       wpa_s->manual_scan_freqs = freqs;
+       wpa_s->after_wps = 0;
+       wpa_s->known_wps_freq = 0;
        wpa_supplicant_req_scan(wpa_s, 0, 0);
 
        return 0;
@@ -2039,6 +2976,7 @@ static void gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
                        const struct wpabuf *resp, u16 status_code)
 {
        struct wpa_supplicant *wpa_s = ctx;
+       struct wpabuf *n;
 
        wpa_msg(wpa_s, MSG_INFO, GAS_RESPONSE_INFO "addr=" MACSTR
                " dialog_token=%d status_code=%d resp_len=%d",
@@ -2047,10 +2985,14 @@ static void gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
        if (!resp)
                return;
 
-       wpabuf_free(wpa_s->last_gas_resp);
-       wpa_s->last_gas_resp = wpabuf_dup(resp);
-       if (wpa_s->last_gas_resp == NULL)
+       n = wpabuf_dup(resp);
+       if (n == NULL)
                return;
+       wpabuf_free(wpa_s->prev_gas_resp);
+       wpa_s->prev_gas_resp = wpa_s->last_gas_resp;
+       os_memcpy(wpa_s->prev_gas_addr, wpa_s->last_gas_addr, ETH_ALEN);
+       wpa_s->prev_gas_dialog_token = wpa_s->last_gas_dialog_token;
+       wpa_s->last_gas_resp = n;
        os_memcpy(wpa_s->last_gas_addr, addr, ETH_ALEN);
        wpa_s->last_gas_dialog_token = dialog_token;
 }
@@ -2066,7 +3008,7 @@ int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst,
        struct wpa_bss *bss;
        int res;
        size_t len;
-       u8 query_resp_len_limit = 0, pame_bi = 0;
+       u8 query_resp_len_limit = 0;
 
        freq = wpa_s->assoc_freq;
        bss = wpa_bss_get_bssid(wpa_s, dst);
@@ -2075,8 +3017,8 @@ int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst,
        if (freq <= 0)
                return -1;
 
-       wpa_printf(MSG_DEBUG, "GAS request to " MACSTR " (freq %d MHz)",
-                  MAC2STR(dst), freq);
+       wpa_msg(wpa_s, MSG_DEBUG, "GAS request to " MACSTR " (freq %d MHz)",
+               MAC2STR(dst), freq);
        wpa_hexdump_buf(MSG_DEBUG, "Advertisement Protocol ID", adv_proto);
        wpa_hexdump_buf(MSG_DEBUG, "GAS Query", query);
 
@@ -2090,8 +3032,7 @@ int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst,
        /* Advertisement Protocol IE */
        wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
        wpabuf_put_u8(buf, 1 + wpabuf_len(adv_proto)); /* Length */
-       wpabuf_put_u8(buf, (query_resp_len_limit & 0x7f) |
-                     (pame_bi ? 0x80 : 0));
+       wpabuf_put_u8(buf, query_resp_len_limit & 0x7f);
        wpabuf_put_buf(buf, adv_proto);
 
        /* GAS Query */
@@ -2103,12 +3044,12 @@ int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst,
 
        res = gas_query_req(wpa_s->gas, dst, freq, buf, gas_resp_cb, wpa_s);
        if (res < 0) {
-               wpa_printf(MSG_DEBUG, "GAS: Failed to send Query Request");
+               wpa_msg(wpa_s, MSG_DEBUG, "GAS: Failed to send Query Request");
+               wpabuf_free(buf);
                ret = -1;
        } else
-               wpa_printf(MSG_DEBUG, "GAS: Query started with dialog token "
-                          "%u", res);
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "GAS: Query started with dialog token %u", res);
 
-       wpabuf_free(buf);
        return ret;
 }
old mode 100644 (file)
new mode 100755 (executable)
index 4a4af82..3743dc0
@@ -12,7 +12,7 @@
 enum gas_query_result;
 
 int anqp_send_req(struct wpa_supplicant *wpa_s, const u8 *dst,
-                 u16 info_ids[], size_t num_ids);
+                 u16 info_ids[], size_t num_ids, u32 subtypes);
 void anqp_resp_cb(void *ctx, const u8 *dst, u8 dialog_token,
                  enum gas_query_result result,
                  const struct wpabuf *adv_proto,
@@ -22,11 +22,15 @@ int gas_send_request(struct wpa_supplicant *wpa_s, const u8 *dst,
                     const struct wpabuf *query);
 int interworking_fetch_anqp(struct wpa_supplicant *wpa_s);
 void interworking_stop_fetch_anqp(struct wpa_supplicant *wpa_s);
-int interworking_select(struct wpa_supplicant *wpa_s, int auto_select);
-int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss);
+int interworking_select(struct wpa_supplicant *wpa_s, int auto_select,
+                       int *freqs);
+int interworking_connect(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
+                        int only_add);
 void interworking_start_fetch_anqp(struct wpa_supplicant *wpa_s);
 int interworking_home_sp_cred(struct wpa_supplicant *wpa_s,
                              struct wpa_cred *cred,
                              struct wpabuf *domain_names);
+int domain_name_list_contains(struct wpabuf *domain_names,
+                             const char *domain, int exact_match);
 
 #endif /* INTERWORKING_H */
old mode 100644 (file)
new mode 100755 (executable)
index 39b837e..2282747
 #include "driver_i.h"
 #include "p2p_supplicant.h"
 
-extern struct wpa_driver_ops *wpa_drivers[];
-
 
 static void usage(void)
 {
        int i;
        printf("%s\n\n%s\n"
               "usage:\n"
-              "  wpa_supplicant [-BddhKLqqstuvW] [-P<pid file>] "
+              "  wpa_supplicant [-BddhKLqq"
+#ifdef CONFIG_DEBUG_SYSLOG
+              "s"
+#endif /* CONFIG_DEBUG_SYSLOG */
+              "t"
+#ifdef CONFIG_DBUS
+              "u"
+#endif /* CONFIG_DBUS */
+              "vW] [-P<pid file>] "
               "[-g<global ctrl>] \\\n"
               "        [-G<group>] \\\n"
               "        -i<ifname> -c<config file> [-C<ctrl>] [-D<driver>] "
               "[-p<driver_param>] \\\n"
-              "        [-b<br_ifname>] [-f<debug file>] [-e<entropy file>] "
-              "\\\n"
+              "        [-b<br_ifname>] [-e<entropy file>]"
+#ifdef CONFIG_DEBUG_FILE
+              " [-f<debug file>]"
+#endif /* CONFIG_DEBUG_FILE */
+              " \\\n"
               "        [-o<override driver>] [-O<override ctrl>] \\\n"
               "        [-N -i<ifname> -c<conf> [-C<ctrl>] "
               "[-D<driver>] \\\n"
+#ifdef CONFIG_P2P
+              "        [-m<P2P Device config file>] \\\n"
+#endif /* CONFIG_P2P */
               "        [-p<driver_param>] [-b<br_ifname>] [-I<config file>] "
               "...]\n"
               "\n"
@@ -83,6 +95,9 @@ static void usage(void)
 #endif /* CONFIG_DBUS */
        printf("  -v = show version\n"
               "  -W = wait for a control interface monitor before starting\n"
+#ifdef CONFIG_P2P
+              "  -m = Configuration file for the P2P Device interface\n"
+#endif /* CONFIG_P2P */
               "  -N = start describing new interface\n");
 
        printf("example:\n"
@@ -160,7 +175,7 @@ int main(int argc, char *argv[])
 
        for (;;) {
                c = getopt(argc, argv,
-                          "b:Bc:C:D:de:f:g:G:hi:I:KLNo:O:p:P:qsTtuvW");
+                          "b:Bc:C:D:de:f:g:G:hi:I:KLm:No:O:p:P:qsTtuvW");
                if (c < 0)
                        break;
                switch (c) {
@@ -220,6 +235,11 @@ int main(int argc, char *argv[])
                        license();
                        exitcode = 0;
                        goto out;
+#ifdef CONFIG_P2P
+               case 'm':
+                       iface->conf_p2p_dev = optarg;
+                       break;
+#endif /* CONFIG_P2P */
                case 'o':
                        params.override_driver = optarg;
                        break;
@@ -302,18 +322,11 @@ int main(int argc, char *argv[])
                        exitcode = -1;
                        break;
                }
-               wpa_s = wpa_supplicant_add_iface(global, &ifaces[i]);
+               wpa_s = wpa_supplicant_add_iface(global, &ifaces[i], NULL);
                if (wpa_s == NULL) {
                        exitcode = -1;
                        break;
                }
-#ifdef CONFIG_P2P
-               if (wpa_s->global->p2p == NULL &&
-                   (wpa_s->drv_flags &
-                    WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) &&
-                   wpas_p2p_add_p2pdev_interface(wpa_s) < 0)
-                       exitcode = -1;
-#endif /* CONFIG_P2P */
        }
 
        if (exitcode == 0)
old mode 100644 (file)
new mode 100755 (executable)
index 010c30a..4d3caf2
@@ -28,7 +28,7 @@ int main(int argc, char *argv[])
        memset(&iface, 0, sizeof(iface));
        /* TODO: set interface parameters */
 
-       if (wpa_supplicant_add_iface(global, &iface) == NULL)
+       if (wpa_supplicant_add_iface(global, &iface, NULL) == NULL)
                exitcode = -1;
 
        if (exitcode == 0)
old mode 100644 (file)
new mode 100755 (executable)
index 93a68f1..e1dded0
@@ -61,7 +61,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                        exitcode = -1;
                        break;
                }
-               if (wpa_supplicant_add_iface(global, &ifaces[i]) == NULL)
+               if (wpa_supplicant_add_iface(global, &ifaces[i], NULL) == NULL)
                        exitcode = -1;
        }
 
old mode 100644 (file)
new mode 100755 (executable)
index 0b7d5ce..9950aa9
@@ -119,7 +119,7 @@ static int read_interface(struct wpa_global *global, HKEY _hk,
 
        RegCloseKey(hk);
 
-       if (wpa_supplicant_add_iface(global, &iface) == NULL) {
+       if (wpa_supplicant_add_iface(global, &iface, NULL) == NULL) {
                if (skip_on_error)
                        wpa_printf(MSG_DEBUG, "Skipped interface '%s' due to "
                                   "initialization failure", iface.ifname);
diff --git a/wpa_supplicant/mesh.c b/wpa_supplicant/mesh.c
new file mode 100755 (executable)
index 0000000..33b4af3
--- /dev/null
@@ -0,0 +1,540 @@
+/*
+ * WPA Supplicant - Basic mesh mode routines
+ * Copyright (c) 2013-2014, cozybit, Inc.  All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "utils/uuid.h"
+#include "common/ieee802_11_defs.h"
+#include "common/wpa_ctrl.h"
+#include "ap/sta_info.h"
+#include "ap/hostapd.h"
+#include "ap/ieee802_11.h"
+#include "config_ssid.h"
+#include "config.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "notify.h"
+#include "ap.h"
+#include "mesh_mpm.h"
+#include "mesh_rsn.h"
+#include "mesh.h"
+
+
+static void wpa_supplicant_mesh_deinit(struct wpa_supplicant *wpa_s)
+{
+       wpa_supplicant_mesh_iface_deinit(wpa_s, wpa_s->ifmsh);
+       wpa_s->ifmsh = NULL;
+       wpa_s->current_ssid = NULL;
+       os_free(wpa_s->mesh_rsn);
+       wpa_s->mesh_rsn = NULL;
+       /* TODO: leave mesh (stop beacon). This will happen on link down
+        * anyway, so it's not urgent */
+}
+
+
+void wpa_supplicant_mesh_iface_deinit(struct wpa_supplicant *wpa_s,
+                                     struct hostapd_iface *ifmsh)
+{
+       if (!ifmsh)
+               return;
+
+       if (ifmsh->mconf) {
+               mesh_mpm_deinit(wpa_s, ifmsh);
+               if (ifmsh->mconf->ies) {
+                       ifmsh->mconf->ies = NULL;
+                       /* We cannot free this struct
+                        * because wpa_authenticator on
+                        * hostapd side is also using it
+                        * for now just set to NULL and
+                        * let hostapd code free it.
+                        */
+               }
+               os_free(ifmsh->mconf);
+               ifmsh->mconf = NULL;
+       }
+
+       /* take care of shared data */
+       hostapd_interface_deinit(ifmsh);
+       hostapd_interface_free(ifmsh);
+}
+
+
+static struct mesh_conf * mesh_config_create(struct wpa_ssid *ssid)
+{
+       struct mesh_conf *conf;
+
+       conf = os_zalloc(sizeof(struct mesh_conf));
+       if (!conf)
+               return NULL;
+
+       os_memcpy(conf->meshid, ssid->ssid, ssid->ssid_len);
+       conf->meshid_len = ssid->ssid_len;
+
+       if (ssid->key_mgmt & WPA_KEY_MGMT_SAE)
+               conf->security |= MESH_CONF_SEC_AUTH |
+                       MESH_CONF_SEC_AMPE;
+       else
+               conf->security |= MESH_CONF_SEC_NONE;
+
+       /* defaults */
+       conf->mesh_pp_id = MESH_PATH_PROTOCOL_HWMP;
+       conf->mesh_pm_id = MESH_PATH_METRIC_AIRTIME;
+       conf->mesh_cc_id = 0;
+       conf->mesh_sp_id = MESH_SYNC_METHOD_NEIGHBOR_OFFSET;
+       conf->mesh_auth_id = (conf->security & MESH_CONF_SEC_AUTH) ? 1 : 0;
+       conf->dot11MeshMaxRetries = ssid->dot11MeshMaxRetries;
+       conf->dot11MeshRetryTimeout = ssid->dot11MeshRetryTimeout;
+       conf->dot11MeshConfirmTimeout = ssid->dot11MeshConfirmTimeout;
+       conf->dot11MeshHoldingTimeout = ssid->dot11MeshHoldingTimeout;
+
+       return conf;
+}
+
+
+static void wpas_mesh_copy_groups(struct hostapd_data *bss,
+                                 struct wpa_supplicant *wpa_s)
+{
+       int num_groups;
+       size_t groups_size;
+
+       for (num_groups = 0; wpa_s->conf->sae_groups[num_groups] > 0;
+            num_groups++)
+               ;
+
+       groups_size = (num_groups + 1) * sizeof(wpa_s->conf->sae_groups[0]);
+       bss->conf->sae_groups = os_malloc(groups_size);
+       if (bss->conf->sae_groups)
+               os_memcpy(bss->conf->sae_groups, wpa_s->conf->sae_groups,
+                         groups_size);
+}
+
+
+static int wpa_supplicant_mesh_init(struct wpa_supplicant *wpa_s,
+                                   struct wpa_ssid *ssid)
+{
+       struct hostapd_iface *ifmsh;
+       struct hostapd_data *bss;
+       struct hostapd_config *conf;
+       struct mesh_conf *mconf;
+       int basic_rates_erp[] = { 10, 20, 55, 60, 110, 120, 240, -1 };
+       static int default_groups[] = { 19, 20, 21, 25, 26, -1 };
+       size_t len;
+       int rate_len;
+
+       if (!wpa_s->conf->user_mpm) {
+               /* not much for us to do here */
+               wpa_msg(wpa_s, MSG_WARNING,
+                       "user_mpm is not enabled in configuration");
+               return 0;
+       }
+
+       wpa_s->ifmsh = ifmsh = os_zalloc(sizeof(*wpa_s->ifmsh));
+       if (!ifmsh)
+               return -ENOMEM;
+
+       ifmsh->drv_flags = wpa_s->drv_flags;
+       ifmsh->num_bss = 1;
+       ifmsh->bss = os_calloc(wpa_s->ifmsh->num_bss,
+                              sizeof(struct hostapd_data *));
+       if (!ifmsh->bss)
+               goto out_free;
+
+       ifmsh->bss[0] = bss = os_zalloc(sizeof(struct hostapd_data));
+       if (!bss)
+               goto out_free;
+
+       os_memcpy(bss->own_addr, wpa_s->own_addr, ETH_ALEN);
+       bss->driver = wpa_s->driver;
+       bss->drv_priv = wpa_s->drv_priv;
+       bss->iface = ifmsh;
+       bss->mesh_sta_free_cb = mesh_mpm_free_sta;
+       wpa_s->assoc_freq = ssid->frequency;
+       wpa_s->current_ssid = ssid;
+
+       /* setup an AP config for auth processing */
+       conf = hostapd_config_defaults();
+       if (!conf)
+               goto out_free;
+
+       bss->conf = *conf->bss;
+       bss->conf->start_disabled = 1;
+       bss->conf->mesh = MESH_ENABLED;
+       bss->conf->ap_max_inactivity = wpa_s->conf->mesh_max_inactivity;
+       bss->iconf = conf;
+       ifmsh->conf = conf;
+
+       ifmsh->bss[0]->max_plinks = wpa_s->conf->max_peer_links;
+       os_strlcpy(bss->conf->iface, wpa_s->ifname, sizeof(bss->conf->iface));
+
+       mconf = mesh_config_create(ssid);
+       if (!mconf)
+               goto out_free;
+       ifmsh->mconf = mconf;
+
+       /* need conf->hw_mode for supported rates. */
+       if (ssid->frequency == 0) {
+               conf->hw_mode = HOSTAPD_MODE_IEEE80211G;
+               conf->channel = 1;
+       } else {
+               conf->hw_mode = ieee80211_freq_to_chan(ssid->frequency,
+                                                      &conf->channel);
+       }
+       if (conf->hw_mode == NUM_HOSTAPD_MODES) {
+               wpa_printf(MSG_ERROR, "Unsupported mesh mode frequency: %d MHz",
+                          ssid->frequency);
+               goto out_free;
+       }
+
+       if (ssid->mesh_basic_rates == NULL) {
+               /*
+                * XXX: Hack! This is so an MPM which correctly sets the ERP
+                * mandatory rates as BSSBasicRateSet doesn't reject us. We
+                * could add a new hw_mode HOSTAPD_MODE_IEEE80211G_ERP, but
+                * this is way easier. This also makes our BSSBasicRateSet
+                * advertised in beacons match the one in peering frames, sigh.
+                */
+               if (conf->hw_mode == HOSTAPD_MODE_IEEE80211G) {
+                       conf->basic_rates = os_malloc(sizeof(basic_rates_erp));
+                       if (!conf->basic_rates)
+                               goto out_free;
+                       os_memcpy(conf->basic_rates, basic_rates_erp,
+                                 sizeof(basic_rates_erp));
+               }
+       } else {
+               rate_len = 0;
+               while (1) {
+                       if (ssid->mesh_basic_rates[rate_len] < 1)
+                               break;
+                       rate_len++;
+               }
+               conf->basic_rates = os_calloc(rate_len + 1, sizeof(int));
+               if (conf->basic_rates == NULL)
+                       goto out_free;
+               os_memcpy(conf->basic_rates, ssid->mesh_basic_rates,
+                         rate_len * sizeof(int));
+               conf->basic_rates[rate_len] = -1;
+       }
+
+       if (hostapd_setup_interface(ifmsh)) {
+               wpa_printf(MSG_ERROR,
+                          "Failed to initialize hostapd interface for mesh");
+               return -1;
+       }
+
+       if (wpa_drv_init_mesh(wpa_s)) {
+               wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh in driver");
+               return -1;
+       }
+
+       if (mconf->security != MESH_CONF_SEC_NONE) {
+               if (ssid->passphrase == NULL) {
+                       wpa_printf(MSG_ERROR,
+                                  "mesh: Passphrase for SAE not configured");
+                       goto out_free;
+               }
+
+               bss->conf->wpa = ssid->proto;
+               bss->conf->wpa_key_mgmt = ssid->key_mgmt;
+
+               if (wpa_s->conf->sae_groups &&
+                   wpa_s->conf->sae_groups[0] > 0) {
+                       wpas_mesh_copy_groups(bss, wpa_s);
+               } else {
+                       bss->conf->sae_groups =
+                               os_malloc(sizeof(default_groups));
+                       if (!bss->conf->sae_groups)
+                               goto out_free;
+                       os_memcpy(bss->conf->sae_groups, default_groups,
+                                 sizeof(default_groups));
+               }
+
+               len = os_strlen(ssid->passphrase);
+               bss->conf->ssid.wpa_passphrase =
+                       dup_binstr(ssid->passphrase, len);
+
+               wpa_s->mesh_rsn = mesh_rsn_auth_init(wpa_s, mconf);
+               if (!wpa_s->mesh_rsn)
+                       goto out_free;
+       }
+
+       wpa_supplicant_conf_ap_ht(wpa_s, ssid, conf);
+
+       return 0;
+out_free:
+       wpa_supplicant_mesh_deinit(wpa_s);
+       return -ENOMEM;
+}
+
+
+void wpa_mesh_notify_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+                         const u8 *ies, size_t ie_len)
+{
+       struct ieee802_11_elems elems;
+
+       wpa_msg(wpa_s, MSG_INFO,
+               "new peer notification for " MACSTR, MAC2STR(addr));
+
+       if (ieee802_11_parse_elems(ies, ie_len, &elems, 0) == ParseFailed) {
+               wpa_msg(wpa_s, MSG_INFO, "Could not parse beacon from " MACSTR,
+                       MAC2STR(addr));
+               return;
+       }
+       wpa_mesh_new_mesh_peer(wpa_s, addr, &elems);
+}
+
+
+void wpa_supplicant_mesh_add_scan_ie(struct wpa_supplicant *wpa_s,
+                                    struct wpabuf **extra_ie)
+{
+       /* EID + 0-length (wildcard) mesh-id */
+       size_t ielen = 2;
+
+       if (wpabuf_resize(extra_ie, ielen) == 0) {
+               wpabuf_put_u8(*extra_ie, WLAN_EID_MESH_ID);
+               wpabuf_put_u8(*extra_ie, 0);
+       }
+}
+
+
+int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s,
+                            struct wpa_ssid *ssid)
+{
+       struct wpa_driver_mesh_join_params params;
+       int ret = 0;
+
+       if (!ssid || !ssid->ssid || !ssid->ssid_len || !ssid->frequency) {
+               ret = -ENOENT;
+               goto out;
+       }
+
+       wpa_supplicant_mesh_deinit(wpa_s);
+
+       os_memset(&params, 0, sizeof(params));
+       params.meshid = ssid->ssid;
+       params.meshid_len = ssid->ssid_len;
+       ibss_mesh_setup_freq(wpa_s, ssid, &params.freq);
+       wpa_s->mesh_ht_enabled = !!params.freq.ht_enabled;
+       if (ssid->beacon_int > 0)
+               params.beacon_int = ssid->beacon_int;
+       else if (wpa_s->conf->beacon_int > 0)
+               params.beacon_int = wpa_s->conf->beacon_int;
+       params.max_peer_links = wpa_s->conf->max_peer_links;
+
+       if (ssid->key_mgmt & WPA_KEY_MGMT_SAE) {
+               params.flags |= WPA_DRIVER_MESH_FLAG_SAE_AUTH;
+               params.flags |= WPA_DRIVER_MESH_FLAG_AMPE;
+               wpa_s->conf->user_mpm = 1;
+       }
+
+       if (wpa_s->conf->user_mpm) {
+               params.flags |= WPA_DRIVER_MESH_FLAG_USER_MPM;
+               params.conf.flags &= ~WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS;
+       } else {
+               params.flags |= WPA_DRIVER_MESH_FLAG_DRIVER_MPM;
+               params.conf.flags |= WPA_DRIVER_MESH_CONF_FLAG_AUTO_PLINKS;
+       }
+       params.conf.peer_link_timeout = wpa_s->conf->mesh_max_inactivity;
+
+       if (wpa_supplicant_mesh_init(wpa_s, ssid)) {
+               wpa_msg(wpa_s, MSG_ERROR, "Failed to init mesh");
+               wpa_drv_leave_mesh(wpa_s);
+               ret = -1;
+               goto out;
+       }
+
+       if (wpa_s->ifmsh) {
+               params.ies = wpa_s->ifmsh->mconf->ies;
+               params.ie_len = wpa_s->ifmsh->mconf->ie_len;
+               params.basic_rates = wpa_s->ifmsh->basic_rates;
+       }
+
+       wpa_msg(wpa_s, MSG_INFO, "joining mesh %s",
+               wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+       ret = wpa_drv_join_mesh(wpa_s, &params);
+       if (ret)
+               wpa_msg(wpa_s, MSG_ERROR, "mesh join error=%d\n", ret);
+
+       /* hostapd sets the interface down until we associate */
+       wpa_drv_set_operstate(wpa_s, 1);
+
+out:
+       return ret;
+}
+
+
+int wpa_supplicant_leave_mesh(struct wpa_supplicant *wpa_s)
+{
+       int ret = 0;
+
+       wpa_msg(wpa_s, MSG_INFO, "leaving mesh");
+
+       /* Need to send peering close messages first */
+       wpa_supplicant_mesh_deinit(wpa_s);
+
+       ret = wpa_drv_leave_mesh(wpa_s);
+       if (ret)
+               wpa_msg(wpa_s, MSG_ERROR, "mesh leave error=%d", ret);
+
+       wpa_drv_set_operstate(wpa_s, 1);
+
+       return ret;
+}
+
+
+static int mesh_attr_text(const u8 *ies, size_t ies_len, char *buf, char *end)
+{
+       struct ieee802_11_elems elems;
+       char *mesh_id, *pos = buf;
+       u8 *bss_basic_rate_set;
+       int bss_basic_rate_set_len, ret, i;
+
+       if (ieee802_11_parse_elems(ies, ies_len, &elems, 0) == ParseFailed)
+               return -1;
+
+       if (elems.mesh_id_len < 1)
+               return 0;
+
+       mesh_id = os_malloc(elems.mesh_id_len + 1);
+       if (mesh_id == NULL)
+               return -1;
+
+       os_memcpy(mesh_id, elems.mesh_id, elems.mesh_id_len);
+       mesh_id[elems.mesh_id_len] = '\0';
+       ret = os_snprintf(pos, end - pos, "mesh_id=%s\n", mesh_id);
+       os_free(mesh_id);
+       if (os_snprintf_error(end - pos, ret))
+               return pos - buf;
+       pos += ret;
+
+       if (elems.mesh_config_len > 6) {
+               ret = os_snprintf(pos, end - pos,
+                                 "active_path_selection_protocol_id=0x%02x\n"
+                                 "active_path_selection_metric_id=0x%02x\n"
+                                 "congestion_control_mode_id=0x%02x\n"
+                                 "synchronization_method_id=0x%02x\n"
+                                 "authentication_protocol_id=0x%02x\n"
+                                 "mesh_formation_info=0x%02x\n"
+                                 "mesh_capability=0x%02x\n",
+                                 elems.mesh_config[0], elems.mesh_config[1],
+                                 elems.mesh_config[2], elems.mesh_config[3],
+                                 elems.mesh_config[4], elems.mesh_config[5],
+                                 elems.mesh_config[6]);
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+
+       bss_basic_rate_set = os_malloc(elems.supp_rates_len +
+               elems.ext_supp_rates_len);
+       if (bss_basic_rate_set == NULL)
+               return -1;
+
+       bss_basic_rate_set_len = 0;
+       for (i = 0; i < elems.supp_rates_len; i++) {
+               if (elems.supp_rates[i] & 0x80) {
+                       bss_basic_rate_set[bss_basic_rate_set_len++] =
+                               (elems.supp_rates[i] & 0x7f) * 5;
+               }
+       }
+       for (i = 0; i < elems.ext_supp_rates_len; i++) {
+               if (elems.ext_supp_rates[i] & 0x80) {
+                       bss_basic_rate_set[bss_basic_rate_set_len++] =
+                               (elems.ext_supp_rates[i] & 0x7f) * 5;
+               }
+       }
+       if (bss_basic_rate_set_len > 0) {
+               ret = os_snprintf(pos, end - pos, "bss_basic_rate_set=%d",
+                                 bss_basic_rate_set[0]);
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+
+               for (i = 1; i < bss_basic_rate_set_len; i++) {
+                       ret = os_snprintf(pos, end - pos, " %d",
+                                         bss_basic_rate_set[i]);
+                       if (os_snprintf_error(end - pos, ret))
+                               return pos - buf;
+                       pos += ret;
+               }
+
+               ret = os_snprintf(pos, end - pos, "\n");
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+       os_free(bss_basic_rate_set);
+
+       return pos - buf;
+}
+
+
+int wpas_mesh_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
+                              char *end)
+{
+       return mesh_attr_text(ies, ies_len, buf, end);
+}
+
+
+static int wpas_mesh_get_ifname(struct wpa_supplicant *wpa_s, char *ifname,
+                               size_t len)
+{
+       char *ifname_ptr = wpa_s->ifname;
+       int res;
+
+       res = os_snprintf(ifname, len, "mesh-%s-%d", ifname_ptr,
+                         wpa_s->mesh_if_idx);
+       if (os_snprintf_error(len, res) ||
+           (os_strlen(ifname) >= IFNAMSIZ &&
+            os_strlen(wpa_s->ifname) < IFNAMSIZ)) {
+               /* Try to avoid going over the IFNAMSIZ length limit */
+               res = os_snprintf(ifname, len, "mesh-%d", wpa_s->mesh_if_idx);
+               if (os_snprintf_error(len, res))
+                       return -1;
+       }
+       wpa_s->mesh_if_idx++;
+       return 0;
+}
+
+
+int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname,
+                           size_t len)
+{
+       struct wpa_interface iface;
+       struct wpa_supplicant *mesh_wpa_s;
+       u8 addr[ETH_ALEN];
+
+       if (ifname[0] == '\0' && wpas_mesh_get_ifname(wpa_s, ifname, len) < 0)
+               return -1;
+
+       if (wpa_drv_if_add(wpa_s, WPA_IF_MESH, ifname, NULL, NULL, NULL, addr,
+                          NULL) < 0) {
+               wpa_printf(MSG_ERROR,
+                          "mesh: Failed to create new mesh interface");
+               return -1;
+       }
+       wpa_printf(MSG_INFO, "mesh: Created virtual interface %s addr "
+                  MACSTR, ifname, MAC2STR(addr));
+
+       os_memset(&iface, 0, sizeof(iface));
+       iface.ifname = ifname;
+       iface.driver = wpa_s->driver->name;
+       iface.driver_param = wpa_s->conf->driver_param;
+       iface.ctrl_interface = wpa_s->conf->ctrl_interface;
+
+       mesh_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s);
+       if (!mesh_wpa_s) {
+               wpa_printf(MSG_ERROR,
+                          "mesh: Failed to create new wpa_supplicant interface");
+               wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0);
+               return -1;
+       }
+       mesh_wpa_s->mesh_if_created = 1;
+       return 0;
+}
diff --git a/wpa_supplicant/mesh.h b/wpa_supplicant/mesh.h
new file mode 100755 (executable)
index 0000000..3cb7f1b
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * WPA Supplicant - Basic mesh mode routines
+ * Copyright (c) 2013-2014, cozybit, Inc.  All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MESH_H
+#define MESH_H
+
+int wpa_supplicant_join_mesh(struct wpa_supplicant *wpa_s,
+                            struct wpa_ssid *ssid);
+int wpa_supplicant_leave_mesh(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_mesh_iface_deinit(struct wpa_supplicant *wpa_s,
+                                     struct hostapd_iface *ifmsh);
+int wpas_mesh_scan_result_text(const u8 *ies, size_t ies_len, char *buf,
+                              char *end);
+int wpas_mesh_add_interface(struct wpa_supplicant *wpa_s, char *ifname,
+                           size_t len);
+
+#ifdef CONFIG_MESH
+
+void wpa_mesh_notify_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+                         const u8 *ies, size_t ie_len);
+void wpa_supplicant_mesh_add_scan_ie(struct wpa_supplicant *wpa_s,
+                                    struct wpabuf **extra_ie);
+
+#else /* CONFIG_MESH */
+
+static inline void wpa_mesh_notify_peer(struct wpa_supplicant *wpa_s,
+                                       const u8 *addr,
+                                       const u8 *ies, size_t ie_len)
+{
+}
+
+static inline void wpa_supplicant_mesh_add_scan_ie(struct wpa_supplicant *wpa_s,
+                                                  struct wpabuf **extra_ie)
+{
+}
+
+#endif /* CONFIG_MESH */
+
+#endif /* MESH_H */
diff --git a/wpa_supplicant/mesh_mpm.c b/wpa_supplicant/mesh_mpm.c
new file mode 100755 (executable)
index 0000000..1d6f2be
--- /dev/null
@@ -0,0 +1,1059 @@
+/*
+ * WPA Supplicant - Basic mesh peer management
+ * Copyright (c) 2013-2014, cozybit, Inc.  All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_defs.h"
+#include "ap/hostapd.h"
+#include "ap/sta_info.h"
+#include "ap/ieee802_11.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "mesh_mpm.h"
+#include "mesh_rsn.h"
+
+struct mesh_peer_mgmt_ie {
+       const u8 *proto_id;
+       const u8 *llid;
+       const u8 *plid;
+       const u8 *reason;
+       const u8 *pmk;
+};
+
+static void plink_timer(void *eloop_ctx, void *user_data);
+
+
+enum plink_event {
+       PLINK_UNDEFINED,
+       OPN_ACPT,
+       OPN_RJCT,
+       OPN_IGNR,
+       CNF_ACPT,
+       CNF_RJCT,
+       CNF_IGNR,
+       CLS_ACPT,
+       CLS_IGNR
+};
+
+static const char * const mplstate[] = {
+       [PLINK_LISTEN] = "LISTEN",
+       [PLINK_OPEN_SENT] = "OPEN_SENT",
+       [PLINK_OPEN_RCVD] = "OPEN_RCVD",
+       [PLINK_CNF_RCVD] = "CNF_RCVD",
+       [PLINK_ESTAB] = "ESTAB",
+       [PLINK_HOLDING] = "HOLDING",
+       [PLINK_BLOCKED] = "BLOCKED"
+};
+
+static const char * const mplevent[] = {
+       [PLINK_UNDEFINED] = "UNDEFINED",
+       [OPN_ACPT] = "OPN_ACPT",
+       [OPN_RJCT] = "OPN_RJCT",
+       [OPN_IGNR] = "OPN_IGNR",
+       [CNF_ACPT] = "CNF_ACPT",
+       [CNF_RJCT] = "CNF_RJCT",
+       [CNF_IGNR] = "CNF_IGNR",
+       [CLS_ACPT] = "CLS_ACPT",
+       [CLS_IGNR] = "CLS_IGNR"
+};
+
+
+static int mesh_mpm_parse_peer_mgmt(struct wpa_supplicant *wpa_s,
+                                   u8 action_field,
+                                   const u8 *ie, size_t len,
+                                   struct mesh_peer_mgmt_ie *mpm_ie)
+{
+       os_memset(mpm_ie, 0, sizeof(*mpm_ie));
+
+       /* remove optional PMK at end */
+       if (len >= 16) {
+               len -= 16;
+               mpm_ie->pmk = ie + len - 16;
+       }
+
+       if ((action_field == PLINK_OPEN && len != 4) ||
+           (action_field == PLINK_CONFIRM && len != 6) ||
+           (action_field == PLINK_CLOSE && len != 6 && len != 8)) {
+               wpa_msg(wpa_s, MSG_DEBUG, "MPM: Invalid peer mgmt ie");
+               return -1;
+       }
+
+       /* required fields */
+       if (len < 4)
+               return -1;
+       mpm_ie->proto_id = ie;
+       mpm_ie->llid = ie + 2;
+       ie += 4;
+       len -= 4;
+
+       /* close reason is always present at end for close */
+       if (action_field == PLINK_CLOSE) {
+               if (len < 2)
+                       return -1;
+               mpm_ie->reason = ie + len - 2;
+               len -= 2;
+       }
+
+       /* plid, present for confirm, and possibly close */
+       if (len)
+               mpm_ie->plid = ie;
+
+       return 0;
+}
+
+
+static int plink_free_count(struct hostapd_data *hapd)
+{
+       if (hapd->max_plinks > hapd->num_plinks)
+               return hapd->max_plinks - hapd->num_plinks;
+       return 0;
+}
+
+
+static u16 copy_supp_rates(struct wpa_supplicant *wpa_s,
+                          struct sta_info *sta,
+                          struct ieee802_11_elems *elems)
+{
+       if (!elems->supp_rates) {
+               wpa_msg(wpa_s, MSG_ERROR, "no supported rates from " MACSTR,
+                       MAC2STR(sta->addr));
+               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+       }
+
+       if (elems->supp_rates_len + elems->ext_supp_rates_len >
+           sizeof(sta->supported_rates)) {
+               wpa_msg(wpa_s, MSG_ERROR,
+                       "Invalid supported rates element length " MACSTR
+                       " %d+%d", MAC2STR(sta->addr), elems->supp_rates_len,
+                       elems->ext_supp_rates_len);
+               return WLAN_STATUS_UNSPECIFIED_FAILURE;
+       }
+
+       sta->supported_rates_len = merge_byte_arrays(
+               sta->supported_rates, sizeof(sta->supported_rates),
+               elems->supp_rates, elems->supp_rates_len,
+               elems->ext_supp_rates, elems->ext_supp_rates_len);
+
+       return WLAN_STATUS_SUCCESS;
+}
+
+
+/* return true if elems from a neighbor match this MBSS */
+static Boolean matches_local(struct wpa_supplicant *wpa_s,
+                            struct ieee802_11_elems *elems)
+{
+       struct mesh_conf *mconf = wpa_s->ifmsh->mconf;
+
+       if (elems->mesh_config_len < 5)
+               return FALSE;
+
+       return (mconf->meshid_len == elems->mesh_id_len &&
+               os_memcmp(mconf->meshid, elems->mesh_id,
+                         elems->mesh_id_len) == 0 &&
+               mconf->mesh_pp_id == elems->mesh_config[0] &&
+               mconf->mesh_pm_id == elems->mesh_config[1] &&
+               mconf->mesh_cc_id == elems->mesh_config[2] &&
+               mconf->mesh_sp_id == elems->mesh_config[3] &&
+               mconf->mesh_auth_id == elems->mesh_config[4]);
+}
+
+
+/* check if local link id is already used with another peer */
+static Boolean llid_in_use(struct wpa_supplicant *wpa_s, u16 llid)
+{
+       struct sta_info *sta;
+       struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+
+       for (sta = hapd->sta_list; sta; sta = sta->next) {
+               if (sta->my_lid == llid)
+                       return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+/* generate an llid for a link and set to initial state */
+static void mesh_mpm_init_link(struct wpa_supplicant *wpa_s,
+                              struct sta_info *sta)
+{
+       u16 llid;
+
+       do {
+               if (os_get_random((u8 *) &llid, sizeof(llid)) < 0)
+                       continue;
+       } while (!llid || llid_in_use(wpa_s, llid));
+
+       sta->my_lid = llid;
+       sta->peer_lid = 0;
+
+       /*
+        * We do not use wpa_mesh_set_plink_state() here because there is no
+        * entry in kernel yet.
+        */
+       sta->plink_state = PLINK_LISTEN;
+}
+
+
+static void mesh_mpm_send_plink_action(struct wpa_supplicant *wpa_s,
+                                      struct sta_info *sta,
+                                      enum plink_action_field type,
+                                      u16 close_reason)
+{
+       struct wpabuf *buf;
+       struct hostapd_iface *ifmsh = wpa_s->ifmsh;
+       struct hostapd_data *bss = ifmsh->bss[0];
+       struct mesh_conf *conf = ifmsh->mconf;
+       u8 supp_rates[2 + 2 + 32];
+#ifdef CONFIG_IEEE80211N
+       u8 ht_capa_oper[2 + 26 + 2 + 22];
+#endif /* CONFIG_IEEE80211N */
+       u8 *pos, *cat;
+       u8 ie_len, add_plid = 0;
+       int ret;
+       int ampe = conf->security & MESH_CONF_SEC_AMPE;
+       size_t buf_len;
+
+       if (!sta)
+               return;
+
+       buf_len = 2 +      /* capability info */
+                 2 +      /* AID */
+                 2 + 8 +  /* supported rates */
+                 2 + (32 - 8) +
+                 2 + 32 + /* mesh ID */
+                 2 + 7 +  /* mesh config */
+                 2 + 23 + /* peering management */
+                 2 + 96 + /* AMPE */
+                 2 + 16;  /* MIC */
+#ifdef CONFIG_IEEE80211N
+       if (type != PLINK_CLOSE && wpa_s->mesh_ht_enabled) {
+               buf_len += 2 + 26 + /* HT capabilities */
+                          2 + 22;  /* HT operation */
+       }
+#endif /* CONFIG_IEEE80211N */
+       buf = wpabuf_alloc(buf_len);
+       if (!buf)
+               return;
+
+       cat = wpabuf_mhead_u8(buf);
+       wpabuf_put_u8(buf, WLAN_ACTION_SELF_PROTECTED);
+       wpabuf_put_u8(buf, type);
+
+       if (type != PLINK_CLOSE) {
+               u8 info;
+
+               /* capability info */
+               wpabuf_put_le16(buf, ampe ? IEEE80211_CAP_PRIVACY : 0);
+
+               /* aid */
+               if (type == PLINK_CONFIRM)
+                       wpabuf_put_le16(buf, sta->peer_lid);
+
+               /* IE: supp + ext. supp rates */
+               pos = hostapd_eid_supp_rates(bss, supp_rates);
+               pos = hostapd_eid_ext_supp_rates(bss, pos);
+               wpabuf_put_data(buf, supp_rates, pos - supp_rates);
+
+               /* IE: Mesh ID */
+               wpabuf_put_u8(buf, WLAN_EID_MESH_ID);
+               wpabuf_put_u8(buf, conf->meshid_len);
+               wpabuf_put_data(buf, conf->meshid, conf->meshid_len);
+
+               /* IE: mesh conf */
+               wpabuf_put_u8(buf, WLAN_EID_MESH_CONFIG);
+               wpabuf_put_u8(buf, 7);
+               wpabuf_put_u8(buf, conf->mesh_pp_id);
+               wpabuf_put_u8(buf, conf->mesh_pm_id);
+               wpabuf_put_u8(buf, conf->mesh_cc_id);
+               wpabuf_put_u8(buf, conf->mesh_sp_id);
+               wpabuf_put_u8(buf, conf->mesh_auth_id);
+               info = (bss->num_plinks > 63 ? 63 : bss->num_plinks) << 1;
+               /* TODO: Add Connected to Mesh Gate/AS subfields */
+               wpabuf_put_u8(buf, info);
+               /* always forwarding & accepting plinks for now */
+               wpabuf_put_u8(buf, 0x1 | 0x8);
+       } else {        /* Peer closing frame */
+               /* IE: Mesh ID */
+               wpabuf_put_u8(buf, WLAN_EID_MESH_ID);
+               wpabuf_put_u8(buf, conf->meshid_len);
+               wpabuf_put_data(buf, conf->meshid, conf->meshid_len);
+       }
+
+       /* IE: Mesh Peering Management element */
+       ie_len = 4;
+       if (ampe)
+               ie_len += PMKID_LEN;
+       switch (type) {
+       case PLINK_OPEN:
+               break;
+       case PLINK_CONFIRM:
+               ie_len += 2;
+               add_plid = 1;
+               break;
+       case PLINK_CLOSE:
+               ie_len += 2;
+               add_plid = 1;
+               ie_len += 2; /* reason code */
+               break;
+       }
+
+       wpabuf_put_u8(buf, WLAN_EID_PEER_MGMT);
+       wpabuf_put_u8(buf, ie_len);
+       /* peering protocol */
+       if (ampe)
+               wpabuf_put_le16(buf, 1);
+       else
+               wpabuf_put_le16(buf, 0);
+       wpabuf_put_le16(buf, sta->my_lid);
+       if (add_plid)
+               wpabuf_put_le16(buf, sta->peer_lid);
+       if (type == PLINK_CLOSE)
+               wpabuf_put_le16(buf, close_reason);
+       if (ampe) {
+               if (sta->sae == NULL) {
+                       wpa_msg(wpa_s, MSG_INFO, "Mesh MPM: no SAE session");
+                       goto fail;
+               }
+               mesh_rsn_get_pmkid(wpa_s->mesh_rsn, sta,
+                                  wpabuf_put(buf, PMKID_LEN));
+       }
+
+#ifdef CONFIG_IEEE80211N
+       if (type != PLINK_CLOSE && wpa_s->mesh_ht_enabled) {
+               pos = hostapd_eid_ht_capabilities(bss, ht_capa_oper);
+               pos = hostapd_eid_ht_operation(bss, pos);
+               wpabuf_put_data(buf, ht_capa_oper, pos - ht_capa_oper);
+       }
+#endif /* CONFIG_IEEE80211N */
+
+       if (ampe && mesh_rsn_protect_frame(wpa_s->mesh_rsn, sta, cat, buf)) {
+               wpa_msg(wpa_s, MSG_INFO,
+                       "Mesh MPM: failed to add AMPE and MIC IE");
+               goto fail;
+       }
+
+       ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0,
+                                 sta->addr, wpa_s->own_addr, wpa_s->own_addr,
+                                 wpabuf_head(buf), wpabuf_len(buf), 0);
+       if (ret < 0)
+               wpa_msg(wpa_s, MSG_INFO,
+                       "Mesh MPM: failed to send peering frame");
+
+fail:
+       wpabuf_free(buf);
+}
+
+
+/* configure peering state in ours and driver's station entry */
+void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s,
+                             struct sta_info *sta,
+                             enum mesh_plink_state state)
+{
+       struct hostapd_sta_add_params params;
+       int ret;
+
+       sta->plink_state = state;
+
+       os_memset(&params, 0, sizeof(params));
+       params.addr = sta->addr;
+       params.plink_state = state;
+       params.set = 1;
+
+       wpa_msg(wpa_s, MSG_DEBUG, "MPM set " MACSTR " into %s",
+               MAC2STR(sta->addr), mplstate[state]);
+       ret = wpa_drv_sta_add(wpa_s, &params);
+       if (ret) {
+               wpa_msg(wpa_s, MSG_ERROR, "Driver failed to set " MACSTR
+                       ": %d", MAC2STR(sta->addr), ret);
+       }
+}
+
+
+static void mesh_mpm_fsm_restart(struct wpa_supplicant *wpa_s,
+                                struct sta_info *sta)
+{
+       struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+
+       eloop_cancel_timeout(plink_timer, wpa_s, sta);
+
+       ap_free_sta(hapd, sta);
+}
+
+
+static void plink_timer(void *eloop_ctx, void *user_data)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       struct sta_info *sta = user_data;
+       u16 reason = 0;
+       struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+
+       switch (sta->plink_state) {
+       case PLINK_OPEN_RCVD:
+       case PLINK_OPEN_SENT:
+               /* retry timer */
+               if (sta->mpm_retries < conf->dot11MeshMaxRetries) {
+                       eloop_register_timeout(
+                               conf->dot11MeshRetryTimeout / 1000,
+                               (conf->dot11MeshRetryTimeout % 1000) * 1000,
+                               plink_timer, wpa_s, sta);
+                       mesh_mpm_send_plink_action(wpa_s, sta, PLINK_OPEN, 0);
+                       sta->mpm_retries++;
+                       break;
+               }
+               reason = WLAN_REASON_MESH_MAX_RETRIES;
+               /* fall through on else */
+
+       case PLINK_CNF_RCVD:
+               /* confirm timer */
+               if (!reason)
+                       reason = WLAN_REASON_MESH_CONFIRM_TIMEOUT;
+               wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
+               eloop_register_timeout(conf->dot11MeshHoldingTimeout / 1000,
+                       (conf->dot11MeshHoldingTimeout % 1000) * 1000,
+                       plink_timer, wpa_s, sta);
+               mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CLOSE, reason);
+               break;
+       case PLINK_HOLDING:
+               /* holding timer */
+               mesh_mpm_fsm_restart(wpa_s, sta);
+               break;
+       default:
+               break;
+       }
+}
+
+
+/* initiate peering with station */
+static void
+mesh_mpm_plink_open(struct wpa_supplicant *wpa_s, struct sta_info *sta,
+                   enum mesh_plink_state next_state)
+{
+       struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+
+       eloop_cancel_timeout(plink_timer, wpa_s, sta);
+       eloop_register_timeout(conf->dot11MeshRetryTimeout / 1000,
+                              (conf->dot11MeshRetryTimeout % 1000) * 1000,
+                              plink_timer, wpa_s, sta);
+       mesh_mpm_send_plink_action(wpa_s, sta, PLINK_OPEN, 0);
+       wpa_mesh_set_plink_state(wpa_s, sta, next_state);
+}
+
+
+int mesh_mpm_plink_close(struct hostapd_data *hapd,
+                        struct sta_info *sta, void *ctx)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       int reason = WLAN_REASON_MESH_PEERING_CANCELLED;
+
+       if (sta) {
+               wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
+               mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CLOSE, reason);
+               wpa_printf(MSG_DEBUG, "MPM closing plink sta=" MACSTR,
+                          MAC2STR(sta->addr));
+               eloop_cancel_timeout(plink_timer, wpa_s, sta);
+               return 0;
+       }
+
+       return 1;
+}
+
+
+void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh)
+{
+       struct hostapd_data *hapd = ifmsh->bss[0];
+
+       /* notify peers we're leaving */
+       ap_for_each_sta(hapd, mesh_mpm_plink_close, wpa_s);
+
+       hapd->num_plinks = 0;
+       hostapd_free_stas(hapd);
+}
+
+
+/* for mesh_rsn to indicate this peer has completed authentication, and we're
+ * ready to start AMPE */
+void mesh_mpm_auth_peer(struct wpa_supplicant *wpa_s, const u8 *addr)
+{
+       struct hostapd_data *data = wpa_s->ifmsh->bss[0];
+       struct hostapd_sta_add_params params;
+       struct sta_info *sta;
+       int ret;
+
+       sta = ap_get_sta(data, addr);
+       if (!sta) {
+               wpa_msg(wpa_s, MSG_DEBUG, "no such mesh peer");
+               return;
+       }
+
+       /* TODO: Should do nothing if this STA is already authenticated, but
+        * the AP code already sets this flag. */
+       sta->flags |= WLAN_STA_AUTH;
+
+       mesh_rsn_init_ampe_sta(wpa_s, sta);
+
+       os_memset(&params, 0, sizeof(params));
+       params.addr = sta->addr;
+       params.flags = WPA_STA_AUTHENTICATED | WPA_STA_AUTHORIZED;
+       params.set = 1;
+
+       wpa_msg(wpa_s, MSG_DEBUG, "MPM authenticating " MACSTR,
+               MAC2STR(sta->addr));
+       ret = wpa_drv_sta_add(wpa_s, &params);
+       if (ret) {
+               wpa_msg(wpa_s, MSG_ERROR,
+                       "Driver failed to set " MACSTR ": %d",
+                       MAC2STR(sta->addr), ret);
+       }
+
+       if (!sta->my_lid)
+               mesh_mpm_init_link(wpa_s, sta);
+
+       mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT);
+}
+
+/*
+ * Initialize a sta_info structure for a peer and upload it into the driver
+ * in preparation for beginning authentication or peering. This is done when a
+ * Beacon (secure or open mesh) or a peering open frame (for open mesh) is
+ * received from the peer for the first time.
+ */
+static struct sta_info * mesh_mpm_add_peer(struct wpa_supplicant *wpa_s,
+                                          const u8 *addr,
+                                          struct ieee802_11_elems *elems)
+{
+       struct hostapd_sta_add_params params;
+       struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+       struct hostapd_data *data = wpa_s->ifmsh->bss[0];
+       struct sta_info *sta;
+       int ret;
+
+       sta = ap_get_sta(data, addr);
+       if (!sta) {
+               sta = ap_sta_add(data, addr);
+               if (!sta)
+                       return NULL;
+       }
+
+       /* initialize sta */
+       if (copy_supp_rates(wpa_s, sta, elems)) {
+               ap_free_sta(data, sta);
+               return NULL;
+       }
+
+       mesh_mpm_init_link(wpa_s, sta);
+
+#ifdef CONFIG_IEEE80211N
+       copy_sta_ht_capab(data, sta, elems->ht_capabilities,
+                       elems->ht_capabilities_len);
+       update_ht_state(data, sta);
+#endif /* CONFIG_IEEE80211N */
+
+       /* insert into driver */
+       os_memset(&params, 0, sizeof(params));
+       params.supp_rates = sta->supported_rates;
+       params.supp_rates_len = sta->supported_rates_len;
+       params.addr = addr;
+       params.plink_state = sta->plink_state;
+       params.aid = sta->peer_lid;
+       params.listen_interval = 100;
+       params.ht_capabilities = sta->ht_capabilities;
+       params.flags |= WPA_STA_WMM;
+       params.flags_mask |= WPA_STA_AUTHENTICATED;
+       if (conf->security == MESH_CONF_SEC_NONE) {
+               params.flags |= WPA_STA_AUTHORIZED;
+               params.flags |= WPA_STA_AUTHENTICATED;
+       } else {
+               sta->flags |= WLAN_STA_MFP;
+               params.flags |= WPA_STA_MFP;
+       }
+
+       ret = wpa_drv_sta_add(wpa_s, &params);
+       if (ret) {
+               wpa_msg(wpa_s, MSG_ERROR,
+                       "Driver failed to insert " MACSTR ": %d",
+                       MAC2STR(addr), ret);
+               ap_free_sta(data, sta);
+               return NULL;
+       }
+
+       return sta;
+}
+
+
+void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+                           struct ieee802_11_elems *elems)
+{
+       struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+       struct hostapd_data *data = wpa_s->ifmsh->bss[0];
+       struct sta_info *sta;
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+       sta = mesh_mpm_add_peer(wpa_s, addr, elems);
+       if (!sta)
+               return;
+
+       if (ssid && ssid->no_auto_peer) {
+               wpa_msg(wpa_s, MSG_INFO, "will not initiate new peer link with "
+                       MACSTR " because of no_auto_peer", MAC2STR(addr));
+               if (data->mesh_pending_auth) {
+                       struct os_reltime age;
+                       const struct ieee80211_mgmt *mgmt;
+                       struct hostapd_frame_info fi;
+
+                       mgmt = wpabuf_head(data->mesh_pending_auth);
+                       os_reltime_age(&data->mesh_pending_auth_time, &age);
+                       if (age.sec < 2 &&
+                           os_memcmp(mgmt->sa, addr, ETH_ALEN) == 0) {
+                               wpa_printf(MSG_DEBUG,
+                                          "mesh: Process pending Authentication frame from %u.%06u seconds ago",
+                                          (unsigned int) age.sec,
+                                          (unsigned int) age.usec);
+                               os_memset(&fi, 0, sizeof(fi));
+                               ieee802_11_mgmt(
+                                       data,
+                                       wpabuf_head(data->mesh_pending_auth),
+                                       wpabuf_len(data->mesh_pending_auth),
+                                       &fi);
+                       }
+                       wpabuf_free(data->mesh_pending_auth);
+                       data->mesh_pending_auth = NULL;
+               }
+               return;
+       }
+
+       if (conf->security == MESH_CONF_SEC_NONE)
+               mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_SENT);
+       else
+               mesh_rsn_auth_sae_sta(wpa_s, sta);
+}
+
+
+void mesh_mpm_mgmt_rx(struct wpa_supplicant *wpa_s, struct rx_mgmt *rx_mgmt)
+{
+       struct hostapd_frame_info fi;
+
+       os_memset(&fi, 0, sizeof(fi));
+       fi.datarate = rx_mgmt->datarate;
+       fi.ssi_signal = rx_mgmt->ssi_signal;
+       ieee802_11_mgmt(wpa_s->ifmsh->bss[0], rx_mgmt->frame,
+                       rx_mgmt->frame_len, &fi);
+}
+
+
+static void mesh_mpm_plink_estab(struct wpa_supplicant *wpa_s,
+                                struct sta_info *sta)
+{
+       struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+       struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+       u8 seq[6] = {};
+
+       wpa_msg(wpa_s, MSG_INFO, "mesh plink with " MACSTR " established",
+               MAC2STR(sta->addr));
+
+       if (conf->security & MESH_CONF_SEC_AMPE) {
+               wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 0, 0,
+                               seq, sizeof(seq), sta->mtk, sizeof(sta->mtk));
+               wpa_drv_set_key(wpa_s, WPA_ALG_CCMP, sta->addr, 1, 0,
+                               seq, sizeof(seq),
+                               sta->mgtk, sizeof(sta->mgtk));
+               wpa_drv_set_key(wpa_s, WPA_ALG_IGTK, sta->addr, 4, 0,
+                               seq, sizeof(seq),
+                               sta->mgtk, sizeof(sta->mgtk));
+
+               wpa_hexdump_key(MSG_DEBUG, "mtk:", sta->mtk, sizeof(sta->mtk));
+               wpa_hexdump_key(MSG_DEBUG, "mgtk:",
+                               sta->mgtk, sizeof(sta->mgtk));
+       }
+
+       wpa_mesh_set_plink_state(wpa_s, sta, PLINK_ESTAB);
+       hapd->num_plinks++;
+
+       sta->flags |= WLAN_STA_ASSOC;
+
+       eloop_cancel_timeout(plink_timer, wpa_s, sta);
+
+       /* Send ctrl event */
+       wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_PEER_CONNECTED MACSTR,
+                    MAC2STR(sta->addr));
+}
+
+
+static void mesh_mpm_fsm(struct wpa_supplicant *wpa_s, struct sta_info *sta,
+                        enum plink_event event)
+{
+       struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+       struct mesh_conf *conf = wpa_s->ifmsh->mconf;
+       u16 reason = 0;
+
+       wpa_msg(wpa_s, MSG_DEBUG, "MPM " MACSTR " state %s event %s",
+               MAC2STR(sta->addr), mplstate[sta->plink_state],
+               mplevent[event]);
+
+       switch (sta->plink_state) {
+       case PLINK_LISTEN:
+               switch (event) {
+               case CLS_ACPT:
+                       mesh_mpm_fsm_restart(wpa_s, sta);
+                       break;
+               case OPN_ACPT:
+                       mesh_mpm_plink_open(wpa_s, sta, PLINK_OPEN_RCVD);
+                       mesh_mpm_send_plink_action(wpa_s, sta, PLINK_CONFIRM,
+                                                  0);
+                       break;
+               default:
+                       break;
+               }
+               break;
+       case PLINK_OPEN_SENT:
+               switch (event) {
+               case OPN_RJCT:
+               case CNF_RJCT:
+                       reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION;
+                       /* fall-through */
+               case CLS_ACPT:
+                       wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
+                       if (!reason)
+                               reason = WLAN_REASON_MESH_CLOSE_RCVD;
+                       eloop_register_timeout(
+                               conf->dot11MeshHoldingTimeout / 1000,
+                               (conf->dot11MeshHoldingTimeout % 1000) * 1000,
+                               plink_timer, wpa_s, sta);
+                       mesh_mpm_send_plink_action(wpa_s, sta,
+                                                  PLINK_CLOSE, reason);
+                       break;
+               case OPN_ACPT:
+                       /* retry timer is left untouched */
+                       wpa_mesh_set_plink_state(wpa_s, sta, PLINK_OPEN_RCVD);
+                       mesh_mpm_send_plink_action(wpa_s, sta,
+                                                  PLINK_CONFIRM, 0);
+                       break;
+               case CNF_ACPT:
+                       wpa_mesh_set_plink_state(wpa_s, sta, PLINK_CNF_RCVD);
+                       eloop_register_timeout(
+                               conf->dot11MeshConfirmTimeout / 1000,
+                               (conf->dot11MeshConfirmTimeout % 1000) * 1000,
+                               plink_timer, wpa_s, sta);
+                       break;
+               default:
+                       break;
+               }
+               break;
+       case PLINK_OPEN_RCVD:
+               switch (event) {
+               case OPN_RJCT:
+               case CNF_RJCT:
+                       reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION;
+                       /* fall-through */
+               case CLS_ACPT:
+                       wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
+                       if (!reason)
+                               reason = WLAN_REASON_MESH_CLOSE_RCVD;
+                       eloop_register_timeout(
+                               conf->dot11MeshHoldingTimeout / 1000,
+                               (conf->dot11MeshHoldingTimeout % 1000) * 1000,
+                               plink_timer, wpa_s, sta);
+                       sta->mpm_close_reason = reason;
+                       mesh_mpm_send_plink_action(wpa_s, sta,
+                                                  PLINK_CLOSE, reason);
+                       break;
+               case OPN_ACPT:
+                       mesh_mpm_send_plink_action(wpa_s, sta,
+                                                  PLINK_CONFIRM, 0);
+                       break;
+               case CNF_ACPT:
+                       if (conf->security & MESH_CONF_SEC_AMPE)
+                               mesh_rsn_derive_mtk(wpa_s, sta);
+                       mesh_mpm_plink_estab(wpa_s, sta);
+                       break;
+               default:
+                       break;
+               }
+               break;
+       case PLINK_CNF_RCVD:
+               switch (event) {
+               case OPN_RJCT:
+               case CNF_RJCT:
+                       reason = WLAN_REASON_MESH_CONFIG_POLICY_VIOLATION;
+                       /* fall-through */
+               case CLS_ACPT:
+                       wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
+                       if (!reason)
+                               reason = WLAN_REASON_MESH_CLOSE_RCVD;
+                       eloop_register_timeout(
+                               conf->dot11MeshHoldingTimeout / 1000,
+                               (conf->dot11MeshHoldingTimeout % 1000) * 1000,
+                               plink_timer, wpa_s, sta);
+                       sta->mpm_close_reason = reason;
+                       mesh_mpm_send_plink_action(wpa_s, sta,
+                                                  PLINK_CLOSE, reason);
+                       break;
+               case OPN_ACPT:
+                       mesh_mpm_plink_estab(wpa_s, sta);
+                       mesh_mpm_send_plink_action(wpa_s, sta,
+                                                  PLINK_CONFIRM, 0);
+                       break;
+               default:
+                       break;
+               }
+               break;
+       case PLINK_ESTAB:
+               switch (event) {
+               case CLS_ACPT:
+                       wpa_mesh_set_plink_state(wpa_s, sta, PLINK_HOLDING);
+                       reason = WLAN_REASON_MESH_CLOSE_RCVD;
+
+                       eloop_register_timeout(
+                               conf->dot11MeshHoldingTimeout / 1000,
+                               (conf->dot11MeshHoldingTimeout % 1000) * 1000,
+                               plink_timer, wpa_s, sta);
+                       sta->mpm_close_reason = reason;
+
+                       wpa_msg(wpa_s, MSG_INFO, "mesh plink with " MACSTR
+                               " closed with reason %d",
+                               MAC2STR(sta->addr), reason);
+
+                       wpa_msg_ctrl(wpa_s, MSG_INFO,
+                                    MESH_PEER_DISCONNECTED MACSTR,
+                                    MAC2STR(sta->addr));
+
+                       hapd->num_plinks--;
+
+                       mesh_mpm_send_plink_action(wpa_s, sta,
+                                                  PLINK_CLOSE, reason);
+                       break;
+               case OPN_ACPT:
+                       mesh_mpm_send_plink_action(wpa_s, sta,
+                                                  PLINK_CONFIRM, 0);
+                       break;
+               default:
+                       break;
+               }
+               break;
+       case PLINK_HOLDING:
+               switch (event) {
+               case CLS_ACPT:
+                       mesh_mpm_fsm_restart(wpa_s, sta);
+                       break;
+               case OPN_ACPT:
+               case CNF_ACPT:
+               case OPN_RJCT:
+               case CNF_RJCT:
+                       reason = sta->mpm_close_reason;
+                       mesh_mpm_send_plink_action(wpa_s, sta,
+                                                  PLINK_CLOSE, reason);
+                       break;
+               default:
+                       break;
+               }
+               break;
+       default:
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "Unsupported MPM event %s for state %s",
+                       mplevent[event], mplstate[sta->plink_state]);
+               break;
+       }
+}
+
+
+void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s,
+                       const struct ieee80211_mgmt *mgmt, size_t len)
+{
+       u8 action_field;
+       struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+       struct mesh_conf *mconf = wpa_s->ifmsh->mconf;
+       struct sta_info *sta;
+       u16 plid = 0, llid = 0;
+       enum plink_event event;
+       struct ieee802_11_elems elems;
+       struct mesh_peer_mgmt_ie peer_mgmt_ie;
+       const u8 *ies;
+       size_t ie_len;
+       int ret;
+
+       if (mgmt->u.action.category != WLAN_ACTION_SELF_PROTECTED)
+               return;
+
+       action_field = mgmt->u.action.u.slf_prot_action.action;
+       if (action_field != PLINK_OPEN &&
+           action_field != PLINK_CONFIRM &&
+           action_field != PLINK_CLOSE)
+               return;
+
+       ies = mgmt->u.action.u.slf_prot_action.variable;
+       ie_len = (const u8 *) mgmt + len -
+               mgmt->u.action.u.slf_prot_action.variable;
+
+       /* at least expect mesh id and peering mgmt */
+       if (ie_len < 2 + 2) {
+               wpa_printf(MSG_DEBUG,
+                          "MPM: Ignore too short action frame %u ie_len %u",
+                          action_field, (unsigned int) ie_len);
+               return;
+       }
+       wpa_printf(MSG_DEBUG, "MPM: Received PLINK action %u", action_field);
+
+       if (action_field == PLINK_OPEN || action_field == PLINK_CONFIRM) {
+               wpa_printf(MSG_DEBUG, "MPM: Capability 0x%x",
+                          WPA_GET_LE16(ies));
+               ies += 2;       /* capability */
+               ie_len -= 2;
+       }
+       if (action_field == PLINK_CONFIRM) {
+               wpa_printf(MSG_DEBUG, "MPM: AID 0x%x", WPA_GET_LE16(ies));
+               ies += 2;       /* aid */
+               ie_len -= 2;
+       }
+
+       /* check for mesh peering, mesh id and mesh config IEs */
+       if (ieee802_11_parse_elems(ies, ie_len, &elems, 0) == ParseFailed) {
+               wpa_printf(MSG_DEBUG, "MPM: Failed to parse PLINK IEs");
+               return;
+       }
+       if (!elems.peer_mgmt) {
+               wpa_printf(MSG_DEBUG,
+                          "MPM: No Mesh Peering Management element");
+               return;
+       }
+       if (action_field != PLINK_CLOSE) {
+               if (!elems.mesh_id || !elems.mesh_config) {
+                       wpa_printf(MSG_DEBUG,
+                                  "MPM: No Mesh ID or Mesh Configuration element");
+                       return;
+               }
+
+               if (!matches_local(wpa_s, &elems)) {
+                       wpa_printf(MSG_DEBUG,
+                                  "MPM: Mesh ID or Mesh Configuration element do not match local MBSS");
+                       return;
+               }
+       }
+
+       ret = mesh_mpm_parse_peer_mgmt(wpa_s, action_field,
+                                      elems.peer_mgmt,
+                                      elems.peer_mgmt_len,
+                                      &peer_mgmt_ie);
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "MPM: Mesh parsing rejected frame");
+               return;
+       }
+
+       /* the sender's llid is our plid and vice-versa */
+       plid = WPA_GET_LE16(peer_mgmt_ie.llid);
+       if (peer_mgmt_ie.plid)
+               llid = WPA_GET_LE16(peer_mgmt_ie.plid);
+       wpa_printf(MSG_DEBUG, "MPM: plid=0x%x llid=0x%x", plid, llid);
+
+       sta = ap_get_sta(hapd, mgmt->sa);
+
+       /*
+        * If this is an open frame from an unknown STA, and this is an
+        * open mesh, then go ahead and add the peer before proceeding.
+        */
+       if (!sta && action_field == PLINK_OPEN &&
+           !(mconf->security & MESH_CONF_SEC_AMPE))
+               sta = mesh_mpm_add_peer(wpa_s, mgmt->sa, &elems);
+
+       if (!sta) {
+               wpa_printf(MSG_DEBUG, "MPM: No STA entry for peer");
+               return;
+       }
+
+#ifdef CONFIG_SAE
+       /* peer is in sae_accepted? */
+       if (sta->sae && sta->sae->state != SAE_ACCEPTED) {
+               wpa_printf(MSG_DEBUG, "MPM: SAE not yet accepted for peer");
+               return;
+       }
+#endif /* CONFIG_SAE */
+
+       if (!sta->my_lid)
+               mesh_mpm_init_link(wpa_s, sta);
+
+       if ((mconf->security & MESH_CONF_SEC_AMPE) &&
+           mesh_rsn_process_ampe(wpa_s, sta, &elems,
+                                 &mgmt->u.action.category,
+                                 ies, ie_len)) {
+               wpa_printf(MSG_DEBUG, "MPM: RSN process rejected frame");
+               return;
+       }
+
+       if (sta->plink_state == PLINK_BLOCKED) {
+               wpa_printf(MSG_DEBUG, "MPM: PLINK_BLOCKED");
+               return;
+       }
+
+       /* Now we will figure out the appropriate event... */
+       switch (action_field) {
+       case PLINK_OPEN:
+               if (plink_free_count(hapd) == 0) {
+                       event = OPN_IGNR;
+                       wpa_printf(MSG_INFO,
+                                  "MPM: Peer link num over quota(%d)",
+                                  hapd->max_plinks);
+               } else if (sta->peer_lid && sta->peer_lid != plid) {
+                       event = OPN_IGNR;
+               } else {
+                       sta->peer_lid = plid;
+                       event = OPN_ACPT;
+               }
+               break;
+       case PLINK_CONFIRM:
+               if (plink_free_count(hapd) == 0) {
+                       event = CNF_IGNR;
+                       wpa_printf(MSG_INFO,
+                                  "MPM: Peer link num over quota(%d)",
+                                  hapd->max_plinks);
+               } else if (sta->my_lid != llid ||
+                          (sta->peer_lid && sta->peer_lid != plid)) {
+                       event = CNF_IGNR;
+               } else {
+                       if (!sta->peer_lid)
+                               sta->peer_lid = plid;
+                       event = CNF_ACPT;
+               }
+               break;
+       case PLINK_CLOSE:
+               if (sta->plink_state == PLINK_ESTAB)
+                       /* Do not check for llid or plid. This does not
+                        * follow the standard but since multiple plinks
+                        * per cand are not supported, it is necessary in
+                        * order to avoid a livelock when MP A sees an
+                        * establish peer link to MP B but MP B does not
+                        * see it. This can be caused by a timeout in
+                        * B's peer link establishment or B being
+                        * restarted.
+                        */
+                       event = CLS_ACPT;
+               else if (sta->peer_lid != plid)
+                       event = CLS_IGNR;
+               else if (peer_mgmt_ie.plid && sta->my_lid != llid)
+                       event = CLS_IGNR;
+               else
+                       event = CLS_ACPT;
+               break;
+       default:
+               /*
+                * This cannot be hit due to the action_field check above, but
+                * compilers may not be able to figure that out and can warn
+                * about uninitialized event below.
+                */
+               return;
+       }
+       mesh_mpm_fsm(wpa_s, sta, event);
+}
+
+
+/* called by ap_free_sta */
+void mesh_mpm_free_sta(struct sta_info *sta)
+{
+       eloop_cancel_timeout(plink_timer, ELOOP_ALL_CTX, sta);
+       eloop_cancel_timeout(mesh_auth_timer, ELOOP_ALL_CTX, sta);
+}
diff --git a/wpa_supplicant/mesh_mpm.h b/wpa_supplicant/mesh_mpm.h
new file mode 100755 (executable)
index 0000000..7ebaef0
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * WPA Supplicant - Basic mesh peer management
+ * Copyright (c) 2013-2014, cozybit, Inc.  All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MESH_MPM_H
+#define MESH_MPM_H
+
+/* notify MPM of new mesh peer to be inserted in MPM and driver */
+void wpa_mesh_new_mesh_peer(struct wpa_supplicant *wpa_s, const u8 *addr,
+                           struct ieee802_11_elems *elems);
+void mesh_mpm_deinit(struct wpa_supplicant *wpa_s, struct hostapd_iface *ifmsh);
+void mesh_mpm_auth_peer(struct wpa_supplicant *wpa_s, const u8 *addr);
+void mesh_mpm_free_sta(struct sta_info *sta);
+void wpa_mesh_set_plink_state(struct wpa_supplicant *wpa_s,
+                             struct sta_info *sta,
+                             enum mesh_plink_state state);
+
+#ifdef CONFIG_MESH
+
+void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s,
+                       const struct ieee80211_mgmt *mgmt, size_t len);
+void mesh_mpm_mgmt_rx(struct wpa_supplicant *wpa_s, struct rx_mgmt *rx_mgmt);
+
+#else /* CONFIG_MESH */
+
+static inline void mesh_mpm_action_rx(struct wpa_supplicant *wpa_s,
+                                     const struct ieee80211_mgmt *mgmt,
+                                     size_t len)
+{
+}
+
+static inline void mesh_mpm_mgmt_rx(struct wpa_supplicant *wpa_s,
+                                   struct rx_mgmt *rx_mgmt)
+{
+}
+
+#endif /* CONFIG_MESH */
+
+#endif /* MESH_MPM_H */
diff --git a/wpa_supplicant/mesh_rsn.c b/wpa_supplicant/mesh_rsn.c
new file mode 100755 (executable)
index 0000000..936002d
--- /dev/null
@@ -0,0 +1,574 @@
+/*
+ * WPA Supplicant - Mesh RSN routines
+ * Copyright (c) 2013-2014, cozybit, Inc.  All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "utils/eloop.h"
+#include "crypto/sha256.h"
+#include "crypto/random.h"
+#include "crypto/aes.h"
+#include "crypto/aes_siv.h"
+#include "rsn_supp/wpa.h"
+#include "ap/hostapd.h"
+#include "ap/wpa_auth.h"
+#include "ap/sta_info.h"
+#include "ap/ieee802_11.h"
+#include "wpa_supplicant_i.h"
+#include "driver_i.h"
+#include "wpas_glue.h"
+#include "mesh_mpm.h"
+#include "mesh_rsn.h"
+
+#define MESH_AUTH_TIMEOUT 10
+#define MESH_AUTH_RETRY 3
+#define MESH_AUTH_BLOCK_DURATION 3600
+
+void mesh_auth_timer(void *eloop_ctx, void *user_data)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       struct sta_info *sta = user_data;
+
+       if (sta->sae->state != SAE_ACCEPTED) {
+               wpa_printf(MSG_DEBUG, "AUTH: Re-authenticate with " MACSTR
+                          " (attempt %d) ",
+                          MAC2STR(sta->addr), sta->sae_auth_retry);
+               wpa_msg(wpa_s, MSG_INFO, MESH_SAE_AUTH_FAILURE "addr=" MACSTR,
+                       MAC2STR(sta->addr));
+               if (sta->sae_auth_retry < MESH_AUTH_RETRY) {
+                       mesh_rsn_auth_sae_sta(wpa_s, sta);
+               } else {
+                       if (sta->sae_auth_retry > MESH_AUTH_RETRY) {
+                               ap_free_sta(wpa_s->ifmsh->bss[0], sta);
+                               return;
+                       }
+
+                       /* block the STA if exceeded the number of attempts */
+                       wpa_mesh_set_plink_state(wpa_s, sta, PLINK_BLOCKED);
+                       sta->sae->state = SAE_NOTHING;
+                       if (wpa_s->mesh_auth_block_duration <
+                           MESH_AUTH_BLOCK_DURATION)
+                               wpa_s->mesh_auth_block_duration += 60;
+                       eloop_register_timeout(wpa_s->mesh_auth_block_duration,
+                                              0, mesh_auth_timer, wpa_s, sta);
+                       wpa_msg(wpa_s, MSG_INFO, MESH_SAE_AUTH_BLOCKED "addr="
+                               MACSTR " duration=%d",
+                               MAC2STR(sta->addr),
+                               wpa_s->mesh_auth_block_duration);
+               }
+               sta->sae_auth_retry++;
+       }
+}
+
+
+static void auth_logger(void *ctx, const u8 *addr, logger_level level,
+                       const char *txt)
+{
+       if (addr)
+               wpa_printf(MSG_DEBUG, "AUTH: " MACSTR " - %s",
+                          MAC2STR(addr), txt);
+       else
+               wpa_printf(MSG_DEBUG, "AUTH: %s", txt);
+}
+
+
+static const u8 *auth_get_psk(void *ctx, const u8 *addr,
+                             const u8 *p2p_dev_addr, const u8 *prev_psk)
+{
+       struct mesh_rsn *mesh_rsn = ctx;
+       struct hostapd_data *hapd = mesh_rsn->wpa_s->ifmsh->bss[0];
+       struct sta_info *sta = ap_get_sta(hapd, addr);
+
+       wpa_printf(MSG_DEBUG, "AUTH: %s (addr=" MACSTR " prev_psk=%p)",
+                  __func__, MAC2STR(addr), prev_psk);
+
+       if (sta && sta->auth_alg == WLAN_AUTH_SAE) {
+               if (!sta->sae || prev_psk)
+                       return NULL;
+               return sta->sae->pmk;
+       }
+
+       return NULL;
+}
+
+
+static int auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
+                       const u8 *addr, int idx, u8 *key, size_t key_len)
+{
+       struct mesh_rsn *mesh_rsn = ctx;
+       u8 seq[6];
+
+       os_memset(seq, 0, sizeof(seq));
+
+       if (addr) {
+               wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d addr=" MACSTR
+                          " key_idx=%d)",
+                          __func__, alg, MAC2STR(addr), idx);
+       } else {
+               wpa_printf(MSG_DEBUG, "AUTH: %s(alg=%d key_idx=%d)",
+                          __func__, alg, idx);
+       }
+       wpa_hexdump_key(MSG_DEBUG, "AUTH: set_key - key", key, key_len);
+
+       return wpa_drv_set_key(mesh_rsn->wpa_s, alg, addr, idx,
+                              1, seq, 6, key, key_len);
+}
+
+
+static int auth_start_ampe(void *ctx, const u8 *addr)
+{
+       struct mesh_rsn *mesh_rsn = ctx;
+       struct hostapd_data *hapd;
+       struct sta_info *sta;
+
+       if (mesh_rsn->wpa_s->current_ssid->mode != WPAS_MODE_MESH)
+               return -1;
+
+       hapd = mesh_rsn->wpa_s->ifmsh->bss[0];
+       sta = ap_get_sta(hapd, addr);
+       if (sta)
+               eloop_cancel_timeout(mesh_auth_timer, mesh_rsn->wpa_s, sta);
+
+       mesh_mpm_auth_peer(mesh_rsn->wpa_s, addr);
+       return 0;
+}
+
+
+static int __mesh_rsn_auth_init(struct mesh_rsn *rsn, const u8 *addr)
+{
+       struct wpa_auth_config conf;
+       struct wpa_auth_callbacks cb;
+       u8 seq[6] = {};
+
+       wpa_printf(MSG_DEBUG, "AUTH: Initializing group state machine");
+
+       os_memset(&conf, 0, sizeof(conf));
+       conf.wpa = 2;
+       conf.wpa_key_mgmt = WPA_KEY_MGMT_SAE;
+       conf.wpa_pairwise = WPA_CIPHER_CCMP;
+       conf.rsn_pairwise = WPA_CIPHER_CCMP;
+       conf.wpa_group = WPA_CIPHER_CCMP;
+       conf.eapol_version = 0;
+       conf.wpa_group_rekey = -1;
+
+       os_memset(&cb, 0, sizeof(cb));
+       cb.ctx = rsn;
+       cb.logger = auth_logger;
+       cb.get_psk = auth_get_psk;
+       cb.set_key = auth_set_key;
+       cb.start_ampe = auth_start_ampe;
+
+       rsn->auth = wpa_init(addr, &conf, &cb);
+       if (rsn->auth == NULL) {
+               wpa_printf(MSG_DEBUG, "AUTH: wpa_init() failed");
+               return -1;
+       }
+
+       /* TODO: support rekeying */
+       if (random_get_bytes(rsn->mgtk, 16) < 0) {
+               wpa_deinit(rsn->auth);
+               return -1;
+       }
+
+       /* group mgmt */
+       wpa_drv_set_key(rsn->wpa_s, WPA_ALG_IGTK, NULL, 4, 1,
+                       seq, sizeof(seq), rsn->mgtk, sizeof(rsn->mgtk));
+
+       /* group privacy / data frames */
+       wpa_drv_set_key(rsn->wpa_s, WPA_ALG_CCMP, NULL, 1, 1,
+                       seq, sizeof(seq), rsn->mgtk, sizeof(rsn->mgtk));
+
+       return 0;
+}
+
+
+static void mesh_rsn_deinit(struct mesh_rsn *rsn)
+{
+       os_memset(rsn->mgtk, 0, sizeof(rsn->mgtk));
+       wpa_deinit(rsn->auth);
+}
+
+
+struct mesh_rsn *mesh_rsn_auth_init(struct wpa_supplicant *wpa_s,
+                                   struct mesh_conf *conf)
+{
+       struct mesh_rsn *mesh_rsn;
+       struct hostapd_data *bss = wpa_s->ifmsh->bss[0];
+       const u8 *ie;
+       size_t ie_len;
+
+       mesh_rsn = os_zalloc(sizeof(*mesh_rsn));
+       if (mesh_rsn == NULL)
+               return NULL;
+       mesh_rsn->wpa_s = wpa_s;
+
+       if (__mesh_rsn_auth_init(mesh_rsn, wpa_s->own_addr) < 0) {
+               mesh_rsn_deinit(mesh_rsn);
+               return NULL;
+       }
+
+       bss->wpa_auth = mesh_rsn->auth;
+
+       ie = wpa_auth_get_wpa_ie(mesh_rsn->auth, &ie_len);
+       conf->ies = (u8 *) ie;
+       conf->ie_len = ie_len;
+
+       wpa_supplicant_rsn_supp_set_config(wpa_s, wpa_s->current_ssid);
+
+       return mesh_rsn;
+}
+
+
+static int index_within_array(const int *array, int idx)
+{
+       int i;
+
+       for (i = 0; i < idx; i++) {
+               if (array[i] == -1)
+                       return 0;
+       }
+
+       return 1;
+}
+
+
+static int mesh_rsn_sae_group(struct wpa_supplicant *wpa_s,
+                             struct sae_data *sae)
+{
+       int *groups = wpa_s->ifmsh->bss[0]->conf->sae_groups;
+
+       /* Configuration may have changed, so validate current index */
+       if (!index_within_array(groups, wpa_s->mesh_rsn->sae_group_index))
+               return -1;
+
+       for (;;) {
+               int group = groups[wpa_s->mesh_rsn->sae_group_index];
+
+               if (group <= 0)
+                       break;
+               if (sae_set_group(sae, group) == 0) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "SME: Selected SAE group %d",
+                               sae->group);
+                       return 0;
+               }
+               wpa_s->mesh_rsn->sae_group_index++;
+       }
+
+       return -1;
+}
+
+
+static int mesh_rsn_build_sae_commit(struct wpa_supplicant *wpa_s,
+                                    struct wpa_ssid *ssid,
+                                    struct sta_info *sta)
+{
+       if (ssid->passphrase == NULL) {
+               wpa_msg(wpa_s, MSG_DEBUG, "SAE: No password available");
+               return -1;
+       }
+
+       if (mesh_rsn_sae_group(wpa_s, sta->sae) < 0) {
+               wpa_msg(wpa_s, MSG_DEBUG, "SAE: Failed to select group");
+               return -1;
+       }
+
+       return sae_prepare_commit(wpa_s->own_addr, sta->addr,
+                                 (u8 *) ssid->passphrase,
+                                 os_strlen(ssid->passphrase), sta->sae);
+}
+
+
+/* initiate new SAE authentication with sta */
+int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s,
+                         struct sta_info *sta)
+{
+       struct hostapd_data *hapd = wpa_s->ifmsh->bss[0];
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+       unsigned int rnd;
+       int ret;
+
+       if (!ssid) {
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "AUTH: No current_ssid known to initiate new SAE");
+               return -1;
+       }
+
+       if (!sta->sae) {
+               sta->sae = os_zalloc(sizeof(*sta->sae));
+               if (sta->sae == NULL)
+                       return -1;
+       }
+
+       if (mesh_rsn_build_sae_commit(wpa_s, ssid, sta))
+               return -1;
+
+       wpa_msg(wpa_s, MSG_DEBUG,
+               "AUTH: started authentication with SAE peer: " MACSTR,
+               MAC2STR(sta->addr));
+
+       wpa_supplicant_set_state(wpa_s, WPA_AUTHENTICATING);
+       ret = auth_sae_init_committed(hapd, sta);
+       if (ret)
+               return ret;
+
+       eloop_cancel_timeout(mesh_auth_timer, wpa_s, sta);
+       rnd = rand() % MESH_AUTH_TIMEOUT;
+       eloop_register_timeout(MESH_AUTH_TIMEOUT + rnd, 0, mesh_auth_timer,
+                              wpa_s, sta);
+       return 0;
+}
+
+
+void mesh_rsn_get_pmkid(struct mesh_rsn *rsn, struct sta_info *sta, u8 *pmkid)
+{
+       /* don't expect wpa auth to cache the pmkid for now */
+       rsn_pmkid(sta->sae->pmk, PMK_LEN, rsn->wpa_s->own_addr,
+                 sta->addr, pmkid,
+                 wpa_key_mgmt_sha256(wpa_auth_sta_key_mgmt(sta->wpa_sm)));
+}
+
+
+static void
+mesh_rsn_derive_aek(struct mesh_rsn *rsn, struct sta_info *sta)
+{
+       u8 *myaddr = rsn->wpa_s->own_addr;
+       u8 *peer = sta->addr;
+       u8 *addr1 = peer, *addr2 = myaddr;
+       u8 context[AES_BLOCK_SIZE];
+
+       /* SAE */
+       RSN_SELECTOR_PUT(context, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP));
+
+       if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) {
+               addr1 = myaddr;
+               addr2 = peer;
+       }
+       os_memcpy(context + 4, addr1, ETH_ALEN);
+       os_memcpy(context + 10, addr2, ETH_ALEN);
+
+       sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk), "AEK Derivation",
+                  context, sizeof(context), sta->aek, sizeof(sta->aek));
+}
+
+
+/* derive mesh temporal key from pmk */
+int mesh_rsn_derive_mtk(struct wpa_supplicant *wpa_s, struct sta_info *sta)
+{
+       u8 *ptr;
+       u8 *min, *max;
+       u16 min_lid, max_lid;
+       size_t nonce_len = sizeof(sta->my_nonce);
+       size_t lid_len = sizeof(sta->my_lid);
+       u8 *myaddr = wpa_s->own_addr;
+       u8 *peer = sta->addr;
+       /* 2 nonces, 2 linkids, akm suite, 2 mac addrs */
+       u8 context[64 + 4 + 4 + 12];
+
+       ptr = context;
+       if (os_memcmp(sta->my_nonce, sta->peer_nonce, nonce_len) < 0) {
+               min = sta->my_nonce;
+               max = sta->peer_nonce;
+       } else {
+               min = sta->peer_nonce;
+               max = sta->my_nonce;
+       }
+       os_memcpy(ptr, min, nonce_len);
+       os_memcpy(ptr + nonce_len, max, nonce_len);
+       ptr += 2 * nonce_len;
+
+       if (sta->my_lid < sta->peer_lid) {
+               min_lid = host_to_le16(sta->my_lid);
+               max_lid = host_to_le16(sta->peer_lid);
+       } else {
+               min_lid = host_to_le16(sta->peer_lid);
+               max_lid = host_to_le16(sta->my_lid);
+       }
+       os_memcpy(ptr, &min_lid, lid_len);
+       os_memcpy(ptr + lid_len, &max_lid, lid_len);
+       ptr += 2 * lid_len;
+
+       /* SAE */
+       RSN_SELECTOR_PUT(ptr, wpa_cipher_to_suite(0, WPA_CIPHER_GCMP));
+       ptr += 4;
+
+       if (os_memcmp(myaddr, peer, ETH_ALEN) < 0) {
+               min = myaddr;
+               max = peer;
+       } else {
+               min = peer;
+               max = myaddr;
+       }
+       os_memcpy(ptr, min, ETH_ALEN);
+       os_memcpy(ptr + ETH_ALEN, max, ETH_ALEN);
+
+       sha256_prf(sta->sae->pmk, sizeof(sta->sae->pmk),
+                  "Temporal Key Derivation", context, sizeof(context),
+                  sta->mtk, sizeof(sta->mtk));
+       return 0;
+}
+
+
+void mesh_rsn_init_ampe_sta(struct wpa_supplicant *wpa_s, struct sta_info *sta)
+{
+       if (random_get_bytes(sta->my_nonce, 32) < 0) {
+               wpa_printf(MSG_INFO, "mesh: Failed to derive random nonce");
+               /* TODO: How to handle this more cleanly? */
+       }
+       os_memset(sta->peer_nonce, 0, 32);
+       mesh_rsn_derive_aek(wpa_s->mesh_rsn, sta);
+}
+
+
+/* insert AMPE and encrypted MIC at @ie.
+ * @mesh_rsn: mesh RSN context
+ * @sta: STA we're sending to
+ * @cat: pointer to category code in frame header.
+ * @buf: wpabuf to add encrypted AMPE and MIC to.
+ * */
+int mesh_rsn_protect_frame(struct mesh_rsn *rsn, struct sta_info *sta,
+                          const u8 *cat, struct wpabuf *buf)
+{
+       struct ieee80211_ampe_ie *ampe;
+       u8 const *ie = wpabuf_head_u8(buf) + wpabuf_len(buf);
+       u8 *ampe_ie = NULL, *mic_ie = NULL, *mic_payload;
+       const u8 *aad[] = { rsn->wpa_s->own_addr, sta->addr, cat };
+       const size_t aad_len[] = { ETH_ALEN, ETH_ALEN, ie - cat };
+       int ret = 0;
+
+       if (AES_BLOCK_SIZE + 2 + sizeof(*ampe) + 2 > wpabuf_tailroom(buf)) {
+               wpa_printf(MSG_ERROR, "protect frame: buffer too small");
+               return -EINVAL;
+       }
+
+       ampe_ie = os_zalloc(2 + sizeof(*ampe));
+       if (!ampe_ie) {
+               wpa_printf(MSG_ERROR, "protect frame: out of memory");
+               return -ENOMEM;
+       }
+
+       mic_ie = os_zalloc(2 + AES_BLOCK_SIZE);
+       if (!mic_ie) {
+               wpa_printf(MSG_ERROR, "protect frame: out of memory");
+               ret = -ENOMEM;
+               goto free;
+       }
+
+       /*  IE: AMPE */
+       ampe_ie[0] = WLAN_EID_AMPE;
+       ampe_ie[1] = sizeof(*ampe);
+       ampe = (struct ieee80211_ampe_ie *) (ampe_ie + 2);
+
+       RSN_SELECTOR_PUT(ampe->selected_pairwise_suite,
+                    wpa_cipher_to_suite(WPA_PROTO_RSN, WPA_CIPHER_CCMP));
+       os_memcpy(ampe->local_nonce, sta->my_nonce, 32);
+       os_memcpy(ampe->peer_nonce, sta->peer_nonce, 32);
+       /* incomplete: see 13.5.4 */
+       /* TODO: static mgtk for now since we don't support rekeying! */
+       os_memcpy(ampe->mgtk, rsn->mgtk, 16);
+       /*  TODO: Populate Key RSC */
+       /*  expire in 13 decades or so */
+       os_memset(ampe->key_expiration, 0xff, 4);
+
+       /* IE: MIC */
+       mic_ie[0] = WLAN_EID_MIC;
+       mic_ie[1] = AES_BLOCK_SIZE;
+       wpabuf_put_data(buf, mic_ie, 2);
+       /* MIC field is output ciphertext */
+
+       /* encrypt after MIC */
+       mic_payload = (u8 *) wpabuf_put(buf, 2 + sizeof(*ampe) +
+                                       AES_BLOCK_SIZE);
+
+       if (aes_siv_encrypt(sta->aek, ampe_ie, 2 + sizeof(*ampe), 3,
+                           aad, aad_len, mic_payload)) {
+               wpa_printf(MSG_ERROR, "protect frame: failed to encrypt");
+               ret = -ENOMEM;
+               goto free;
+       }
+
+free:
+       os_free(ampe_ie);
+       os_free(mic_ie);
+
+       return ret;
+}
+
+
+int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
+                         struct ieee802_11_elems *elems, const u8 *cat,
+                         const u8 *start, size_t elems_len)
+{
+       int ret = 0;
+       struct ieee80211_ampe_ie *ampe;
+       u8 null_nonce[32] = {};
+       u8 ampe_eid;
+       u8 ampe_ie_len;
+       u8 *ampe_buf, *crypt = NULL;
+       size_t crypt_len;
+       const u8 *aad[] = { sta->addr, wpa_s->own_addr, cat };
+       const size_t aad_len[] = { ETH_ALEN, ETH_ALEN,
+                                  (elems->mic - 2) - cat };
+
+       if (!elems->mic || elems->mic_len < AES_BLOCK_SIZE) {
+               wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing mic ie");
+               return -1;
+       }
+
+       ampe_buf = (u8 *) elems->mic + elems->mic_len;
+       if ((int) elems_len < ampe_buf - start)
+               return -1;
+
+       crypt_len = elems_len - (elems->mic - start);
+       if (crypt_len < 2) {
+               wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: missing ampe ie");
+               return -1;
+       }
+
+       /* crypt is modified by siv_decrypt */
+       crypt = os_zalloc(crypt_len);
+       if (!crypt) {
+               wpa_printf(MSG_ERROR, "Mesh RSN: out of memory");
+               ret = -ENOMEM;
+               goto free;
+       }
+
+       os_memcpy(crypt, elems->mic, crypt_len);
+
+       if (aes_siv_decrypt(sta->aek, crypt, crypt_len, 3,
+                           aad, aad_len, ampe_buf)) {
+               wpa_printf(MSG_ERROR, "Mesh RSN: frame verification failed!");
+               ret = -1;
+               goto free;
+       }
+
+       ampe_eid = *ampe_buf++;
+       ampe_ie_len = *ampe_buf++;
+
+       if (ampe_eid != WLAN_EID_AMPE ||
+           ampe_ie_len < sizeof(struct ieee80211_ampe_ie)) {
+               wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid ampe ie");
+               ret = -1;
+               goto free;
+       }
+
+       ampe = (struct ieee80211_ampe_ie *) ampe_buf;
+       if (os_memcmp(ampe->peer_nonce, null_nonce, 32) != 0 &&
+           os_memcmp(ampe->peer_nonce, sta->my_nonce, 32) != 0) {
+               wpa_msg(wpa_s, MSG_DEBUG, "Mesh RSN: invalid peer nonce");
+               ret = -1;
+               goto free;
+       }
+       os_memcpy(sta->peer_nonce, ampe->local_nonce,
+                 sizeof(ampe->local_nonce));
+       os_memcpy(sta->mgtk, ampe->mgtk, sizeof(ampe->mgtk));
+
+       /* todo parse mgtk expiration */
+free:
+       os_free(crypt);
+       return ret;
+}
diff --git a/wpa_supplicant/mesh_rsn.h b/wpa_supplicant/mesh_rsn.h
new file mode 100755 (executable)
index 0000000..b1471b2
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * WPA Supplicant - Mesh RSN routines
+ * Copyright (c) 2013-2014, cozybit, Inc.  All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef MESH_RSN_H
+#define MESH_RSN_H
+
+struct mesh_rsn {
+       struct wpa_supplicant *wpa_s;
+       struct wpa_authenticator *auth;
+       u8 mgtk[16];
+#ifdef CONFIG_SAE
+       struct wpabuf *sae_token;
+       int sae_group_index;
+#endif /* CONFIG_SAE */
+};
+
+struct mesh_rsn * mesh_rsn_auth_init(struct wpa_supplicant *wpa_s,
+                                    struct mesh_conf *conf);
+int mesh_rsn_auth_sae_sta(struct wpa_supplicant *wpa_s, struct sta_info *sta);
+int mesh_rsn_derive_mtk(struct wpa_supplicant *wpa_s, struct sta_info *sta);
+void mesh_rsn_get_pmkid(struct mesh_rsn *rsn, struct sta_info *sta, u8 *pmkid);
+void mesh_rsn_init_ampe_sta(struct wpa_supplicant *wpa_s,
+                           struct sta_info *sta);
+int mesh_rsn_protect_frame(struct mesh_rsn *rsn, struct sta_info *sta,
+                          const u8 *cat, struct wpabuf *buf);
+int mesh_rsn_process_ampe(struct wpa_supplicant *wpa_s, struct sta_info *sta,
+                         struct ieee802_11_elems *elems, const u8 *cat,
+                         const u8 *start, size_t elems_len);
+void mesh_auth_timer(void *eloop_ctx, void *user_data);
+
+#endif /* MESH_RSN_H */
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
index 35a029f..f011c1b
@@ -48,6 +48,9 @@ void wpas_notify_supplicant_deinitialized(struct wpa_global *global)
 
 int wpas_notify_iface_added(struct wpa_supplicant *wpa_s)
 {
+       if (wpa_s->p2p_mgmt)
+               return 0;
+
        if (wpas_dbus_register_iface(wpa_s))
                return -1;
 
@@ -60,6 +63,9 @@ int wpas_notify_iface_added(struct wpa_supplicant *wpa_s)
 
 void wpas_notify_iface_removed(struct wpa_supplicant *wpa_s)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        /* unregister interface in old DBus ctrl iface */
        wpas_dbus_unregister_iface(wpa_s);
 
@@ -72,6 +78,9 @@ void wpas_notify_state_changed(struct wpa_supplicant *wpa_s,
                               enum wpa_states new_state,
                               enum wpa_states old_state)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        /* notify the old DBus API */
        wpa_supplicant_dbus_notify_state_change(wpa_s, new_state,
                                                old_state);
@@ -79,50 +88,67 @@ void wpas_notify_state_changed(struct wpa_supplicant *wpa_s,
        /* notify the new DBus API */
        wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_STATE);
 
-#ifdef CONFIG_P2P
        if (new_state == WPA_COMPLETED)
                wpas_p2p_notif_connected(wpa_s);
        else if (old_state >= WPA_ASSOCIATED && new_state < WPA_ASSOCIATED)
                wpas_p2p_notif_disconnected(wpa_s);
-#endif /* CONFIG_P2P */
 
        sme_state_changed(wpa_s);
 
 #ifdef ANDROID
        wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_STATE_CHANGE
-                    "id=%d state=%d BSSID=" MACSTR,
+                    "id=%d state=%d BSSID=" MACSTR " SSID=%s",
                     wpa_s->current_ssid ? wpa_s->current_ssid->id : -1,
-                    new_state, MAC2STR(wpa_s->pending_bssid));
+                    new_state,
+                    MAC2STR(wpa_s->bssid),
+                    wpa_s->current_ssid && wpa_s->current_ssid->ssid ?
+                    wpa_ssid_txt(wpa_s->current_ssid->ssid,
+                                 wpa_s->current_ssid->ssid_len) : "");
 #endif /* ANDROID */
 }
 
 
 void wpas_notify_disconnect_reason(struct wpa_supplicant *wpa_s)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_DISCONNECT_REASON);
 }
 
 
 void wpas_notify_network_changed(struct wpa_supplicant *wpa_s)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_NETWORK);
 }
 
 
 void wpas_notify_ap_scan_changed(struct wpa_supplicant *wpa_s)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_AP_SCAN);
 }
 
 
 void wpas_notify_bssid_changed(struct wpa_supplicant *wpa_s)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_BSS);
 }
 
 
 void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        wpas_dbus_signal_prop_changed(wpa_s, WPAS_DBUS_PROP_CURRENT_AUTH_MODE);
 }
 
@@ -130,6 +156,9 @@ void wpas_notify_auth_changed(struct wpa_supplicant *wpa_s)
 void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s,
                                         struct wpa_ssid *ssid)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        wpas_dbus_signal_network_enabled_changed(wpa_s, ssid);
 }
 
@@ -137,6 +166,9 @@ void wpas_notify_network_enabled_changed(struct wpa_supplicant *wpa_s,
 void wpas_notify_network_selected(struct wpa_supplicant *wpa_s,
                                  struct wpa_ssid *ssid)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        wpas_dbus_signal_network_selected(wpa_s, ssid->id);
 }
 
@@ -146,12 +178,18 @@ void wpas_notify_network_request(struct wpa_supplicant *wpa_s,
                                 enum wpa_ctrl_req_type rtype,
                                 const char *default_txt)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        wpas_dbus_signal_network_request(wpa_s, ssid, rtype, default_txt);
 }
 
 
 void wpas_notify_scanning(struct wpa_supplicant *wpa_s)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        /* notify the old DBus API */
        wpa_supplicant_dbus_notify_scanning(wpa_s);
 
@@ -162,12 +200,18 @@ void wpas_notify_scanning(struct wpa_supplicant *wpa_s)
 
 void wpas_notify_scan_done(struct wpa_supplicant *wpa_s, int success)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        wpas_dbus_signal_scan_done(wpa_s, success);
 }
 
 
 void wpas_notify_scan_results(struct wpa_supplicant *wpa_s)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        /* notify the old DBus API */
        wpa_supplicant_dbus_notify_scan_results(wpa_s);
 
@@ -175,9 +219,20 @@ void wpas_notify_scan_results(struct wpa_supplicant *wpa_s)
 }
 
 
+void wpas_notify_find_stopped(struct wpa_supplicant *wpa_s)
+{
+#ifdef CONFIG_P2P
+       wpas_dbus_signal_find_stopped(wpa_s);
+#endif /* CONFIG_P2P */
+}
+
+
 void wpas_notify_wps_credential(struct wpa_supplicant *wpa_s,
                                const struct wps_credential *cred)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
 #ifdef CONFIG_WPS
        /* notify the old DBus API */
        wpa_supplicant_dbus_notify_wps_cred(wpa_s, cred);
@@ -190,6 +245,9 @@ void wpas_notify_wps_credential(struct wpa_supplicant *wpa_s,
 void wpas_notify_wps_event_m2d(struct wpa_supplicant *wpa_s,
                               struct wps_event_m2d *m2d)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
 #ifdef CONFIG_WPS
        wpas_dbus_signal_wps_event_m2d(wpa_s, m2d);
 #endif /* CONFIG_WPS */
@@ -199,6 +257,9 @@ void wpas_notify_wps_event_m2d(struct wpa_supplicant *wpa_s,
 void wpas_notify_wps_event_fail(struct wpa_supplicant *wpa_s,
                                struct wps_event_fail *fail)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
 #ifdef CONFIG_WPS
        wpas_dbus_signal_wps_event_fail(wpa_s, fail);
 #endif /* CONFIG_WPS */
@@ -207,6 +268,9 @@ void wpas_notify_wps_event_fail(struct wpa_supplicant *wpa_s,
 
 void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
 #ifdef CONFIG_WPS
        wpas_dbus_signal_wps_event_success(wpa_s);
 #endif /* CONFIG_WPS */
@@ -216,13 +280,16 @@ void wpas_notify_wps_event_success(struct wpa_supplicant *wpa_s)
 void wpas_notify_network_added(struct wpa_supplicant *wpa_s,
                               struct wpa_ssid *ssid)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        /*
         * Networks objects created during any P2P activities should not be
         * exposed out. They might/will confuse certain non-P2P aware
         * applications since these network objects won't behave like
         * regular ones.
         */
-       if (wpa_s->global->p2p_group_formation != wpa_s)
+       if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s)
                wpas_dbus_register_network(wpa_s, ssid);
 }
 
@@ -248,19 +315,28 @@ void wpas_notify_persistent_group_removed(struct wpa_supplicant *wpa_s,
 void wpas_notify_network_removed(struct wpa_supplicant *wpa_s,
                                 struct wpa_ssid *ssid)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
+       if (wpa_s->next_ssid == ssid)
+               wpa_s->next_ssid = NULL;
        if (wpa_s->wpa)
                wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
-       if (wpa_s->global->p2p_group_formation != wpa_s)
+       if (!ssid->p2p_group && wpa_s->global->p2p_group_formation != wpa_s)
                wpas_dbus_unregister_network(wpa_s, ssid->id);
-#ifdef CONFIG_P2P
+       if (network_is_persistent_group(ssid))
+               wpas_notify_persistent_group_removed(wpa_s, ssid);
+
        wpas_p2p_network_removed(wpa_s, ssid);
-#endif /* CONFIG_P2P */
 }
 
 
 void wpas_notify_bss_added(struct wpa_supplicant *wpa_s,
                           u8 bssid[], unsigned int id)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        wpas_dbus_register_bss(wpa_s, bssid, id);
        wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_BSS_ADDED "%u " MACSTR,
                     id, MAC2STR(bssid));
@@ -270,6 +346,9 @@ void wpas_notify_bss_added(struct wpa_supplicant *wpa_s,
 void wpas_notify_bss_removed(struct wpa_supplicant *wpa_s,
                             u8 bssid[], unsigned int id)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        wpas_dbus_unregister_bss(wpa_s, bssid, id);
        wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_BSS_REMOVED "%u " MACSTR,
                     id, MAC2STR(bssid));
@@ -279,6 +358,9 @@ void wpas_notify_bss_removed(struct wpa_supplicant *wpa_s,
 void wpas_notify_bss_freq_changed(struct wpa_supplicant *wpa_s,
                                  unsigned int id)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_FREQ, id);
 }
 
@@ -286,6 +368,9 @@ void wpas_notify_bss_freq_changed(struct wpa_supplicant *wpa_s,
 void wpas_notify_bss_signal_changed(struct wpa_supplicant *wpa_s,
                                    unsigned int id)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_SIGNAL,
                                          id);
 }
@@ -294,6 +379,9 @@ void wpas_notify_bss_signal_changed(struct wpa_supplicant *wpa_s,
 void wpas_notify_bss_privacy_changed(struct wpa_supplicant *wpa_s,
                                     unsigned int id)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_PRIVACY,
                                          id);
 }
@@ -302,6 +390,9 @@ void wpas_notify_bss_privacy_changed(struct wpa_supplicant *wpa_s,
 void wpas_notify_bss_mode_changed(struct wpa_supplicant *wpa_s,
                                  unsigned int id)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_MODE, id);
 }
 
@@ -309,6 +400,9 @@ void wpas_notify_bss_mode_changed(struct wpa_supplicant *wpa_s,
 void wpas_notify_bss_wpaie_changed(struct wpa_supplicant *wpa_s,
                                   unsigned int id)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_WPA, id);
 }
 
@@ -316,6 +410,9 @@ void wpas_notify_bss_wpaie_changed(struct wpa_supplicant *wpa_s,
 void wpas_notify_bss_rsnie_changed(struct wpa_supplicant *wpa_s,
                                   unsigned int id)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_RSN, id);
 }
 
@@ -323,6 +420,9 @@ void wpas_notify_bss_rsnie_changed(struct wpa_supplicant *wpa_s,
 void wpas_notify_bss_wps_changed(struct wpa_supplicant *wpa_s,
                                 unsigned int id)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
 #ifdef CONFIG_WPS
        wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_WPS, id);
 #endif /* CONFIG_WPS */
@@ -332,6 +432,9 @@ void wpas_notify_bss_wps_changed(struct wpa_supplicant *wpa_s,
 void wpas_notify_bss_ies_changed(struct wpa_supplicant *wpa_s,
                                   unsigned int id)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_IES, id);
 }
 
@@ -339,18 +442,36 @@ void wpas_notify_bss_ies_changed(struct wpa_supplicant *wpa_s,
 void wpas_notify_bss_rates_changed(struct wpa_supplicant *wpa_s,
                                   unsigned int id)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_RATES, id);
 }
 
 
+void wpas_notify_bss_seen(struct wpa_supplicant *wpa_s, unsigned int id)
+{
+       if (wpa_s->p2p_mgmt)
+               return;
+
+       wpas_dbus_bss_signal_prop_changed(wpa_s, WPAS_DBUS_BSS_PROP_AGE, id);
+}
+
+
 void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        wpas_dbus_signal_blob_added(wpa_s, name);
 }
 
 
 void wpas_notify_blob_removed(struct wpa_supplicant *wpa_s, const char *name)
 {
+       if (wpa_s->p2p_mgmt)
+               return;
+
        wpas_dbus_signal_blob_removed(wpa_s, name);
 }
 
@@ -436,9 +557,9 @@ void wpas_notify_p2p_group_removed(struct wpa_supplicant *wpa_s,
                                   const struct wpa_ssid *ssid,
                                   const char *role)
 {
-       wpas_dbus_unregister_p2p_group(wpa_s, ssid);
-
        wpas_dbus_signal_p2p_group_removed(wpa_s, role);
+
+       wpas_dbus_unregister_p2p_group(wpa_s, ssid);
 }
 
 
@@ -481,6 +602,21 @@ void wpas_notify_p2p_sd_response(struct wpa_supplicant *wpa_s,
                                         tlvs, tlvs_len);
 }
 
+void wpas_notify_p2p_group_formation_failure(
+                               struct wpa_supplicant *wpa_s)
+{
+       wpas_dbus_signal_p2p_group_formation_failure(wpa_s);
+}
+
+
+void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+                                      const u8 *sa, const u8 *go_dev_add,
+                                      const u8 *bssid,int id,int op_freq)
+{
+       wpas_dbus_signal_p2p_invitation_received(wpa_s, sa, go_dev_add,bssid,
+                                                       id,op_freq);
+}
+
 
 /**
  * wpas_notify_p2p_provision_discovery - Notification of provision discovery
@@ -535,17 +671,11 @@ static void wpas_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
        wpas_p2p_notify_ap_sta_authorized(wpa_s, p2p_dev_addr);
 
        /*
-        * Register a group member object corresponding to this peer and
-        * emit a PeerJoined signal. This will check if it really is a
-        * P2P group.
-        */
-       wpas_dbus_register_p2p_groupmember(wpa_s, sta);
-
-       /*
         * Create 'peer-joined' signal on group object -- will also
         * check P2P itself.
         */
-       wpas_dbus_signal_p2p_peer_joined(wpa_s, sta);
+       if (p2p_dev_addr)
+               wpas_dbus_signal_p2p_peer_joined(wpa_s, p2p_dev_addr);
 #endif /* CONFIG_P2P */
 
        /* Notify listeners a new station has been authorized */
@@ -554,20 +684,16 @@ static void wpas_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
 
 
 static void wpas_notify_ap_sta_deauthorized(struct wpa_supplicant *wpa_s,
-                                           const u8 *sta)
+                                           const u8 *sta,
+                                           const u8 *p2p_dev_addr)
 {
 #ifdef CONFIG_P2P
        /*
-        * Unregister a group member object corresponding to this peer
-        * if this is a P2P group.
-        */
-       wpas_dbus_unregister_p2p_groupmember(wpa_s, sta);
-
-       /*
         * Create 'peer-disconnected' signal on group object if this
         * is a P2P group.
         */
-       wpas_dbus_signal_p2p_peer_disconnected(wpa_s, sta);
+       if (p2p_dev_addr)
+               wpas_dbus_signal_p2p_peer_disconnected(wpa_s, p2p_dev_addr);
 #endif /* CONFIG_P2P */
 
        /* Notify listeners a station has been deauthorized */
@@ -582,18 +708,18 @@ void wpas_notify_sta_authorized(struct wpa_supplicant *wpa_s,
        if (authorized)
                wpas_notify_ap_sta_authorized(wpa_s, mac_addr, p2p_dev_addr);
        else
-               wpas_notify_ap_sta_deauthorized(wpa_s, mac_addr);
+               wpas_notify_ap_sta_deauthorized(wpa_s, mac_addr, p2p_dev_addr);
 }
 
 
 void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth,
-                              const char *subject, const char *cert_hash,
+                              const char *subject, const char *altsubject[],
+                              int num_altsubject, const char *cert_hash,
                               const struct wpabuf *cert)
 {
        wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_CERT
                "depth=%d subject='%s'%s%s",
-               depth, subject,
-               cert_hash ? " hash=" : "",
+               depth, subject, cert_hash ? " hash=" : "",
                cert_hash ? cert_hash : "");
 
        if (cert) {
@@ -611,11 +737,20 @@ void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth,
                }
        }
 
+       if (altsubject) {
+               int i;
+
+               for (i = 0; i < num_altsubject; i++)
+                       wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_EAP_PEER_ALT
+                               "depth=%d %s", depth, altsubject[i]);
+       }
+
        /* notify the old DBus API */
        wpa_supplicant_dbus_notify_certification(wpa_s, depth, subject,
                                                 cert_hash, cert);
        /* notify the new DBus API */
-       wpas_dbus_signal_certification(wpa_s, depth, subject, cert_hash, cert);
+       wpas_dbus_signal_certification(wpa_s, depth, subject, altsubject,
+                                      num_altsubject, cert_hash, cert);
 }
 
 
@@ -637,3 +772,37 @@ void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status,
                     "status='%s' parameter='%s'",
                     status, parameter);
 }
+
+
+void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s,
+                                          struct wpa_ssid *ssid)
+{
+       if (wpa_s->current_ssid != ssid)
+               return;
+
+       wpa_dbg(wpa_s, MSG_DEBUG,
+               "Network bssid config changed for the current network - within-ESS roaming %s",
+               ssid->bssid_set ? "disabled" : "enabled");
+
+       wpa_drv_roaming(wpa_s, !ssid->bssid_set,
+                       ssid->bssid_set ? ssid->bssid : NULL);
+}
+
+
+void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s,
+                                     struct wpa_ssid *ssid)
+{
+#ifdef CONFIG_P2P
+       if (ssid->disabled == 2) {
+               /* Changed from normal network profile to persistent group */
+               ssid->disabled = 0;
+               wpas_dbus_unregister_network(wpa_s, ssid->id);
+               ssid->disabled = 2;
+               wpas_dbus_register_persistent_group(wpa_s, ssid);
+       } else {
+               /* Changed from persistent group to normal network profile */
+               wpas_dbus_unregister_persistent_group(wpa_s, ssid->id);
+               wpas_dbus_register_network(wpa_s, ssid);
+       }
+#endif /* CONFIG_P2P */
+}
old mode 100644 (file)
new mode 100755 (executable)
index 58675ac..4b3f543
@@ -37,6 +37,7 @@ void wpas_notify_network_request(struct wpa_supplicant *wpa_s,
                                 const char *default_txt);
 void wpas_notify_scanning(struct wpa_supplicant *wpa_s);
 void wpas_notify_scan_done(struct wpa_supplicant *wpa_s, int success);
+void wpas_notify_find_stopped(struct wpa_supplicant *wpa_s);
 void wpas_notify_scan_results(struct wpa_supplicant *wpa_s);
 void wpas_notify_wps_credential(struct wpa_supplicant *wpa_s,
                                const struct wps_credential *cred);
@@ -71,6 +72,7 @@ void wpas_notify_bss_ies_changed(struct wpa_supplicant *wpa_s,
                                 unsigned int id);
 void wpas_notify_bss_rates_changed(struct wpa_supplicant *wpa_s,
                                   unsigned int id);
+void wpas_notify_bss_seen(struct wpa_supplicant *wpa_s, unsigned int id);
 void wpas_notify_blob_added(struct wpa_supplicant *wpa_s, const char *name);
 void wpas_notify_blob_removed(struct wpa_supplicant *wpa_s, const char *name);
 
@@ -120,12 +122,22 @@ void wpas_notify_p2p_wps_failed(struct wpa_supplicant *wpa_s,
                                struct wps_event_fail *fail);
 
 void wpas_notify_certification(struct wpa_supplicant *wpa_s, int depth,
-                              const char *subject, const char *cert_hash,
+                              const char *subject, const char *altsubject[],
+                              int num_altsubject, const char *cert_hash,
                               const struct wpabuf *cert);
 void wpas_notify_preq(struct wpa_supplicant *wpa_s,
                      const u8 *addr, const u8 *dst, const u8 *bssid,
                      const u8 *ie, size_t ie_len, u32 ssi_signal);
 void wpas_notify_eap_status(struct wpa_supplicant *wpa_s, const char *status,
                            const char *parameter);
+void wpas_notify_network_bssid_set_changed(struct wpa_supplicant *wpa_s,
+                                          struct wpa_ssid *ssid);
+void wpas_notify_network_type_changed(struct wpa_supplicant *wpa_s,
+                                     struct wpa_ssid *ssid);
+void wpas_notify_p2p_group_formation_failure(
+                                          struct wpa_supplicant *wpa_s);
+void wpas_notify_p2p_invitation_received(struct wpa_supplicant *wpa_s,
+                           const u8 *sa, const u8 *go_dev_add,
+                           const u8 *bssid,int id,int op_freq);
 
 #endif /* NOTIFY_H */
old mode 100644 (file)
new mode 100755 (executable)
index 856eca7..63af83a
@@ -12,6 +12,7 @@
 #include "common.h"
 #include "utils/eloop.h"
 #include "wpa_supplicant_i.h"
+#include "p2p_supplicant.h"
 #include "driver_i.h"
 #include "offchannel.h"
 
@@ -30,8 +31,7 @@ wpas_get_tx_interface(struct wpa_supplicant *wpa_s, const u8 *src)
         */
        iface = wpa_s->global->ifaces;
        while (iface) {
-               if (os_memcmp(wpa_s->pending_action_src,
-                             iface->own_addr, ETH_ALEN) == 0)
+               if (os_memcmp(src, iface->own_addr, ETH_ALEN) == 0)
                        break;
                iface = iface->next;
        }
@@ -55,11 +55,12 @@ static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
 
        without_roc = wpa_s->pending_action_without_roc;
        wpa_s->pending_action_without_roc = 0;
-       wpa_printf(MSG_DEBUG, "Off-channel: Send Action callback "
-                  "(without_roc=%d pending_action_tx=%p)",
-                  without_roc, wpa_s->pending_action_tx);
+       wpa_printf(MSG_DEBUG,
+                  "Off-channel: Send Action callback (without_roc=%d pending_action_tx=%p pending_action_tx_done=%d)",
+                  without_roc, wpa_s->pending_action_tx,
+                  !!wpa_s->pending_action_tx_done);
 
-       if (wpa_s->pending_action_tx == NULL)
+       if (wpa_s->pending_action_tx == NULL || wpa_s->pending_action_tx_done)
                return;
 
        /*
@@ -83,6 +84,7 @@ static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
                           wpa_s->off_channel_freq,
                           iface->assoc_freq);
                if (without_roc && wpa_s->off_channel_freq == 0) {
+                       unsigned int duration = 200;
                        /*
                         * We may get here if wpas_send_action() found us to be
                         * on the correct channel, but remain-on-channel cancel
@@ -90,9 +92,18 @@ static void wpas_send_action_cb(void *eloop_ctx, void *timeout_ctx)
                         */
                        wpa_printf(MSG_DEBUG, "Off-channel: Schedule "
                                   "remain-on-channel to send Action frame");
+#ifdef CONFIG_TESTING_OPTIONS
+                       if (wpa_s->extra_roc_dur) {
+                               wpa_printf(MSG_DEBUG,
+                                          "TESTING: Increase ROC duration %u -> %u",
+                                          duration,
+                                          duration + wpa_s->extra_roc_dur);
+                               duration += wpa_s->extra_roc_dur;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
                        if (wpa_drv_remain_on_channel(
-                                   wpa_s, wpa_s->pending_action_freq, 200) <
-                           0) {
+                                   wpa_s, wpa_s->pending_action_freq,
+                                   duration) < 0) {
                                wpa_printf(MSG_DEBUG, "Off-channel: Failed to "
                                           "request driver to remain on "
                                           "channel (%u MHz) for Action Frame "
@@ -159,6 +170,21 @@ void offchannel_send_action_tx_status(
                return;
        }
 
+       /* Accept report only if the contents of the frame matches */
+       if (data_len - wpabuf_len(wpa_s->pending_action_tx) != 24 ||
+           os_memcmp(data + 24, wpabuf_head(wpa_s->pending_action_tx),
+                     wpabuf_len(wpa_s->pending_action_tx)) != 0) {
+               wpa_printf(MSG_DEBUG, "Off-channel: Ignore Action TX status - "
+                                  "mismatching contents with pending frame");
+               wpa_hexdump(MSG_MSGDUMP, "TX status frame data",
+                           data, data_len);
+               wpa_hexdump_buf(MSG_MSGDUMP, "Pending TX frame",
+                               wpa_s->pending_action_tx);
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "Off-channel: Delete matching pending action frame");
+
        wpabuf_free(wpa_s->pending_action_tx);
        wpa_s->pending_action_tx = NULL;
 
@@ -172,6 +198,14 @@ void offchannel_send_action_tx_status(
                        wpa_s->pending_action_bssid,
                        data, data_len, result);
        }
+
+#ifdef CONFIG_P2P
+       if (wpa_s->p2p_long_listen > 0) {
+               /* Continue the listen */
+               wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state");
+               wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen);
+       }
+#endif /* CONFIG_P2P */
 }
 
 
@@ -220,6 +254,7 @@ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
                           MAC2STR(wpa_s->pending_action_dst));
                wpabuf_free(wpa_s->pending_action_tx);
        }
+       wpa_s->pending_action_tx_done = 0;
        wpa_s->pending_action_tx = wpabuf_alloc(len);
        if (wpa_s->pending_action_tx == NULL) {
                wpa_printf(MSG_DEBUG, "Off-channel: Failed to allocate Action "
@@ -236,18 +271,21 @@ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
 
        if (freq != 0 && wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX) {
                struct wpa_supplicant *iface;
+               int ret;
 
-               iface = wpas_get_tx_interface(wpa_s,
-                                             wpa_s->pending_action_src);
+               iface = wpas_get_tx_interface(wpa_s, src);
                wpa_s->action_tx_wait_time = wait_time;
 
-               return wpa_drv_send_action(
+               ret = wpa_drv_send_action(
                        iface, wpa_s->pending_action_freq,
                        wait_time, wpa_s->pending_action_dst,
                        wpa_s->pending_action_src, wpa_s->pending_action_bssid,
                        wpabuf_head(wpa_s->pending_action_tx),
                        wpabuf_len(wpa_s->pending_action_tx),
                        wpa_s->pending_action_no_cck);
+               if (ret == 0)
+                       wpa_s->pending_action_tx_done = 1;
+               return ret;
        }
 
        if (freq) {
@@ -285,6 +323,15 @@ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
                   "channel");
        if (wait_time > wpa_s->max_remain_on_chan)
                wait_time = wpa_s->max_remain_on_chan;
+       else if (wait_time == 0)
+               wait_time = 20;
+#ifdef CONFIG_TESTING_OPTIONS
+       if (wpa_s->extra_roc_dur) {
+               wpa_printf(MSG_DEBUG, "TESTING: Increase ROC duration %u -> %u",
+                          wait_time, wait_time + wpa_s->extra_roc_dur);
+               wait_time += wpa_s->extra_roc_dur;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
        if (wpa_drv_remain_on_channel(wpa_s, freq, wait_time) < 0) {
                wpa_printf(MSG_DEBUG, "Off-channel: Failed to request driver "
                           "to remain on channel (%u MHz) for Action "
@@ -307,15 +354,18 @@ int offchannel_send_action(struct wpa_supplicant *wpa_s, unsigned int freq,
  */
 void offchannel_send_action_done(struct wpa_supplicant *wpa_s)
 {
-       wpa_printf(MSG_DEBUG, "Off-channel: Action frame sequence done "
-                  "notification");
+       wpa_printf(MSG_DEBUG,
+                  "Off-channel: Action frame sequence done notification: pending_action_tx=%p drv_offchan_tx=%d action_tx_wait_time=%d off_channel_freq=%d roc_waiting_drv_freq=%d",
+                  wpa_s->pending_action_tx,
+                  !!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX),
+                  wpa_s->action_tx_wait_time, wpa_s->off_channel_freq,
+                  wpa_s->roc_waiting_drv_freq);
        wpabuf_free(wpa_s->pending_action_tx);
        wpa_s->pending_action_tx = NULL;
        if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_OFFCHANNEL_TX &&
            wpa_s->action_tx_wait_time)
                wpa_drv_send_action_cancel_wait(wpa_s);
-
-       if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
+       else if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
                wpa_drv_cancel_remain_on_channel(wpa_s);
                wpa_s->off_channel_freq = 0;
                wpa_s->roc_waiting_drv_freq = 0;
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
index c817947..39ebbcb
@@ -1,6 +1,7 @@
 /*
  * wpa_supplicant - P2P
  * Copyright (c) 2009-2010, Atheros Communications
+ * Copyright (c) 2010-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 #include "p2p/p2p.h"
 #include "ap/hostapd.h"
 #include "ap/ap_config.h"
+#include "ap/sta_info.h"
+#include "ap/ap_drv_ops.h"
+#include "ap/wps_hostapd.h"
 #include "ap/p2p_hostapd.h"
+#include "ap/dfs.h"
 #include "eapol_supp/eapol_supp_sm.h"
 #include "rsn_supp/wpa.h"
 #include "wpa_supplicant_i.h"
@@ -31,9 +36,8 @@
 #include "offchannel.h"
 #include "wps_supplicant.h"
 #include "p2p_supplicant.h"
-#ifdef TIZEN_EXT_BUSY_DEVICE
-#include "p2p/p2p_i.h"
-#endif
+#include "wifi_display.h"
+
 
 /*
  * How many times to try to scan to find the GO before giving up on join
 #ifndef P2P_MAX_INITIAL_CONN_WAIT
 /*
  * How many seconds to wait for initial 4-way handshake to get completed after
- * WPS provisioning step.
+ * WPS provisioning step or after the re-invocation of a persistent group on a
+ * P2P Client.
  */
 #define P2P_MAX_INITIAL_CONN_WAIT 10
 #endif /* P2P_MAX_INITIAL_CONN_WAIT */
 
-#ifndef P2P_CONCURRENT_SEARCH_DELAY
-#define P2P_CONCURRENT_SEARCH_DELAY 500
-#endif /* P2P_CONCURRENT_SEARCH_DELAY */
+#ifndef P2P_MAX_INITIAL_CONN_WAIT_GO
+/*
+ * How many seconds to wait for initial 4-way handshake to get completed after
+ * WPS provisioning step on the GO. This controls the extra time the P2P
+ * operation is considered to be in progress (e.g., to delay other scans) after
+ * WPS provisioning has been completed on the GO during group formation.
+ */
+#define P2P_MAX_INITIAL_CONN_WAIT_GO 10
+#endif /* P2P_MAX_INITIAL_CONN_WAIT_GO */
+
+#ifndef P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE
+/*
+ * How many seconds to wait for initial 4-way handshake to get completed after
+ * re-invocation of a persistent group on the GO when the client is expected
+ * to connect automatically (no user interaction).
+ */
+#define P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE 15
+#endif /* P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE */
 
 #define P2P_MGMT_DEVICE_PREFIX         "p2p-dev-"
 
@@ -72,7 +92,9 @@ enum p2p_group_removal_reason {
        P2P_GROUP_REMOVAL_REQUESTED,
        P2P_GROUP_REMOVAL_IDLE_TIMEOUT,
        P2P_GROUP_REMOVAL_UNAVAILABLE,
-       P2P_GROUP_REMOVAL_GO_ENDING_SESSION
+       P2P_GROUP_REMOVAL_GO_ENDING_SESSION,
+       P2P_GROUP_REMOVAL_PSK_FAILURE,
+       P2P_GROUP_REMOVAL_FREQ_CONFLICT
 };
 
 
@@ -80,21 +102,88 @@ static void wpas_p2p_long_listen_timeout(void *eloop_ctx, void *timeout_ctx);
 static struct wpa_supplicant *
 wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
                         int go);
-static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s);
-static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq);
+static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq,
+                              const u8 *ssid, size_t ssid_len);
+static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq,
+                                  const u8 *ssid, size_t ssid_len);
 static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx);
 static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
                         const u8 *dev_addr, enum p2p_wps_method wps_method,
-                        int auto_join);
+                        int auto_join, int freq,
+                        const u8 *ssid, size_t ssid_len);
 static int wpas_p2p_create_iface(struct wpa_supplicant *wpa_s);
 static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s);
 static void wpas_p2p_group_idle_timeout(void *eloop_ctx, void *timeout_ctx);
 static void wpas_p2p_set_group_idle_timeout(struct wpa_supplicant *wpa_s);
 static void wpas_p2p_group_formation_timeout(void *eloop_ctx,
                                             void *timeout_ctx);
-static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
-                                       int group_added);
-static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s);
+static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx);
+static int wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
+                                      int group_added);
+static void wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s);
+static void wpas_stop_listen(void *ctx);
+static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx);
+static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s);
+static int wpas_p2p_add_group_interface(struct wpa_supplicant *wpa_s,
+                                       enum wpa_driver_if_type type);
+
+
+/*
+ * Get the number of concurrent channels that the HW can operate, but that are
+ * currently not in use by any of the wpa_supplicant interfaces.
+ */
+static int wpas_p2p_num_unused_channels(struct wpa_supplicant *wpa_s)
+{
+       int *freqs;
+       int num, unused;
+
+       freqs = os_calloc(wpa_s->num_multichan_concurrent, sizeof(int));
+       if (!freqs)
+               return -1;
+
+       num = get_shared_radio_freqs(wpa_s, freqs,
+                                    wpa_s->num_multichan_concurrent);
+       os_free(freqs);
+
+       unused = wpa_s->num_multichan_concurrent - num;
+       wpa_dbg(wpa_s, MSG_DEBUG, "P2P: num_unused_channels: %d", unused);
+       return unused;
+}
+
+
+/*
+ * Get the frequencies that are currently in use by one or more of the virtual
+ * interfaces, and that are also valid for P2P operation.
+ */
+static unsigned int
+wpas_p2p_valid_oper_freqs(struct wpa_supplicant *wpa_s,
+                         struct wpa_used_freq_data *p2p_freqs,
+                         unsigned int len)
+{
+       struct wpa_used_freq_data *freqs;
+       unsigned int num, i, j;
+
+       freqs = os_calloc(wpa_s->num_multichan_concurrent,
+                         sizeof(struct wpa_used_freq_data));
+       if (!freqs)
+               return 0;
+
+       num = get_shared_radio_freqs_data(wpa_s, freqs,
+                                         wpa_s->num_multichan_concurrent);
+
+       os_memset(p2p_freqs, 0, sizeof(struct wpa_used_freq_data) * len);
+
+       for (i = 0, j = 0; i < num && j < len; i++) {
+               if (p2p_supported_freq(wpa_s->global->p2p, freqs[i].freq))
+                       p2p_freqs[j++] = freqs[i];
+       }
+
+       os_free(freqs);
+
+       dump_freq_data(wpa_s, "valid for P2P", p2p_freqs, j);
+
+       return j;
+}
 
 
 static void wpas_p2p_set_own_freq_preference(struct wpa_supplicant *wpa_s,
@@ -102,10 +191,13 @@ static void wpas_p2p_set_own_freq_preference(struct wpa_supplicant *wpa_s,
 {
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return;
-       if (freq > 0 &&
-           (wpa_s->drv_flags & WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT) &&
-           wpa_s->parent->conf->p2p_ignore_shared_freq)
+       if (wpa_s->parent->conf->p2p_ignore_shared_freq &&
+           freq > 0 && wpa_s->num_multichan_concurrent > 1 &&
+           wpas_p2p_num_unused_channels(wpa_s) > 0) {
+               wpa_printf(MSG_DEBUG, "P2P: Ignore own channel preference %d MHz due to p2p_ignore_shared_freq=1 configuration",
+                          freq);
                freq = 0;
+       }
        p2p_set_own_freq_preference(wpa_s->global->p2p, freq);
 }
 
@@ -115,6 +207,12 @@ static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s,
 {
        size_t i;
 
+       if (wpa_s->p2p_scan_work) {
+               struct wpa_radio_work *work = wpa_s->p2p_scan_work;
+               wpa_s->p2p_scan_work = NULL;
+               radio_work_done(work);
+       }
+
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return;
 
@@ -123,14 +221,29 @@ static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s,
 
        for (i = 0; i < scan_res->num; i++) {
                struct wpa_scan_res *bss = scan_res->res[i];
-               struct os_time time_tmp_age, entry_ts;
+               struct os_reltime time_tmp_age, entry_ts;
+               const u8 *ies;
+               size_t ies_len;
+
                time_tmp_age.sec = bss->age / 1000;
                time_tmp_age.usec = (bss->age % 1000) * 1000;
-               os_time_sub(&scan_res->fetch_time, &time_tmp_age, &entry_ts);
+               os_reltime_sub(&scan_res->fetch_time, &time_tmp_age, &entry_ts);
+
+               ies = (const u8 *) (bss + 1);
+               ies_len = bss->ie_len;
+               if (bss->beacon_ie_len > 0 &&
+                   !wpa_scan_get_vendor_ie(bss, P2P_IE_VENDOR_TYPE) &&
+                   wpa_scan_get_vendor_ie_beacon(bss, P2P_IE_VENDOR_TYPE)) {
+                       wpa_printf(MSG_DEBUG, "P2P: Use P2P IE(s) from Beacon frame since no P2P IE(s) in Probe Response frames received for "
+                                  MACSTR, MAC2STR(bss->bssid));
+                       ies = ies + ies_len;
+                       ies_len = bss->beacon_ie_len;
+               }
+
+
                if (p2p_scan_res_handler(wpa_s->global->p2p, bss->bssid,
                                         bss->freq, &entry_ts, bss->level,
-                                        (const u8 *) (bss + 1),
-                                        bss->ie_len) > 0)
+                                        ies, ies_len) > 0)
                        break;
        }
 
@@ -138,94 +251,166 @@ static void wpas_p2p_scan_res_handler(struct wpa_supplicant *wpa_s,
 }
 
 
+static void wpas_p2p_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
+{
+       struct wpa_supplicant *wpa_s = work->wpa_s;
+       struct wpa_driver_scan_params *params = work->ctx;
+       int ret;
+
+       if (deinit) {
+               if (!work->started) {
+                       wpa_scan_free_params(params);
+                       return;
+               }
+
+               wpa_s->p2p_scan_work = NULL;
+               return;
+       }
+
+       ret = wpa_drv_scan(wpa_s, params);
+       wpa_scan_free_params(params);
+       work->ctx = NULL;
+       if (ret) {
+               radio_work_done(work);
+               p2p_notify_scan_trigger_status(wpa_s->global->p2p, ret);
+               return;
+       }
+
+       p2p_notify_scan_trigger_status(wpa_s->global->p2p, ret);
+       os_get_reltime(&wpa_s->scan_trigger_time);
+       wpa_s->scan_res_handler = wpas_p2p_scan_res_handler;
+       wpa_s->own_scan_requested = 1;
+       wpa_s->p2p_scan_work = work;
+}
+
+
+static int wpas_p2p_search_social_channel(struct wpa_supplicant *wpa_s,
+                                         int freq)
+{
+       if (wpa_s->global->p2p_24ghz_social_channels &&
+           (freq == 2412 || freq == 2437 || freq == 2462)) {
+               /*
+                * Search all social channels regardless of whether these have
+                * been disabled for P2P operating channel use to avoid missing
+                * peers.
+                */
+               return 1;
+       }
+       return p2p_supported_freq(wpa_s->global->p2p, freq);
+}
+
+
 static int wpas_p2p_scan(void *ctx, enum p2p_scan_type type, int freq,
                         unsigned int num_req_dev_types,
                         const u8 *req_dev_types, const u8 *dev_id, u16 pw_id)
 {
        struct wpa_supplicant *wpa_s = ctx;
-       struct wpa_supplicant *ifs;
-       struct wpa_driver_scan_params params;
-       int ret;
+       struct wpa_driver_scan_params *params = NULL;
        struct wpabuf *wps_ie, *ies;
-       int social_channels[] = { 2412, 2437, 2462, 0, 0 };
+       unsigned int num_channels = 0;
+       int social_channels_freq[] = { 2412, 2437, 2462, 60480 };
        size_t ielen;
+       u8 *n, i;
 
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return -1;
 
-       for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
-               if (ifs->sta_scan_pending &&
-                   (wpas_scan_scheduled(ifs) || ifs->scanning) &&
-                   wpas_p2p_in_progress(wpa_s) == 2) {
-                       wpa_printf(MSG_DEBUG, "Delaying P2P scan to allow "
-                                  "pending station mode scan to be "
-                                  "completed on interface %s", ifs->ifname);
-                       wpa_s->global->p2p_cb_on_scan_complete = 1;
-                       wpa_supplicant_req_scan(ifs, 0, 0);
-                       return 1;
-               }
+       if (wpa_s->p2p_scan_work) {
+               wpa_dbg(wpa_s, MSG_INFO, "P2P: Reject scan trigger since one is already pending");
+               return -1;
        }
 
-       os_memset(&params, 0, sizeof(params));
+       params = os_zalloc(sizeof(*params));
+       if (params == NULL)
+               return -1;
 
        /* P2P Wildcard SSID */
-       params.num_ssids = 1;
-       params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
-       params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
+       params->num_ssids = 1;
+       n = os_malloc(P2P_WILDCARD_SSID_LEN);
+       if (n == NULL)
+               goto fail;
+       os_memcpy(n, P2P_WILDCARD_SSID, P2P_WILDCARD_SSID_LEN);
+       params->ssids[0].ssid = n;
+       params->ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
 
        wpa_s->wps->dev.p2p = 1;
        wps_ie = wps_build_probe_req_ie(pw_id, &wpa_s->wps->dev,
                                        wpa_s->wps->uuid, WPS_REQ_ENROLLEE,
                                        num_req_dev_types, req_dev_types);
        if (wps_ie == NULL)
-               return -1;
+               goto fail;
 
        ielen = p2p_scan_ie_buf_len(wpa_s->global->p2p);
        ies = wpabuf_alloc(wpabuf_len(wps_ie) + ielen);
        if (ies == NULL) {
                wpabuf_free(wps_ie);
-               return -1;
+               goto fail;
        }
        wpabuf_put_buf(ies, wps_ie);
        wpabuf_free(wps_ie);
 
        p2p_scan_ie(wpa_s->global->p2p, ies, dev_id);
 
-       params.p2p_probe = 1;
-       params.extra_ies = wpabuf_head(ies);
-       params.extra_ies_len = wpabuf_len(ies);
+       params->p2p_probe = 1;
+       n = os_malloc(wpabuf_len(ies));
+       if (n == NULL) {
+               wpabuf_free(ies);
+               goto fail;
+       }
+       os_memcpy(n, wpabuf_head(ies), wpabuf_len(ies));
+       params->extra_ies = n;
+       params->extra_ies_len = wpabuf_len(ies);
+       wpabuf_free(ies);
 
        switch (type) {
        case P2P_SCAN_SOCIAL:
-               params.freqs = social_channels;
+               params->freqs = os_calloc(ARRAY_SIZE(social_channels_freq) + 1,
+                                         sizeof(int));
+               if (params->freqs == NULL)
+                       goto fail;
+               for (i = 0; i < ARRAY_SIZE(social_channels_freq); i++) {
+                       if (wpas_p2p_search_social_channel(
+                                   wpa_s, social_channels_freq[i]))
+                               params->freqs[num_channels++] =
+                                       social_channels_freq[i];
+               }
+               params->freqs[num_channels++] = 0;
                break;
        case P2P_SCAN_FULL:
                break;
+       case P2P_SCAN_SPECIFIC:
+               params->freqs = os_calloc(2, sizeof(int));
+               if (params->freqs == NULL)
+                       goto fail;
+               params->freqs[0] = freq;
+               params->freqs[1] = 0;
+               break;
        case P2P_SCAN_SOCIAL_PLUS_ONE:
-               social_channels[3] = freq;
-               params.freqs = social_channels;
+               params->freqs = os_calloc(ARRAY_SIZE(social_channels_freq) + 2,
+                                         sizeof(int));
+               if (params->freqs == NULL)
+                       goto fail;
+               for (i = 0; i < ARRAY_SIZE(social_channels_freq); i++) {
+                       if (wpas_p2p_search_social_channel(
+                                   wpa_s, social_channels_freq[i]))
+                               params->freqs[num_channels++] =
+                                       social_channels_freq[i];
+               }
+               if (p2p_supported_freq(wpa_s->global->p2p, freq))
+                       params->freqs[num_channels++] = freq;
+               params->freqs[num_channels++] = 0;
                break;
        }
 
-       ret = wpa_drv_scan(wpa_s, &params);
-
-       wpabuf_free(ies);
-
-       if (ret) {
-               for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
-                       if (ifs->scanning ||
-                           ifs->scan_res_handler == wpas_p2p_scan_res_handler) {
-                               wpa_s->global->p2p_cb_on_scan_complete = 1;
-                               ret = 1;
-                               break;
-                       }
-               }
-       } else {
-               os_get_time(&wpa_s->scan_trigger_time);
-               wpa_s->scan_res_handler = wpas_p2p_scan_res_handler;
-       }
+       radio_remove_works(wpa_s, "p2p-scan", 0);
+       if (radio_add_work(wpa_s, 0, "p2p-scan", 0, wpas_p2p_trigger_scan_cb,
+                          params) < 0)
+               goto fail;
+       return 0;
 
-       return ret;
+fail:
+       wpa_scan_free_params(params);
+       return -1;
 }
 
 
@@ -269,6 +454,318 @@ static struct wpa_supplicant * wpas_get_p2p_group(struct wpa_supplicant *wpa_s,
 }
 
 
+static void run_wpas_p2p_disconnect(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       wpa_printf(MSG_DEBUG,
+                  "P2P: Complete previously requested removal of %s",
+                  wpa_s->ifname);
+       wpas_p2p_disconnect(wpa_s);
+}
+
+
+static int wpas_p2p_disconnect_safely(struct wpa_supplicant *wpa_s,
+                                     struct wpa_supplicant *calling_wpa_s)
+{
+       if (calling_wpa_s == wpa_s && wpa_s &&
+           wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
+               /*
+                * The calling wpa_s instance is going to be removed. Do that
+                * from an eloop callback to keep the instance available until
+                * the caller has returned. This my be needed, e.g., to provide
+                * control interface responses on the per-interface socket.
+                */
+               if (eloop_register_timeout(0, 0, run_wpas_p2p_disconnect,
+                                          wpa_s, NULL) < 0)
+                       return -1;
+               return 0;
+       }
+
+       return wpas_p2p_disconnect(wpa_s);
+}
+
+
+/* Determine total number of clients in active groups where we are the GO */
+static unsigned int p2p_group_go_member_count(struct wpa_supplicant *wpa_s)
+{
+       unsigned int count = 0;
+       struct wpa_ssid *s;
+
+       for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               for (s = wpa_s->conf->ssid; s; s = s->next) {
+                       wpa_printf(MSG_DEBUG,
+                                  "P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d",
+                                  wpa_s, s, s->disabled, s->p2p_group,
+                                  s->mode);
+                       if (!s->disabled && s->p2p_group &&
+                           s->mode == WPAS_MODE_P2P_GO) {
+                               count += p2p_get_group_num_members(
+                                       wpa_s->p2p_group);
+                       }
+               }
+       }
+
+       return count;
+}
+
+
+/* Find an interface for a P2P group where we are the GO */
+static struct wpa_supplicant *
+wpas_p2p_get_go_group(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_supplicant *save = NULL;
+       struct wpa_ssid *s;
+
+       if (!wpa_s)
+               return NULL;
+
+       for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               for (s = wpa_s->conf->ssid; s; s = s->next) {
+                       if (s->disabled || !s->p2p_group ||
+                           s->mode != WPAS_MODE_P2P_GO)
+                               continue;
+
+                       /* Prefer a group with connected clients */
+                       if (p2p_get_group_num_members(wpa_s->p2p_group))
+                               return wpa_s;
+                       save = wpa_s;
+               }
+       }
+
+       /* No group with connected clients, so pick the one without (if any) */
+       return save;
+}
+
+
+/* Find an active P2P group where we are the GO */
+static struct wpa_ssid * wpas_p2p_group_go_ssid(struct wpa_supplicant *wpa_s,
+                                               u8 *bssid)
+{
+       struct wpa_ssid *s, *empty = NULL;
+
+       if (!wpa_s)
+               return 0;
+
+       for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               for (s = wpa_s->conf->ssid; s; s = s->next) {
+                       if (s->disabled || !s->p2p_group ||
+                           s->mode != WPAS_MODE_P2P_GO)
+                               continue;
+
+                       os_memcpy(bssid, wpa_s->own_addr, ETH_ALEN);
+                       if (p2p_get_group_num_members(wpa_s->p2p_group))
+                               return s;
+                       empty = s;
+               }
+       }
+
+       return empty;
+}
+
+
+/* Find a persistent group where we are the GO */
+static struct wpa_ssid *
+wpas_p2p_get_persistent_go(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_ssid *s;
+
+       for (s = wpa_s->conf->ssid; s; s = s->next) {
+               if (s->disabled == 2 && s->mode == WPAS_MODE_P2P_GO)
+                       return s;
+       }
+
+       return NULL;
+}
+
+
+static u8 p2ps_group_capability(void *ctx, u8 incoming, u8 role)
+{
+       struct wpa_supplicant *wpa_s = ctx, *tmp_wpa_s;
+       struct wpa_ssid *s;
+       u8 conncap = P2PS_SETUP_NONE;
+       unsigned int owned_members = 0;
+       unsigned int owner = 0;
+       unsigned int client = 0;
+       struct wpa_supplicant *go_wpa_s;
+       struct wpa_ssid *persistent_go;
+       int p2p_no_group_iface;
+
+       wpa_printf(MSG_DEBUG, "P2P: Conncap - in:%d role:%d", incoming, role);
+
+       /*
+        * For non-concurrent capable devices:
+        * If persistent_go, then no new.
+        * If GO, then no client.
+        * If client, then no GO.
+        */
+       go_wpa_s = wpas_p2p_get_go_group(wpa_s);
+       persistent_go = wpas_p2p_get_persistent_go(wpa_s);
+       p2p_no_group_iface = wpa_s->conf->p2p_no_group_iface;
+
+       wpa_printf(MSG_DEBUG, "P2P: GO(iface)=%p persistent(ssid)=%p",
+                  go_wpa_s, persistent_go);
+
+       for (tmp_wpa_s = wpa_s->global->ifaces; tmp_wpa_s;
+            tmp_wpa_s = tmp_wpa_s->next) {
+               for (s = tmp_wpa_s->conf->ssid; s; s = s->next) {
+                       wpa_printf(MSG_DEBUG,
+                                  "P2P: sup:%p ssid:%p disabled:%d p2p:%d mode:%d",
+                                  tmp_wpa_s, s, s->disabled,
+                                  s->p2p_group, s->mode);
+                       if (!s->disabled && s->p2p_group) {
+                               if (s->mode == WPAS_MODE_P2P_GO) {
+                                       owned_members +=
+                                               p2p_get_group_num_members(
+                                                       tmp_wpa_s->p2p_group);
+                                       owner++;
+                               } else
+                                       client++;
+                       }
+               }
+       }
+
+       /* If not concurrent, restrict our choices */
+       if (p2p_no_group_iface) {
+               wpa_printf(MSG_DEBUG, "P2P: p2p_no_group_iface");
+
+               if (client)
+                       return P2PS_SETUP_NONE;
+
+               if (go_wpa_s) {
+                       if (role == P2PS_SETUP_CLIENT ||
+                           incoming == P2PS_SETUP_GROUP_OWNER ||
+                           p2p_client_limit_reached(go_wpa_s->p2p_group))
+                               return P2PS_SETUP_NONE;
+
+                       return P2PS_SETUP_GROUP_OWNER;
+               }
+
+               if (persistent_go) {
+                       if (role == P2PS_SETUP_NONE || role == P2PS_SETUP_NEW) {
+                               if (!incoming)
+                                       return P2PS_SETUP_GROUP_OWNER |
+                                               P2PS_SETUP_CLIENT;
+                               if (incoming == P2PS_SETUP_NEW) {
+                                       u8 r;
+
+                                       if (os_get_random(&r, sizeof(r)) < 0 ||
+                                           (r & 1))
+                                               return P2PS_SETUP_CLIENT;
+                                       return P2PS_SETUP_GROUP_OWNER;
+                               }
+                       }
+               }
+       }
+
+       /* If a required role has been specified, handle it here */
+       if (role && role != P2PS_SETUP_NEW) {
+               switch (incoming) {
+               case P2PS_SETUP_NONE:
+               case P2PS_SETUP_NEW:
+               case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
+               case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
+                       conncap = role;
+                       goto grp_owner;
+
+               case P2PS_SETUP_GROUP_OWNER:
+                       /*
+                        * Must be a complimentary role - cannot be a client to
+                        * more than one peer.
+                        */
+                       if (incoming == role || client)
+                               return P2PS_SETUP_NONE;
+
+                       return P2PS_SETUP_CLIENT;
+
+               case P2PS_SETUP_CLIENT:
+                       /* Must be a complimentary role */
+                       if (incoming != role) {
+                               conncap = P2PS_SETUP_GROUP_OWNER;
+                               goto grp_owner;
+                       }
+
+               default:
+                       return P2PS_SETUP_NONE;
+               }
+       }
+
+       /*
+        * For now, we only will support ownership of one group, and being a
+        * client of one group. Therefore, if we have either an existing GO
+        * group, or an existing client group, we will not do a new GO
+        * negotiation, but rather try to re-use the existing groups.
+        */
+       switch (incoming) {
+       case P2PS_SETUP_NONE:
+       case P2PS_SETUP_NEW:
+               if (client)
+                       conncap = P2PS_SETUP_GROUP_OWNER;
+               else if (!owned_members)
+                       conncap = P2PS_SETUP_NEW;
+               else if (incoming == P2PS_SETUP_NONE)
+                       conncap = P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT;
+               else
+                       conncap = P2PS_SETUP_CLIENT;
+               break;
+
+       case P2PS_SETUP_CLIENT:
+               conncap = P2PS_SETUP_GROUP_OWNER;
+               break;
+
+       case P2PS_SETUP_GROUP_OWNER:
+               if (!client)
+                       conncap = P2PS_SETUP_CLIENT;
+               break;
+
+       case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_NEW:
+       case P2PS_SETUP_GROUP_OWNER | P2PS_SETUP_CLIENT:
+               if (client)
+                       conncap = P2PS_SETUP_GROUP_OWNER;
+               else {
+                       u8 r;
+
+                       if (os_get_random(&r, sizeof(r)) < 0 ||
+                           (r & 1))
+                               conncap = P2PS_SETUP_CLIENT;
+                       else
+                               conncap = P2PS_SETUP_GROUP_OWNER;
+               }
+               break;
+
+       default:
+               return P2PS_SETUP_NONE;
+       }
+
+grp_owner:
+       if ((conncap & P2PS_SETUP_GROUP_OWNER) ||
+           (!incoming && (conncap & P2PS_SETUP_NEW))) {
+               if (go_wpa_s && p2p_client_limit_reached(go_wpa_s->p2p_group))
+                       conncap &= ~P2PS_SETUP_GROUP_OWNER;
+               wpa_printf(MSG_DEBUG, "P2P: GOs:%d members:%d conncap:%d",
+                          owner, owned_members, conncap);
+
+               s = wpas_p2p_get_persistent_go(wpa_s);
+
+               if (!s && !owner && p2p_no_group_iface) {
+                       p2p_set_intended_addr(wpa_s->global->p2p,
+                                             wpa_s->own_addr);
+               } else if (!s && !owner) {
+                       if (wpas_p2p_add_group_interface(wpa_s,
+                                                        WPA_IF_P2P_GO) < 0) {
+                               wpa_printf(MSG_ERROR,
+                                          "P2P: Failed to allocate a new interface for the group");
+                               return P2PS_SETUP_NONE;
+                       }
+                       wpa_s->global->pending_group_iface_for_p2ps = 1;
+                       p2p_set_intended_addr(wpa_s->global->p2p,
+                                             wpa_s->pending_interface_addr);
+               }
+       }
+
+       return conncap;
+}
+
+
 static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s,
                                 enum p2p_group_removal_reason removal_reason)
 {
@@ -303,11 +800,25 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s,
                 (ssid && ssid->mode == WPAS_MODE_INFRA)) {
                wpa_s->reassociate = 0;
                wpa_s->disconnected = 1;
-               wpa_supplicant_deauthenticate(wpa_s,
-                                             WLAN_REASON_DEAUTH_LEAVING);
                gtype = "client";
        } else
                gtype = "GO";
+
+       if (removal_reason != P2P_GROUP_REMOVAL_SILENT && ssid)
+               wpas_notify_p2p_group_removed(wpa_s, ssid, gtype);
+
+       if (os_strcmp(gtype, "client") == 0) {
+               wpa_supplicant_deauthenticate(wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+               if (eloop_is_timeout_registered(wpas_p2p_psk_failure_removal,
+                                               wpa_s, NULL)) {
+                       wpa_printf(MSG_DEBUG,
+                                  "P2P: PSK failure removal was scheduled, so use PSK failure as reason for group removal");
+                       removal_reason = P2P_GROUP_REMOVAL_PSK_FAILURE;
+                       eloop_cancel_timeout(wpas_p2p_psk_failure_removal,
+                                            wpa_s, NULL);
+               }
+       }
+
        if (wpa_s->cross_connect_in_use) {
                wpa_s->cross_connect_in_use = 0;
                wpa_msg_global(wpa_s->parent, MSG_INFO,
@@ -330,6 +841,12 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s,
        case P2P_GROUP_REMOVAL_GO_ENDING_SESSION:
                reason = " reason=GO_ENDING_SESSION";
                break;
+       case P2P_GROUP_REMOVAL_PSK_FAILURE:
+               reason = " reason=PSK_FAILURE";
+               break;
+       case P2P_GROUP_REMOVAL_FREQ_CONFLICT:
+               reason = " reason=FREQ_CONFLICT";
+               break;
        default:
                reason = "";
                break;
@@ -340,6 +857,8 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s,
                               wpa_s->ifname, gtype, reason);
        }
 
+       if (eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL) > 0)
+               wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group freq_conflict timeout");
        if (eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL) > 0)
                wpa_printf(MSG_DEBUG, "P2P: Cancelled P2P group idle timeout");
        if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
@@ -349,15 +868,20 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s,
                wpa_s->p2p_in_provisioning = 0;
        }
 
-       if (removal_reason != P2P_GROUP_REMOVAL_SILENT && ssid)
-               wpas_notify_p2p_group_removed(wpa_s, ssid, gtype);
+       wpa_s->p2p_in_invitation = 0;
+
+       /*
+        * Make sure wait for the first client does not remain active after the
+        * group has been removed.
+        */
+       wpa_s->global->p2p_go_wait_client.sec = 0;
 
 #ifdef TIZEN_EXT_P2P
        /* Currently Tizen uses just one group interface.
         * So, group index should be decreased when group interface removed.
        */
        wpa_s->parent->p2p_group_idx--;
-#endif
+#endif /* TIZEN_EXT_P2P */
        if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE) {
                struct wpa_global *global;
                char *ifname;
@@ -367,6 +891,7 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s,
                global = wpa_s->global;
                ifname = os_strdup(wpa_s->ifname);
                type = wpas_p2p_if_type(wpa_s->p2p_group_interface);
+               eloop_cancel_timeout(run_wpas_p2p_disconnect, wpa_s, NULL);
                wpa_supplicant_remove_iface(wpa_s->global, wpa_s, 0);
                wpa_s = global->ifaces;
                if (wpa_s && ifname)
@@ -375,6 +900,21 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s,
                return 1;
        }
 
+       if (!wpa_s->p2p_go_group_formation_completed) {
+               wpa_s->global->p2p_group_formation = NULL;
+               wpa_s->p2p_in_provisioning = 0;
+       }
+
+       wpa_s->show_group_started = 0;
+       os_free(wpa_s->go_params);
+       wpa_s->go_params = NULL;
+
+       os_free(wpa_s->p2p_group_common_freqs);
+       wpa_s->p2p_group_common_freqs = NULL;
+       wpa_s->p2p_group_common_freqs_num = 0;
+
+       wpa_s->waiting_presence_resp = 0;
+
        wpa_printf(MSG_DEBUG, "P2P: Remove temporary group network");
        if (ssid && (ssid->p2p_group ||
                     ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION ||
@@ -397,7 +937,6 @@ static int wpas_p2p_group_delete(struct wpa_supplicant *wpa_s,
                wpa_config_remove_network(wpa_s->conf, id);
                wpa_supplicant_clear_status(wpa_s);
                wpa_supplicant_cancel_sched_scan(wpa_s);
-               wpa_s->sta_scan_pending = 0;
        } else {
                wpa_printf(MSG_DEBUG, "P2P: Temporary group network not "
                           "found");
@@ -427,6 +966,10 @@ static int wpas_p2p_persistent_group(struct wpa_supplicant *wpa_s,
                bssid = wpa_s->bssid;
 
        bss = wpa_bss_get(wpa_s, bssid, ssid, ssid_len);
+       if (bss == NULL && wpa_s->go_params &&
+           !is_zero_ether_addr(wpa_s->go_params->peer_device_addr))
+               bss = wpa_bss_get_p2p_dev_addr(
+                       wpa_s, wpa_s->go_params->peer_device_addr);
        if (bss == NULL) {
                u8 iface_addr[ETH_ALEN];
                if (p2p_get_interface_addr(wpa_s->global->p2p, bssid,
@@ -441,6 +984,9 @@ static int wpas_p2p_persistent_group(struct wpa_supplicant *wpa_s,
        }
 
        p2p = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
+       if (p2p == NULL)
+               p2p = wpa_bss_get_vendor_ie_multi_beacon(bss,
+                                                        P2P_IE_VENDOR_TYPE);
        if (p2p == NULL) {
                wpa_printf(MSG_DEBUG, "P2P: Could not figure out whether "
                           "group is persistent - BSS " MACSTR
@@ -547,13 +1093,16 @@ static int wpas_p2p_store_persistent_group(struct wpa_supplicant *wpa_s,
                s->ssid_len = ssid->ssid_len;
                os_memcpy(s->ssid, ssid->ssid, s->ssid_len);
        }
+       if (ssid->mode == WPAS_MODE_P2P_GO && wpa_s->global->add_psk) {
+               dl_list_add(&s->psk_list, &wpa_s->global->add_psk->list);
+               wpa_s->global->add_psk = NULL;
+               changed = 1;
+       }
 
-#ifndef CONFIG_NO_CONFIG_WRITE
        if (changed && wpa_s->conf->update_config &&
            wpa_config_write(wpa_s->confname, wpa_s->conf)) {
                wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
        }
-#endif /* CONFIG_NO_CONFIG_WRITE */
 
        return s->id;
 }
@@ -585,7 +1134,7 @@ static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s,
                return;
 
        for (i = 0; s->p2p_client_list && i < s->num_p2p_clients; i++) {
-               if (os_memcmp(s->p2p_client_list + i * ETH_ALEN, addr,
+               if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN, addr,
                              ETH_ALEN) != 0)
                        continue;
 
@@ -593,39 +1142,92 @@ static void wpas_p2p_add_persistent_group_client(struct wpa_supplicant *wpa_s,
                        return; /* already the most recent entry */
 
                /* move the entry to mark it most recent */
-               os_memmove(s->p2p_client_list + i * ETH_ALEN,
-                          s->p2p_client_list + (i + 1) * ETH_ALEN,
-                          (s->num_p2p_clients - i - 1) * ETH_ALEN);
+               os_memmove(s->p2p_client_list + i * 2 * ETH_ALEN,
+                          s->p2p_client_list + (i + 1) * 2 * ETH_ALEN,
+                          (s->num_p2p_clients - i - 1) * 2 * ETH_ALEN);
                os_memcpy(s->p2p_client_list +
-                         (s->num_p2p_clients - 1) * ETH_ALEN, addr, ETH_ALEN);
+                         (s->num_p2p_clients - 1) * 2 * ETH_ALEN, addr,
+                         ETH_ALEN);
+               os_memset(s->p2p_client_list +
+                         (s->num_p2p_clients - 1) * 2 * ETH_ALEN + ETH_ALEN,
+                         0xff, ETH_ALEN);
                found = 1;
                break;
        }
 
        if (!found && s->num_p2p_clients < P2P_MAX_STORED_CLIENTS) {
                n = os_realloc_array(s->p2p_client_list,
-                                    s->num_p2p_clients + 1, ETH_ALEN);
+                                    s->num_p2p_clients + 1, 2 * ETH_ALEN);
                if (n == NULL)
                        return;
-               os_memcpy(n + s->num_p2p_clients * ETH_ALEN, addr, ETH_ALEN);
+               os_memcpy(n + s->num_p2p_clients * 2 * ETH_ALEN, addr,
+                         ETH_ALEN);
+               os_memset(n + s->num_p2p_clients * 2 * ETH_ALEN + ETH_ALEN,
+                         0xff, ETH_ALEN);
                s->p2p_client_list = n;
                s->num_p2p_clients++;
-       } else if (!found) {
+       } else if (!found && s->p2p_client_list) {
                /* Not enough room for an additional entry - drop the oldest
                 * entry */
                os_memmove(s->p2p_client_list,
-                          s->p2p_client_list + ETH_ALEN,
-                          (s->num_p2p_clients - 1) * ETH_ALEN);
+                          s->p2p_client_list + 2 * ETH_ALEN,
+                          (s->num_p2p_clients - 1) * 2 * ETH_ALEN);
                os_memcpy(s->p2p_client_list +
-                         (s->num_p2p_clients - 1) * ETH_ALEN,
+                         (s->num_p2p_clients - 1) * 2 * ETH_ALEN,
                          addr, ETH_ALEN);
+               os_memset(s->p2p_client_list +
+                         (s->num_p2p_clients - 1) * 2 * ETH_ALEN + ETH_ALEN,
+                         0xff, ETH_ALEN);
        }
 
-#ifndef CONFIG_NO_CONFIG_WRITE
        if (wpa_s->parent->conf->update_config &&
            wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
                wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
-#endif /* CONFIG_NO_CONFIG_WRITE */
+}
+
+
+static void wpas_p2p_group_started(struct wpa_supplicant *wpa_s,
+                                  int go, struct wpa_ssid *ssid, int freq,
+                                  const u8 *psk, const char *passphrase,
+                                  const u8 *go_dev_addr, int persistent,
+                                  const char *extra)
+{
+       const char *ssid_txt;
+       char psk_txt[65];
+
+       if (psk)
+               wpa_snprintf_hex(psk_txt, sizeof(psk_txt), psk, 32);
+       else
+               psk_txt[0] = '\0';
+
+       if (ssid)
+               ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len);
+       else
+               ssid_txt = "";
+
+       if (passphrase && passphrase[0] == '\0')
+               passphrase = NULL;
+
+       /*
+        * Include PSK/passphrase only in the control interface message and
+        * leave it out from the debug log entry.
+        */
+       wpa_msg_global_ctrl(wpa_s->parent, MSG_INFO,
+                           P2P_EVENT_GROUP_STARTED
+                           "%s %s ssid=\"%s\" freq=%d%s%s%s%s%s go_dev_addr="
+                           MACSTR "%s%s",
+                           wpa_s->ifname, go ? "GO" : "client", ssid_txt, freq,
+                           psk ? " psk=" : "", psk_txt,
+                           passphrase ? " passphrase=\"" : "",
+                           passphrase ? passphrase : "",
+                           passphrase ? "\"" : "",
+                           MAC2STR(go_dev_addr),
+                           persistent ? " [PERSISTENT]" : "", extra);
+       wpa_printf(MSG_INFO, P2P_EVENT_GROUP_STARTED
+                  "%s %s ssid=\"%s\" freq=%d go_dev_addr=" MACSTR "%s%s",
+                  wpa_s->ifname, go ? "GO" : "client", ssid_txt, freq,
+                  MAC2STR(go_dev_addr), persistent ? " [PERSISTENT]" : "",
+                  extra);
 }
 
 
@@ -633,7 +1235,6 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
                                           int success)
 {
        struct wpa_ssid *ssid;
-       const char *ssid_txt;
        int client;
        int persistent;
        u8 go_dev_addr[ETH_ALEN];
@@ -646,12 +1247,18 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
         */
        if (wpa_s->global->p2p_group_formation)
                wpa_s = wpa_s->global->p2p_group_formation;
-       wpa_s->global->p2p_group_formation = NULL;
-       wpa_s->p2p_in_provisioning = 0;
+       if (wpa_s->p2p_go_group_formation_completed) {
+               wpa_s->global->p2p_group_formation = NULL;
+               wpa_s->p2p_in_provisioning = 0;
+       }
+       wpa_s->p2p_in_invitation = 0;
+       wpa_s->group_formation_reported = 1;
 
        if (!success) {
                wpa_msg_global(wpa_s->parent, MSG_INFO,
                               P2P_EVENT_GROUP_FORMATION_FAILURE);
+               wpa_printf(MSG_INFO, "dbus: Notify Group Formation Failure");
+               wpas_notify_p2p_group_formation_failure(wpa_s);
                wpas_p2p_group_delete(wpa_s,
                                      P2P_GROUP_REMOVAL_FORMATION_FAILED);
                return;
@@ -669,7 +1276,6 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
 
        persistent = 0;
        if (ssid) {
-               ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len);
                client = ssid->mode == WPAS_MODE_INFRA;
                if (ssid->mode == WPAS_MODE_P2P_GO) {
                        persistent = ssid->p2p_persistent_group;
@@ -681,7 +1287,6 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
                                                               ssid->ssid,
                                                               ssid->ssid_len);
        } else {
-               ssid_txt = "";
                client = wpa_s->p2p_group_interface ==
                        P2P_GROUP_INTERFACE_CLIENT;
                os_memset(go_dev_addr, 0, ETH_ALEN);
@@ -695,26 +1300,13 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
                 * packets.
                 */
                wpa_s->show_group_started = 1;
-       } else if (ssid && ssid->passphrase == NULL && ssid->psk_set) {
-               char psk[65];
-               wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32);
-               wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
-                              "%s GO ssid=\"%s\" freq=%d psk=%s go_dev_addr="
-                              MACSTR "%s",
-                              wpa_s->ifname, ssid_txt, ssid->frequency, psk,
-                              MAC2STR(go_dev_addr),
-                              persistent ? " [PERSISTENT]" : "");
-               wpas_p2p_cross_connect_setup(wpa_s);
-               wpas_p2p_set_group_idle_timeout(wpa_s);
        } else {
-               wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
-                              "%s GO ssid=\"%s\" freq=%d passphrase=\"%s\" "
-                              "go_dev_addr=" MACSTR "%s",
-                              wpa_s->ifname, ssid_txt,
-                              ssid ? ssid->frequency : 0,
-                              ssid && ssid->passphrase ? ssid->passphrase : "",
-                              MAC2STR(go_dev_addr),
-                              persistent ? " [PERSISTENT]" : "");
+               wpas_p2p_group_started(wpa_s, 1, ssid,
+                                      ssid ? ssid->frequency : 0,
+                                      ssid && ssid->passphrase == NULL &&
+                                      ssid->psk_set ? ssid->psk : NULL,
+                                      ssid ? ssid->passphrase : NULL,
+                                      go_dev_addr, persistent, "");
                wpas_p2p_cross_connect_setup(wpa_s);
                wpas_p2p_set_group_idle_timeout(wpa_s);
        }
@@ -722,10 +1314,69 @@ static void wpas_group_formation_completed(struct wpa_supplicant *wpa_s,
        if (persistent)
                network_id = wpas_p2p_store_persistent_group(wpa_s->parent,
                                                             ssid, go_dev_addr);
+       else {
+               os_free(wpa_s->global->add_psk);
+               wpa_s->global->add_psk = NULL;
+       }
        if (network_id < 0 && ssid)
                network_id = ssid->id;
-       if (!client)
+       if (!client) {
                wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0);
+               os_get_reltime(&wpa_s->global->p2p_go_wait_client);
+       }
+}
+
+
+struct send_action_work {
+       unsigned int freq;
+       u8 dst[ETH_ALEN];
+       u8 src[ETH_ALEN];
+       u8 bssid[ETH_ALEN];
+       size_t len;
+       unsigned int wait_time;
+       u8 buf[0];
+};
+
+
+static void wpas_p2p_send_action_work_timeout(void *eloop_ctx,
+                                             void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+
+       if (!wpa_s->p2p_send_action_work)
+               return;
+
+       wpa_printf(MSG_DEBUG, "P2P: Send Action frame radio work timed out");
+       os_free(wpa_s->p2p_send_action_work->ctx);
+       radio_work_done(wpa_s->p2p_send_action_work);
+       wpa_s->p2p_send_action_work = NULL;
+}
+
+
+static void wpas_p2p_action_tx_clear(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->p2p_send_action_work) {
+               struct send_action_work *awork;
+               awork = wpa_s->p2p_send_action_work->ctx;
+               if (awork->wait_time == 0) {
+                       os_free(awork);
+                       radio_work_done(wpa_s->p2p_send_action_work);
+                       wpa_s->p2p_send_action_work = NULL;
+               } else {
+                       /*
+                        * In theory, this should not be needed, but number of
+                        * places in the P2P code is still using non-zero wait
+                        * time for the last Action frame in the sequence and
+                        * some of these do not call send_action_done().
+                        */
+                       eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
+                                            wpa_s, NULL);
+                       eloop_register_timeout(
+                               0, awork->wait_time * 1000,
+                               wpas_p2p_send_action_work_timeout,
+                               wpa_s, NULL);
+               }
+       }
 }
 
 
@@ -739,10 +1390,10 @@ static void wpas_p2p_send_action_tx_status(struct wpa_supplicant *wpa_s,
 {
        enum p2p_send_action_result res = P2P_SEND_ACTION_SUCCESS;
 
+       wpas_p2p_action_tx_clear(wpa_s);
+
        if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled)
                return;
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-               return;
 
        switch (result) {
        case OFFCHANNEL_SEND_ACTION_SUCCESS:
@@ -766,17 +1417,96 @@ static void wpas_p2p_send_action_tx_status(struct wpa_supplicant *wpa_s,
                wpa_s->pending_pd_before_join = 0;
                wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No ACK for PD Req "
                        "during p2p_connect-auto");
+               wpa_msg_global(wpa_s->parent, MSG_INFO,
+                              P2P_EVENT_FALLBACK_TO_GO_NEG
+                              "reason=no-ACK-to-PD-Req");
                wpas_p2p_fallback_to_go_neg(wpa_s, 0);
                return;
        }
 }
 
 
+static void wpas_send_action_cb(struct wpa_radio_work *work, int deinit)
+{
+       struct wpa_supplicant *wpa_s = work->wpa_s;
+       struct send_action_work *awork = work->ctx;
+
+       if (deinit) {
+               if (work->started) {
+                       eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
+                                            wpa_s, NULL);
+                       wpa_s->p2p_send_action_work = NULL;
+                       offchannel_send_action_done(wpa_s);
+               }
+               os_free(awork);
+               return;
+       }
+
+       if (offchannel_send_action(wpa_s, awork->freq, awork->dst, awork->src,
+                                  awork->bssid, awork->buf, awork->len,
+                                  awork->wait_time,
+                                  wpas_p2p_send_action_tx_status, 1) < 0) {
+               os_free(awork);
+               radio_work_done(work);
+               return;
+       }
+       wpa_s->p2p_send_action_work = work;
+}
+
+
+static int wpas_send_action_work(struct wpa_supplicant *wpa_s,
+                                unsigned int freq, const u8 *dst,
+                                const u8 *src, const u8 *bssid, const u8 *buf,
+                                size_t len, unsigned int wait_time)
+{
+       struct send_action_work *awork;
+
+       if (wpa_s->p2p_send_action_work) {
+               wpa_printf(MSG_DEBUG, "P2P: Cannot schedule new p2p-send-action work since one is already pending");
+               return -1;
+       }
+
+       awork = os_zalloc(sizeof(*awork) + len);
+       if (awork == NULL)
+               return -1;
+
+       awork->freq = freq;
+       os_memcpy(awork->dst, dst, ETH_ALEN);
+       os_memcpy(awork->src, src, ETH_ALEN);
+       os_memcpy(awork->bssid, bssid, ETH_ALEN);
+       awork->len = len;
+       awork->wait_time = wait_time;
+       os_memcpy(awork->buf, buf, len);
+
+       if (radio_add_work(wpa_s, freq, "p2p-send-action", 0,
+                          wpas_send_action_cb, awork) < 0) {
+               os_free(awork);
+               return -1;
+       }
+
+       return 0;
+}
+
+
 static int wpas_send_action(void *ctx, unsigned int freq, const u8 *dst,
                            const u8 *src, const u8 *bssid, const u8 *buf,
                            size_t len, unsigned int wait_time)
 {
        struct wpa_supplicant *wpa_s = ctx;
+       int listen_freq = -1, send_freq = -1;
+
+       if (wpa_s->p2p_listen_work)
+               listen_freq = wpa_s->p2p_listen_work->freq;
+       if (wpa_s->p2p_send_action_work)
+               send_freq = wpa_s->p2p_send_action_work->freq;
+       if (listen_freq != (int) freq && send_freq != (int) freq) {
+               wpa_printf(MSG_DEBUG, "P2P: Schedule new radio work for Action frame TX (listen_freq=%d send_freq=%d)",
+                          listen_freq, send_freq);
+               return wpas_send_action_work(wpa_s, freq, dst, src, bssid, buf,
+                                            len, wait_time);
+       }
+
+       wpa_printf(MSG_DEBUG, "P2P: Use ongoing radio work for Action frame TX");
        return offchannel_send_action(wpa_s, freq, dst, src, bssid, buf, len,
                                      wait_time,
                                      wpas_p2p_send_action_tx_status, 1);
@@ -786,6 +1516,15 @@ static int wpas_send_action(void *ctx, unsigned int freq, const u8 *dst,
 static void wpas_send_action_done(void *ctx)
 {
        struct wpa_supplicant *wpa_s = ctx;
+
+       if (wpa_s->p2p_send_action_work) {
+               eloop_cancel_timeout(wpas_p2p_send_action_work_timeout,
+                                    wpa_s, NULL);
+               os_free(wpa_s->p2p_send_action_work->ctx);
+               radio_work_done(wpa_s->p2p_send_action_work);
+               wpa_s->p2p_send_action_work = NULL;
+       }
+
        offchannel_send_action_done(wpa_s);
 }
 
@@ -806,16 +1545,33 @@ static int wpas_copy_go_neg_results(struct wpa_supplicant *wpa_s,
 static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s,
                                    struct p2p_go_neg_results *res)
 {
-       wpa_printf(MSG_DEBUG, "P2P: Start WPS Enrollee for peer " MACSTR,
-                  MAC2STR(res->peer_interface_addr));
+       wpa_s->group_formation_reported = 0;
+       wpa_printf(MSG_DEBUG, "P2P: Start WPS Enrollee for peer " MACSTR
+                  " dev_addr " MACSTR " wps_method %d",
+                  MAC2STR(res->peer_interface_addr),
+                  MAC2STR(res->peer_device_addr), res->wps_method);
        wpa_hexdump_ascii(MSG_DEBUG, "P2P: Start WPS Enrollee for SSID",
                          res->ssid, res->ssid_len);
        wpa_supplicant_ap_deinit(wpa_s);
        wpas_copy_go_neg_results(wpa_s, res);
-       if (res->wps_method == WPS_PBC)
+       if (res->wps_method == WPS_PBC) {
                wpas_wps_start_pbc(wpa_s, res->peer_interface_addr, 1);
-       else {
+#ifdef CONFIG_WPS_NFC
+       } else if (res->wps_method == WPS_NFC) {
+               wpas_wps_start_nfc(wpa_s, res->peer_device_addr,
+                                  res->peer_interface_addr,
+                                  wpa_s->parent->p2p_oob_dev_pw,
+                                  wpa_s->parent->p2p_oob_dev_pw_id, 1,
+                                  wpa_s->parent->p2p_oob_dev_pw_id ==
+                                  DEV_PW_NFC_CONNECTION_HANDOVER ?
+                                  wpa_s->parent->p2p_peer_oob_pubkey_hash :
+                                  NULL,
+                                  NULL, 0, 0);
+#endif /* CONFIG_WPS_NFC */
+       } else {
                u16 dev_pw_id = DEV_PW_DEFAULT;
+               if (wpa_s->p2p_wps_method == WPS_P2PS)
+                       dev_pw_id = DEV_PW_P2PS_DEFAULT;
                if (wpa_s->p2p_wps_method == WPS_PIN_KEYPAD)
                        dev_pw_id = DEV_PW_REGISTRAR_SPECIFIED;
                wpas_wps_start_pin(wpa_s, res->peer_interface_addr,
@@ -824,6 +1580,88 @@ static void wpas_start_wps_enrollee(struct wpa_supplicant *wpa_s,
 }
 
 
+static void wpas_p2p_add_psk_list(struct wpa_supplicant *wpa_s,
+                                 struct wpa_ssid *ssid)
+{
+       struct wpa_ssid *persistent;
+       struct psk_list_entry *psk;
+       struct hostapd_data *hapd;
+
+       if (!wpa_s->ap_iface)
+               return;
+
+       persistent = wpas_p2p_get_persistent(wpa_s->parent, NULL, ssid->ssid,
+                                            ssid->ssid_len);
+       if (persistent == NULL)
+               return;
+
+       hapd = wpa_s->ap_iface->bss[0];
+
+       dl_list_for_each(psk, &persistent->psk_list, struct psk_list_entry,
+                        list) {
+               struct hostapd_wpa_psk *hpsk;
+
+               wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Add persistent group PSK entry for "
+                       MACSTR " psk=%d",
+                       MAC2STR(psk->addr), psk->p2p);
+               hpsk = os_zalloc(sizeof(*hpsk));
+               if (hpsk == NULL)
+                       break;
+               os_memcpy(hpsk->psk, psk->psk, PMK_LEN);
+               if (psk->p2p)
+                       os_memcpy(hpsk->p2p_dev_addr, psk->addr, ETH_ALEN);
+               else
+                       os_memcpy(hpsk->addr, psk->addr, ETH_ALEN);
+               hpsk->next = hapd->conf->ssid.wpa_psk;
+               hapd->conf->ssid.wpa_psk = hpsk;
+       }
+}
+
+
+static void p2p_go_dump_common_freqs(struct wpa_supplicant *wpa_s)
+{
+       unsigned int i;
+
+       wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Common group frequencies (len=%u):",
+               wpa_s->p2p_group_common_freqs_num);
+
+       for (i = 0; i < wpa_s->p2p_group_common_freqs_num; i++)
+               wpa_dbg(wpa_s, MSG_DEBUG, "freq[%u]: %d",
+                       i, wpa_s->p2p_group_common_freqs[i]);
+}
+
+
+static void p2p_go_save_group_common_freqs(struct wpa_supplicant *wpa_s,
+                                          struct p2p_go_neg_results *params)
+{
+       unsigned int i, len = int_array_len(wpa_s->go_params->freq_list);
+
+       wpa_s->p2p_group_common_freqs_num = 0;
+       os_free(wpa_s->p2p_group_common_freqs);
+       wpa_s->p2p_group_common_freqs = os_calloc(len, sizeof(int));
+       if (!wpa_s->p2p_group_common_freqs)
+               return;
+
+       for (i = 0; i < len; i++) {
+               if (!wpa_s->go_params->freq_list[i])
+                       break;
+               wpa_s->p2p_group_common_freqs[i] =
+                       wpa_s->go_params->freq_list[i];
+       }
+       wpa_s->p2p_group_common_freqs_num = i;
+}
+
+
+static void p2p_config_write(struct wpa_supplicant *wpa_s)
+{
+#ifndef CONFIG_NO_CONFIG_WRITE
+       if (wpa_s->parent->conf->update_config &&
+           wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
+               wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
+#endif /* CONFIG_NO_CONFIG_WRITE */
+}
+
+
 static void p2p_go_configured(void *ctx, void *data)
 {
        struct wpa_supplicant *wpa_s = ctx;
@@ -831,47 +1669,59 @@ static void p2p_go_configured(void *ctx, void *data)
        struct wpa_ssid *ssid;
        int network_id = -1;
 
+       p2p_go_save_group_common_freqs(wpa_s, params);
+       p2p_go_dump_common_freqs(wpa_s);
+
        ssid = wpa_s->current_ssid;
        if (ssid && ssid->mode == WPAS_MODE_P2P_GO) {
                wpa_printf(MSG_DEBUG, "P2P: Group setup without provisioning");
                if (wpa_s->global->p2p_group_formation == wpa_s)
                        wpa_s->global->p2p_group_formation = NULL;
-               if (os_strlen(params->passphrase) > 0) {
-                       wpa_msg_global(wpa_s->parent, MSG_INFO,
-                                      P2P_EVENT_GROUP_STARTED
-                                      "%s GO ssid=\"%s\" freq=%d "
-                                      "passphrase=\"%s\" go_dev_addr=" MACSTR
-                                      "%s", wpa_s->ifname,
-                                      wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
-                                      ssid->frequency, params->passphrase,
-                                      MAC2STR(wpa_s->global->p2p_dev_addr),
-                                      params->persistent_group ?
-                                      " [PERSISTENT]" : "");
-               } else {
-                       char psk[65];
-                       wpa_snprintf_hex(psk, sizeof(psk), params->psk,
-                                        sizeof(params->psk));
-                       wpa_msg_global(wpa_s->parent, MSG_INFO,
-                                      P2P_EVENT_GROUP_STARTED
-                                      "%s GO ssid=\"%s\" freq=%d psk=%s "
-                                      "go_dev_addr=" MACSTR "%s",
-                                      wpa_s->ifname,
-                                      wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
-                                      ssid->frequency, psk,
-                                      MAC2STR(wpa_s->global->p2p_dev_addr),
-                                      params->persistent_group ?
-                                      " [PERSISTENT]" : "");
-               }
-
-               if (params->persistent_group)
+               wpas_p2p_group_started(wpa_s, 1, ssid, ssid->frequency,
+                                      params->passphrase[0] == '\0' ?
+                                      params->psk : NULL,
+                                      params->passphrase,
+                                      wpa_s->global->p2p_dev_addr,
+                                      params->persistent_group, "");
+               wpa_s->group_formation_reported = 1;
+
+               if (wpa_s->parent->p2ps_join_addr_valid) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "P2PS: Setting default PIN for " MACSTR,
+                               MAC2STR(wpa_s->parent->p2ps_join_addr));
+                       wpa_supplicant_ap_wps_pin(wpa_s,
+                                                 wpa_s->parent->p2ps_join_addr,
+                                                 "12345670", NULL, 0, 0);
+                       wpa_s->parent->p2ps_join_addr_valid = 0;
+               }
+
+               os_get_reltime(&wpa_s->global->p2p_go_wait_client);
+               if (params->persistent_group) {
                        network_id = wpas_p2p_store_persistent_group(
                                wpa_s->parent, ssid,
                                wpa_s->global->p2p_dev_addr);
+                       wpas_p2p_add_psk_list(wpa_s, ssid);
+               }
                if (network_id < 0)
                        network_id = ssid->id;
                wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 0);
                wpas_p2p_cross_connect_setup(wpa_s);
                wpas_p2p_set_group_idle_timeout(wpa_s);
+
+               if (wpa_s->p2p_first_connection_timeout) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "P2P: Start group formation timeout of %d seconds until first data connection on GO",
+                               wpa_s->p2p_first_connection_timeout);
+                       wpa_s->p2p_go_group_formation_completed = 0;
+                       wpa_s->global->p2p_group_formation = wpa_s;
+                       eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+                                            wpa_s->parent, NULL);
+                       eloop_register_timeout(
+                               wpa_s->p2p_first_connection_timeout, 0,
+                               wpas_p2p_group_formation_timeout,
+                               wpa_s->parent, NULL);
+               }
+
                return;
        }
 
@@ -882,10 +1732,24 @@ static void p2p_go_configured(void *ctx, void *data)
                           "filtering");
                return;
        }
-       if (params->wps_method == WPS_PBC)
+       if (params->wps_method == WPS_PBC) {
                wpa_supplicant_ap_wps_pbc(wpa_s, params->peer_interface_addr,
                                          params->peer_device_addr);
-       else if (wpa_s->p2p_pin[0])
+#ifdef CONFIG_WPS_NFC
+       } else if (params->wps_method == WPS_NFC) {
+               if (wpa_s->parent->p2p_oob_dev_pw_id !=
+                   DEV_PW_NFC_CONNECTION_HANDOVER &&
+                   !wpa_s->parent->p2p_oob_dev_pw) {
+                       wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known");
+                       return;
+               }
+               wpas_ap_wps_add_nfc_pw(
+                       wpa_s, wpa_s->parent->p2p_oob_dev_pw_id,
+                       wpa_s->parent->p2p_oob_dev_pw,
+                       wpa_s->parent->p2p_peer_oob_pk_hash_known ?
+                       wpa_s->parent->p2p_peer_oob_pubkey_hash : NULL);
+#endif /* CONFIG_WPS_NFC */
+       } else if (wpa_s->p2p_pin[0])
                wpa_supplicant_ap_wps_pin(wpa_s, params->peer_interface_addr,
                                          wpa_s->p2p_pin, NULL, 0, 0);
        os_free(wpa_s->go_params);
@@ -913,6 +1777,8 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s,
        }
 
        wpa_s->show_group_started = 0;
+       wpa_s->p2p_go_group_formation_completed = 0;
+       wpa_s->group_formation_reported = 0;
 
        wpa_config_set_network_defaults(ssid);
        ssid->temporary = 1;
@@ -922,6 +1788,7 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s,
                WPAS_MODE_P2P_GO;
        ssid->frequency = params->freq;
        ssid->ht40 = params->ht40;
+       ssid->vht = params->vht;
        ssid->ssid = os_zalloc(params->ssid_len + 1);
        if (ssid->ssid) {
                os_memcpy(ssid->ssid, params->ssid, params->ssid_len);
@@ -931,6 +1798,15 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s,
        ssid->key_mgmt = WPA_KEY_MGMT_PSK;
        ssid->proto = WPA_PROTO_RSN;
        ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+       ssid->group_cipher = WPA_CIPHER_CCMP;
+       if (params->freq > 56160) {
+               /*
+                * Enable GCMP instead of CCMP as pairwise_cipher and
+                * group_cipher in 60 GHz.
+                */
+               ssid->pairwise_cipher = WPA_CIPHER_GCMP;
+               ssid->group_cipher = WPA_CIPHER_GCMP;
+       }
        if (os_strlen(params->passphrase) > 0) {
                ssid->passphrase = os_strdup(params->passphrase);
                if (ssid->passphrase == NULL) {
@@ -951,6 +1827,7 @@ static void wpas_start_wps_go(struct wpa_supplicant *wpa_s,
        wpa_s->ap_configured_cb = p2p_go_configured;
        wpa_s->ap_configured_cb_ctx = wpa_s;
        wpa_s->ap_configured_cb_data = wpa_s->go_params;
+       wpa_s->scan_req = NORMAL_SCAN_REQ;
        wpa_s->connect_without_scan = ssid;
        wpa_s->reassociate = 1;
        wpa_s->disconnected = 0;
@@ -990,6 +1867,16 @@ static void wpas_p2p_clone_config(struct wpa_supplicant *dst,
        d->pbc_in_m1 = s->pbc_in_m1;
        d->ignore_old_scan_res = s->ignore_old_scan_res;
        d->beacon_int = s->beacon_int;
+       d->dtim_period = s->dtim_period;
+       d->p2p_go_ctwindow = s->p2p_go_ctwindow;
+       d->disassoc_low_ack = s->disassoc_low_ack;
+       d->disable_scan_offload = s->disable_scan_offload;
+       d->passive_scan = s->passive_scan;
+
+       if (s->wps_nfc_dh_privkey && s->wps_nfc_dh_pubkey) {
+               d->wps_nfc_dh_privkey = wpabuf_dup(s->wps_nfc_dh_privkey);
+               d->wps_nfc_dh_pubkey = wpabuf_dup(s->wps_nfc_dh_pubkey);
+       }
 }
 
 
@@ -1006,9 +1893,12 @@ static void wpas_p2p_get_group_ifname(struct wpa_supplicant *wpa_s,
        os_snprintf(ifname, len, "p2p-%s-%d", ifname_ptr, wpa_s->p2p_group_idx);
        if (os_strlen(ifname) >= IFNAMSIZ &&
            os_strlen(wpa_s->ifname) < IFNAMSIZ) {
+               int res;
+
                /* Try to avoid going over the IFNAMSIZ length limit */
-               os_snprintf(ifname, sizeof(ifname), "p2p-%d",
-                           wpa_s->p2p_group_idx);
+               res = os_snprintf(ifname, len, "p2p-%d", wpa_s->p2p_group_idx);
+               if (os_snprintf_error(len, res) && len)
+                       ifname[len - 1] = '\0';
        }
 }
 
@@ -1074,6 +1964,7 @@ static void wpas_p2p_remove_pending_group_interface(
                          wpa_s->pending_interface_name);
        os_memset(wpa_s->pending_interface_addr, 0, ETH_ALEN);
        wpa_s->pending_interface_name[0] = '\0';
+       wpa_s->global->pending_group_iface_for_p2ps = 0;
 }
 
 
@@ -1108,17 +1999,17 @@ wpas_p2p_init_group_interface(struct wpa_supplicant *wpa_s, int go)
        else
                iface.ctrl_interface = wpa_s->conf->ctrl_interface;
        iface.driver_param = wpa_s->conf->driver_param;
-       group_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface);
+       group_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s);
        if (group_wpa_s == NULL) {
                wpa_printf(MSG_ERROR, "P2P: Failed to create new "
                           "wpa_supplicant interface");
                return NULL;
        }
        wpa_s->pending_interface_name[0] = '\0';
-       group_wpa_s->parent = wpa_s;
        group_wpa_s->p2p_group_interface = go ? P2P_GROUP_INTERFACE_GO :
                P2P_GROUP_INTERFACE_CLIENT;
        wpa_s->global->p2p_group_formation = group_wpa_s;
+       wpa_s->global->pending_group_iface_for_p2ps = 0;
 
        wpas_p2p_clone_config(group_wpa_s, wpa_s);
 
@@ -1131,15 +2022,44 @@ static void wpas_p2p_group_formation_timeout(void *eloop_ctx,
 {
        struct wpa_supplicant *wpa_s = eloop_ctx;
        wpa_printf(MSG_DEBUG, "P2P: Group Formation timed out");
+       wpas_p2p_group_formation_failed(wpa_s);
+}
+
+
+void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s)
+{
+       eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+                            wpa_s->parent, NULL);
        if (wpa_s->global->p2p)
                p2p_group_formation_failed(wpa_s->global->p2p);
-       else if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-               wpa_drv_p2p_group_formation_failed(wpa_s);
        wpas_group_formation_completed(wpa_s, 0);
 }
 
 
-void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
+static void wpas_p2p_grpform_fail_after_wps(struct wpa_supplicant *wpa_s)
+{
+       wpa_printf(MSG_DEBUG, "P2P: Reject group formation due to WPS provisioning failure");
+       eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+                            wpa_s->parent, NULL);
+       eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout,
+                              wpa_s->parent, NULL);
+       wpa_s->global->p2p_fail_on_wps_complete = 0;
+}
+
+
+void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->global->p2p_group_formation != wpa_s)
+               return;
+       /* Speed up group formation timeout since this cannot succeed */
+       eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+                            wpa_s->parent, NULL);
+       eloop_register_timeout(0, 0, wpas_p2p_group_formation_timeout,
+                              wpa_s->parent, NULL);
+}
+
+
+static void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
 {
        struct wpa_supplicant *wpa_s = ctx;
 
@@ -1160,8 +2080,16 @@ void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
 
        if (wpa_s->p2p_go_ht40)
                res->ht40 = 1;
-
-       wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS);
+       if (wpa_s->p2p_go_vht)
+               res->vht = 1;
+
+       wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_SUCCESS "role=%s "
+                      "freq=%d ht40=%d peer_dev=" MACSTR " peer_iface=" MACSTR
+                      " wps_method=%s",
+                      res->role_go ? "GO" : "client", res->freq, res->ht40,
+                      MAC2STR(res->peer_device_addr),
+                      MAC2STR(res->peer_interface_addr),
+                      p2p_wps_method_text(res->wps_method));
        wpas_notify_p2p_go_neg_completed(wpa_s, res);
 
        if (res->role_go && wpa_s->p2p_persistent_id >= 0) {
@@ -1183,6 +2111,9 @@ void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
                        wpas_p2p_init_group_interface(wpa_s, res->role_go);
                if (group_wpa_s == NULL) {
                        wpas_p2p_remove_pending_group_interface(wpa_s);
+                       eloop_cancel_timeout(wpas_p2p_long_listen_timeout,
+                                            wpa_s, NULL);
+                       wpas_p2p_group_formation_failed(wpa_s);
                        return;
                }
                if (group_wpa_s != wpa_s) {
@@ -1218,7 +2149,7 @@ void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res)
 }
 
 
-void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id)
+static void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id)
 {
        struct wpa_supplicant *wpa_s = ctx;
        wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_GO_NEG_REQUEST MACSTR
@@ -1228,48 +2159,82 @@ void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id)
 }
 
 
-void wpas_dev_found(void *ctx, const u8 *addr,
-                   const struct p2p_peer_info *info,
-                   int new_device)
+static void wpas_dev_found(void *ctx, const u8 *addr,
+                          const struct p2p_peer_info *info,
+                          int new_device)
 {
 #ifndef CONFIG_NO_STDOUT_DEBUG
        struct wpa_supplicant *wpa_s = ctx;
        char devtype[WPS_DEV_TYPE_BUFSIZE];
+       char *wfd_dev_info_hex = NULL;
+
+#ifdef CONFIG_WIFI_DISPLAY
+       wfd_dev_info_hex = wifi_display_subelem_hex(info->wfd_subelems,
+                                                   WFD_SUBELEM_DEVICE_INFO);
+#endif /* CONFIG_WIFI_DISPLAY */
+
+       if (info->p2ps_instance) {
+               char str[256];
+               const u8 *buf = wpabuf_head(info->p2ps_instance);
+               size_t len = wpabuf_len(info->p2ps_instance);
+
+               while (len) {
+                       u32 id;
+                       u16 methods;
+                       u8 str_len;
+
+                       if (len < 4 + 2 + 1)
+                               break;
+                       id = WPA_GET_LE32(buf);
+                       buf += sizeof(u32);
+                       methods = WPA_GET_BE16(buf);
+                       buf += sizeof(u16);
+                       str_len = *buf++;
+                       if (str_len > len - 4 - 2 - 1)
+                               break;
+                       os_memcpy(str, buf, str_len);
+                       str[str_len] = '\0';
+                       buf += str_len;
+                       len -= str_len + sizeof(u32) + sizeof(u16) + sizeof(u8);
+
+                       wpa_msg_global(wpa_s, MSG_INFO,
+                                      P2P_EVENT_DEVICE_FOUND MACSTR
+                                      " p2p_dev_addr=" MACSTR
+                                      " pri_dev_type=%s name='%s'"
+                                      " config_methods=0x%x"
+                                      " dev_capab=0x%x"
+                                      " group_capab=0x%x"
+                                      " adv_id=%x asp_svc=%s%s",
+                                      MAC2STR(addr),
+                                      MAC2STR(info->p2p_device_addr),
+                                      wps_dev_type_bin2str(
+                                              info->pri_dev_type,
+                                              devtype, sizeof(devtype)),
+                                      info->device_name, methods,
+                                      info->dev_capab, info->group_capab,
+                                      id, str,
+                                      info->vendor_elems ?
+                                      " vendor_elems=1" : "");
+               }
+               goto done;
+       }
 
-#if defined TIZEN_EXT_BUSY_DEVICE // to identify Group Client
-       struct p2p_device *dev;
-       struct p2p_data *p2p = wpa_s->global->p2p;
-       u8 p2p_go_addr[ETH_ALEN];
-
-       os_memset(p2p_go_addr, 0x0, ETH_ALEN);
-
-       dev = p2p_get_device(p2p, addr);
-       if (dev)
-               os_memcpy((void*) p2p_go_addr, (const void*) dev->member_in_go_dev, ETH_ALEN);
-
-       if (!is_zero_ether_addr(p2p_go_addr))
-               wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR
-                       " p2p_dev_addr=" MACSTR
-                       " pri_dev_type=%s name='%s' config_methods=0x%x "
-                       "dev_capab=0x%x group_capab=0x%x"
-                       "p2p_go_addr=" MACSTR,
-                       MAC2STR(addr), MAC2STR(info->p2p_device_addr),
-                       wps_dev_type_bin2str(info->pri_dev_type, devtype,
-                                            sizeof(devtype)),
-                       info->device_name, info->config_methods,
-                       info->dev_capab, info->group_capab,
-                       MAC2STR(p2p_go_addr));
-       else
-#endif /* TIZEN_EXT_BUSY_DEVICE */
        wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_DEVICE_FOUND MACSTR
                       " p2p_dev_addr=" MACSTR
                       " pri_dev_type=%s name='%s' config_methods=0x%x "
-                      "dev_capab=0x%x group_capab=0x%x",
+                      "dev_capab=0x%x group_capab=0x%x%s%s%s new=%d",
                       MAC2STR(addr), MAC2STR(info->p2p_device_addr),
                       wps_dev_type_bin2str(info->pri_dev_type, devtype,
                                            sizeof(devtype)),
-               info->device_name, info->config_methods,
-               info->dev_capab, info->group_capab);
+                      info->device_name, info->config_methods,
+                      info->dev_capab, info->group_capab,
+                      wfd_dev_info_hex ? " wfd_dev_info=0x" : "",
+                      wfd_dev_info_hex ? wfd_dev_info_hex : "",
+                      info->vendor_elems ? " vendor_elems=1" : "",
+                      new_device);
+
+done:
+       os_free(wfd_dev_info_hex);
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 
        wpas_notify_p2p_device_found(ctx, info->p2p_device_addr, new_device);
@@ -1291,35 +2256,121 @@ static void wpas_find_stopped(void *ctx)
 {
        struct wpa_supplicant *wpa_s = ctx;
        wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_FIND_STOPPED);
+       wpas_notify_find_stopped(wpa_s);
 }
 
 
-static int wpas_start_listen(void *ctx, unsigned int freq,
-                            unsigned int duration,
-                            const struct wpabuf *probe_resp_ie)
+struct wpas_p2p_listen_work {
+       unsigned int freq;
+       unsigned int duration;
+       struct wpabuf *probe_resp_ie;
+};
+
+
+static void wpas_p2p_listen_work_free(struct wpas_p2p_listen_work *lwork)
 {
-       struct wpa_supplicant *wpa_s = ctx;
+       if (lwork == NULL)
+               return;
+       wpabuf_free(lwork->probe_resp_ie);
+       os_free(lwork);
+}
+
+
+static void wpas_p2p_listen_work_done(struct wpa_supplicant *wpa_s)
+{
+       struct wpas_p2p_listen_work *lwork;
+
+       if (!wpa_s->p2p_listen_work)
+               return;
+
+       lwork = wpa_s->p2p_listen_work->ctx;
+       wpas_p2p_listen_work_free(lwork);
+       radio_work_done(wpa_s->p2p_listen_work);
+       wpa_s->p2p_listen_work = NULL;
+}
+
+
+static void wpas_start_listen_cb(struct wpa_radio_work *work, int deinit)
+{
+       struct wpa_supplicant *wpa_s = work->wpa_s;
+       struct wpas_p2p_listen_work *lwork = work->ctx;
+       unsigned int duration;
+
+       if (deinit) {
+               if (work->started) {
+                       wpa_s->p2p_listen_work = NULL;
+                       wpas_stop_listen(wpa_s);
+               }
+               wpas_p2p_listen_work_free(lwork);
+               return;
+       }
 
-       wpa_drv_set_ap_wps_ie(wpa_s, NULL, probe_resp_ie, NULL);
+       wpa_s->p2p_listen_work = work;
+
+       wpa_drv_set_ap_wps_ie(wpa_s, NULL, lwork->probe_resp_ie, NULL);
 
        if (wpa_drv_probe_req_report(wpa_s, 1) < 0) {
                wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver to "
                           "report received Probe Request frames");
-               return -1;
+               wpas_p2p_listen_work_done(wpa_s);
+               return;
        }
 
-       wpa_s->pending_listen_freq = freq;
-       wpa_s->pending_listen_duration = duration;
+       wpa_s->pending_listen_freq = lwork->freq;
+       wpa_s->pending_listen_duration = lwork->duration;
+
+       duration = lwork->duration;
+#ifdef CONFIG_TESTING_OPTIONS
+       if (wpa_s->extra_roc_dur) {
+               wpa_printf(MSG_DEBUG, "TESTING: Increase ROC duration %u -> %u",
+                          duration, duration + wpa_s->extra_roc_dur);
+               duration += wpa_s->extra_roc_dur;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
 
-       if (wpa_drv_remain_on_channel(wpa_s, freq, duration) < 0) {
+       if (wpa_drv_remain_on_channel(wpa_s, lwork->freq, duration) < 0) {
                wpa_printf(MSG_DEBUG, "P2P: Failed to request the driver "
                           "to remain on channel (%u MHz) for Listen "
-                          "state", freq);
+                          "state", lwork->freq);
+               wpas_p2p_listen_work_done(wpa_s);
                wpa_s->pending_listen_freq = 0;
-               return -1;
+               return;
        }
        wpa_s->off_channel_freq = 0;
-       wpa_s->roc_waiting_drv_freq = freq;
+       wpa_s->roc_waiting_drv_freq = lwork->freq;
+}
+
+
+static int wpas_start_listen(void *ctx, unsigned int freq,
+                            unsigned int duration,
+                            const struct wpabuf *probe_resp_ie)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       struct wpas_p2p_listen_work *lwork;
+
+       if (wpa_s->p2p_listen_work) {
+               wpa_printf(MSG_DEBUG, "P2P: Reject start_listen since p2p_listen_work already exists");
+               return -1;
+       }
+
+       lwork = os_zalloc(sizeof(*lwork));
+       if (lwork == NULL)
+               return -1;
+       lwork->freq = freq;
+       lwork->duration = duration;
+       if (probe_resp_ie) {
+               lwork->probe_resp_ie = wpabuf_dup(probe_resp_ie);
+               if (lwork->probe_resp_ie == NULL) {
+                       wpas_p2p_listen_work_free(lwork);
+                       return -1;
+               }
+       }
+
+       if (radio_add_work(wpa_s, freq, "p2p-listen", 0, wpas_start_listen_cb,
+                          lwork) < 0) {
+               wpas_p2p_listen_work_free(lwork);
+               return -1;
+       }
 
        return 0;
 }
@@ -1335,6 +2386,7 @@ static void wpas_stop_listen(void *ctx)
        }
        wpa_drv_set_ap_wps_ie(wpa_s, NULL, NULL, NULL);
        wpa_drv_probe_req_report(wpa_s, 0);
+       wpas_p2p_listen_work_done(wpa_s);
 }
 
 
@@ -1509,8 +2561,8 @@ wpas_p2p_service_get_upnp(struct wpa_supplicant *wpa_s, u8 version,
 }
 
 
-static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto,
-                                       u8 srv_trans_id)
+static void wpas_sd_add_empty(struct wpabuf *resp, u8 srv_proto,
+                             u8 srv_trans_id, u8 status)
 {
        u8 *len_pos;
 
@@ -1522,12 +2574,35 @@ static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto,
        wpabuf_put_u8(resp, srv_proto);
        wpabuf_put_u8(resp, srv_trans_id);
        /* Status Code */
-       wpabuf_put_u8(resp, P2P_SD_PROTO_NOT_AVAILABLE);
+       wpabuf_put_u8(resp, status);
        /* Response Data: empty */
        WPA_PUT_LE16(len_pos, (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
 }
 
 
+static void wpas_sd_add_proto_not_avail(struct wpabuf *resp, u8 srv_proto,
+                                       u8 srv_trans_id)
+{
+       wpas_sd_add_empty(resp, srv_proto, srv_trans_id,
+                         P2P_SD_PROTO_NOT_AVAILABLE);
+}
+
+
+static void wpas_sd_add_bad_request(struct wpabuf *resp, u8 srv_proto,
+                                   u8 srv_trans_id)
+{
+       wpas_sd_add_empty(resp, srv_proto, srv_trans_id, P2P_SD_BAD_REQUEST);
+}
+
+
+static void wpas_sd_add_not_found(struct wpabuf *resp, u8 srv_proto,
+                                 u8 srv_trans_id)
+{
+       wpas_sd_add_empty(resp, srv_proto, srv_trans_id,
+                         P2P_SD_REQUESTED_INFO_NOT_AVAILABLE);
+}
+
+
 static void wpas_sd_all_bonjour(struct wpa_supplicant *wpa_s,
                                struct wpabuf *resp, u8 srv_trans_id)
 {
@@ -1835,13 +2910,155 @@ static void wpas_sd_req_wfd(struct wpa_supplicant *wpa_s,
 #endif /* CONFIG_WIFI_DISPLAY */
 
 
-void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
-                    u16 update_indic, const u8 *tlvs, size_t tlvs_len)
+static int find_p2ps_substr(struct p2ps_advertisement *adv_data,
+                           const u8 *needle, size_t needle_len)
 {
-       struct wpa_supplicant *wpa_s = ctx;
-       const u8 *pos = tlvs;
-       const u8 *end = tlvs + tlvs_len;
-       const u8 *tlv_end;
+       const u8 *haystack = (const u8 *) adv_data->svc_info;
+       size_t haystack_len, i;
+
+       /* Allow search term to be empty */
+       if (!needle || !needle_len)
+               return 1;
+
+       if (!haystack)
+               return 0;
+
+       haystack_len = os_strlen(adv_data->svc_info);
+       for (i = 0; i < haystack_len; i++) {
+               if (haystack_len - i < needle_len)
+                       break;
+               if (os_memcmp(haystack + i, needle, needle_len) == 0)
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+static void wpas_sd_req_asp(struct wpa_supplicant *wpa_s,
+                           struct wpabuf *resp, u8 srv_trans_id,
+                           const u8 *query, size_t query_len)
+{
+       struct p2ps_advertisement *adv_data;
+       const u8 *svc = &query[1];
+       const u8 *info = NULL;
+       size_t svc_len = query[0];
+       size_t info_len = 0;
+       int prefix = 0;
+       u8 *count_pos = NULL;
+       u8 *len_pos = NULL;
+
+       wpa_hexdump(MSG_DEBUG, "P2P: SD Request for ASP", query, query_len);
+
+       if (!wpa_s->global->p2p) {
+               wpa_printf(MSG_DEBUG, "P2P: ASP protocol not available");
+               wpas_sd_add_proto_not_avail(resp, P2P_SERV_P2PS, srv_trans_id);
+               return;
+       }
+
+       /* Info block is optional */
+       if (svc_len + 1 < query_len) {
+               info = &svc[svc_len];
+               info_len = *info++;
+       }
+
+       /* Range check length of svc string and info block */
+       if (svc_len + (info_len ? info_len + 2 : 1) > query_len) {
+               wpa_printf(MSG_DEBUG, "P2P: ASP bad request");
+               wpas_sd_add_bad_request(resp, P2P_SERV_P2PS, srv_trans_id);
+               return;
+       }
+
+       /* Detect and correct for prefix search */
+       if (svc_len && svc[svc_len - 1] == '*') {
+               prefix = 1;
+               svc_len--;
+       }
+
+       for (adv_data = p2p_get_p2ps_adv_list(wpa_s->global->p2p);
+            adv_data; adv_data = adv_data->next) {
+               /* If not a prefix match, reject length mismatches */
+               if (!prefix && svc_len != os_strlen(adv_data->svc_name))
+                       continue;
+
+               /* Search each service for request */
+               if (os_memcmp(adv_data->svc_name, svc, svc_len) == 0 &&
+                   find_p2ps_substr(adv_data, info, info_len)) {
+                       size_t len = os_strlen(adv_data->svc_name);
+                       size_t svc_info_len = 0;
+
+                       if (adv_data->svc_info)
+                               svc_info_len = os_strlen(adv_data->svc_info);
+
+                       if (len > 0xff || svc_info_len > 0xffff)
+                               return;
+
+                       /* Length & Count to be filled as we go */
+                       if (!len_pos && !count_pos) {
+                               if (wpabuf_tailroom(resp) <
+                                   len + svc_info_len + 16)
+                                       return;
+
+                               len_pos = wpabuf_put(resp, 2);
+                               wpabuf_put_u8(resp, P2P_SERV_P2PS);
+                               wpabuf_put_u8(resp, srv_trans_id);
+                               /* Status Code */
+                               wpabuf_put_u8(resp, P2P_SD_SUCCESS);
+                               count_pos = wpabuf_put(resp, 1);
+                               *count_pos = 0;
+                       } else if (wpabuf_tailroom(resp) <
+                                  len + svc_info_len + 10)
+                               return;
+
+                       if (svc_info_len) {
+                               wpa_printf(MSG_DEBUG,
+                                          "P2P: Add Svc: %s info: %s",
+                                          adv_data->svc_name,
+                                          adv_data->svc_info);
+                       } else {
+                               wpa_printf(MSG_DEBUG, "P2P: Add Svc: %s",
+                                          adv_data->svc_name);
+                       }
+
+                       /* Advertisement ID */
+                       wpabuf_put_le32(resp, adv_data->id);
+
+                       /* Config Methods */
+                       wpabuf_put_be16(resp, adv_data->config_methods);
+
+                       /* Service Name */
+                       wpabuf_put_u8(resp, (u8) len);
+                       wpabuf_put_data(resp, adv_data->svc_name, len);
+
+                       /* Service State */
+                       wpabuf_put_u8(resp, adv_data->state);
+
+                       /* Service Information */
+                       wpabuf_put_le16(resp, (u16) svc_info_len);
+                       wpabuf_put_data(resp, adv_data->svc_info, svc_info_len);
+
+                       /* Update length and count */
+                       (*count_pos)++;
+                       WPA_PUT_LE16(len_pos,
+                                    (u8 *) wpabuf_put(resp, 0) - len_pos - 2);
+               }
+       }
+
+       /* Return error if no matching svc found */
+       if (count_pos == NULL) {
+               wpa_printf(MSG_DEBUG, "P2P: ASP service not found");
+               wpas_sd_add_not_found(resp, P2P_SERV_P2PS, srv_trans_id);
+       }
+}
+
+
+static void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
+                           u16 update_indic, const u8 *tlvs, size_t tlvs_len)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       const u8 *pos = tlvs;
+       const u8 *end = tlvs + tlvs_len;
+       const u8 *tlv_end;
        u16 slen;
        struct wpabuf *resp;
        u8 srv_proto, srv_trans_id;
@@ -1932,6 +3149,10 @@ void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
                                        pos, tlv_end - pos);
                        break;
 #endif /* CONFIG_WIFI_DISPLAY */
+               case P2P_SERV_P2PS:
+                       wpas_sd_req_asp(wpa_s, resp, srv_trans_id,
+                                       pos, tlv_end - pos);
+                       break;
                default:
                        wpa_printf(MSG_DEBUG, "P2P: Unavailable service "
                                   "protocol %u", srv_proto);
@@ -1953,8 +3174,82 @@ done:
 }
 
 
-void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
-                     const u8 *tlvs, size_t tlvs_len)
+static void wpas_sd_p2ps_serv_response(struct wpa_supplicant *wpa_s,
+                                      const u8 *sa, u8 srv_trans_id,
+                                      const u8 *pos, const u8 *tlv_end)
+{
+       u8 left = *pos++;
+       u32 adv_id;
+       u8 svc_status;
+       u16 config_methods;
+       char svc_str[256];
+
+       while (left-- && pos < tlv_end) {
+               char *buf = NULL;
+               size_t buf_len;
+               u8 svc_len;
+
+               /* Sanity check fixed length+svc_str */
+               if (pos + 6 >= tlv_end)
+                       break;
+               svc_len = pos[6];
+               if (pos + svc_len + 10 > tlv_end)
+                       break;
+
+               /* Advertisement ID */
+               adv_id = WPA_GET_LE32(pos);
+               pos += sizeof(u32);
+
+               /* Config Methods */
+               config_methods = WPA_GET_BE16(pos);
+               pos += sizeof(u16);
+
+               /* Service Name */
+               pos++; /* svc_len */
+               os_memcpy(svc_str, pos, svc_len);
+               svc_str[svc_len] = '\0';
+               pos += svc_len;
+
+               /* Service Status */
+               svc_status = *pos++;
+
+               /* Service Information Length */
+               buf_len = WPA_GET_LE16(pos);
+               pos += sizeof(u16);
+
+               /* Sanity check buffer length */
+               if (buf_len > (unsigned int) (tlv_end - pos))
+                       break;
+
+               if (buf_len) {
+                       buf = os_zalloc(2 * buf_len + 1);
+                       if (buf) {
+                               utf8_escape((const char *) pos, buf_len, buf,
+                                           2 * buf_len + 1);
+                       }
+               }
+
+               pos += buf_len;
+
+               if (buf) {
+                       wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP
+                                      MACSTR " %x %x %x %x %s '%s'",
+                                      MAC2STR(sa), srv_trans_id, adv_id,
+                                      svc_status, config_methods, svc_str,
+                                      buf);
+                       os_free(buf);
+               } else {
+                       wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_SERV_ASP_RESP
+                                      MACSTR " %x %x %x %x %s",
+                                      MAC2STR(sa), srv_trans_id, adv_id,
+                                      svc_status, config_methods, svc_str);
+               }
+       }
+}
+
+
+static void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
+                            const u8 *tlvs, size_t tlvs_len)
 {
        struct wpa_supplicant *wpa_s = ctx;
        const u8 *pos = tlvs;
@@ -2011,6 +3306,11 @@ void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
                wpa_hexdump(MSG_MSGDUMP, "P2P: Response Data",
                            pos, tlv_end - pos);
 
+               if (srv_proto == P2P_SERV_P2PS && pos < tlv_end) {
+                       wpas_sd_p2ps_serv_response(wpa_s, sa, srv_trans_id,
+                                                  pos, tlv_end);
+               }
+
                pos = tlv_end;
        }
 
@@ -2021,8 +3321,6 @@ void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
 u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
                        const struct wpabuf *tlvs)
 {
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-               return wpa_drv_p2p_sd_request(wpa_s, dst, tlvs);
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return 0;
        return (uintptr_t) p2p_sd_request(wpa_s->global->p2p, dst, tlvs);
@@ -2049,13 +3347,44 @@ u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
 }
 
 
+u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id,
+                           const char *svc_str, const char *info_substr)
+{
+       struct wpabuf *tlvs;
+       size_t plen, svc_len, substr_len = 0;
+       u64 ret;
+
+       svc_len = os_strlen(svc_str);
+       if (info_substr)
+               substr_len = os_strlen(info_substr);
+
+       if (svc_len > 0xff || substr_len > 0xff)
+               return 0;
+
+       plen = 1 + 1 + 1 + svc_len + 1 + substr_len;
+       tlvs = wpabuf_alloc(2 + plen);
+       if (tlvs == NULL)
+               return 0;
+
+       wpabuf_put_le16(tlvs, plen);
+       wpabuf_put_u8(tlvs, P2P_SERV_P2PS);
+       wpabuf_put_u8(tlvs, id); /* Service Transaction ID */
+       wpabuf_put_u8(tlvs, (u8) svc_len); /* Service String Length */
+       wpabuf_put_data(tlvs, svc_str, svc_len);
+       wpabuf_put_u8(tlvs, (u8) substr_len); /* Info Substring Length */
+       wpabuf_put_data(tlvs, info_substr, substr_len);
+       ret = wpas_p2p_sd_request(wpa_s, dst, tlvs);
+       wpabuf_free(tlvs);
+
+       return ret;
+}
+
+
 #ifdef CONFIG_WIFI_DISPLAY
 
 static u64 wpas_p2p_sd_request_wfd(struct wpa_supplicant *wpa_s, const u8 *dst,
                                   const struct wpabuf *tlvs)
 {
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-               return 0;
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return 0;
        return (uintptr_t) p2p_sd_request_wfd(wpa_s->global->p2p, dst, tlvs);
@@ -2133,8 +3462,6 @@ u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
 
 int wpas_p2p_sd_cancel_request(struct wpa_supplicant *wpa_s, u64 req)
 {
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-               return wpa_drv_p2p_sd_cancel_request(wpa_s, req);
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return -1;
        return p2p_sd_cancel_request(wpa_s->global->p2p,
@@ -2146,11 +3473,6 @@ void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
                          const u8 *dst, u8 dialog_token,
                          const struct wpabuf *resp_tlvs)
 {
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
-               wpa_drv_p2p_sd_response(wpa_s, freq, dst, dialog_token,
-                                       resp_tlvs);
-               return;
-       }
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return;
        p2p_sd_response(wpa_s->global->p2p, freq, dst, dialog_token,
@@ -2160,10 +3482,6 @@ void wpas_p2p_sd_response(struct wpa_supplicant *wpa_s, int freq,
 
 void wpas_p2p_sd_service_update(struct wpa_supplicant *wpa_s)
 {
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
-               wpa_drv_p2p_service_update(wpa_s);
-               return;
-       }
        if (wpa_s->global->p2p)
                p2p_sd_service_update(wpa_s->global->p2p);
 }
@@ -2203,6 +3521,35 @@ void wpas_p2p_service_flush(struct wpa_supplicant *wpa_s)
 }
 
 
+int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id)
+{
+       if (adv_id == 0)
+               return 1;
+
+       if (p2p_service_p2ps_id(wpa_s->global->p2p, adv_id))
+               return 1;
+
+       return 0;
+}
+
+
+int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id)
+{
+       return p2p_service_del_asp(wpa_s->global->p2p, adv_id);
+}
+
+
+int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s,
+                            int auto_accept, u32 adv_id,
+                            const char *adv_str, u8 svc_state,
+                            u16 config_methods, const char *svc_info)
+{
+       return p2p_service_add_asp(wpa_s->global->p2p, auto_accept, adv_id,
+                                  adv_str, svc_state, config_methods,
+                                  svc_info);
+}
+
+
 int wpas_p2p_service_add_bonjour(struct wpa_supplicant *wpa_s,
                                 struct wpabuf *query, struct wpabuf *resp)
 {
@@ -2288,11 +3635,11 @@ static void wpas_prov_disc_local_keypad(struct wpa_supplicant *wpa_s,
 }
 
 
-void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods,
-                       const u8 *dev_addr, const u8 *pri_dev_type,
-                       const char *dev_name, u16 supp_config_methods,
-                       u8 dev_capab, u8 group_capab, const u8 *group_id,
-                       size_t group_id_len)
+static void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods,
+                              const u8 *dev_addr, const u8 *pri_dev_type,
+                              const char *dev_name, u16 supp_config_methods,
+                              u8 dev_capab, u8 group_capab, const u8 *group_id,
+                              size_t group_id_len)
 {
        struct wpa_supplicant *wpa_s = ctx;
        char devtype[WPS_DEV_TYPE_BUFSIZE];
@@ -2300,6 +3647,7 @@ void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods,
        u8 empty_dev_type[8];
        unsigned int generated_pin = 0;
        struct wpa_supplicant *group = NULL;
+       int res;
 
        if (group_id) {
                for (group = wpa_s->global->ifaces; group; group = group->next)
@@ -2318,15 +3666,17 @@ void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods,
                os_memset(empty_dev_type, 0, sizeof(empty_dev_type));
                pri_dev_type = empty_dev_type;
        }
-       os_snprintf(params, sizeof(params), " p2p_dev_addr=" MACSTR
-                   " pri_dev_type=%s name='%s' config_methods=0x%x "
-                   "dev_capab=0x%x group_capab=0x%x%s%s",
-                   MAC2STR(dev_addr),
-                   wps_dev_type_bin2str(pri_dev_type, devtype,
-                                        sizeof(devtype)),
-                   dev_name, supp_config_methods, dev_capab, group_capab,
-                   group ? " group=" : "",
-                   group ? group->ifname : "");
+       res = os_snprintf(params, sizeof(params), " p2p_dev_addr=" MACSTR
+                         " pri_dev_type=%s name='%s' config_methods=0x%x "
+                         "dev_capab=0x%x group_capab=0x%x%s%s",
+                         MAC2STR(dev_addr),
+                         wps_dev_type_bin2str(pri_dev_type, devtype,
+                                              sizeof(devtype)),
+                         dev_name, supp_config_methods, dev_capab, group_capab,
+                         group ? " group=" : "",
+                         group ? group->ifname : "");
+       if (os_snprintf_error(sizeof(params), res))
+               wpa_printf(MSG_DEBUG, "P2P: PD Request event truncated");
        params[sizeof(params) - 1] = '\0';
 
        if (config_methods & WPS_CONFIG_DISPLAY) {
@@ -2345,7 +3695,7 @@ void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods,
 }
 
 
-void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
+static void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
 {
        struct wpa_supplicant *wpa_s = ctx;
        unsigned int generated_pin = 0;
@@ -2357,15 +3707,19 @@ void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
                wpa_s->pending_pd_before_join = 0;
                wpa_printf(MSG_DEBUG, "P2P: Starting pending "
                           "join-existing-group operation");
-               wpas_p2p_join_start(wpa_s);
+               wpas_p2p_join_start(wpa_s, 0, NULL, 0);
                return;
        }
 
        if (wpa_s->pending_pd_use == AUTO_PD_JOIN ||
-           wpa_s->pending_pd_use == AUTO_PD_GO_NEG)
-               os_snprintf(params, sizeof(params), " peer_go=%d",
-                           wpa_s->pending_pd_use == AUTO_PD_JOIN);
-       else
+           wpa_s->pending_pd_use == AUTO_PD_GO_NEG) {
+               int res;
+
+               res = os_snprintf(params, sizeof(params), " peer_go=%d",
+                                 wpa_s->pending_pd_use == AUTO_PD_JOIN);
+               if (os_snprintf_error(sizeof(params), res))
+                       params[sizeof(params) - 1] = '\0';
+       } else
                params[0] = '\0';
 
        if (config_methods & WPS_CONFIG_DISPLAY)
@@ -2385,13 +3739,18 @@ void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods)
 
 
 static void wpas_prov_disc_fail(void *ctx, const u8 *peer,
-                               enum p2p_prov_disc_status status)
+                               enum p2p_prov_disc_status status,
+                               u32 adv_id, const u8 *adv_mac,
+                               const char *deferred_session_resp)
 {
        struct wpa_supplicant *wpa_s = ctx;
 
        if (wpa_s->p2p_fallback_to_go_neg) {
                wpa_dbg(wpa_s, MSG_DEBUG, "P2P: PD for p2p_connect-auto "
                        "failed - fall back to GO Negotiation");
+               wpa_msg_global(wpa_s->parent, MSG_INFO,
+                              P2P_EVENT_FALLBACK_TO_GO_NEG
+                              "reason=PD-failed");
                wpas_p2p_fallback_to_go_neg(wpa_s, 0);
                return;
        }
@@ -2401,13 +3760,25 @@ static void wpas_prov_disc_fail(void *ctx, const u8 *peer,
                wpa_printf(MSG_DEBUG, "P2P: Starting pending "
                           "join-existing-group operation (no ACK for PD "
                           "Req attempts)");
-               wpas_p2p_join_start(wpa_s);
+               wpas_p2p_join_start(wpa_s, 0, NULL, 0);
                return;
        }
 
-       wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
-                      " p2p_dev_addr=" MACSTR " status=%d",
-                      MAC2STR(peer), status);
+       if (adv_id && adv_mac && deferred_session_resp) {
+               wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
+                              " p2p_dev_addr=" MACSTR " status=%d adv_id=%x"
+                              " deferred_session_resp='%s'",
+                              MAC2STR(peer), status, adv_id,
+                              deferred_session_resp);
+       } else if (adv_id && adv_mac) {
+               wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
+                              " p2p_dev_addr=" MACSTR " status=%d adv_id=%x",
+                              MAC2STR(peer), status, adv_id);
+       } else {
+               wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_PROV_DISC_FAILURE
+                              " p2p_dev_addr=" MACSTR " status=%d",
+                              MAC2STR(peer), status);
+       }
 
        wpas_notify_p2p_provision_discovery(wpa_s, peer, 0 /* response */,
                                            status, 0, 0);
@@ -2423,21 +3794,62 @@ static int freq_included(const struct p2p_channels *channels, unsigned int freq)
 }
 
 
+/**
+ * Pick the best frequency to use from all the currently used frequencies.
+ */
+static int wpas_p2p_pick_best_used_freq(struct wpa_supplicant *wpa_s,
+                                       struct wpa_used_freq_data *freqs,
+                                       unsigned int num)
+{
+       unsigned int i, c;
+
+       /* find a candidate freq that is supported by P2P */
+       for (c = 0; c < num; c++)
+               if (p2p_supported_freq(wpa_s->global->p2p, freqs[c].freq))
+                       break;
+
+       if (c == num)
+               return 0;
+
+       /* once we have a candidate, try to find a 'better' one */
+       for (i = c + 1; i < num; i++) {
+               if (!p2p_supported_freq(wpa_s->global->p2p, freqs[i].freq))
+                       continue;
+
+               /*
+                * 1. Infrastructure station interfaces have higher preference.
+                * 2. P2P Clients have higher preference.
+                * 3. All others.
+                */
+               if (freqs[i].flags & WPA_FREQ_USED_BY_INFRA_STATION) {
+                       c = i;
+                       break;
+               }
+
+               if ((freqs[i].flags & WPA_FREQ_USED_BY_P2P_CLIENT))
+                       c = i;
+       }
+       return freqs[c].freq;
+}
+
+
 static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid,
                                  const u8 *go_dev_addr, const u8 *ssid,
                                  size_t ssid_len, int *go, u8 *group_bssid,
                                  int *force_freq, int persistent_group,
-                                 const struct p2p_channels *channels)
+                                 const struct p2p_channels *channels,
+                                 int dev_pw_id)
 {
        struct wpa_supplicant *wpa_s = ctx;
        struct wpa_ssid *s;
-       u8 cur_bssid[ETH_ALEN];
-       int res;
+       struct wpa_used_freq_data *freqs;
        struct wpa_supplicant *grp;
+       int best_freq;
 
        if (!persistent_group) {
                wpa_printf(MSG_DEBUG, "P2P: Invitation from " MACSTR
-                          " to join an active group", MAC2STR(sa));
+                          " to join an active group (SSID: %s)",
+                          MAC2STR(sa), wpa_ssid_txt(ssid, ssid_len));
                if (!is_zero_ether_addr(wpa_s->p2p_auth_invite) &&
                    (os_memcmp(go_dev_addr, wpa_s->p2p_auth_invite, ETH_ALEN)
                     == 0 ||
@@ -2446,6 +3858,21 @@ static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid,
                                   "authorized invitation");
                        goto accept_inv;
                }
+
+#ifdef CONFIG_WPS_NFC
+               if (dev_pw_id >= 0 && wpa_s->p2p_nfc_tag_enabled &&
+                   dev_pw_id == wpa_s->p2p_oob_dev_pw_id) {
+                       wpa_printf(MSG_DEBUG, "P2P: Accept invitation based on local enabled NFC Tag");
+                       wpa_s->p2p_wps_method = WPS_NFC;
+                       wpa_s->pending_join_wps_method = WPS_NFC;
+                       os_memcpy(wpa_s->pending_join_dev_addr,
+                                 go_dev_addr, ETH_ALEN);
+                       os_memcpy(wpa_s->pending_join_iface_addr,
+                                 bssid, ETH_ALEN);
+                       goto accept_inv;
+               }
+#endif /* CONFIG_WPS_NFC */
+
                /*
                 * Do not accept the invitation automatically; notify user and
                 * request approval.
@@ -2466,6 +3893,7 @@ static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid,
            os_memcmp(sa, wpa_s->p2p_auth_invite, ETH_ALEN) == 0) {
                wpa_printf(MSG_DEBUG, "P2P: Accept previously initiated "
                           "invitation to re-invoke a persistent group");
+               os_memset(wpa_s->p2p_auth_invite, 0, ETH_ALEN);
        } else if (!wpa_s->conf->persistent_reconnect)
                return P2P_SC_FAIL_INFO_CURRENTLY_UNAVAILABLE;
 
@@ -2508,25 +3936,30 @@ static u8 wpas_invitation_process(void *ctx, const u8 *sa, const u8 *bssid,
 accept_inv:
        wpas_p2p_set_own_freq_preference(wpa_s, 0);
 
-       if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, cur_bssid) == 0 &&
-           wpa_s->assoc_freq) {
-               wpa_printf(MSG_DEBUG, "P2P: Trying to force channel to match "
-                          "the channel we are already using");
-               *force_freq = wpa_s->assoc_freq;
-               wpas_p2p_set_own_freq_preference(wpa_s, wpa_s->assoc_freq);
-       }
-
-       res = wpa_drv_shared_freq(wpa_s);
-       if (res > 0) {
-               wpa_printf(MSG_DEBUG, "P2P: Trying to force channel to match "
-                          "with the channel we are already using on a "
-                          "shared interface");
-               *force_freq = res;
-               wpas_p2p_set_own_freq_preference(wpa_s, res);
+       best_freq = 0;
+       freqs = os_calloc(wpa_s->num_multichan_concurrent,
+                         sizeof(struct wpa_used_freq_data));
+       if (freqs) {
+               int num_channels = wpa_s->num_multichan_concurrent;
+               int num = wpas_p2p_valid_oper_freqs(wpa_s, freqs, num_channels);
+               best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
+               os_free(freqs);
+       }
+
+       /* Get one of the frequencies currently in use */
+       if (best_freq > 0) {
+               wpa_printf(MSG_DEBUG, "P2P: Trying to prefer a channel already used by one of the interfaces");
+               wpas_p2p_set_own_freq_preference(wpa_s, best_freq);
+
+               if (wpa_s->num_multichan_concurrent < 2 ||
+                   wpas_p2p_num_unused_channels(wpa_s) < 1) {
+                       wpa_printf(MSG_DEBUG, "P2P: No extra channels available - trying to force channel to match a channel already used by one of the interfaces");
+                       *force_freq = best_freq;
+               }
        }
 
-       if (*force_freq > 0 &&
-           (wpa_s->drv_flags & WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
+       if (*force_freq > 0 && wpa_s->num_multichan_concurrent > 1 &&
+           wpas_p2p_num_unused_channels(wpa_s) > 0) {
                if (*go == 0) {
                        /* We are the client */
                        wpa_printf(MSG_DEBUG, "P2P: Peer was found to be "
@@ -2565,16 +3998,18 @@ static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid,
 
        if (status == P2P_SC_SUCCESS) {
                wpa_printf(MSG_DEBUG, "P2P: Invitation from peer " MACSTR
-                          " was accepted; op_freq=%d MHz",
-                          MAC2STR(sa), op_freq);
+                          " was accepted; op_freq=%d MHz, SSID=%s",
+                          MAC2STR(sa), op_freq, wpa_ssid_txt(ssid, ssid_len));
                if (s) {
                        int go = s->mode == WPAS_MODE_P2P_GO;
                        wpas_p2p_group_add_persistent(
-                               wpa_s, s, go, go ? op_freq : 0, 0, NULL);
+                               wpa_s, s, go, 0, op_freq, 0, 0, NULL,
+                               go ? P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0);
                } else if (bssid) {
                        wpa_s->user_initiated_pd = 0;
                        wpas_p2p_join(wpa_s, bssid, go_dev_addr,
-                                     wpa_s->p2p_wps_method, 0);
+                                     wpa_s->p2p_wps_method, 0, op_freq,
+                                     ssid, ssid_len);
                }
                return;
        }
@@ -2600,6 +4035,8 @@ static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid,
                                       " unknown-network",
                                       MAC2STR(sa), MAC2STR(go_dev_addr));
                }
+               wpas_notify_p2p_invitation_received(wpa_s,sa,go_dev_addr,bssid,
+                                                       0,op_freq);
                return;
        }
 
@@ -2612,12 +4049,14 @@ static void wpas_invitation_received(void *ctx, const u8 *sa, const u8 *bssid,
                               "sa=" MACSTR " persistent=%d",
                               MAC2STR(sa), s->id);
        }
+       wpas_notify_p2p_invitation_received(wpa_s,sa,go_dev_addr,bssid,
+                                               s->id,op_freq);
 }
 
 
 static void wpas_remove_persistent_peer(struct wpa_supplicant *wpa_s,
                                        struct wpa_ssid *ssid,
-                                       const u8 *peer)
+                                       const u8 *peer, int inv)
 {
        size_t i;
 
@@ -2625,11 +4064,11 @@ static void wpas_remove_persistent_peer(struct wpa_supplicant *wpa_s,
                return;
 
        for (i = 0; ssid->p2p_client_list && i < ssid->num_p2p_clients; i++) {
-               if (os_memcmp(ssid->p2p_client_list + i * ETH_ALEN, peer,
+               if (os_memcmp(ssid->p2p_client_list + i * 2 * ETH_ALEN, peer,
                              ETH_ALEN) == 0)
                        break;
        }
-       if (i >= ssid->num_p2p_clients) {
+       if (i >= ssid->num_p2p_clients || !ssid->p2p_client_list) {
                if (ssid->mode != WPAS_MODE_P2P_GO &&
                    os_memcmp(ssid->bssid, peer, ETH_ALEN) == 0) {
                        wpa_printf(MSG_DEBUG, "P2P: Remove persistent group %d "
@@ -2642,17 +4081,16 @@ static void wpas_remove_persistent_peer(struct wpa_supplicant *wpa_s,
        }
 
        wpa_printf(MSG_DEBUG, "P2P: Remove peer " MACSTR " from persistent "
-                  "group %d client list due to invitation result",
-                  MAC2STR(peer), ssid->id);
-       os_memmove(ssid->p2p_client_list + i * ETH_ALEN,
-                  ssid->p2p_client_list + (i + 1) * ETH_ALEN,
-                  (ssid->num_p2p_clients - i - 1) * ETH_ALEN);
+                  "group %d client list%s",
+                  MAC2STR(peer), ssid->id,
+                  inv ? " due to invitation result" : "");
+       os_memmove(ssid->p2p_client_list + i * 2 * ETH_ALEN,
+                  ssid->p2p_client_list + (i + 1) * 2 * ETH_ALEN,
+                  (ssid->num_p2p_clients - i - 1) * 2 * ETH_ALEN);
        ssid->num_p2p_clients--;
-#ifndef CONFIG_NO_CONFIG_WRITE
        if (wpa_s->parent->conf->update_config &&
            wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
                wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
-#endif /* CONFIG_NO_CONFIG_WRITE */
 }
 
 
@@ -2670,16 +4108,18 @@ static void wpas_remove_persistent_client(struct wpa_supplicant *wpa_s,
                return; /* Not operating as a GO in persistent group */
        ssid = wpas_p2p_get_persistent(wpa_s->parent, peer,
                                       ssid->ssid, ssid->ssid_len);
-       wpas_remove_persistent_peer(wpa_s, ssid, peer);
+       wpas_remove_persistent_peer(wpa_s, ssid, peer, 1);
 }
 
 
 static void wpas_invitation_result(void *ctx, int status, const u8 *bssid,
                                   const struct p2p_channels *channels,
-                                  const u8 *peer)
+                                  const u8 *peer, int neg_freq,
+                                  int peer_oper_freq)
 {
        struct wpa_supplicant *wpa_s = ctx;
        struct wpa_ssid *ssid;
+       int freq;
 
        if (bssid) {
                wpa_msg_global(wpa_s, MSG_INFO, P2P_EVENT_INVITATION_RESULT
@@ -2709,7 +4149,7 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid,
                if (status == P2P_SC_FAIL_UNKNOWN_GROUP) {
                        ssid = wpa_config_get_network(
                                wpa_s->conf, wpa_s->pending_invite_ssid_id);
-                       wpas_remove_persistent_peer(wpa_s, ssid, peer);
+                       wpas_remove_persistent_peer(wpa_s, ssid, peer, 1);
                }
                wpas_p2p_remove_pending_group_interface(wpa_s);
                return;
@@ -2735,28 +4175,35 @@ static void wpas_invitation_result(void *ctx, int status, const u8 *bssid,
                "starting persistent group");
        os_sleep(0, 50000);
 
+       if (neg_freq > 0 && ssid->mode == WPAS_MODE_P2P_GO &&
+           freq_included(channels, neg_freq))
+               freq = neg_freq;
+       else if (peer_oper_freq > 0 && ssid->mode != WPAS_MODE_P2P_GO &&
+                freq_included(channels, peer_oper_freq))
+               freq = peer_oper_freq;
+       else
+               freq = 0;
+
+       wpa_printf(MSG_DEBUG, "P2P: Persistent group invitation success - op_freq=%d MHz SSID=%s",
+                  freq, wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
        wpas_p2p_group_add_persistent(wpa_s, ssid,
                                      ssid->mode == WPAS_MODE_P2P_GO,
                                      wpa_s->p2p_persistent_go_freq,
-                                     wpa_s->p2p_go_ht40, channels);
+                                     freq,
+                                     wpa_s->p2p_go_ht40, wpa_s->p2p_go_vht,
+                                     channels,
+                                     ssid->mode == WPAS_MODE_P2P_GO ?
+                                     P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
+                                     0);
 }
 
 
 static int wpas_p2p_disallowed_freq(struct wpa_global *global,
                                    unsigned int freq)
 {
-       unsigned int i;
-
-       if (global->p2p_disallow_freq == NULL)
-               return 0;
-
-       for (i = 0; i < global->num_p2p_disallow_freq; i++) {
-               if (freq >= global->p2p_disallow_freq[i].min &&
-                   freq <= global->p2p_disallow_freq[i].max)
-                       return 1;
-       }
-
-       return 0;
+       if (freq_range_list_includes(&global->p2p_go_avoid_freq, freq))
+               return 1;
+       return freq_range_list_includes(&global->p2p_disallow_freq, freq);
 }
 
 
@@ -2768,10 +4215,15 @@ static void wpas_p2p_add_chan(struct p2p_reg_class *reg, u8 chan)
 
 
 static int wpas_p2p_default_channels(struct wpa_supplicant *wpa_s,
-                                    struct p2p_channels *chan)
+                                    struct p2p_channels *chan,
+                                    struct p2p_channels *cli_chan)
 {
        int i, cla = 0;
 
+       wpa_s->global->p2p_24ghz_social_channels = 1;
+
+       os_memset(cli_chan, 0, sizeof(*cli_chan));
+
        wpa_printf(MSG_DEBUG, "P2P: Enable operating classes for 2.4 GHz "
                   "band");
 
@@ -2839,6 +4291,10 @@ static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
 }
 
 
+enum chan_allowed {
+       NOT_ALLOWED, NO_IR, ALLOWED
+};
+
 static int has_channel(struct wpa_global *global,
                       struct hostapd_hw_modes *mode, u8 chan, int *flags)
 {
@@ -2848,21 +4304,23 @@ static int has_channel(struct wpa_global *global,
        freq = (mode->mode == HOSTAPD_MODE_IEEE80211A ? 5000 : 2407) +
                chan * 5;
        if (wpas_p2p_disallowed_freq(global, freq))
-               return 0;
+               return NOT_ALLOWED;
 
        for (i = 0; i < mode->num_channels; i++) {
                if (mode->channels[i].chan == chan) {
                        if (flags)
                                *flags = mode->channels[i].flag;
-                       return !(mode->channels[i].flag &
-                                (HOSTAPD_CHAN_DISABLED |
-                                 HOSTAPD_CHAN_PASSIVE_SCAN |
-                                 HOSTAPD_CHAN_NO_IBSS |
-                                 HOSTAPD_CHAN_RADAR));
+                       if (mode->channels[i].flag &
+                           (HOSTAPD_CHAN_DISABLED |
+                            HOSTAPD_CHAN_RADAR))
+                               return NOT_ALLOWED;
+                       if (mode->channels[i].flag & HOSTAPD_CHAN_NO_IR)
+                               return NO_IR;
+                       return ALLOWED;
                }
        }
 
-       return 0;
+       return NOT_ALLOWED;
 }
 
 
@@ -2872,7 +4330,7 @@ struct p2p_oper_class_map {
        u8 min_chan;
        u8 max_chan;
        u8 inc;
-       enum { BW20, BW40PLUS, BW40MINUS } bw;
+       enum { BW20, BW40PLUS, BW40MINUS, BW80, BW2160 } bw;
 };
 
 static struct p2p_oper_class_map op_class[] = {
@@ -2887,73 +4345,174 @@ static struct p2p_oper_class_map op_class[] = {
        { HOSTAPD_MODE_IEEE80211A, 117, 40, 48, 8, BW40MINUS },
        { HOSTAPD_MODE_IEEE80211A, 126, 149, 157, 8, BW40PLUS },
        { HOSTAPD_MODE_IEEE80211A, 127, 153, 161, 8, BW40MINUS },
+
+       /*
+        * IEEE P802.11ac/D7.0 Table E-4 actually talks about channel center
+        * frequency index 42, 58, 106, 122, 138, 155 with channel spacing of
+        * 80 MHz, but currently use the following definition for simplicity
+        * (these center frequencies are not actual channels, which makes
+        * has_channel() fail). wpas_p2p_verify_80mhz() should take care of
+        * removing invalid channels.
+        */
+       { HOSTAPD_MODE_IEEE80211A, 128, 36, 161, 4, BW80 },
+       { HOSTAPD_MODE_IEEE80211AD, 180, 1, 4, 1, BW2160 },
        { -1, 0, 0, 0, 0, BW20 }
 };
 
 
-static int wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s,
-                                  struct hostapd_hw_modes *mode,
-                                  u8 channel, u8 bw)
+static int wpas_p2p_get_center_80mhz(struct wpa_supplicant *wpa_s,
+                                    struct hostapd_hw_modes *mode,
+                                    u8 channel)
 {
-       int flag;
+       u8 center_channels[] = { 42, 58, 106, 122, 138, 155 };
+       unsigned int i;
 
-       if (!has_channel(wpa_s->global, mode, channel, &flag))
-               return -1;
-       if (bw == BW40MINUS &&
-           (!(flag & HOSTAPD_CHAN_HT40MINUS) ||
-            !has_channel(wpa_s->global, mode, channel - 4, NULL)))
-               return 0;
-       if (bw == BW40PLUS &&
-           (!(flag & HOSTAPD_CHAN_HT40PLUS) ||
-            !has_channel(wpa_s->global, mode, channel + 4, NULL)))
+       if (mode->mode != HOSTAPD_MODE_IEEE80211A)
                return 0;
-       return 1;
+
+       for (i = 0; i < ARRAY_SIZE(center_channels); i++)
+               /*
+                * In 80 MHz, the bandwidth "spans" 12 channels (e.g., 36-48),
+                * so the center channel is 6 channels away from the start/end.
+                */
+               if (channel >= center_channels[i] - 6 &&
+                   channel <= center_channels[i] + 6)
+                       return center_channels[i];
+
+       return 0;
+}
+
+
+static enum chan_allowed wpas_p2p_verify_80mhz(struct wpa_supplicant *wpa_s,
+                                              struct hostapd_hw_modes *mode,
+                                              u8 channel, u8 bw)
+{
+       u8 center_chan;
+       int i, flags;
+       enum chan_allowed res, ret = ALLOWED;
+
+       center_chan = wpas_p2p_get_center_80mhz(wpa_s, mode, channel);
+       if (!center_chan)
+               return NOT_ALLOWED;
+       if (center_chan >= 58 && center_chan <= 138)
+               return NOT_ALLOWED; /* Do not allow DFS channels for P2P */
+
+       /* check all the channels are available */
+       for (i = 0; i < 4; i++) {
+               int adj_chan = center_chan - 6 + i * 4;
+
+               res = has_channel(wpa_s->global, mode, adj_chan, &flags);
+               if (res == NOT_ALLOWED)
+                       return NOT_ALLOWED;
+               if (res == NO_IR)
+                       ret = NO_IR;
+
+               if (i == 0 && !(flags & HOSTAPD_CHAN_VHT_10_70))
+                       return NOT_ALLOWED;
+               if (i == 1 && !(flags & HOSTAPD_CHAN_VHT_30_50))
+                       return NOT_ALLOWED;
+               if (i == 2 && !(flags & HOSTAPD_CHAN_VHT_50_30))
+                       return NOT_ALLOWED;
+               if (i == 3 && !(flags & HOSTAPD_CHAN_VHT_70_10))
+                       return NOT_ALLOWED;
+       }
+
+       return ret;
+}
+
+
+static enum chan_allowed wpas_p2p_verify_channel(struct wpa_supplicant *wpa_s,
+                                                struct hostapd_hw_modes *mode,
+                                                u8 channel, u8 bw)
+{
+       int flag = 0;
+       enum chan_allowed res, res2;
+
+       res2 = res = has_channel(wpa_s->global, mode, channel, &flag);
+       if (bw == BW40MINUS) {
+               if (!(flag & HOSTAPD_CHAN_HT40MINUS))
+                       return NOT_ALLOWED;
+               res2 = has_channel(wpa_s->global, mode, channel - 4, NULL);
+       } else if (bw == BW40PLUS) {
+               if (!(flag & HOSTAPD_CHAN_HT40PLUS))
+                       return NOT_ALLOWED;
+               res2 = has_channel(wpa_s->global, mode, channel + 4, NULL);
+       } else if (bw == BW80) {
+               res2 = wpas_p2p_verify_80mhz(wpa_s, mode, channel, bw);
+       }
+
+       if (res == NOT_ALLOWED || res2 == NOT_ALLOWED)
+               return NOT_ALLOWED;
+       if (res == NO_IR || res2 == NO_IR)
+               return NO_IR;
+       return res;
 }
 
 
 static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s,
-                                  struct p2p_channels *chan)
+                                  struct p2p_channels *chan,
+                                  struct p2p_channels *cli_chan)
 {
        struct hostapd_hw_modes *mode;
-       int cla, op;
+       int cla, op, cli_cla;
 
        if (wpa_s->hw.modes == NULL) {
                wpa_printf(MSG_DEBUG, "P2P: Driver did not support fetching "
                           "of all supported channels; assume dualband "
                           "support");
-               return wpas_p2p_default_channels(wpa_s, chan);
+               return wpas_p2p_default_channels(wpa_s, chan, cli_chan);
        }
 
-       cla = 0;
+       cla = cli_cla = 0;
 
        for (op = 0; op_class[op].op_class; op++) {
                struct p2p_oper_class_map *o = &op_class[op];
                u8 ch;
-               struct p2p_reg_class *reg = NULL;
+               struct p2p_reg_class *reg = NULL, *cli_reg = NULL;
 
                mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, o->mode);
                if (mode == NULL)
                        continue;
+               if (mode->mode == HOSTAPD_MODE_IEEE80211G)
+                       wpa_s->global->p2p_24ghz_social_channels = 1;
                for (ch = o->min_chan; ch <= o->max_chan; ch += o->inc) {
-                       if (wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw) < 1)
-                               continue;
-                       if (reg == NULL) {
-                               wpa_printf(MSG_DEBUG, "P2P: Add operating "
-                                          "class %u", o->op_class);
-                               reg = &chan->reg_class[cla];
-                               cla++;
-                               reg->reg_class = o->op_class;
+                       enum chan_allowed res;
+                       res = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw);
+                       if (res == ALLOWED) {
+                               if (reg == NULL) {
+                                       wpa_printf(MSG_DEBUG, "P2P: Add operating class %u",
+                                                  o->op_class);
+                                       reg = &chan->reg_class[cla];
+                                       cla++;
+                                       reg->reg_class = o->op_class;
+                               }
+                               reg->channel[reg->channels] = ch;
+                               reg->channels++;
+                       } else if (res == NO_IR &&
+                                  wpa_s->conf->p2p_add_cli_chan) {
+                               if (cli_reg == NULL) {
+                                       wpa_printf(MSG_DEBUG, "P2P: Add operating class %u (client only)",
+                                                  o->op_class);
+                                       cli_reg = &cli_chan->reg_class[cli_cla];
+                                       cli_cla++;
+                                       cli_reg->reg_class = o->op_class;
+                               }
+                               cli_reg->channel[cli_reg->channels] = ch;
+                               cli_reg->channels++;
                        }
-                       reg->channel[reg->channels] = ch;
-                       reg->channels++;
                }
                if (reg) {
                        wpa_hexdump(MSG_DEBUG, "P2P: Channels",
                                    reg->channel, reg->channels);
                }
+               if (cli_reg) {
+                       wpa_hexdump(MSG_DEBUG, "P2P: Channels (client only)",
+                                   cli_reg->channel, cli_reg->channels);
+               }
        }
 
        chan->reg_classes = cla;
+       cli_chan->reg_classes = cli_cla;
 
        return 0;
 }
@@ -2962,7 +4521,8 @@ static int wpas_p2p_setup_channels(struct wpa_supplicant *wpa_s,
 int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s,
                           struct hostapd_hw_modes *mode, u8 channel)
 {
-       int op, ret;
+       int op;
+       enum chan_allowed ret;
 
        for (op = 0; op_class[op].op_class; op++) {
                struct p2p_oper_class_map *o = &op_class[op];
@@ -2973,18 +4533,24 @@ int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s,
                            o->bw == BW20 || ch != channel)
                                continue;
                        ret = wpas_p2p_verify_channel(wpa_s, mode, ch, o->bw);
-                       if (ret < 0)
-                               continue;
-                       else if (ret > 0)
+                       if (ret == ALLOWED)
                                return (o->bw == BW40MINUS) ? -1 : 1;
-                       else
-                               return 0;
                }
        }
        return 0;
 }
 
 
+int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s,
+                             struct hostapd_hw_modes *mode, u8 channel)
+{
+       if (!wpas_p2p_verify_channel(wpa_s, mode, channel, BW80))
+               return 0;
+
+       return wpas_p2p_get_center_80mhz(wpa_s, mode, channel);
+}
+
+
 static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf,
                        size_t buf_len)
 {
@@ -3001,10 +4567,31 @@ static int wpas_get_noa(void *ctx, const u8 *interface_addr, u8 *buf,
 }
 
 
-static int wpas_go_connected(void *ctx, const u8 *dev_addr)
+struct wpa_supplicant * wpas_get_p2p_go_iface(struct wpa_supplicant *wpa_s,
+                                             const u8 *ssid, size_t ssid_len)
 {
-       struct wpa_supplicant *wpa_s = ctx;
+       for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               struct wpa_ssid *s = wpa_s->current_ssid;
+               if (s == NULL)
+                       continue;
+               if (s->mode != WPAS_MODE_P2P_GO &&
+                   s->mode != WPAS_MODE_AP &&
+                   s->mode != WPAS_MODE_P2P_GROUP_FORMATION)
+                       continue;
+               if (s->ssid_len != ssid_len ||
+                   os_memcmp(ssid, s->ssid, ssid_len) != 0)
+                       continue;
+               return wpa_s;
+       }
+
+       return NULL;
+
+}
+
 
+struct wpa_supplicant * wpas_get_p2p_client_iface(struct wpa_supplicant *wpa_s,
+                                                 const u8 *peer_dev_addr)
+{
        for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
                struct wpa_ssid *ssid = wpa_s->current_ssid;
                if (ssid == NULL)
@@ -3014,33 +4601,59 @@ static int wpas_go_connected(void *ctx, const u8 *dev_addr)
                if (wpa_s->wpa_state != WPA_COMPLETED &&
                    wpa_s->wpa_state != WPA_GROUP_HANDSHAKE)
                        continue;
-               if (os_memcmp(wpa_s->go_dev_addr, dev_addr, ETH_ALEN) == 0)
-                       return 1;
+               if (os_memcmp(wpa_s->go_dev_addr, peer_dev_addr, ETH_ALEN) == 0)
+                       return wpa_s;
        }
 
-       return 0;
+       return NULL;
 }
 
 
-static void wpas_p2p_debug_print(void *ctx, int level, const char *msg)
+static int wpas_go_connected(void *ctx, const u8 *dev_addr)
 {
        struct wpa_supplicant *wpa_s = ctx;
-       wpa_msg_global(wpa_s, level, "P2P: %s", msg);
+
+       return wpas_get_p2p_client_iface(wpa_s, dev_addr) != NULL;
 }
 
 
-int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s)
+static int wpas_is_concurrent_session_active(void *ctx)
 {
-       struct wpa_interface iface;
-       struct wpa_supplicant *p2pdev_wpa_s;
-       char ifname[100];
-       char force_name[100];
-       int ret;
+       struct wpa_supplicant *wpa_s = ctx;
+       struct wpa_supplicant *ifs;
 
-       os_snprintf(ifname, sizeof(ifname), P2P_MGMT_DEVICE_PREFIX "%s",
-                   wpa_s->ifname);
-       force_name[0] = '\0';
-       wpa_s->pending_interface_type = WPA_IF_P2P_DEVICE;
+       for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
+               if (ifs == wpa_s)
+                       continue;
+               if (ifs->wpa_state > WPA_ASSOCIATED)
+                       return 1;
+       }
+       return 0;
+}
+
+
+static void wpas_p2p_debug_print(void *ctx, int level, const char *msg)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       wpa_msg_global(wpa_s, level, "P2P: %s", msg);
+}
+
+
+int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s,
+                                 const char *conf_p2p_dev)
+{
+       struct wpa_interface iface;
+       struct wpa_supplicant *p2pdev_wpa_s;
+       char ifname[100];
+       char force_name[100];
+       int ret;
+
+       ret = os_snprintf(ifname, sizeof(ifname), P2P_MGMT_DEVICE_PREFIX "%s",
+                         wpa_s->ifname);
+       if (os_snprintf_error(sizeof(ifname), ret))
+               return -1;
+       force_name[0] = '\0';
+       wpa_s->pending_interface_type = WPA_IF_P2P_DEVICE;
        ret = wpa_drv_if_add(wpa_s, WPA_IF_P2P_DEVICE, ifname, NULL, NULL,
                             force_name, wpa_s->pending_interface_addr, NULL);
        if (ret < 0) {
@@ -3055,19 +4668,419 @@ int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s)
        iface.ifname = wpa_s->pending_interface_name;
        iface.driver = wpa_s->driver->name;
        iface.driver_param = wpa_s->conf->driver_param;
-       iface.confname = wpa_s->confname;
-       p2pdev_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface);
+
+       /*
+        * If a P2P Device configuration file was given, use it as the interface
+        * configuration file (instead of using parent's configuration file.
+        */
+       if (conf_p2p_dev) {
+               iface.confname = conf_p2p_dev;
+               iface.ctrl_interface = NULL;
+       } else {
+               iface.confname = wpa_s->confname;
+               iface.ctrl_interface = wpa_s->conf->ctrl_interface;
+       }
+       iface.conf_p2p_dev = NULL;
+
+       p2pdev_wpa_s = wpa_supplicant_add_iface(wpa_s->global, &iface, wpa_s);
        if (!p2pdev_wpa_s) {
                wpa_printf(MSG_DEBUG, "P2P: Failed to add P2P Device interface");
                return -1;
        }
-       p2pdev_wpa_s->parent = wpa_s;
+       wpa_s->p2p_dev = p2pdev_wpa_s;
 
        wpa_s->pending_interface_name[0] = '\0';
        return 0;
 }
 
 
+static void wpas_presence_resp(void *ctx, const u8 *src, u8 status,
+                              const u8 *noa, size_t noa_len)
+{
+       struct wpa_supplicant *wpa_s, *intf = ctx;
+       char hex[100];
+
+       for (wpa_s = intf->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               if (wpa_s->waiting_presence_resp)
+                       break;
+       }
+       if (!wpa_s) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No group interface was waiting for presence response");
+               return;
+       }
+       wpa_s->waiting_presence_resp = 0;
+
+       wpa_snprintf_hex(hex, sizeof(hex), noa, noa_len);
+       wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_PRESENCE_RESPONSE "src=" MACSTR
+               " status=%u noa=%s", MAC2STR(src), status, hex);
+}
+
+
+static int wpas_get_persistent_group(void *ctx, const u8 *addr, const u8 *ssid,
+                                    size_t ssid_len, u8 *go_dev_addr,
+                                    u8 *ret_ssid, size_t *ret_ssid_len)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       struct wpa_ssid *s;
+
+       s = wpas_p2p_get_persistent(wpa_s, addr, ssid, ssid_len);
+       if (s) {
+               os_memcpy(ret_ssid, s->ssid, s->ssid_len);
+               *ret_ssid_len = s->ssid_len;
+               os_memcpy(go_dev_addr, s->bssid, ETH_ALEN);
+               return 1;
+       }
+
+       return 0;
+}
+
+
+static int wpas_get_go_info(void *ctx, u8 *intended_addr,
+                           u8 *ssid, size_t *ssid_len, int *group_iface)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       struct wpa_ssid *s;
+       u8 bssid[ETH_ALEN];
+
+       s = wpas_p2p_group_go_ssid(wpa_s, bssid);
+       if (!s) {
+               s = wpas_p2p_get_persistent_go(wpa_s);
+               if (s)
+                       os_memcpy(bssid, s->bssid, ETH_ALEN);
+       }
+
+       *group_iface = wpas_p2p_create_iface(wpa_s);
+       if (!s)
+               return 0;
+
+       os_memcpy(intended_addr, bssid, ETH_ALEN);
+       os_memcpy(ssid, s->ssid, s->ssid_len);
+       *ssid_len = s->ssid_len;
+
+       return 1;
+}
+
+
+static int wpas_remove_stale_groups(void *ctx, const u8 *peer, const u8 *go,
+                                   const u8 *ssid, size_t ssid_len)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       struct wpa_ssid *s;
+       int save_config = 0;
+       size_t i;
+
+       /* Start with our first choice of Persistent Groups */
+       while ((s = wpas_p2p_get_persistent(wpa_s, peer, NULL, 0))) {
+               if (go && ssid && ssid_len &&
+                   s->ssid_len == ssid_len &&
+                   os_memcmp(go, s->bssid, ETH_ALEN) == 0 &&
+                   os_memcmp(ssid, s->ssid, ssid_len) == 0)
+                       break;
+
+               /* Remove stale persistent group */
+               if (s->mode != WPAS_MODE_P2P_GO || s->num_p2p_clients <= 1) {
+                       wpa_config_remove_network(wpa_s->conf, s->id);
+                       save_config = 1;
+                       continue;
+               }
+
+               for (i = 0; i < s->num_p2p_clients; i++) {
+                       if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN,
+                                     peer, ETH_ALEN) != 0)
+                               continue;
+
+                       os_memmove(s->p2p_client_list + i * 2 * ETH_ALEN,
+                                  s->p2p_client_list + (i + 1) * 2 * ETH_ALEN,
+                                  (s->num_p2p_clients - i - 1) * 2 * ETH_ALEN);
+                       break;
+               }
+               s->num_p2p_clients--;
+               save_config = 1;
+       }
+
+       if (save_config)
+               p2p_config_write(wpa_s);
+
+       /* Return TRUE if valid SSID remains */
+       return s != NULL;
+}
+
+
+static void wpas_p2ps_prov_complete(void *ctx, u8 status, const u8 *dev,
+                                   const u8 *adv_mac, const u8 *ses_mac,
+                                   const u8 *grp_mac, u32 adv_id, u32 ses_id,
+                                   u8 conncap, int passwd_id,
+                                   const u8 *persist_ssid,
+                                   size_t persist_ssid_size, int response_done,
+                                   int prov_start, const char *session_info)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       u8 mac[ETH_ALEN];
+       struct wpa_ssid *persistent_go, *stale, *s;
+       int save_config = 0;
+       struct wpa_supplicant *go_wpa_s;
+
+       if (!dev)
+               return;
+
+       os_memset(mac, 0, ETH_ALEN);
+       if (!adv_mac)
+               adv_mac = mac;
+       if (!ses_mac)
+               ses_mac = mac;
+       if (!grp_mac)
+               grp_mac = mac;
+
+       if (prov_start) {
+               if (session_info == NULL) {
+                       wpa_msg_global(wpa_s, MSG_INFO,
+                                      P2P_EVENT_P2PS_PROVISION_START MACSTR
+                                      " adv_id=%x conncap=%x"
+                                      " adv_mac=" MACSTR
+                                      " session=%x mac=" MACSTR
+                                      " dev_passwd_id=%d",
+                                      MAC2STR(dev), adv_id, conncap,
+                                      MAC2STR(adv_mac),
+                                      ses_id, MAC2STR(ses_mac),
+                                      passwd_id);
+               } else {
+                       wpa_msg_global(wpa_s, MSG_INFO,
+                                      P2P_EVENT_P2PS_PROVISION_START MACSTR
+                                      " adv_id=%x conncap=%x"
+                                      " adv_mac=" MACSTR
+                                      " session=%x mac=" MACSTR
+                                      " dev_passwd_id=%d info='%s'",
+                                      MAC2STR(dev), adv_id, conncap,
+                                      MAC2STR(adv_mac),
+                                      ses_id, MAC2STR(ses_mac),
+                                      passwd_id, session_info);
+               }
+               return;
+       }
+
+       go_wpa_s = wpas_p2p_get_go_group(wpa_s);
+       persistent_go = wpas_p2p_get_persistent_go(wpa_s);
+
+       if (status && status != P2P_SC_SUCCESS_DEFERRED) {
+               if (go_wpa_s && !p2p_group_go_member_count(wpa_s))
+                       wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname);
+
+               if (persistent_go && !persistent_go->num_p2p_clients) {
+                       /* remove empty persistent GO */
+                       wpa_config_remove_network(wpa_s->conf,
+                                                 persistent_go->id);
+               }
+
+               wpa_msg_global(wpa_s, MSG_INFO,
+                              P2P_EVENT_P2PS_PROVISION_DONE MACSTR
+                              " status=%d"
+                              " adv_id=%x adv_mac=" MACSTR
+                              " session=%x mac=" MACSTR,
+                              MAC2STR(dev), status,
+                              adv_id, MAC2STR(adv_mac),
+                              ses_id, MAC2STR(ses_mac));
+               return;
+       }
+
+       /* Clean up stale persistent groups with this device */
+       s = wpas_p2p_get_persistent(wpa_s, dev, persist_ssid,
+                                   persist_ssid_size);
+       for (;;) {
+               stale = wpas_p2p_get_persistent(wpa_s, dev, NULL, 0);
+               if (!stale)
+                       break;
+
+               if (s && s->ssid_len == stale->ssid_len &&
+                   os_memcmp(stale->bssid, s->bssid, ETH_ALEN) == 0 &&
+                   os_memcmp(stale->ssid, s->ssid, s->ssid_len) == 0)
+                       break;
+
+               /* Remove stale persistent group */
+               if (stale->mode != WPAS_MODE_P2P_GO ||
+                   stale->num_p2p_clients <= 1) {
+                       wpa_config_remove_network(wpa_s->conf, stale->id);
+               } else {
+                       size_t i;
+
+                       for (i = 0; i < stale->num_p2p_clients; i++) {
+                               if (os_memcmp(stale->p2p_client_list +
+                                             i * ETH_ALEN,
+                                             dev, ETH_ALEN) == 0) {
+                                       os_memmove(stale->p2p_client_list +
+                                                  i * ETH_ALEN,
+                                                  stale->p2p_client_list +
+                                                  (i + 1) * ETH_ALEN,
+                                                  (stale->num_p2p_clients -
+                                                   i - 1) * ETH_ALEN);
+                                       break;
+                               }
+                       }
+                       stale->num_p2p_clients--;
+               }
+               save_config = 1;
+       }
+
+       if (save_config)
+               p2p_config_write(wpa_s);
+
+       if (s) {
+               if (go_wpa_s && !p2p_group_go_member_count(wpa_s))
+                       wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname);
+
+               if (persistent_go && s != persistent_go &&
+                   !persistent_go->num_p2p_clients) {
+                       /* remove empty persistent GO */
+                       wpa_config_remove_network(wpa_s->conf,
+                                                 persistent_go->id);
+                       /* Save config */
+               }
+
+               wpa_msg_global(wpa_s, MSG_INFO,
+                              P2P_EVENT_P2PS_PROVISION_DONE MACSTR
+                              " status=%d"
+                              " adv_id=%x adv_mac=" MACSTR
+                              " session=%x mac=" MACSTR
+                              " persist=%d",
+                              MAC2STR(dev), status,
+                              adv_id, MAC2STR(adv_mac),
+                              ses_id, MAC2STR(ses_mac), s->id);
+               return;
+       }
+
+       if (conncap == P2PS_SETUP_GROUP_OWNER) {
+               const char *go_ifname = NULL;
+               if (!go_wpa_s) {
+                       wpa_s->global->pending_p2ps_group = 1;
+
+                       if (wpa_s->conf->p2p_no_group_iface)
+                               go_ifname = wpa_s->ifname;
+                       else if (wpa_s->pending_interface_name[0])
+                               go_ifname = wpa_s->pending_interface_name;
+
+                       if (!go_ifname) {
+                               wpas_p2ps_prov_complete(
+                                       wpa_s, P2P_SC_FAIL_UNKNOWN_GROUP,
+                                       dev, adv_mac, ses_mac,
+                                       NULL, adv_id, ses_id, 0, 0,
+                                       NULL, 0, 0, 0, NULL);
+                               return;
+                       }
+
+                       /* If PD Resp complete, start up the GO */
+                       if (response_done && persistent_go) {
+                               wpas_p2p_group_add_persistent(
+                                       wpa_s, persistent_go,
+                                       0, 0, 0, 0, 0, NULL,
+                                       persistent_go->mode ==
+                                       WPAS_MODE_P2P_GO ?
+                                       P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE :
+                                       0);
+                       } else if (response_done) {
+                               wpas_p2p_group_add(wpa_s, 1, 0, 0, 0, NULL);
+                       }
+
+                       if (passwd_id == DEV_PW_P2PS_DEFAULT) {
+                               os_memcpy(wpa_s->p2ps_join_addr, dev, ETH_ALEN);
+                               wpa_s->p2ps_join_addr_valid = 1;
+                               wpa_dbg(wpa_s, MSG_DEBUG,
+                                       "P2PS: Saving PIN for " MACSTR,
+                                       MAC2STR(dev));
+                       }
+               } else if (passwd_id == DEV_PW_P2PS_DEFAULT) {
+                       go_ifname = go_wpa_s->ifname;
+
+                       wpa_dbg(go_wpa_s, MSG_DEBUG,
+                               "P2P: Setting PIN-1 For " MACSTR, MAC2STR(dev));
+                       wpa_supplicant_ap_wps_pin(go_wpa_s, dev, "12345670",
+                                                 NULL, 0, 0);
+
+                       os_memcpy(wpa_s->p2ps_join_addr, dev, ETH_ALEN);
+                       wpa_s->p2ps_join_addr_valid = 1;
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "P2PS: Saving PIN for " MACSTR, MAC2STR(dev));
+               }
+
+               wpa_msg_global(wpa_s, MSG_INFO,
+                              P2P_EVENT_P2PS_PROVISION_DONE MACSTR
+                              " status=%d conncap=%x"
+                              " adv_id=%x adv_mac=" MACSTR
+                              " session=%x mac=" MACSTR
+                              " dev_passwd_id=%d go=%s",
+                              MAC2STR(dev), status, conncap,
+                              adv_id, MAC2STR(adv_mac),
+                              ses_id, MAC2STR(ses_mac),
+                              passwd_id, go_ifname);
+               return;
+       }
+
+       if (go_wpa_s && !p2p_group_go_member_count(wpa_s))
+               wpas_p2p_group_remove(wpa_s, go_wpa_s->ifname);
+
+       if (persistent_go && !persistent_go->num_p2p_clients) {
+               /* remove empty persistent GO */
+               wpa_config_remove_network(wpa_s->conf, persistent_go->id);
+       }
+
+       if (conncap == P2PS_SETUP_CLIENT) {
+               wpa_msg_global(wpa_s, MSG_INFO,
+                              P2P_EVENT_P2PS_PROVISION_DONE MACSTR
+                              " status=%d conncap=%x"
+                              " adv_id=%x adv_mac=" MACSTR
+                              " session=%x mac=" MACSTR
+                              " dev_passwd_id=%d join=" MACSTR,
+                              MAC2STR(dev), status, conncap,
+                              adv_id, MAC2STR(adv_mac),
+                              ses_id, MAC2STR(ses_mac),
+                              passwd_id, MAC2STR(grp_mac));
+       } else {
+               wpa_msg_global(wpa_s, MSG_INFO,
+                              P2P_EVENT_P2PS_PROVISION_DONE MACSTR
+                              " status=%d conncap=%x"
+                              " adv_id=%x adv_mac=" MACSTR
+                              " session=%x mac=" MACSTR
+                              " dev_passwd_id=%d",
+                              MAC2STR(dev), status, conncap,
+                              adv_id, MAC2STR(adv_mac),
+                              ses_id, MAC2STR(ses_mac),
+                              passwd_id);
+       }
+}
+
+
+static int _wpas_p2p_in_progress(void *ctx)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       return wpas_p2p_in_progress(wpa_s);
+}
+
+
+static int wpas_prov_disc_resp_cb(void *ctx)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       struct wpa_ssid *persistent_go;
+
+       if (!wpa_s->global->pending_p2ps_group)
+               return 0;
+
+       wpa_s->global->pending_p2ps_group = 0;
+
+       if (wpas_p2p_get_go_group(wpa_s))
+               return 0;
+       persistent_go = wpas_p2p_get_persistent_go(wpa_s);
+
+       if (persistent_go) {
+               wpas_p2p_group_add_persistent(
+                       wpa_s, persistent_go, 0, 0, 0, 0, 0, NULL,
+                       persistent_go->mode == WPAS_MODE_P2P_GO ?
+                       P2P_MAX_INITIAL_CONN_WAIT_GO_REINVOKE : 0);
+       } else {
+               wpas_p2p_group_add(wpa_s, 1, 0, 0, 0, NULL);
+       }
+
+       return 1;
+}
+
+
 /**
  * wpas_p2p_init - Initialize P2P module for %wpa_supplicant
  * @global: Pointer to global data from wpa_supplicant_init()
@@ -3077,33 +5090,16 @@ int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s)
 int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
 {
        struct p2p_config p2p;
-       unsigned int r;
        int i;
 
-       if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE))
+       if (wpa_s->conf->p2p_disabled)
                return 0;
 
-       if (global->p2p)
+       if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE))
                return 0;
 
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
-               struct p2p_params params;
-
-               wpa_printf(MSG_DEBUG, "P2P: Use driver-based P2P management");
-               os_memset(&params, 0, sizeof(params));
-               params.dev_name = wpa_s->conf->device_name;
-               os_memcpy(params.pri_dev_type, wpa_s->conf->device_type,
-                         WPS_DEV_TYPE_LEN);
-               params.num_sec_dev_types = wpa_s->conf->num_sec_device_types;
-               os_memcpy(params.sec_dev_type,
-                         wpa_s->conf->sec_device_type,
-                         params.num_sec_dev_types * WPS_DEV_TYPE_LEN);
-
-               if (wpa_drv_p2p_set_params(wpa_s, &params) < 0)
-                       return -1;
-
+       if (global->p2p)
                return 0;
-       }
 
        os_memset(&p2p, 0, sizeof(p2p));
        p2p.cb_ctx = wpa_s;
@@ -3129,8 +5125,17 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
        p2p.invitation_result = wpas_invitation_result;
        p2p.get_noa = wpas_get_noa;
        p2p.go_connected = wpas_go_connected;
-
-#if defined(TIZEN_EXT_P2P) && defined(BCM_DRIVER_V115)
+       p2p.presence_resp = wpas_presence_resp;
+       p2p.is_concurrent_session_active = wpas_is_concurrent_session_active;
+       p2p.is_p2p_in_progress = _wpas_p2p_in_progress;
+       p2p.get_persistent_group = wpas_get_persistent_group;
+       p2p.get_go_info = wpas_get_go_info;
+       p2p.remove_stale_groups = wpas_remove_stale_groups;
+       p2p.p2ps_prov_complete = wpas_p2ps_prov_complete;
+       p2p.prov_disc_resp_cb = wpas_prov_disc_resp_cb;
+       p2p.p2ps_group_capability = p2ps_group_capability;
+
+#ifdef BCM_DRIVER_V115
        char buf[64];
        wpa_s->driver->priv_cmd(wpa_s->drv_priv,  "P2P_DEV_ADDR", buf, sizeof(buf));
        os_memcpy(wpa_s->global->p2p_dev_addr, buf, ETH_ALEN);
@@ -3151,20 +5156,32 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
                p2p.config_methods = wpa_s->wps->config_methods;
        }
 
+       if (wpas_p2p_setup_channels(wpa_s, &p2p.channels, &p2p.cli_channels)) {
+               wpa_printf(MSG_ERROR,
+                          "P2P: Failed to configure supported channel list");
+               return -1;
+       }
+
        if (wpa_s->conf->p2p_listen_reg_class &&
            wpa_s->conf->p2p_listen_channel) {
                p2p.reg_class = wpa_s->conf->p2p_listen_reg_class;
                p2p.channel = wpa_s->conf->p2p_listen_channel;
+               p2p.channel_forced = 1;
        } else {
-               p2p.reg_class = 81;
                /*
                 * Pick one of the social channels randomly as the listen
                 * channel.
                 */
-               os_get_random((u8 *) &r, sizeof(r));
-               p2p.channel = 1 + (r % 3) * 5;
+               if (p2p_config_get_random_social(&p2p, &p2p.reg_class,
+                                                &p2p.channel) != 0) {
+                       wpa_printf(MSG_ERROR,
+                                  "P2P: Failed to select random social channel as listen channel");
+                       return -1;
+               }
+               p2p.channel_forced = 0;
        }
-       wpa_printf(MSG_DEBUG, "P2P: Own listen channel: %d", p2p.channel);
+       wpa_printf(MSG_DEBUG, "P2P: Own listen channel: %d:%d",
+                  p2p.reg_class, p2p.channel);
 
        if (wpa_s->conf->p2p_oper_reg_class &&
            wpa_s->conf->p2p_oper_channel) {
@@ -3175,13 +5192,17 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
                           "%d:%d", p2p.op_reg_class, p2p.op_channel);
 
        } else {
-               p2p.op_reg_class = 81;
                /*
-                * Use random operation channel from (1, 6, 11) if no other
-                * preference is indicated.
+                * Use random operation channel from 2.4 GHz band social
+                * channels (1, 6, 11) or band 60 GHz social channel (2) if no
+                * other preference is indicated.
                 */
-               os_get_random((u8 *) &r, sizeof(r));
-               p2p.op_channel = 1 + (r % 3) * 5;
+               if (p2p_config_get_random_social(&p2p, &p2p.op_reg_class,
+                                                &p2p.op_channel) != 0) {
+                       wpa_printf(MSG_ERROR,
+                                  "P2P: Failed to select random social channel as operation channel");
+                       return -1;
+               }
                p2p.cfg_op_channel = 0;
                wpa_printf(MSG_DEBUG, "P2P: Random operating channel: "
                           "%d:%d", p2p.op_reg_class, p2p.op_channel);
@@ -3198,12 +5219,6 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
        } else
                os_memcpy(p2p.country, "XX\x04", 3);
 
-       if (wpas_p2p_setup_channels(wpa_s, &p2p.channels)) {
-               wpa_printf(MSG_ERROR, "P2P: Failed to configure supported "
-                          "channel list");
-               return -1;
-       }
-
        os_memcpy(p2p.pri_dev_type, wpa_s->conf->device_type,
                  WPS_DEV_TYPE_LEN);
 
@@ -3229,6 +5244,12 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
 
        p2p.max_listen = wpa_s->max_remain_on_chan;
 
+       if (wpa_s->conf->p2p_passphrase_len >= 8 &&
+           wpa_s->conf->p2p_passphrase_len <= 63)
+               p2p.passphrase_len = wpa_s->conf->p2p_passphrase_len;
+       else
+               p2p.passphrase_len = 8;
+
        global->p2p = p2p_init(&p2p);
        if (global->p2p == NULL)
                return -1;
@@ -3241,6 +5262,8 @@ int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
                        global->p2p, wpa_s->conf->wps_vendor_ext[i]);
        }
 
+       p2p_set_no_go_freq(global->p2p, &wpa_s->conf->p2p_no_go_freq);
+
        return 0;
 }
 
@@ -3265,12 +5288,28 @@ void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
 
        os_free(wpa_s->go_params);
        wpa_s->go_params = NULL;
+       eloop_cancel_timeout(wpas_p2p_psk_failure_removal, wpa_s, NULL);
        eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
        eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
        wpa_s->p2p_long_listen = 0;
        eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
        eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
        wpas_p2p_remove_pending_group_interface(wpa_s);
+       eloop_cancel_timeout(wpas_p2p_group_freq_conflict, wpa_s, NULL);
+       wpas_p2p_listen_work_done(wpa_s);
+       if (wpa_s->p2p_send_action_work) {
+               os_free(wpa_s->p2p_send_action_work->ctx);
+               radio_work_done(wpa_s->p2p_send_action_work);
+               wpa_s->p2p_send_action_work = NULL;
+       }
+       eloop_cancel_timeout(wpas_p2p_send_action_work_timeout, wpa_s, NULL);
+
+       wpabuf_free(wpa_s->p2p_oob_dev_pw);
+       wpa_s->p2p_oob_dev_pw = NULL;
+
+       os_free(wpa_s->p2p_group_common_freqs);
+       wpa_s->p2p_group_common_freqs = NULL;
+       wpa_s->p2p_group_common_freqs_num = 0;
 
        /* TODO: remove group interface from the driver if this wpa_s instance
         * is on top of a P2P group interface */
@@ -3283,16 +5322,13 @@ void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
  *
  * This function deinitializes the global (per device) P2P module.
  */
-void wpas_p2p_deinit_global(struct wpa_global *global)
+static void wpas_p2p_deinit_global(struct wpa_global *global)
 {
        struct wpa_supplicant *wpa_s, *tmp;
 
        wpa_s = global->ifaces;
-       if (wpa_s)
-               wpas_p2p_service_flush(wpa_s);
 
-       if (global->p2p == NULL)
-               return;
+       wpas_p2p_service_flush(global->p2p_init_wpa_s);
 
        /* Remove remaining P2P group interfaces */
        while (wpa_s && wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE)
@@ -3355,12 +5391,6 @@ static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s,
        if (persistent_group && wpa_s->conf->persistent_reconnect)
                persistent_group = 2;
 
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
-               return wpa_drv_p2p_connect(wpa_s, peer_addr, wps_method,
-                                          go_intent, own_interface_addr,
-                                          force_freq, persistent_group);
-       }
-
        /*
         * Increase GO config timeout if HT40 is used since it takes some time
         * to scan channels for coex purposes before the BSS can be started.
@@ -3372,7 +5402,9 @@ static int wpas_p2p_start_go_neg(struct wpa_supplicant *wpa_s,
                           go_intent, own_interface_addr, force_freq,
                           persistent_group, ssid ? ssid->ssid : NULL,
                           ssid ? ssid->ssid_len : 0,
-                          wpa_s->p2p_pd_before_go_neg, pref_freq);
+                          wpa_s->p2p_pd_before_go_neg, pref_freq,
+                          wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id :
+                          0);
 }
 
 
@@ -3386,13 +5418,12 @@ static int wpas_p2p_auth_go_neg(struct wpa_supplicant *wpa_s,
        if (persistent_group && wpa_s->conf->persistent_reconnect)
                persistent_group = 2;
 
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-               return -1;
-
        return p2p_authorize(wpa_s->global->p2p, peer_addr, wps_method,
                             go_intent, own_interface_addr, force_freq,
                             persistent_group, ssid ? ssid->ssid : NULL,
-                            ssid ? ssid->ssid_len : 0, pref_freq);
+                            ssid ? ssid->ssid_len : 0, pref_freq,
+                            wps_method == WPS_NFC ? wpa_s->p2p_oob_dev_pw_id :
+                            0);
 }
 
 
@@ -3416,49 +5447,45 @@ static void wpas_p2p_check_join_scan_limit(struct wpa_supplicant *wpa_s)
                }
                wpa_msg_global(wpa_s->parent, MSG_INFO,
                               P2P_EVENT_GROUP_FORMATION_FAILURE);
+               wpas_notify_p2p_group_formation_failure(wpa_s);
        }
 }
 
 
 static int wpas_check_freq_conflict(struct wpa_supplicant *wpa_s, int freq)
 {
-       struct wpa_supplicant *iface;
-       int shared_freq;
-       u8 bssid[ETH_ALEN];
+       int res;
+       unsigned int num, i;
+       struct wpa_used_freq_data *freqs;
 
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)
+       if (wpas_p2p_num_unused_channels(wpa_s) > 0) {
+               /* Multiple channels are supported and not all are in use */
                return 0;
+       }
 
-       for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
-               if (!wpas_p2p_create_iface(wpa_s) && iface == wpa_s)
-                       continue;
-               if (iface->current_ssid == NULL || iface->assoc_freq == 0)
-                       continue;
-               if (iface->current_ssid->mode == WPAS_MODE_AP ||
-                   iface->current_ssid->mode == WPAS_MODE_P2P_GO)
-                       shared_freq = iface->current_ssid->frequency;
-               else if (wpa_drv_get_bssid(iface, bssid) == 0)
-                       shared_freq = iface->assoc_freq;
-               else
-                       shared_freq = 0;
+       freqs = os_calloc(wpa_s->num_multichan_concurrent,
+                         sizeof(struct wpa_used_freq_data));
+       if (!freqs)
+               return 1;
 
-               if (shared_freq && freq != shared_freq) {
-                       wpa_printf(MSG_DEBUG, "P2P: Frequency conflict - %s "
-                                  "connected on %d MHz - new connection on "
-                                  "%d MHz", iface->ifname, shared_freq, freq);
-                       return 1;
+       num = wpas_p2p_valid_oper_freqs(wpa_s, freqs,
+                                       wpa_s->num_multichan_concurrent);
+
+       for (i = 0; i < num; i++) {
+               if (freqs[i].freq == freq) {
+                       wpa_printf(MSG_DEBUG, "P2P: Frequency %d MHz in use by another virtual interface and can be used",
+                                  freq);
+                       res = 0;
+                       goto exit_free;
                }
        }
 
-       shared_freq = wpa_drv_shared_freq(wpa_s);
-       if (shared_freq > 0 && shared_freq != freq) {
-               wpa_printf(MSG_DEBUG, "P2P: Frequency conflict - shared "
-                          "virtual interface connected on %d MHz - new "
-                          "connection on %d MHz", shared_freq, freq);
-               return 1;
-       }
+       wpa_printf(MSG_DEBUG, "P2P: No valid operating frequencies");
+       res = 1;
 
-       return 0;
+exit_free:
+       os_free(freqs);
+       return res;
 }
 
 
@@ -3477,7 +5504,8 @@ static int wpas_p2p_peer_go(struct wpa_supplicant *wpa_s,
                return 0;
        }
 
-       updated = os_time_before(&wpa_s->p2p_auto_started, &bss->last_update);
+       updated = os_reltime_before(&wpa_s->p2p_auto_started,
+                                   &bss->last_update);
        wpa_printf(MSG_DEBUG, "P2P: Current BSS entry for peer updated at "
                   "%ld.%06ld (%supdated in last scan)",
                   bss->last_update.sec, bss->last_update.usec,
@@ -3490,7 +5518,7 @@ static int wpas_p2p_peer_go(struct wpa_supplicant *wpa_s,
 static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
                                   struct wpa_scan_results *scan_res)
 {
-       struct wpa_bss *bss;
+       struct wpa_bss *bss = NULL;
        int freq;
        u8 iface_addr[ETH_ALEN];
 
@@ -3522,7 +5550,7 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
                                           MAC2STR(wpa_s->
                                                   pending_join_dev_addr),
                                           freq);
-                               wpas_p2p_join_scan_req(wpa_s, freq);
+                               wpas_p2p_join_scan_req(wpa_s, freq, NULL, 0);
                                return;
                        }
                }
@@ -3535,7 +5563,7 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
                wpa_printf(MSG_DEBUG, "P2P: Auto PD with " MACSTR " join=%d",
                           MAC2STR(wpa_s->pending_join_dev_addr), join);
                if (p2p_prov_disc_req(wpa_s->global->p2p,
-                                     wpa_s->pending_join_dev_addr,
+                                     wpa_s->pending_join_dev_addr, NULL,
                                      wpa_s->pending_pd_config_methods, join,
                                      0, wpa_s->user_initiated_pd) < 0) {
                        wpa_s->p2p_auto_pd = 0;
@@ -3553,6 +5581,9 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
                if (join < 0) {
                        wpa_printf(MSG_DEBUG, "P2P: Peer was not found to be "
                                   "running a GO -> use GO Negotiation");
+                       wpa_msg_global(wpa_s->parent, MSG_INFO,
+                                      P2P_EVENT_FALLBACK_TO_GO_NEG
+                                      "reason=peer-not-running-GO");
                        wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr,
                                         wpa_s->p2p_pin, wpa_s->p2p_wps_method,
                                         wpa_s->p2p_persistent_group, 0, 0, 0,
@@ -3560,15 +5591,19 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
                                         wpa_s->p2p_connect_freq,
                                         wpa_s->p2p_persistent_id,
                                         wpa_s->p2p_pd_before_go_neg,
-                                        wpa_s->p2p_go_ht40);
+                                        wpa_s->p2p_go_ht40,
+                                        wpa_s->p2p_go_vht);
                        return;
                }
 
                wpa_printf(MSG_DEBUG, "P2P: Peer was found running GO%s -> "
                           "try to join the group", join ? "" :
                           " in older scan");
-               if (!join)
+               if (!join) {
+                       wpa_msg_global(wpa_s->parent, MSG_INFO,
+                                      P2P_EVENT_FALLBACK_TO_GO_NEG_ENABLED);
                        wpa_s->p2p_fallback_to_go_neg = 1;
+               }
        }
 
        freq = p2p_get_oper_freq(wpa_s->global->p2p,
@@ -3577,8 +5612,8 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
            p2p_get_interface_addr(wpa_s->global->p2p,
                                   wpa_s->pending_join_dev_addr,
                                   iface_addr) == 0 &&
-           os_memcmp(iface_addr, wpa_s->pending_join_dev_addr, ETH_ALEN) != 0)
-       {
+           os_memcmp(iface_addr, wpa_s->pending_join_dev_addr, ETH_ALEN) != 0
+           && !wpa_bss_get_bssid(wpa_s, wpa_s->pending_join_iface_addr)) {
                wpa_printf(MSG_DEBUG, "P2P: Overwrite pending interface "
                           "address for join from " MACSTR " to " MACSTR
                           " based on newly discovered P2P peer entry",
@@ -3594,11 +5629,27 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
                wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
                           "from P2P peer table: %d MHz", freq);
        }
-       bss = wpa_bss_get_bssid_latest(wpa_s, wpa_s->pending_join_iface_addr);
+       if (wpa_s->p2p_join_ssid_len) {
+               wpa_printf(MSG_DEBUG, "P2P: Trying to find target GO BSS entry based on BSSID "
+                          MACSTR " and SSID %s",
+                          MAC2STR(wpa_s->pending_join_iface_addr),
+                          wpa_ssid_txt(wpa_s->p2p_join_ssid,
+                                       wpa_s->p2p_join_ssid_len));
+               bss = wpa_bss_get(wpa_s, wpa_s->pending_join_iface_addr,
+                                 wpa_s->p2p_join_ssid,
+                                 wpa_s->p2p_join_ssid_len);
+       }
+       if (!bss) {
+               wpa_printf(MSG_DEBUG, "P2P: Trying to find target GO BSS entry based on BSSID "
+                          MACSTR, MAC2STR(wpa_s->pending_join_iface_addr));
+               bss = wpa_bss_get_bssid_latest(wpa_s,
+                                              wpa_s->pending_join_iface_addr);
+       }
        if (bss) {
                freq = bss->freq;
                wpa_printf(MSG_DEBUG, "P2P: Target GO operating frequency "
-                          "from BSS table: %d MHz", freq);
+                          "from BSS table: %d MHz (SSID %s)", freq,
+                          wpa_ssid_txt(bss->ssid, bss->ssid_len));
        }
        if (freq > 0) {
                u16 method;
@@ -3607,6 +5658,7 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
                        wpa_msg_global(wpa_s->parent, MSG_INFO,
                                       P2P_EVENT_GROUP_FORMATION_FAILURE
                                       "reason=FREQ_CONFLICT");
+                       wpas_notify_p2p_group_formation_failure(wpa_s);
                        return;
                }
 
@@ -3647,7 +5699,8 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
                }
 
                if (p2p_prov_disc_req(wpa_s->global->p2p,
-                                     wpa_s->pending_join_dev_addr, method, 1,
+                                     wpa_s->pending_join_dev_addr,
+                                     NULL, method, 1,
                                      freq, wpa_s->user_initiated_pd) < 0) {
                        wpa_printf(MSG_DEBUG, "P2P: Failed to send Provision "
                                   "Discovery Request before joining an "
@@ -3666,11 +5719,12 @@ static void wpas_p2p_scan_res_join(struct wpa_supplicant *wpa_s,
 
 start:
        /* Start join operation immediately */
-       wpas_p2p_join_start(wpa_s);
+       wpas_p2p_join_start(wpa_s, 0, NULL, 0);
 }
 
 
-static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq)
+static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq,
+                                  const u8 *ssid, size_t ssid_len)
 {
        int ret;
        struct wpa_driver_scan_params params;
@@ -3682,8 +5736,16 @@ static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq)
 
        /* P2P Wildcard SSID */
        params.num_ssids = 1;
-       params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
-       params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
+       if (ssid && ssid_len) {
+               params.ssids[0].ssid = ssid;
+               params.ssids[0].ssid_len = ssid_len;
+               os_memcpy(wpa_s->p2p_join_ssid, ssid, ssid_len);
+               wpa_s->p2p_join_ssid_len = ssid_len;
+       } else {
+               params.ssids[0].ssid = (u8 *) P2P_WILDCARD_SSID;
+               params.ssids[0].ssid_len = P2P_WILDCARD_SSID_LEN;
+               wpa_s->p2p_join_ssid_len = 0;
+       }
 
        wpa_s->wps->dev.p2p = 1;
        wps_ie = wps_build_probe_req_ie(DEV_PW_DEFAULT, &wpa_s->wps->dev,
@@ -3709,6 +5771,18 @@ static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq)
        params.p2p_probe = 1;
        params.extra_ies = wpabuf_head(ies);
        params.extra_ies_len = wpabuf_len(ies);
+
+       if (!freq) {
+               int oper_freq;
+               /*
+                * If freq is not provided, check the operating freq of the GO
+                * and use a single channel scan on if possible.
+                */
+               oper_freq = p2p_get_oper_freq(wpa_s->global->p2p,
+                                             wpa_s->pending_join_iface_addr);
+               if (oper_freq > 0)
+                       freq = oper_freq;
+       }
        if (freq > 0) {
                freqs[0] = freq;
                params.freqs = freqs;
@@ -3720,8 +5794,9 @@ static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq)
         */
        ret = wpa_drv_scan(wpa_s, &params);
        if (!ret) {
-               os_get_time(&wpa_s->scan_trigger_time);
+               os_get_reltime(&wpa_s->scan_trigger_time);
                wpa_s->scan_res_handler = wpas_p2p_scan_res_join;
+               wpa_s->own_scan_requested = 1;
        }
 
        wpabuf_free(ies);
@@ -3739,18 +5814,23 @@ static void wpas_p2p_join_scan_req(struct wpa_supplicant *wpa_s, int freq)
 static void wpas_p2p_join_scan(void *eloop_ctx, void *timeout_ctx)
 {
        struct wpa_supplicant *wpa_s = eloop_ctx;
-       wpas_p2p_join_scan_req(wpa_s, 0);
+       wpas_p2p_join_scan_req(wpa_s, 0, NULL, 0);
 }
 
 
 static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
                         const u8 *dev_addr, enum p2p_wps_method wps_method,
-                        int auto_join)
+                        int auto_join, int op_freq,
+                        const u8 *ssid, size_t ssid_len)
 {
        wpa_printf(MSG_DEBUG, "P2P: Request to join existing group (iface "
-                  MACSTR " dev " MACSTR ")%s",
-                  MAC2STR(iface_addr), MAC2STR(dev_addr),
+                  MACSTR " dev " MACSTR " op_freq=%d)%s",
+                  MAC2STR(iface_addr), MAC2STR(dev_addr), op_freq,
                   auto_join ? " (auto_join)" : "");
+       if (ssid && ssid_len) {
+               wpa_printf(MSG_DEBUG, "P2P: Group SSID specified: %s",
+                          wpa_ssid_txt(ssid, ssid_len));
+       }
 
        wpa_s->p2p_auto_pd = 0;
        wpa_s->p2p_auto_join = !!auto_join;
@@ -3762,12 +5842,13 @@ static int wpas_p2p_join(struct wpa_supplicant *wpa_s, const u8 *iface_addr,
        wpas_p2p_stop_find(wpa_s);
 
        wpa_s->p2p_join_scan_count = 0;
-       wpas_p2p_join_scan(wpa_s, NULL);
+       wpas_p2p_join_scan_req(wpa_s, op_freq, ssid, ssid_len);
        return 0;
 }
 
 
-static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s)
+static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s, int freq,
+                              const u8 *ssid, size_t ssid_len)
 {
        struct wpa_supplicant *group;
        struct p2p_go_neg_results res;
@@ -3795,14 +5876,25 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s)
        group->p2p_fallback_to_go_neg = wpa_s->p2p_fallback_to_go_neg;
 
        os_memset(&res, 0, sizeof(res));
+       os_memcpy(res.peer_device_addr, wpa_s->pending_join_dev_addr, ETH_ALEN);
        os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr,
                  ETH_ALEN);
        res.wps_method = wpa_s->pending_join_wps_method;
-       bss = wpa_bss_get_bssid_latest(wpa_s, wpa_s->pending_join_iface_addr);
-       if (bss) {
-               res.freq = bss->freq;
-               res.ssid_len = bss->ssid_len;
-               os_memcpy(res.ssid, bss->ssid, bss->ssid_len);
+       if (freq && ssid && ssid_len) {
+               res.freq = freq;
+               res.ssid_len = ssid_len;
+               os_memcpy(res.ssid, ssid, ssid_len);
+       } else {
+               bss = wpa_bss_get_bssid_latest(wpa_s,
+                                              wpa_s->pending_join_iface_addr);
+               if (bss) {
+                       res.freq = bss->freq;
+                       res.ssid_len = bss->ssid_len;
+                       os_memcpy(res.ssid, bss->ssid, bss->ssid_len);
+                       wpa_printf(MSG_DEBUG, "P2P: Join target GO operating frequency from BSS table: %d MHz (SSID %s)",
+                                  bss->freq,
+                                  wpa_ssid_txt(bss->ssid, bss->ssid_len));
+               }
        }
 
        if (wpa_s->off_channel_freq || wpa_s->roc_waiting_drv_freq) {
@@ -3828,56 +5920,102 @@ static int wpas_p2p_join_start(struct wpa_supplicant *wpa_s)
 
 
 static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
-                               int *force_freq, int *pref_freq,
-                               int *oper_freq)
+                               int *force_freq, int *pref_freq, int go)
 {
-       if (freq > 0) {
-               if (!p2p_supported_freq(wpa_s->global->p2p, freq)) {
-                       wpa_printf(MSG_DEBUG, "P2P: The forced channel "
-                                  "(%u MHz) is not supported for P2P uses",
-                                  freq);
-                       return -3;
-               }
+       struct wpa_used_freq_data *freqs;
+       int res, best_freq, num_unused;
+       unsigned int freq_in_use = 0, num, i;
 
-               if (*oper_freq > 0 && freq != *oper_freq &&
-                   !(wpa_s->drv_flags &
-                     WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
-                       wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group "
-                                  "on %u MHz while connected on another "
-                                  "channel (%u MHz)", freq, *oper_freq);
-                       return -2;
-               }
-               wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the "
-                          "requested channel (%u MHz)", freq);
-               *force_freq = freq;
-       } else if (*oper_freq > 0 &&
-                  !p2p_supported_freq(wpa_s->global->p2p, *oper_freq)) {
-               if (!(wpa_s->drv_flags &
-                     WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
-                       wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group "
-                                  "while connected on non-P2P supported "
-                                  "channel (%u MHz)", *oper_freq);
-                       return -2;
-               }
-               wpa_printf(MSG_DEBUG, "P2P: Current operating channel "
-                          "(%u MHz) not available for P2P - try to use "
-                          "another channel", *oper_freq);
-               *force_freq = 0;
-       } else if (*oper_freq > 0 && *pref_freq == 0 &&
-                  (wpa_s->drv_flags &
-                   WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
-               wpa_printf(MSG_DEBUG, "P2P: Trying to prefer the channel we "
-                          "are already using (%u MHz) on another interface",
-                          *oper_freq);
-               *pref_freq = *oper_freq;
-       } else if (*oper_freq > 0) {
-               wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the "
-                          "channel we are already using (%u MHz) on another "
-                          "interface", *oper_freq);
-               *force_freq = *oper_freq;
-       }
+       freqs = os_calloc(wpa_s->num_multichan_concurrent,
+                         sizeof(struct wpa_used_freq_data));
+       if (!freqs)
+               return -1;
 
-       return 0;
+       num = wpas_p2p_valid_oper_freqs(wpa_s, freqs,
+                                       wpa_s->num_multichan_concurrent);
+
+       /*
+        * It is possible that the total number of used frequencies is bigger
+        * than the number of frequencies used for P2P, so get the system wide
+        * number of unused frequencies.
+        */
+       num_unused = wpas_p2p_num_unused_channels(wpa_s);
+
+       wpa_printf(MSG_DEBUG,
+                  "P2P: Setup freqs: freq=%d num_MCC=%d shared_freqs=%u num_unused=%d",
+                  freq, wpa_s->num_multichan_concurrent, num, num_unused);
+
+       if (freq > 0) {
+               int ret;
+               if (go)
+                       ret = p2p_supported_freq(wpa_s->global->p2p, freq);
+               else
+                       ret = p2p_supported_freq_cli(wpa_s->global->p2p, freq);
+               if (!ret) {
+                       if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+                           ieee80211_is_dfs(freq)) {
+                               /*
+                                * If freq is a DFS channel and DFS is offloaded
+                                * to the driver, allow P2P GO to use it.
+                                */
+                               wpa_printf(MSG_DEBUG,
+                                          "P2P: The forced channel for GO (%u MHz) is DFS, and DFS is offloaded to the driver",
+                                          freq);
+                       } else {
+                               wpa_printf(MSG_DEBUG,
+                                          "P2P: The forced channel (%u MHz) is not supported for P2P uses",
+                                          freq);
+                               res = -3;
+                               goto exit_free;
+                       }
+               }
+
+               for (i = 0; i < num; i++) {
+                       if (freqs[i].freq == freq)
+                               freq_in_use = 1;
+               }
+
+               if (num_unused <= 0 && !freq_in_use) {
+                       wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group on %u MHz as there are no available channels",
+                                  freq);
+                       res = -2;
+                       goto exit_free;
+               }
+               wpa_printf(MSG_DEBUG, "P2P: Trying to force us to use the "
+                          "requested channel (%u MHz)", freq);
+               *force_freq = freq;
+               goto exit_ok;
+       }
+
+       best_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
+
+       /* We have a candidate frequency to use */
+       if (best_freq > 0) {
+               if (*pref_freq == 0 && num_unused > 0) {
+                       wpa_printf(MSG_DEBUG, "P2P: Try to prefer a frequency (%u MHz) we are already using",
+                                  best_freq);
+                       *pref_freq = best_freq;
+               } else {
+                       wpa_printf(MSG_DEBUG, "P2P: Try to force us to use frequency (%u MHz) which is already in use",
+                                  best_freq);
+                       *force_freq = best_freq;
+               }
+       } else if (num_unused > 0) {
+               wpa_printf(MSG_DEBUG,
+                          "P2P: Current operating channels are not available for P2P. Try to use another channel");
+               *force_freq = 0;
+       } else {
+               wpa_printf(MSG_DEBUG,
+                          "P2P: All channels are in use and none of them are P2P enabled. Cannot start P2P group");
+               res = -2;
+               goto exit_free;
+       }
+
+exit_ok:
+       res = 0;
+exit_free:
+       os_free(freqs);
+       return res;
 }
 
 
@@ -3899,6 +6037,7 @@ static int wpas_p2p_setup_freqs(struct wpa_supplicant *wpa_s, int freq,
  * @pd: Whether to send Provision Discovery prior to GO Negotiation as an
  *     interoperability workaround when initiating group formation
  * @ht40: Start GO with 40 MHz channel width
+ * @vht:  Start GO with VHT support
  * Returns: 0 or new PIN (if pin was %NULL) on success, -1 on unspecified
  *     failure, -2 on failure due to channel not currently available,
  *     -3 if forced channel is not supported
@@ -3907,10 +6046,9 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
                     const char *pin, enum p2p_wps_method wps_method,
                     int persistent_group, int auto_join, int join, int auth,
                     int go_intent, int freq, int persistent_id, int pd,
-                    int ht40)
+                    int ht40, int vht)
 {
-       int force_freq = 0, pref_freq = 0, oper_freq = 0;
-       u8 bssid[ETH_ALEN];
+       int force_freq = 0, pref_freq = 0;
        int ret = 0, res;
        enum wpa_driver_if_type iftype;
        const u8 *if_addr;
@@ -3926,6 +6064,12 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
                        return -1;
        }
 
+       os_free(wpa_s->global->add_psk);
+       wpa_s->global->add_psk = NULL;
+
+       wpa_s->global->p2p_fail_on_wps_complete = 0;
+       wpa_s->global->pending_p2ps_group = 0;
+
        if (go_intent < 0)
                go_intent = wpa_s->conf->p2p_go_intent;
 
@@ -3940,13 +6084,16 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
        wpa_s->p2p_fallback_to_go_neg = 0;
        wpa_s->p2p_pd_before_go_neg = !!pd;
        wpa_s->p2p_go_ht40 = !!ht40;
+       wpa_s->p2p_go_vht = !!vht;
 
        if (pin)
                os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin));
        else if (wps_method == WPS_PIN_DISPLAY) {
                ret = wps_generate_pin();
-               os_snprintf(wpa_s->p2p_pin, sizeof(wpa_s->p2p_pin), "%08d",
-                           ret);
+               res = os_snprintf(wpa_s->p2p_pin, sizeof(wpa_s->p2p_pin),
+                                 "%08d", ret);
+               if (os_snprintf_error(sizeof(wpa_s->p2p_pin), res))
+                       wpa_s->p2p_pin[sizeof(wpa_s->p2p_pin) - 1] = '\0';
                wpa_printf(MSG_DEBUG, "P2P: Randomly generated PIN: %s",
                           wpa_s->p2p_pin);
        } else
@@ -3969,7 +6116,7 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
                                         dev_addr);
                }
                if (auto_join) {
-                       os_get_time(&wpa_s->p2p_auto_started);
+                       os_get_reltime(&wpa_s->p2p_auto_started);
                        wpa_printf(MSG_DEBUG, "P2P: Auto join started at "
                                   "%ld.%06ld",
                                   wpa_s->p2p_auto_started.sec,
@@ -3977,25 +6124,17 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
                }
                wpa_s->user_initiated_pd = 1;
                if (wpas_p2p_join(wpa_s, iface_addr, dev_addr, wps_method,
-                                 auto_join) < 0)
+                                 auto_join, freq, NULL, 0) < 0)
                        return -1;
                return ret;
        }
 
-       if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid) == 0 &&
-           wpa_s->assoc_freq) {
-               oper_freq = wpa_s->assoc_freq;
-       } else {
-               oper_freq = wpa_drv_shared_freq(wpa_s);
-               if (oper_freq < 0)
-                       oper_freq = 0;
-       }
-
        res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
-                                  &oper_freq);
+                                  go_intent == 15);
        if (res)
                return res;
-       wpas_p2p_set_own_freq_preference(wpa_s, oper_freq);
+       wpas_p2p_set_own_freq_preference(wpa_s,
+                                        force_freq ? force_freq : pref_freq);
 
        wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
 
@@ -4034,65 +6173,6 @@ int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 }
 
 
-#if defined(TIZEN_EXT_ENROLLEE) && defined(CONFIG_WPS)
-int wpas_p2p_wps_enrollee(struct wpa_supplicant *wpa_s, const u8 *peer_addr, const char *pin, int wps_method)
-{
-       struct wpa_supplicant *group;
-       struct p2p_device *dev;
-       struct p2p_go_neg_results res;
-       u8 dev_addr[ETH_ALEN];
-       //u8 iface_addr[ETH_ALEN];
-
-       wpa_s->p2p_wps_method = wps_method;
-
-       if (pin)
-               os_strlcpy(wpa_s->p2p_pin, pin, sizeof(wpa_s->p2p_pin));
-
-       dev = p2p_get_device(wpa_s->global->p2p, peer_addr);
-       if (dev == NULL) {
-               wpa_printf(MSG_DEBUG, "P2P: Peer address is not found");
-               return -1;
-       }
-       os_memcpy(wpa_s->pending_join_iface_addr, dev->interface_addr, ETH_ALEN);
-       os_memcpy(wpa_s->pending_join_dev_addr, peer_addr, ETH_ALEN);
-       wpa_s->pending_join_wps_method = wps_method;
-
-       group = wpas_p2p_get_group_iface(wpa_s, 0, 0);
-       if (group == NULL)
-               return -1;
-       if (group != wpa_s) {
-               os_memcpy(group->p2p_pin, wpa_s->p2p_pin,
-                         sizeof(group->p2p_pin));
-               group->p2p_wps_method = wpa_s->p2p_wps_method;
-       }
-
-       group->p2p_in_provisioning = 1;
-
-       wpas_p2p_stop_find(wpa_s);
-
-       os_memset(&res, 0, sizeof(res));
-       os_memcpy(res.peer_interface_addr, wpa_s->pending_join_iface_addr,
-                 ETH_ALEN);
-       res.wps_method = wpa_s->pending_join_wps_method;
-       wpa_printf(MSG_DEBUG, "P2P: WPS Enrollee start for group (iface "
-                  MACSTR " dev " MACSTR ")",
-                  MAC2STR(dev->interface_addr), MAC2STR(dev_addr));
-       wpas_start_wps_enrollee(group, &res);
-
-       /*
-        * Allow a longer timeout for join-a-running-group than normal 15
-        * second group formation timeout since the GO may not have authorized
-        * our connection yet.
-        */
-       eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s, NULL);
-       eloop_register_timeout(60, 0, wpas_p2p_group_formation_timeout,
-                              wpa_s, NULL);
-
-       return 0;
-}
-#endif /* TIZEN_EXT_ENROLLEE && CONFIG_WPS */
-
-
 /**
  * wpas_p2p_remain_on_channel_cb - Indication of remain-on-channel start
  * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
@@ -4107,24 +6187,28 @@ void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
 {
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return;
-       if (wpa_s->off_channel_freq == wpa_s->pending_listen_freq) {
+       wpa_printf(MSG_DEBUG, "P2P: remain-on-channel callback (off_channel_freq=%u pending_listen_freq=%d roc_waiting_drv_freq=%d freq=%u duration=%u)",
+                  wpa_s->off_channel_freq, wpa_s->pending_listen_freq,
+                  wpa_s->roc_waiting_drv_freq, freq, duration);
+       if (wpa_s->off_channel_freq &&
+           wpa_s->off_channel_freq == wpa_s->pending_listen_freq) {
                p2p_listen_cb(wpa_s->global->p2p, wpa_s->pending_listen_freq,
                              wpa_s->pending_listen_duration);
                wpa_s->pending_listen_freq = 0;
+       } else {
+               wpa_printf(MSG_DEBUG, "P2P: Ignore remain-on-channel callback (off_channel_freq=%u pending_listen_freq=%d freq=%u duration=%u)",
+                          wpa_s->off_channel_freq, wpa_s->pending_listen_freq,
+                          freq, duration);
        }
 }
 
 
-static int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s,
-                                unsigned int timeout)
+int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s, unsigned int timeout)
 {
        /* Limit maximum Listen state time based on driver limitation. */
        if (timeout > wpa_s->max_remain_on_chan)
                timeout = wpa_s->max_remain_on_chan;
 
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-               return wpa_drv_p2p_listen(wpa_s, timeout);
-
        return p2p_listen(wpa_s->global->p2p, timeout);
 }
 
@@ -4144,17 +6228,24 @@ void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
        wpa_printf(MSG_DEBUG, "P2P: Cancel remain-on-channel callback "
                   "(p2p_long_listen=%d ms pending_action_tx=%p)",
                   wpa_s->p2p_long_listen, offchannel_pending_action_tx(wpa_s));
+       wpas_p2p_listen_work_done(wpa_s);
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return;
+       if (wpa_s->p2p_long_listen > 0)
+               wpa_s->p2p_long_listen -= wpa_s->max_remain_on_chan;
        if (p2p_listen_end(wpa_s->global->p2p, freq) > 0)
                return; /* P2P module started a new operation */
        if (offchannel_pending_action_tx(wpa_s))
                return;
-       if (wpa_s->p2p_long_listen > 0)
-               wpa_s->p2p_long_listen -= wpa_s->max_remain_on_chan;
        if (wpa_s->p2p_long_listen > 0) {
                wpa_printf(MSG_DEBUG, "P2P: Continuing long Listen state");
                wpas_p2p_listen_start(wpa_s, wpa_s->p2p_long_listen);
+       } else {
+               /*
+                * When listen duration is over, stop listen & update p2p_state
+                * to IDLE.
+                */
+               p2p_stop_listen(wpa_s->global->p2p);
        }
 }
 
@@ -4175,6 +6266,7 @@ void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
 int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname)
 {
        struct wpa_global *global = wpa_s->global;
+       struct wpa_supplicant *calling_wpa_s = wpa_s;
 
        if (os_strcmp(ifname, "*") == 0) {
                struct wpa_supplicant *prev;
@@ -4182,7 +6274,11 @@ int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname)
                while (wpa_s) {
                        prev = wpa_s;
                        wpa_s = wpa_s->next;
-                       wpas_p2p_disconnect(prev);
+                       if (prev->p2p_group_interface !=
+                           NOT_P2P_GROUP_INTERFACE ||
+                           (prev->current_ssid &&
+                            prev->current_ssid->p2p_group))
+                               wpas_p2p_disconnect_safely(prev, calling_wpa_s);
                }
                return 0;
        }
@@ -4192,7 +6288,7 @@ int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname)
                        break;
        }
 
-       return wpas_p2p_disconnect(wpa_s);
+       return wpas_p2p_disconnect_safely(wpa_s, calling_wpa_s);
 }
 
 
@@ -4204,13 +6300,14 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq)
                wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 2.4 GHz "
                           "band");
                if (wpa_s->best_24_freq > 0 &&
-                   p2p_supported_freq(wpa_s->global->p2p,
-                                      wpa_s->best_24_freq)) {
+                   p2p_supported_freq_go(wpa_s->global->p2p,
+                                         wpa_s->best_24_freq)) {
                        freq = wpa_s->best_24_freq;
                        wpa_printf(MSG_DEBUG, "P2P: Use best 2.4 GHz band "
                                   "channel: %d MHz", freq);
                } else {
-                       os_get_random((u8 *) &r, sizeof(r));
+                       if (os_get_random((u8 *) &r, sizeof(r)) < 0)
+                               return -1;
                        freq = 2412 + (r % 3) * 25;
                        wpa_printf(MSG_DEBUG, "P2P: Use random 2.4 GHz band "
                                   "channel: %d MHz", freq);
@@ -4221,15 +6318,16 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq)
                wpa_printf(MSG_DEBUG, "P2P: Request to start GO on 5 GHz "
                           "band");
                if (wpa_s->best_5_freq > 0 &&
-                   p2p_supported_freq(wpa_s->global->p2p,
+                   p2p_supported_freq_go(wpa_s->global->p2p,
                                       wpa_s->best_5_freq)) {
                        freq = wpa_s->best_5_freq;
                        wpa_printf(MSG_DEBUG, "P2P: Use best 5 GHz band "
                                   "channel: %d MHz", freq);
                } else {
-                       os_get_random((u8 *) &r, sizeof(r));
+                       if (os_get_random((u8 *) &r, sizeof(r)) < 0)
+                               return -1;
                        freq = 5180 + (r % 4) * 20;
-                       if (!p2p_supported_freq(wpa_s->global->p2p, freq)) {
+                       if (!p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
                                wpa_printf(MSG_DEBUG, "P2P: Could not select "
                                           "5 GHz channel for P2P group");
                                return -1;
@@ -4239,7 +6337,18 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq)
                }
        }
 
-       if (freq > 0 && !p2p_supported_freq(wpa_s->global->p2p, freq)) {
+       if (freq > 0 && !p2p_supported_freq_go(wpa_s->global->p2p, freq)) {
+               if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+                   ieee80211_is_dfs(freq)) {
+                       /*
+                        * If freq is a DFS channel and DFS is offloaded to the
+                        * driver, allow P2P GO to use it.
+                        */
+                       wpa_printf(MSG_DEBUG, "P2P: "
+                                  "%s: The forced channel for GO (%u MHz) is DFS, and DFS is offloaded",
+                                  __func__, freq);
+                       return freq;
+               }
                wpa_printf(MSG_DEBUG, "P2P: The forced channel for GO "
                           "(%u MHz) is not supported for P2P uses",
                           freq);
@@ -4250,18 +6359,89 @@ static int wpas_p2p_select_go_freq(struct wpa_supplicant *wpa_s, int freq)
 }
 
 
+static int wpas_p2p_select_freq_no_pref(struct wpa_supplicant *wpa_s,
+                                       struct p2p_go_neg_results *params,
+                                       const struct p2p_channels *channels)
+{
+       unsigned int i, r;
+
+       /* first try some random selection of the social channels */
+       if (os_get_random((u8 *) &r, sizeof(r)) < 0)
+               return -1;
+
+       for (i = 0; i < 3; i++) {
+               params->freq = 2412 + ((r + i) % 3) * 25;
+               if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
+                   freq_included(channels, params->freq) &&
+                   p2p_supported_freq(wpa_s->global->p2p, params->freq))
+                       goto out;
+       }
+
+       /* try all channels in reg. class 81 */
+       for (i = 0; i < 11; i++) {
+               params->freq = 2412 + i * 5;
+               if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
+                   freq_included(channels, params->freq) &&
+                   p2p_supported_freq(wpa_s->global->p2p, params->freq))
+                       goto out;
+       }
+
+       /* try all channels in operating class 115 */
+       for (i = 0; i < 4; i++) {
+               params->freq = 5180 + i * 20;
+               if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
+                   freq_included(channels, params->freq) &&
+                   p2p_supported_freq(wpa_s->global->p2p, params->freq))
+                       goto out;
+       }
+
+       /* try all channels in operating class 124 */
+       for (i = 0; i < 4; i++) {
+               params->freq = 5745 + i * 20;
+               if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
+                   freq_included(channels, params->freq) &&
+                   p2p_supported_freq(wpa_s->global->p2p, params->freq))
+                       goto out;
+       }
+
+       /* try social channel class 180 channel 2 */
+       params->freq = 58320 + 1 * 2160;
+       if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
+           freq_included(channels, params->freq) &&
+           p2p_supported_freq(wpa_s->global->p2p, params->freq))
+               goto out;
+
+       /* try all channels in reg. class 180 */
+       for (i = 0; i < 4; i++) {
+               params->freq = 58320 + i * 2160;
+               if (!wpas_p2p_disallowed_freq(wpa_s->global, params->freq) &&
+                   freq_included(channels, params->freq) &&
+                   p2p_supported_freq(wpa_s->global->p2p, params->freq))
+                       goto out;
+       }
+
+       wpa_printf(MSG_DEBUG, "P2P: No 2.4, 5, or 60 GHz channel allowed");
+       return -1;
+out:
+       wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference known)",
+                  params->freq);
+       return 0;
+}
+
+
 static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
                                   struct p2p_go_neg_results *params,
-                                  int freq, int ht40,
+                                  int freq, int ht40, int vht,
                                   const struct p2p_channels *channels)
 {
-       u8 bssid[ETH_ALEN];
-       int res;
-       unsigned int pref_freq;
+       struct wpa_used_freq_data *freqs;
+       unsigned int pref_freq, cand_freq;
+       unsigned int num, i;
 
        os_memset(params, 0, sizeof(*params));
        params->role_go = 1;
        params->ht40 = ht40;
+       params->vht = vht;
        if (freq) {
                if (!freq_included(channels, freq)) {
                        wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not "
@@ -4292,24 +6472,24 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
                           "frequency %d MHz", params->freq);
        } else if (wpa_s->conf->p2p_oper_channel == 0 &&
                   wpa_s->best_overall_freq > 0 &&
-                  p2p_supported_freq(wpa_s->global->p2p,
-                                     wpa_s->best_overall_freq) &&
+                  p2p_supported_freq_go(wpa_s->global->p2p,
+                                        wpa_s->best_overall_freq) &&
                   freq_included(channels, wpa_s->best_overall_freq)) {
                params->freq = wpa_s->best_overall_freq;
                wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best overall "
                           "channel %d MHz", params->freq);
        } else if (wpa_s->conf->p2p_oper_channel == 0 &&
                   wpa_s->best_24_freq > 0 &&
-                  p2p_supported_freq(wpa_s->global->p2p,
-                                     wpa_s->best_24_freq) &&
+                  p2p_supported_freq_go(wpa_s->global->p2p,
+                                        wpa_s->best_24_freq) &&
                   freq_included(channels, wpa_s->best_24_freq)) {
                params->freq = wpa_s->best_24_freq;
                wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 2.4 GHz "
                           "channel %d MHz", params->freq);
        } else if (wpa_s->conf->p2p_oper_channel == 0 &&
                   wpa_s->best_5_freq > 0 &&
-                  p2p_supported_freq(wpa_s->global->p2p,
-                                     wpa_s->best_5_freq) &&
+                  p2p_supported_freq_go(wpa_s->global->p2p,
+                                        wpa_s->best_5_freq) &&
                   freq_included(channels, wpa_s->best_5_freq)) {
                params->freq = wpa_s->best_5_freq;
                wpa_printf(MSG_DEBUG, "P2P: Set GO freq based on best 5 GHz "
@@ -4320,81 +6500,63 @@ static int wpas_p2p_init_go_params(struct wpa_supplicant *wpa_s,
                wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz from preferred "
                           "channels", params->freq);
        } else {
-               int chan;
-               for (chan = 0; chan < 11; chan++) {
-                       params->freq = 2412 + chan * 5;
-                       if (!wpas_p2p_disallowed_freq(wpa_s->global,
-                                                     params->freq) &&
-                           freq_included(channels, params->freq))
-                               break;
-               }
-               if (chan == 11) {
-                       wpa_printf(MSG_DEBUG, "P2P: No 2.4 GHz channel "
-                                  "allowed");
+               /* no preference, select some channel */
+               if (wpas_p2p_select_freq_no_pref(wpa_s, params, channels) < 0)
                        return -1;
-               }
-               wpa_printf(MSG_DEBUG, "P2P: Set GO freq %d MHz (no preference "
-                          "known)", params->freq);
        }
 
-       if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid) == 0 &&
-           wpa_s->assoc_freq && !freq) {
-               if (!p2p_supported_freq(wpa_s->global->p2p, wpa_s->assoc_freq)
-                   || !freq_included(channels, wpa_s->assoc_freq)) {
-                       if (wpa_s->drv_flags &
-                           WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT) {
-                               wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on "
-                                          "the channel we are already using "
-                                          "(%u MHz) - allow multi-channel "
-                                          "concurrency", wpa_s->assoc_freq);
-                       } else {
-                               wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on "
-                                          "the channel we are already using "
-                                          "(%u MHz)", wpa_s->assoc_freq);
+       freqs = os_calloc(wpa_s->num_multichan_concurrent,
+                         sizeof(struct wpa_used_freq_data));
+       if (!freqs)
+               return -1;
+
+       num = wpas_p2p_valid_oper_freqs(wpa_s, freqs,
+                                       wpa_s->num_multichan_concurrent);
+
+       cand_freq = wpas_p2p_pick_best_used_freq(wpa_s, freqs, num);
+
+       /* First try the best used frequency if possible */
+       if (!freq && cand_freq > 0 && freq_included(channels, cand_freq)) {
+               params->freq = cand_freq;
+       } else if (!freq) {
+               /* Try any of the used frequencies */
+               for (i = 0; i < num; i++) {
+                       if (freq_included(channels, freqs[i].freq)) {
+                               wpa_printf(MSG_DEBUG, "P2P: Force GO on a channel we are already using (%u MHz)",
+                                          freqs[i].freq);
+                               params->freq = freqs[i].freq;
+                               break;
+                       }
+               }
+
+               if (i == num) {
+                       if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
+                               wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using");
+                               os_free(freqs);
                                return -1;
+                       } else {
+                               wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on any of the channels we are already using. Use one of the free channels");
                        }
-               } else {
-                       wpa_printf(MSG_DEBUG, "P2P: Force GO on the channel we "
-                                  "are already using (%u MHz)",
-                                  wpa_s->assoc_freq);
-                       params->freq = wpa_s->assoc_freq;
-               }
-       }
-
-       res = wpa_drv_shared_freq(wpa_s);
-       if (res > 0 && !freq &&
-           (!p2p_supported_freq(wpa_s->global->p2p, res) ||
-            !freq_included(channels, res))) {
-               if (wpa_s->drv_flags &
-                   WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT) {
-                       wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on the "
-                                  "channel we are already using on a shared "
-                                  "interface (%u MHz) - allow multi-channel "
-                                  "concurrency", res);
-               } else {
-                       wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on the "
-                                  "channel we are already using on a shared "
-                                  "interface (%u MHz)", res);
-                       return -1;
                }
-       } else if (res > 0 && !freq) {
-               wpa_printf(MSG_DEBUG, "P2P: Force GO on the channel we are "
-                          "already using on a shared interface");
-               params->freq = res;
-               if (!freq_included(channels, params->freq)) {
-                       wpa_printf(MSG_DEBUG, "P2P: Forced GO freq %d MHz not "
-                                  "accepted", params->freq);
-                       return -1;
+       } else {
+               for (i = 0; i < num; i++) {
+                       if (freqs[i].freq == freq)
+                               break;
+               }
+
+               if (i == num) {
+                       if (wpas_p2p_num_unused_channels(wpa_s) <= 0) {
+                               if (freq)
+                                       wpa_printf(MSG_DEBUG, "P2P: Cannot force GO on freq (%u MHz) as all the channels are in use", freq);
+                               os_free(freqs);
+                               return -1;
+                       } else {
+                               wpa_printf(MSG_DEBUG, "P2P: Use one of the free channels");
+                       }
                }
-       } else if (res > 0 && freq != res &&
-                  !(wpa_s->drv_flags &
-                    WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT)) {
-               wpa_printf(MSG_DEBUG, "P2P: Cannot start P2P group on %u MHz "
-                          "while connected on another channel (%u MHz)",
-                          freq, res);
-               return -1;
        }
 
+       os_free(freqs);
        return 0;
 }
 
@@ -4408,6 +6570,7 @@ wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
        if (!wpas_p2p_create_iface(wpa_s)) {
                wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use same interface for group "
                        "operations");
+               wpa_s->p2p_first_connection_timeout = 0;
                return wpa_s;
        }
 
@@ -4427,6 +6590,7 @@ wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
 
        wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use separate group interface %s",
                group_wpa_s->ifname);
+       group_wpa_s->p2p_first_connection_timeout = 0;
        return group_wpa_s;
 }
 
@@ -4436,19 +6600,24 @@ wpas_p2p_get_group_iface(struct wpa_supplicant *wpa_s, int addr_allocated,
  * @wpa_s: Pointer to wpa_supplicant data from wpa_supplicant_add_iface()
  * @persistent_group: Whether to create a persistent group
  * @freq: Frequency for the group or 0 to indicate no hardcoding
+ * @ht40: Start GO with 40 MHz channel width
+ * @vht:  Start GO with VHT support
  * Returns: 0 on success, -1 on failure
  *
  * This function creates a new P2P group with the local end as the Group Owner,
  * i.e., without using Group Owner Negotiation.
  */
 int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
-                      int freq, int ht40)
+                      int freq, int ht40, int vht, char *passphrase)
 {
        struct p2p_go_neg_results params;
 
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return -1;
 
+       os_free(wpa_s->global->add_psk);
+       wpa_s->global->add_psk = NULL;
+
        /* Make sure we are not running find during connection establishment */
        wpa_printf(MSG_DEBUG, "P2P: Stop any on-going P2P FIND");
        wpas_p2p_stop_find_oper(wpa_s);
@@ -4457,18 +6626,32 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
        if (freq < 0)
                return -1;
 
-       if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40, NULL))
+       if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40, vht, NULL))
                return -1;
        if (params.freq &&
-           !p2p_supported_freq(wpa_s->global->p2p, params.freq)) {
-               wpa_printf(MSG_DEBUG, "P2P: The selected channel for GO "
-                          "(%u MHz) is not supported for P2P uses",
-                          params.freq);
-               return -1;
+           !p2p_supported_freq_go(wpa_s->global->p2p, params.freq)) {
+               if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_DFS_OFFLOAD) &&
+                   ieee80211_is_dfs(params.freq)) {
+                       /*
+                        * If freq is a DFS channel and DFS is offloaded to the
+                        * driver, allow P2P GO to use it.
+                        */
+                       wpa_printf(MSG_DEBUG,
+                                  "P2P: %s: The forced channel for GO (%u MHz) is DFS, and DFS is offloaded to driver",
+                               __func__, params.freq);
+               } else {
+                       wpa_printf(MSG_DEBUG,
+                                  "P2P: The selected channel for GO (%u MHz) is not supported for P2P uses",
+                                  params.freq);
+                       return -1;
+               }
        }
        p2p_go_params(wpa_s->global->p2p, &params);
        params.persistent_group = persistent_group;
 
+       if(passphrase != NULL) {
+               os_strlcpy(params.passphrase, passphrase, 64);
+       }
        wpa_s = wpas_p2p_get_group_iface(wpa_s, 0, 1);
        if (wpa_s == NULL)
                return -1;
@@ -4479,13 +6662,15 @@ int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
 
 
 static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s,
-                                struct wpa_ssid *params, int addr_allocated)
+                                struct wpa_ssid *params, int addr_allocated,
+                                int freq)
 {
        struct wpa_ssid *ssid;
 
        wpa_s = wpas_p2p_get_group_iface(wpa_s, addr_allocated, 0);
        if (wpa_s == NULL)
                return -1;
+       wpa_s->p2p_last_4way_hs_fail = NULL;
 
        wpa_supplicant_ap_deinit(wpa_s);
 
@@ -4514,9 +6699,16 @@ static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s,
        if (params->passphrase)
                ssid->passphrase = os_strdup(params->passphrase);
 
-       wpa_supplicant_select_network(wpa_s, ssid);
-
        wpa_s->show_group_started = 1;
+       wpa_s->p2p_in_invitation = 1;
+       wpa_s->p2p_invite_go_freq = freq;
+
+       eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent,
+                            NULL);
+       eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
+                              wpas_p2p_group_formation_timeout,
+                              wpa_s->parent, NULL);
+       wpa_supplicant_select_network(wpa_s, ssid);
 
        return 0;
 }
@@ -4524,11 +6716,12 @@ static int wpas_start_p2p_client(struct wpa_supplicant *wpa_s,
 
 int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
                                  struct wpa_ssid *ssid, int addr_allocated,
-                                 int freq, int ht40,
-                                 const struct p2p_channels *channels)
+                                 int force_freq, int neg_freq, int ht40,
+                                 int vht, const struct p2p_channels *channels,
+                                 int connection_timeout)
 {
        struct p2p_go_neg_results params;
-       int go = 0;
+       int go = 0, freq;
 
        if (ssid->disabled != 2 || ssid->ssid == NULL)
                return -1;
@@ -4540,22 +6733,39 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
                return 0;
        }
 
+       os_free(wpa_s->global->add_psk);
+       wpa_s->global->add_psk = NULL;
+
        /* Make sure we are not running find during connection establishment */
        wpas_p2p_stop_find_oper(wpa_s);
 
        wpa_s->p2p_fallback_to_go_neg = 0;
 
+       if (ssid->mode == WPAS_MODE_P2P_GO) {
+               if (force_freq > 0) {
+                       freq = wpas_p2p_select_go_freq(wpa_s, force_freq);
+                       if (freq < 0)
+                               return -1;
+               } else {
+                       freq = wpas_p2p_select_go_freq(wpa_s, neg_freq);
+                       if (freq < 0 ||
+                           (freq > 0 && !freq_included(channels, freq)))
+                               freq = 0;
+               }
+       } else {
+               freq = neg_freq;
+               if (freq < 0 ||
+                   (freq > 0 && !freq_included(channels, freq)))
+                       freq = 0;
+       }
+
        if (ssid->mode == WPAS_MODE_INFRA)
-               return wpas_start_p2p_client(wpa_s, ssid, addr_allocated);
+               return wpas_start_p2p_client(wpa_s, ssid, addr_allocated, freq);
 
        if (ssid->mode != WPAS_MODE_P2P_GO)
                return -1;
 
-       freq = wpas_p2p_select_go_freq(wpa_s, freq);
-       if (freq < 0)
-               return -1;
-
-       if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40, channels))
+       if (wpas_p2p_init_go_params(wpa_s, &params, freq, ht40, vht, channels))
                return -1;
 
        params.role_go = 1;
@@ -4579,6 +6789,9 @@ int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
        if (wpa_s == NULL)
                return -1;
 
+       p2p_channels_to_freqs(channels, params.freq_list, P2P_MAX_CHANNELS);
+
+       wpa_s->p2p_first_connection_timeout = connection_timeout;
        wpas_start_wps_go(wpa_s, &params, 0);
 
        return 0;
@@ -4616,9 +6829,14 @@ static void wpas_p2p_idle_update(void *ctx, int idle)
        if (!wpa_s->ap_iface)
                return;
        wpa_printf(MSG_DEBUG, "P2P: GO - group %sidle", idle ? "" : "not ");
-       if (idle)
+       if (idle) {
+               if (wpa_s->global->p2p_fail_on_wps_complete &&
+                   wpa_s->p2p_in_provisioning) {
+                       wpas_p2p_grpform_fail_after_wps(wpa_s);
+                       return;
+               }
                wpas_p2p_set_group_idle_timeout(wpa_s);
-       else
+       else
                eloop_cancel_timeout(wpas_p2p_group_idle_timeout, wpa_s, NULL);
 }
 
@@ -4629,8 +6847,6 @@ struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
        struct p2p_group *group;
        struct p2p_group_config *cfg;
 
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-               return NULL;
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return NULL;
 
@@ -4650,6 +6866,7 @@ struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
                cfg->max_clients = wpa_s->conf->max_num_sta;
        os_memcpy(cfg->ssid, ssid->ssid, ssid->ssid_len);
        cfg->ssid_len = ssid->ssid_len;
+       cfg->freq = ssid->frequency;
        cfg->cb_ctx = wpa_s;
        cfg->ie_update = wpas_p2p_ie_update;
        cfg->idle_update = wpas_p2p_idle_update;
@@ -4686,6 +6903,7 @@ void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
 
        eloop_cancel_timeout(wpas_p2p_group_formation_timeout, wpa_s->parent,
                             NULL);
+       wpa_s->p2p_go_group_formation_completed = 1;
        if (ssid && ssid->mode == WPAS_MODE_INFRA) {
                /*
                 * Use a separate timeout for initial data connection to
@@ -4693,14 +6911,31 @@ void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
                 * something goes wrong in this step before the P2P group idle
                 * timeout mechanism is taken into use.
                 */
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "P2P: Re-start group formation timeout (%d seconds) as client for initial connection",
+                       P2P_MAX_INITIAL_CONN_WAIT);
                eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT, 0,
                                       wpas_p2p_group_formation_timeout,
                                       wpa_s->parent, NULL);
+       } else if (ssid) {
+               /*
+                * Use a separate timeout for initial data connection to
+                * complete to allow the group to be removed automatically if
+                * the client does not complete data connection successfully.
+                */
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "P2P: Re-start group formation timeout (%d seconds) as GO for initial connection",
+                       P2P_MAX_INITIAL_CONN_WAIT_GO);
+               eloop_register_timeout(P2P_MAX_INITIAL_CONN_WAIT_GO, 0,
+                                      wpas_p2p_group_formation_timeout,
+                                      wpa_s->parent, NULL);
+               /*
+                * Complete group formation on first successful data connection
+                */
+               wpa_s->p2p_go_group_formation_completed = 0;
        }
        if (wpa_s->global->p2p)
                p2p_wps_success_cb(wpa_s->global->p2p, peer_addr);
-       else if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-               wpa_drv_wps_success_cb(wpa_s, peer_addr);
        wpas_group_formation_completed(wpa_s, 1);
 }
 
@@ -4721,18 +6956,55 @@ void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
        }
 
        wpas_notify_p2p_wps_failed(wpa_s, fail);
+
+       if (wpa_s == wpa_s->global->p2p_group_formation) {
+               /*
+                * Allow some time for the failed WPS negotiation exchange to
+                * complete, but remove the group since group formation cannot
+                * succeed after provisioning failure.
+                */
+               wpa_printf(MSG_DEBUG, "P2P: WPS step failed during group formation - reject connection from timeout");
+               wpa_s->global->p2p_fail_on_wps_complete = 1;
+               eloop_deplete_timeout(0, 50000,
+                                     wpas_p2p_group_formation_timeout,
+                                     wpa_s->parent, NULL);
+       }
+}
+
+
+int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s)
+{
+       if (!wpa_s->global->p2p_fail_on_wps_complete ||
+           !wpa_s->p2p_in_provisioning)
+               return 0;
+
+       wpas_p2p_grpform_fail_after_wps(wpa_s);
+
+       return 1;
 }
 
 
 int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
                       const char *config_method,
-                      enum wpas_p2p_prov_disc_use use)
+                      enum wpas_p2p_prov_disc_use use,
+                      struct p2ps_provision *p2ps_prov)
 {
        u16 config_methods;
 
+       wpa_s->global->pending_p2ps_group = 0;
        wpa_s->p2p_fallback_to_go_neg = 0;
        wpa_s->pending_pd_use = NORMAL_PD;
-       if (os_strncmp(config_method, "display", 7) == 0)
+       if (p2ps_prov && use == WPAS_P2P_PD_FOR_ASP) {
+               p2ps_prov->conncap = p2ps_group_capability(
+                       wpa_s, P2PS_SETUP_NONE, p2ps_prov->role);
+               wpa_printf(MSG_DEBUG,
+                          "P2P: %s conncap: %d - ASP parsed: %x %x %d %s",
+                          __func__, p2ps_prov->conncap,
+                          p2ps_prov->adv_id, p2ps_prov->conncap,
+                          p2ps_prov->status, p2ps_prov->info);
+
+               config_methods = 0;
+       } else if (os_strncmp(config_method, "display", 7) == 0)
                config_methods = WPS_CONFIG_DISPLAY;
        else if (os_strncmp(config_method, "keypad", 6) == 0)
                config_methods = WPS_CONFIG_KEYPAD;
@@ -4741,6 +7013,7 @@ int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
                config_methods = WPS_CONFIG_PUSHBUTTON;
        else {
                wpa_printf(MSG_DEBUG, "P2P: Unknown config method");
+               os_free(p2ps_prov);
                return -1;
        }
 
@@ -4753,7 +7026,7 @@ int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
                wpa_s->auto_pd_scan_retry = 0;
                wpas_p2p_stop_find(wpa_s);
                wpa_s->p2p_join_scan_count = 0;
-               os_get_time(&wpa_s->p2p_auto_started);
+               os_get_reltime(&wpa_s->p2p_auto_started);
                wpa_printf(MSG_DEBUG, "P2P: Auto PD started at %ld.%06ld",
                           wpa_s->p2p_auto_started.sec,
                           wpa_s->p2p_auto_started.usec);
@@ -4761,16 +7034,12 @@ int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
                return 0;
        }
 
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
-               return wpa_drv_p2p_prov_disc_req(wpa_s, peer_addr,
-                                                config_methods,
-                                                use == WPAS_P2P_PD_FOR_JOIN);
-       }
-
-       if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled)
+       if (wpa_s->global->p2p == NULL || wpa_s->global->p2p_disabled) {
+               os_free(p2ps_prov);
                return -1;
+       }
 
-       return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr,
+       return p2p_prov_disc_req(wpa_s->global->p2p, peer_addr, p2ps_prov,
                                 config_methods, use == WPAS_P2P_PD_FOR_JOIN,
                                 0, 1);
 }
@@ -4788,6 +7057,8 @@ static void wpas_p2p_clear_pending_action_tx(struct wpa_supplicant *wpa_s)
        if (!offchannel_pending_action_tx(wpa_s))
                return;
 
+       wpas_p2p_action_tx_clear(wpa_s);
+
        wpa_printf(MSG_DEBUG, "P2P: Drop pending Action TX due to new "
                   "operation request");
        offchannel_clear_pending_action_tx(wpa_s);
@@ -4797,14 +7068,12 @@ static void wpas_p2p_clear_pending_action_tx(struct wpa_supplicant *wpa_s)
 int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
                  enum p2p_discovery_type type,
                  unsigned int num_req_dev_types, const u8 *req_dev_types,
-                 const u8 *dev_id, unsigned int search_delay)
+                 const u8 *dev_id, unsigned int search_delay,
+                 u8 seek_cnt, const char **seek_string, int freq)
 {
        wpas_p2p_clear_pending_action_tx(wpa_s);
        wpa_s->p2p_long_listen = 0;
 
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-               return wpa_drv_p2p_find(wpa_s, timeout, type);
-
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL ||
            wpa_s->p2p_in_provisioning)
                return -1;
@@ -4813,35 +7082,55 @@ int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
 
        return p2p_find(wpa_s->global->p2p, timeout, type,
                        num_req_dev_types, req_dev_types, dev_id,
-                       search_delay);
+                       search_delay, seek_cnt, seek_string, freq);
+}
+
+
+static void wpas_p2p_scan_res_ignore_search(struct wpa_supplicant *wpa_s,
+                                           struct wpa_scan_results *scan_res)
+{
+       wpa_printf(MSG_DEBUG, "P2P: Ignore scan results");
+
+       if (wpa_s->p2p_scan_work) {
+               struct wpa_radio_work *work = wpa_s->p2p_scan_work;
+               wpa_s->p2p_scan_work = NULL;
+               radio_work_done(work);
+       }
+
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+               return;
+
+       /*
+        * Indicate that results have been processed so that the P2P module can
+        * continue pending tasks.
+        */
+       p2p_scan_res_handled(wpa_s->global->p2p);
 }
 
 
-static int wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s)
+static void wpas_p2p_stop_find_oper(struct wpa_supplicant *wpa_s)
 {
        wpas_p2p_clear_pending_action_tx(wpa_s);
        wpa_s->p2p_long_listen = 0;
        eloop_cancel_timeout(wpas_p2p_long_listen_timeout, wpa_s, NULL);
        eloop_cancel_timeout(wpas_p2p_join_scan, wpa_s, NULL);
-       wpa_s->global->p2p_cb_on_scan_complete = 0;
-
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT) {
-               wpa_drv_p2p_stop_find(wpa_s);
-               return 1;
-       }
 
        if (wpa_s->global->p2p)
                p2p_stop_find(wpa_s->global->p2p);
 
-       return 0;
+       if (wpa_s->scan_res_handler == wpas_p2p_scan_res_handler) {
+               wpa_printf(MSG_DEBUG,
+                          "P2P: Do not consider the scan results after stop_find");
+               wpa_s->scan_res_handler = wpas_p2p_scan_res_ignore_search;
+       }
 }
 
 
 void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s)
 {
-       if (wpas_p2p_stop_find_oper(wpa_s) > 0)
-               return;
-       wpas_p2p_remove_pending_group_interface(wpa_s);
+       wpas_p2p_stop_find_oper(wpa_s);
+       if (!wpa_s->global->pending_group_iface_for_p2ps)
+               wpas_p2p_remove_pending_group_interface(wpa_s);
 }
 
 
@@ -4901,16 +7190,14 @@ int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
 
        if (wpa_s->global->p2p_disabled)
                return -1;
+       if (wpa_s->conf->p2p_disabled)
+               return -1;
        if (wpa_s->global->p2p == NULL)
                return -1;
        if (bss == NULL)
                return -1;
 
        p2p_ie = wpa_bss_get_vendor_ie_multi(bss, P2P_IE_VENDOR_TYPE);
-#ifdef TIZEN_EXT_P2P
-       if (p2p_ie == NULL)
-               return -1;
-#endif
        ret = p2p_assoc_req_ie(wpa_s->global->p2p, bss->bssid, buf, len,
                               p2p_group, p2p_ie);
        wpabuf_free(p2p_ie);
@@ -4970,7 +7257,7 @@ void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies)
 }
 
 
-void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s)
+static void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s)
 {
        p2p_group_deinit(wpa_s->p2p_group);
        wpa_s->p2p_group = NULL;
@@ -4986,9 +7273,6 @@ int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr)
 {
        wpa_s->p2p_long_listen = 0;
 
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-               return wpa_drv_p2p_reject(wpa_s, addr);
-
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return -1;
 
@@ -4996,31 +7280,16 @@ int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr)
 }
 
 
-#ifdef TIZEN_EXT_P2P
-int wpas_p2p_reject_connection(struct wpa_supplicant *wpa_s, const u8 *addr)
-{
-       wpa_s->p2p_long_listen = 0;
-
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-               return wpa_drv_p2p_reject(wpa_s, addr);
-
-       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
-               return -1;
-
-       return p2p_reject_connection(wpa_s->global->p2p, addr);
-}
-#endif
-
-
 /* Invite to reinvoke a persistent group */
 int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
                    struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
-                   int ht40, int pref_freq)
+                   int ht40, int vht, int pref_freq)
 {
        enum p2p_invite_role role;
-       u8 *bssid = NULL, bssid_buf[ETH_ALEN];
-       int force_freq = 0, oper_freq = 0;
+       u8 *bssid = NULL;
+       int force_freq = 0;
        int res;
+       int no_pref_freq_given = pref_freq == 0;
 
        wpa_s->global->p2p_invite_group = NULL;
        if (peer_addr)
@@ -5054,33 +7323,32 @@ int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
        }
        wpa_s->pending_invite_ssid_id = ssid->id;
 
-       if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid_buf) == 0 &&
-           wpa_s->assoc_freq) {
-               oper_freq = wpa_s->assoc_freq;
-               if (bssid == NULL)
-                       bssid = bssid_buf;
-       } else {
-               oper_freq = wpa_drv_shared_freq(wpa_s);
-               if (oper_freq < 0)
-                       oper_freq = 0;
-       }
-
        res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
-                                  &oper_freq);
+                                  role == P2P_INVITE_ROLE_GO);
        if (res)
                return res;
 
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-               return wpa_drv_p2p_invite(wpa_s, peer_addr, role, bssid,
-                                         ssid->ssid, ssid->ssid_len,
-                                         go_dev_addr, 1);
-
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return -1;
 
+       if (wpa_s->parent->conf->p2p_ignore_shared_freq &&
+           no_pref_freq_given && pref_freq > 0 &&
+           wpa_s->num_multichan_concurrent > 1 &&
+           wpas_p2p_num_unused_channels(wpa_s) > 0) {
+               wpa_printf(MSG_DEBUG, "P2P: Ignore own channel preference %d MHz for invitation due to p2p_ignore_shared_freq=1 configuration",
+                          pref_freq);
+               pref_freq = 0;
+       }
+
+       /*
+        * Stop any find/listen operations before invitation and possibly
+        * connection establishment.
+        */
+       wpas_p2p_stop_find_oper(wpa_s);
+
        return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
                          ssid->ssid, ssid->ssid_len, force_freq, go_dev_addr,
-                         1, pref_freq);
+                         1, pref_freq, -1);
 }
 
 
@@ -5090,14 +7358,15 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
 {
        struct wpa_global *global = wpa_s->global;
        enum p2p_invite_role role;
-       u8 *bssid = NULL, bssid_buf[ETH_ALEN];
+       u8 *bssid = NULL;
        struct wpa_ssid *ssid;
        int persistent;
-       int force_freq = 0, oper_freq = 0, pref_freq = 0;
+       int freq = 0, force_freq = 0, pref_freq = 0;
        int res;
 
        wpa_s->p2p_persistent_go_freq = 0;
        wpa_s->p2p_go_ht40 = 0;
+       wpa_s->p2p_go_vht = 0;
 
        for (wpa_s = global->ifaces; wpa_s; wpa_s = wpa_s->next) {
                if (os_strcmp(wpa_s->ifname, ifname) == 0)
@@ -5125,6 +7394,7 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
                bssid = wpa_s->own_addr;
                if (go_dev_addr == NULL)
                        go_dev_addr = wpa_s->global->p2p_dev_addr;
+               freq = ssid->frequency;
        } else {
                role = P2P_INVITE_ROLE_CLIENT;
                if (wpa_s->wpa_state < WPA_ASSOCIATED) {
@@ -5136,48 +7406,35 @@ int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
                if (go_dev_addr == NULL &&
                    !is_zero_ether_addr(wpa_s->go_dev_addr))
                        go_dev_addr = wpa_s->go_dev_addr;
+               freq = wpa_s->current_bss ? wpa_s->current_bss->freq :
+                       (int) wpa_s->assoc_freq;
        }
        wpa_s->parent->pending_invite_ssid_id = -1;
 
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-               return wpa_drv_p2p_invite(wpa_s, peer_addr, role, bssid,
-                                         ssid->ssid, ssid->ssid_len,
-                                         go_dev_addr, persistent);
-
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return -1;
 
-       if (wpa_s->current_ssid && wpa_drv_get_bssid(wpa_s, bssid_buf) == 0 &&
-           wpa_s->assoc_freq) {
-               oper_freq = wpa_s->assoc_freq;
-               if (bssid == NULL)
-                       bssid = bssid_buf;
-       } else {
-               oper_freq = wpa_drv_shared_freq(wpa_s);
-               if (oper_freq < 0)
-                       oper_freq = 0;
-       }
-
-       res = wpas_p2p_setup_freqs(wpa_s, 0, &force_freq, &pref_freq,
-                                  &oper_freq);
+       res = wpas_p2p_setup_freqs(wpa_s, freq, &force_freq, &pref_freq,
+                                  role == P2P_INVITE_ROLE_ACTIVE_GO);
        if (res)
                return res;
-       wpas_p2p_set_own_freq_preference(wpa_s, oper_freq);
+       wpas_p2p_set_own_freq_preference(wpa_s, force_freq);
 
        return p2p_invite(wpa_s->global->p2p, peer_addr, role, bssid,
                          ssid->ssid, ssid->ssid_len, force_freq,
-                         go_dev_addr, persistent, pref_freq);
+                         go_dev_addr, persistent, pref_freq, -1);
 }
 
 
 void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
 {
        struct wpa_ssid *ssid = wpa_s->current_ssid;
-       const char *ssid_txt;
        u8 go_dev_addr[ETH_ALEN];
        int network_id = -1;
        int persistent;
        int freq;
+       u8 ip[3 * 4];
+       char ip_addr[100];
 
        if (ssid == NULL || ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION) {
                eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
@@ -5185,11 +7442,10 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
        }
 
        if (!wpa_s->show_group_started || !ssid)
-               goto done;
+               return;
 
        wpa_s->show_group_started = 0;
 
-       ssid_txt = wpa_ssid_txt(ssid->ssid, ssid->ssid_len);
        os_memset(go_dev_addr, 0, ETH_ALEN);
        if (ssid->bssid_set)
                os_memcpy(go_dev_addr, ssid->bssid, ETH_ALEN);
@@ -5202,24 +7458,26 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
 
        freq = wpa_s->current_bss ? wpa_s->current_bss->freq :
                (int) wpa_s->assoc_freq;
-       if (ssid->passphrase == NULL && ssid->psk_set) {
-               char psk[65];
-               wpa_snprintf_hex(psk, sizeof(psk), ssid->psk, 32);
-               wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
-                              "%s client ssid=\"%s\" freq=%d psk=%s "
-                              "go_dev_addr=" MACSTR "%s",
-                              wpa_s->ifname, ssid_txt, freq, psk,
-                              MAC2STR(go_dev_addr),
-                              persistent ? " [PERSISTENT]" : "");
-       } else {
-               wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_GROUP_STARTED
-                              "%s client ssid=\"%s\" freq=%d "
-                              "passphrase=\"%s\" go_dev_addr=" MACSTR "%s",
-                              wpa_s->ifname, ssid_txt, freq,
-                              ssid->passphrase ? ssid->passphrase : "",
-                              MAC2STR(go_dev_addr),
-                              persistent ? " [PERSISTENT]" : "");
-       }
+
+       ip_addr[0] = '\0';
+       if (wpa_sm_get_p2p_ip_addr(wpa_s->wpa, ip) == 0) {
+               int res;
+
+               res = os_snprintf(ip_addr, sizeof(ip_addr),
+                                 " ip_addr=%u.%u.%u.%u "
+                                 "ip_mask=%u.%u.%u.%u go_ip_addr=%u.%u.%u.%u",
+                                 ip[0], ip[1], ip[2], ip[3],
+                                 ip[4], ip[5], ip[6], ip[7],
+                                 ip[8], ip[9], ip[10], ip[11]);
+               if (os_snprintf_error(sizeof(ip_addr), res))
+                       ip_addr[0] = '\0';
+       }
+
+       wpas_p2p_group_started(wpa_s, 0, ssid, freq,
+                              ssid->passphrase == NULL && ssid->psk_set ?
+                              ssid->psk : NULL,
+                              ssid->passphrase, go_dev_addr, persistent,
+                              ip_addr);
 
        if (persistent)
                network_id = wpas_p2p_store_persistent_group(wpa_s->parent,
@@ -5227,17 +7485,14 @@ void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
        if (network_id < 0)
                network_id = ssid->id;
        wpas_notify_p2p_group_started(wpa_s, ssid, network_id, 1);
-
-done:
-       wpas_p2p_continue_after_scan(wpa_s);
 }
 
 
 int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
                          u32 interval1, u32 duration2, u32 interval2)
 {
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-               return -1;
+       int ret;
+
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return -1;
 
@@ -5246,18 +7501,19 @@ int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
            wpa_s->current_ssid->mode != WPAS_MODE_INFRA)
                return -1;
 
-       return p2p_presence_req(wpa_s->global->p2p, wpa_s->bssid,
-                               wpa_s->own_addr, wpa_s->assoc_freq,
-                               duration1, interval1, duration2, interval2);
+       ret = p2p_presence_req(wpa_s->global->p2p, wpa_s->bssid,
+                              wpa_s->own_addr, wpa_s->assoc_freq,
+                              duration1, interval1, duration2, interval2);
+       if (ret == 0)
+               wpa_s->waiting_presence_resp = 1;
+
+       return ret;
 }
 
 
 int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
                        unsigned int interval)
 {
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-               return -1;
-
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return -1;
 
@@ -5359,8 +7615,6 @@ int wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
 {
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return 0;
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-               return 0;
 
        if (!locally_generated)
                p2p_deauth_notif(wpa_s->global->p2p, bssid, reason_code, ie,
@@ -5388,8 +7642,6 @@ void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
 {
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return;
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-               return;
 
        if (!locally_generated)
                p2p_disassoc_notif(wpa_s->global->p2p, bssid, reason_code, ie,
@@ -5466,20 +7718,27 @@ void wpas_p2p_update_config(struct wpa_supplicant *wpa_s)
                u8 reg_class, channel;
                int ret;
                unsigned int r;
+               u8 channel_forced;
+
                if (wpa_s->conf->p2p_listen_reg_class &&
                    wpa_s->conf->p2p_listen_channel) {
                        reg_class = wpa_s->conf->p2p_listen_reg_class;
                        channel = wpa_s->conf->p2p_listen_channel;
+                       channel_forced = 1;
                } else {
                        reg_class = 81;
                        /*
                         * Pick one of the social channels randomly as the
                         * listen channel.
                         */
-                       os_get_random((u8 *) &r, sizeof(r));
-                       channel = 1 + (r % 3) * 5;
+                       if (os_get_random((u8 *) &r, sizeof(r)) < 0)
+                               channel = 1;
+                       else
+                               channel = 1 + (r % 3) * 5;
+                       channel_forced = 0;
                }
-               ret = p2p_set_listen_channel(p2p, reg_class, channel);
+               ret = p2p_set_listen_channel(p2p, reg_class, channel,
+                                            channel_forced);
                if (ret)
                        wpa_printf(MSG_ERROR, "P2P: Own listen channel update "
                                   "failed: %d", ret);
@@ -5499,8 +7758,10 @@ void wpas_p2p_update_config(struct wpa_supplicant *wpa_s)
                         * Use random operation channel from (1, 6, 11)
                         *if no other preference is indicated.
                         */
-                       os_get_random((u8 *) &r, sizeof(r));
-                       op_channel = 1 + (r % 3) * 5;
+                       if (os_get_random((u8 *) &r, sizeof(r)) < 0)
+                               op_channel = 1;
+                       else
+                               op_channel = 1 + (r % 3) * 5;
                        cfg_op_channel = 0;
                }
                ret = p2p_set_oper_channel(p2p, op_reg_class, op_channel,
@@ -5516,7 +7777,15 @@ void wpas_p2p_update_config(struct wpa_supplicant *wpa_s)
                        wpa_printf(MSG_ERROR, "P2P: Preferred channel list "
                                   "update failed");
                }
+
+               if (p2p_set_no_go_freq(p2p, &wpa_s->conf->p2p_no_go_freq) < 0) {
+                       wpa_printf(MSG_ERROR, "P2P: No GO channel list "
+                                  "update failed");
+               }
        }
+
+       if (wpa_s->conf->changed_parameters & CFG_CHANGED_P2P_PASSPHRASE_LEN)
+               p2p_set_passphrase_len(p2p, wpa_s->conf->p2p_passphrase_len);
 }
 
 
@@ -5534,8 +7803,6 @@ int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled)
 {
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return -1;
-       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT)
-               return -1;
 
        wpa_s->global->cross_connection = enabled;
        p2p_set_cross_connect(wpa_s->global->p2p, enabled);
@@ -5645,7 +7912,8 @@ static void wpas_p2p_cross_connect_setup(struct wpa_supplicant *wpa_s)
                if (iface->drv_flags &
                    WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)
                        continue;
-               if (iface->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE)
+               if ((iface->drv_flags & WPA_DRIVER_FLAGS_P2P_CAPABLE) &&
+                   iface != wpa_s->parent)
                        continue;
 
                wpa_s->cross_connect_enabled = 1;
@@ -5680,33 +7948,57 @@ int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s)
                   "session overlap");
        if (wpa_s != wpa_s->parent)
                wpa_msg_ctrl(wpa_s->parent, MSG_INFO, WPS_EVENT_OVERLAP);
+       wpas_p2p_group_formation_failed(wpa_s);
+       return 1;
+}
 
-       if (wpa_s->global->p2p)
-               p2p_group_formation_failed(wpa_s->global->p2p);
-
-       eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
-                            wpa_s->parent, NULL);
 
-       wpas_group_formation_completed(wpa_s, 0);
-       return 1;
+void wpas_p2p_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       wpas_p2p_notif_pbc_overlap(wpa_s);
 }
 
 
 void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s)
 {
-       struct p2p_channels chan;
+       struct p2p_channels chan, cli_chan;
+       struct wpa_supplicant *ifs;
 
        if (wpa_s->global == NULL || wpa_s->global->p2p == NULL)
                return;
 
        os_memset(&chan, 0, sizeof(chan));
-       if (wpas_p2p_setup_channels(wpa_s, &chan)) {
+       os_memset(&cli_chan, 0, sizeof(cli_chan));
+       if (wpas_p2p_setup_channels(wpa_s, &chan, &cli_chan)) {
                wpa_printf(MSG_ERROR, "P2P: Failed to update supported "
                           "channel list");
                return;
        }
 
-       p2p_update_channel_list(wpa_s->global->p2p, &chan);
+       p2p_update_channel_list(wpa_s->global->p2p, &chan, &cli_chan);
+
+       for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
+               int freq;
+               if (!ifs->current_ssid ||
+                   !ifs->current_ssid->p2p_group ||
+                   (ifs->current_ssid->mode != WPAS_MODE_P2P_GO &&
+                    ifs->current_ssid->mode != WPAS_MODE_P2P_GROUP_FORMATION))
+                               continue;
+               freq = ifs->current_ssid->frequency;
+               if (freq_included(&chan, freq)) {
+                       wpa_dbg(ifs, MSG_DEBUG,
+                               "P2P GO operating frequency %d MHz in valid range",
+                               freq);
+                       continue;
+               }
+
+               wpa_dbg(ifs, MSG_DEBUG,
+                       "P2P GO operating in invalid frequency %d MHz", freq);
+               /* TODO: Consider using CSA or removing the group within
+                * wpa_supplicant */
+               wpa_msg(ifs, MSG_INFO, P2P_EVENT_REMOVE_AND_REFORM_GROUP);
+       }
 }
 
 
@@ -5772,6 +8064,11 @@ int wpas_p2p_cancel(struct wpa_supplicant *wpa_s)
                        wpas_p2p_group_delete(wpa_s,
                                              P2P_GROUP_REMOVAL_REQUESTED);
                        break;
+               } else if (wpa_s->p2p_in_invitation) {
+                       wpa_printf(MSG_DEBUG, "P2P: Interface %s in invitation found - cancelling",
+                                  wpa_s->ifname);
+                       found = 1;
+                       wpas_p2p_group_formation_failed(wpa_s);
                }
        }
 
@@ -5799,7 +8096,7 @@ void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
                                   int freq_24, int freq_5, int freq_overall)
 {
        struct p2p_data *p2p = wpa_s->global->p2p;
-       if (p2p == NULL || (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT))
+       if (p2p == NULL)
                return;
        p2p_set_best_channels(p2p, freq_24, freq_5, freq_overall);
 }
@@ -5810,7 +8107,7 @@ int wpas_p2p_unauthorize(struct wpa_supplicant *wpa_s, const char *addr)
        u8 peer[ETH_ALEN];
        struct p2p_data *p2p = wpa_s->global->p2p;
 
-       if (p2p == NULL || (wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_MGMT))
+       if (p2p == NULL)
                return -1;
 
        if (hwaddr_aton(addr, peer))
@@ -5866,6 +8163,19 @@ int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s)
                }
        }
 
+       if (!ret && wpa_s->global->p2p_go_wait_client.sec) {
+               struct os_reltime now;
+               os_get_reltime(&now);
+               if (os_reltime_expired(&now, &wpa_s->global->p2p_go_wait_client,
+                                      P2P_MAX_INITIAL_CONN_WAIT_GO)) {
+                       /* Wait for the first client has expired */
+                       wpa_s->global->p2p_go_wait_client.sec = 0;
+               } else {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Waiting for initial client connection during group formation");
+                       ret = 1;
+               }
+       }
+
        return ret;
 }
 
@@ -5906,12 +8216,17 @@ struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s,
                    (ssid_len != s->ssid_len ||
                     os_memcmp(ssid, s->ssid, ssid_len) != 0))
                        continue;
+               if (addr == NULL) {
+                       if (s->mode == WPAS_MODE_P2P_GO)
+                               return s;
+                       continue;
+               }
                if (os_memcmp(s->bssid, addr, ETH_ALEN) == 0)
                        return s; /* peer is GO in the persistent group */
                if (s->mode != WPAS_MODE_P2P_GO || s->p2p_client_list == NULL)
                        continue;
                for (i = 0; i < s->num_p2p_clients; i++) {
-                       if (os_memcmp(s->p2p_client_list + i * ETH_ALEN,
+                       if (os_memcmp(s->p2p_client_list + i * 2 * ETH_ALEN,
                                      addr, ETH_ALEN) == 0)
                                return s; /* peer is P2P client in persistent
                                           * group */
@@ -5925,34 +8240,75 @@ struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s,
 void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
                                       const u8 *addr)
 {
+       if (eloop_cancel_timeout(wpas_p2p_group_formation_timeout,
+                                wpa_s->parent, NULL) > 0) {
+               /*
+                * This can happen if WPS provisioning step is not terminated
+                * cleanly (e.g., P2P Client does not send WSC_Done). Since the
+                * peer was able to connect, there is no need to time out group
+                * formation after this, though. In addition, this is used with
+                * the initial connection wait on the GO as a separate formation
+                * timeout and as such, expected to be hit after the initial WPS
+                * provisioning step.
+                */
+               wpa_printf(MSG_DEBUG, "P2P: Canceled P2P group formation timeout on data connection");
+
+               if (!wpa_s->p2p_go_group_formation_completed &&
+                   !wpa_s->group_formation_reported) {
+                       /*
+                        * GO has not yet notified group formation success since
+                        * the WPS step was not completed cleanly. Do that
+                        * notification now since the P2P Client was able to
+                        * connect and as such, must have received the
+                        * credential from the WPS step.
+                        */
+                       if (wpa_s->global->p2p)
+                               p2p_wps_success_cb(wpa_s->global->p2p, addr);
+                       wpas_group_formation_completed(wpa_s, 1);
+               }
+       }
+       if (!wpa_s->p2p_go_group_formation_completed) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Marking group formation completed on GO on first data connection");
+               wpa_s->p2p_go_group_formation_completed = 1;
+               wpa_s->global->p2p_group_formation = NULL;
+               wpa_s->p2p_in_provisioning = 0;
+               wpa_s->p2p_in_invitation = 0;
+       }
+       wpa_s->global->p2p_go_wait_client.sec = 0;
        if (addr == NULL)
                return;
        wpas_p2p_add_persistent_group_client(wpa_s, addr);
 }
 
 
-static void wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
-                                       int group_added)
+static int wpas_p2p_fallback_to_go_neg(struct wpa_supplicant *wpa_s,
+                                      int group_added)
 {
        struct wpa_supplicant *group = wpa_s;
+       int ret = 0;
+
        if (wpa_s->global->p2p_group_formation)
                group = wpa_s->global->p2p_group_formation;
        wpa_s = wpa_s->parent;
        offchannel_send_action_done(wpa_s);
        if (group_added)
-               wpas_p2p_group_delete(group, P2P_GROUP_REMOVAL_SILENT);
+               ret = wpas_p2p_group_delete(group, P2P_GROUP_REMOVAL_SILENT);
        wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Fall back to GO Negotiation");
        wpas_p2p_connect(wpa_s, wpa_s->pending_join_dev_addr, wpa_s->p2p_pin,
                         wpa_s->p2p_wps_method, wpa_s->p2p_persistent_group, 0,
                         0, 0, wpa_s->p2p_go_intent, wpa_s->p2p_connect_freq,
                         wpa_s->p2p_persistent_id,
                         wpa_s->p2p_pd_before_go_neg,
-                        wpa_s->p2p_go_ht40);
+                        wpa_s->p2p_go_ht40,
+                        wpa_s->p2p_go_vht);
+       return ret;
 }
 
 
 int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s)
 {
+       int res;
+
        if (!wpa_s->p2p_fallback_to_go_neg ||
            wpa_s->p2p_in_provisioning <= 5)
                return 0;
@@ -5962,69 +8318,1008 @@ int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s)
 
        wpa_dbg(wpa_s, MSG_DEBUG, "P2P: GO not found for p2p_connect-auto - "
                "fallback to GO Negotiation");
-       wpas_p2p_fallback_to_go_neg(wpa_s, 1);
+       wpa_msg_global(wpa_s->parent, MSG_INFO, P2P_EVENT_FALLBACK_TO_GO_NEG
+                      "reason=GO-not-found");
+       res = wpas_p2p_fallback_to_go_neg(wpa_s, 1);
 
-       return 1;
+       return res == 1 ? 2 : 1;
 }
 
 
 unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s)
 {
-       const char *rn, *rn2;
        struct wpa_supplicant *ifs;
 
        if (wpa_s->wpa_state > WPA_SCANNING) {
                wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search delay due to "
                        "concurrent operation",
-                       P2P_CONCURRENT_SEARCH_DELAY);
-               return P2P_CONCURRENT_SEARCH_DELAY;
+                       wpa_s->conf->p2p_search_delay);
+               return wpa_s->conf->p2p_search_delay;
        }
 
-       if (!wpa_s->driver->get_radio_name)
-               return 0;
-       rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv);
-       if (rn == NULL || rn[0] == '\0')
+       dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+                        radio_list) {
+               if (ifs != wpa_s && ifs->wpa_state > WPA_SCANNING) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search "
+                               "delay due to concurrent operation on "
+                               "interface %s",
+                               wpa_s->conf->p2p_search_delay,
+                               ifs->ifname);
+                       return wpa_s->conf->p2p_search_delay;
+               }
+       }
+
+       return 0;
+}
+
+
+static int wpas_p2p_remove_psk_entry(struct wpa_supplicant *wpa_s,
+                                    struct wpa_ssid *s, const u8 *addr,
+                                    int iface_addr)
+{
+       struct psk_list_entry *psk, *tmp;
+       int changed = 0;
+
+       dl_list_for_each_safe(psk, tmp, &s->psk_list, struct psk_list_entry,
+                             list) {
+               if ((iface_addr && !psk->p2p &&
+                    os_memcmp(addr, psk->addr, ETH_ALEN) == 0) ||
+                   (!iface_addr && psk->p2p &&
+                    os_memcmp(addr, psk->addr, ETH_ALEN) == 0)) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "P2P: Remove persistent group PSK list entry for "
+                               MACSTR " p2p=%u",
+                               MAC2STR(psk->addr), psk->p2p);
+                       dl_list_del(&psk->list);
+                       os_free(psk);
+                       changed++;
+               }
+       }
+
+       return changed;
+}
+
+
+void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr,
+                        const u8 *p2p_dev_addr,
+                        const u8 *psk, size_t psk_len)
+{
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+       struct wpa_ssid *persistent;
+       struct psk_list_entry *p, *last;
+
+       if (psk_len != sizeof(p->psk))
+               return;
+
+       if (p2p_dev_addr) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New PSK for addr=" MACSTR
+                       " p2p_dev_addr=" MACSTR,
+                       MAC2STR(mac_addr), MAC2STR(p2p_dev_addr));
+               if (is_zero_ether_addr(p2p_dev_addr))
+                       p2p_dev_addr = NULL;
+       } else {
+               wpa_dbg(wpa_s, MSG_DEBUG, "P2P: New PSK for addr=" MACSTR,
+                       MAC2STR(mac_addr));
+       }
+
+       if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "P2P: new_psk_cb during group formation");
+               /* To be added to persistent group once created */
+               if (wpa_s->global->add_psk == NULL) {
+                       wpa_s->global->add_psk = os_zalloc(sizeof(*p));
+                       if (wpa_s->global->add_psk == NULL)
+                               return;
+               }
+               p = wpa_s->global->add_psk;
+               if (p2p_dev_addr) {
+                       p->p2p = 1;
+                       os_memcpy(p->addr, p2p_dev_addr, ETH_ALEN);
+               } else {
+                       p->p2p = 0;
+                       os_memcpy(p->addr, mac_addr, ETH_ALEN);
+               }
+               os_memcpy(p->psk, psk, psk_len);
+               return;
+       }
+
+       if (ssid->mode != WPAS_MODE_P2P_GO || !ssid->p2p_persistent_group) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Ignore new_psk_cb on not-persistent GO");
+               return;
+       }
+
+       persistent = wpas_p2p_get_persistent(wpa_s->parent, NULL, ssid->ssid,
+                                            ssid->ssid_len);
+       if (!persistent) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not find persistent group information to store the new PSK");
+               return;
+       }
+
+       p = os_zalloc(sizeof(*p));
+       if (p == NULL)
+               return;
+       if (p2p_dev_addr) {
+               p->p2p = 1;
+               os_memcpy(p->addr, p2p_dev_addr, ETH_ALEN);
+       } else {
+               p->p2p = 0;
+               os_memcpy(p->addr, mac_addr, ETH_ALEN);
+       }
+       os_memcpy(p->psk, psk, psk_len);
+
+       if (dl_list_len(&persistent->psk_list) > P2P_MAX_STORED_CLIENTS &&
+           (last = dl_list_last(&persistent->psk_list,
+                                struct psk_list_entry, list))) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove oldest PSK entry for "
+                       MACSTR " (p2p=%u) to make room for a new one",
+                       MAC2STR(last->addr), last->p2p);
+               dl_list_del(&last->list);
+               os_free(last);
+       }
+
+       wpas_p2p_remove_psk_entry(wpa_s->parent, persistent,
+                                 p2p_dev_addr ? p2p_dev_addr : mac_addr,
+                                 p2p_dev_addr == NULL);
+       if (p2p_dev_addr) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Add new PSK for p2p_dev_addr="
+                       MACSTR, MAC2STR(p2p_dev_addr));
+       } else {
+               wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Add new PSK for addr=" MACSTR,
+                       MAC2STR(mac_addr));
+       }
+       dl_list_add(&persistent->psk_list, &p->list);
+
+       if (wpa_s->parent->conf->update_config &&
+           wpa_config_write(wpa_s->parent->confname, wpa_s->parent->conf))
+               wpa_printf(MSG_DEBUG, "P2P: Failed to update configuration");
+}
+
+
+static void wpas_p2p_remove_psk(struct wpa_supplicant *wpa_s,
+                               struct wpa_ssid *s, const u8 *addr,
+                               int iface_addr)
+{
+       int res;
+
+       res = wpas_p2p_remove_psk_entry(wpa_s, s, addr, iface_addr);
+       if (res > 0 && wpa_s->conf->update_config &&
+           wpa_config_write(wpa_s->confname, wpa_s->conf))
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "P2P: Failed to update configuration");
+}
+
+
+static void wpas_p2p_remove_client_go(struct wpa_supplicant *wpa_s,
+                                     const u8 *peer, int iface_addr)
+{
+       struct hostapd_data *hapd;
+       struct hostapd_wpa_psk *psk, *prev, *rem;
+       struct sta_info *sta;
+
+       if (wpa_s->ap_iface == NULL || wpa_s->current_ssid == NULL ||
+           wpa_s->current_ssid->mode != WPAS_MODE_P2P_GO)
+               return;
+
+       /* Remove per-station PSK entry */
+       hapd = wpa_s->ap_iface->bss[0];
+       prev = NULL;
+       psk = hapd->conf->ssid.wpa_psk;
+       while (psk) {
+               if ((iface_addr && os_memcmp(peer, psk->addr, ETH_ALEN) == 0) ||
+                   (!iface_addr &&
+                    os_memcmp(peer, psk->p2p_dev_addr, ETH_ALEN) == 0)) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove operating group PSK entry for "
+                               MACSTR " iface_addr=%d",
+                               MAC2STR(peer), iface_addr);
+                       if (prev)
+                               prev->next = psk->next;
+                       else
+                               hapd->conf->ssid.wpa_psk = psk->next;
+                       rem = psk;
+                       psk = psk->next;
+                       os_free(rem);
+               } else {
+                       prev = psk;
+                       psk = psk->next;
+               }
+       }
+
+       /* Disconnect from group */
+       if (iface_addr)
+               sta = ap_get_sta(hapd, peer);
+       else
+               sta = ap_get_sta_p2p(hapd, peer);
+       if (sta) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disconnect peer " MACSTR
+                       " (iface_addr=%d) from group",
+                       MAC2STR(peer), iface_addr);
+               hostapd_drv_sta_deauth(hapd, sta->addr,
+                                      WLAN_REASON_DEAUTH_LEAVING);
+               ap_sta_deauthenticate(hapd, sta, WLAN_REASON_DEAUTH_LEAVING);
+       }
+}
+
+
+void wpas_p2p_remove_client(struct wpa_supplicant *wpa_s, const u8 *peer,
+                           int iface_addr)
+{
+       struct wpa_ssid *s;
+       struct wpa_supplicant *w;
+
+       wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Remove client " MACSTR, MAC2STR(peer));
+
+       /* Remove from any persistent group */
+       for (s = wpa_s->parent->conf->ssid; s; s = s->next) {
+               if (s->disabled != 2 || s->mode != WPAS_MODE_P2P_GO)
+                       continue;
+               if (!iface_addr)
+                       wpas_remove_persistent_peer(wpa_s, s, peer, 0);
+               wpas_p2p_remove_psk(wpa_s->parent, s, peer, iface_addr);
+       }
+
+       /* Remove from any operating group */
+       for (w = wpa_s->global->ifaces; w; w = w->next)
+               wpas_p2p_remove_client_go(w, peer, iface_addr);
+}
+
+
+static void wpas_p2p_psk_failure_removal(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_PSK_FAILURE);
+}
+
+
+static void wpas_p2p_group_freq_conflict(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+
+       wpa_printf(MSG_DEBUG, "P2P: Frequency conflict - terminate group");
+       wpas_p2p_group_delete(wpa_s, P2P_GROUP_REMOVAL_FREQ_CONFLICT);
+}
+
+
+int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s, int freq,
+                                       struct wpa_ssid *ssid)
+{
+       struct wpa_supplicant *iface;
+
+       for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+               if (!iface->current_ssid ||
+                   iface->current_ssid->frequency == freq ||
+                   (iface->p2p_group_interface == NOT_P2P_GROUP_INTERFACE &&
+                    !iface->current_ssid->p2p_group))
+                       continue;
+
+               /* Remove the connection with least priority */
+               if (!wpas_is_p2p_prioritized(iface)) {
+                       /* STA connection has priority over existing
+                        * P2P connection, so remove the interface. */
+                       wpa_printf(MSG_DEBUG, "P2P: Removing P2P connection due to single channel concurrent mode frequency conflict");
+                       eloop_register_timeout(0, 0,
+                                              wpas_p2p_group_freq_conflict,
+                                              iface, NULL);
+                       /* If connection in progress is P2P connection, do not
+                        * proceed for the connection. */
+                       if (wpa_s == iface)
+                               return -1;
+                       else
+                               return 0;
+               } else {
+                       /* P2P connection has priority, disable the STA network
+                        */
+                       wpa_supplicant_disable_network(wpa_s->global->ifaces,
+                                                      ssid);
+                       wpa_msg(wpa_s->global->ifaces, MSG_INFO,
+                               WPA_EVENT_FREQ_CONFLICT " id=%d", ssid->id);
+                       os_memset(wpa_s->global->ifaces->pending_bssid, 0,
+                                 ETH_ALEN);
+                       /* If P2P connection is in progress, continue
+                        * connecting...*/
+                       if (wpa_s == iface)
+                               return 0;
+                       else
+                               return -1;
+               }
+       }
+
+       return 0;
+}
+
+
+int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+       if (ssid == NULL || !ssid->p2p_group)
                return 0;
 
-       for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
-               if (ifs == wpa_s || !ifs->driver->get_radio_name)
+       if (wpa_s->p2p_last_4way_hs_fail &&
+           wpa_s->p2p_last_4way_hs_fail == ssid) {
+               u8 go_dev_addr[ETH_ALEN];
+               struct wpa_ssid *persistent;
+
+               if (wpas_p2p_persistent_group(wpa_s, go_dev_addr,
+                                             ssid->ssid,
+                                             ssid->ssid_len) <= 0) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Could not determine whether 4-way handshake failures were for a persistent group");
+                       goto disconnect;
+               }
+
+               wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Two 4-way handshake failures for a P2P group - go_dev_addr="
+                       MACSTR, MAC2STR(go_dev_addr));
+               persistent = wpas_p2p_get_persistent(wpa_s->parent, go_dev_addr,
+                                                    ssid->ssid,
+                                                    ssid->ssid_len);
+               if (persistent == NULL || persistent->mode != WPAS_MODE_INFRA) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No matching persistent group stored");
+                       goto disconnect;
+               }
+               wpa_msg_global(wpa_s->parent, MSG_INFO,
+                              P2P_EVENT_PERSISTENT_PSK_FAIL "%d",
+                              persistent->id);
+       disconnect:
+               wpa_s->p2p_last_4way_hs_fail = NULL;
+               /*
+                * Remove the group from a timeout to avoid issues with caller
+                * continuing to use the interface if this is on a P2P group
+                * interface.
+                */
+               eloop_register_timeout(0, 0, wpas_p2p_psk_failure_removal,
+                                      wpa_s, NULL);
+               return 1;
+       }
+
+       wpa_s->p2p_last_4way_hs_fail = ssid;
+       return 0;
+}
+
+
+#ifdef CONFIG_WPS_NFC
+
+static struct wpabuf * wpas_p2p_nfc_handover(int ndef, struct wpabuf *wsc,
+                                            struct wpabuf *p2p)
+{
+       struct wpabuf *ret;
+       size_t wsc_len;
+
+       if (p2p == NULL) {
+               wpabuf_free(wsc);
+               wpa_printf(MSG_DEBUG, "P2P: No p2p buffer for handover");
+               return NULL;
+       }
+
+       wsc_len = wsc ? wpabuf_len(wsc) : 0;
+       ret = wpabuf_alloc(2 + wsc_len + 2 + wpabuf_len(p2p));
+       if (ret == NULL) {
+               wpabuf_free(wsc);
+               wpabuf_free(p2p);
+               return NULL;
+       }
+
+       wpabuf_put_be16(ret, wsc_len);
+       if (wsc)
+               wpabuf_put_buf(ret, wsc);
+       wpabuf_put_be16(ret, wpabuf_len(p2p));
+       wpabuf_put_buf(ret, p2p);
+
+       wpabuf_free(wsc);
+       wpabuf_free(p2p);
+       wpa_hexdump_buf(MSG_DEBUG,
+                       "P2P: Generated NFC connection handover message", ret);
+
+       if (ndef && ret) {
+               struct wpabuf *tmp;
+               tmp = ndef_build_p2p(ret);
+               wpabuf_free(ret);
+               if (tmp == NULL) {
+                       wpa_printf(MSG_DEBUG, "P2P: Failed to NDEF encapsulate handover request");
+                       return NULL;
+               }
+               ret = tmp;
+       }
+
+       return ret;
+}
+
+
+static int wpas_p2p_cli_freq(struct wpa_supplicant *wpa_s,
+                            struct wpa_ssid **ssid, u8 *go_dev_addr)
+{
+       struct wpa_supplicant *iface;
+
+       if (go_dev_addr)
+               os_memset(go_dev_addr, 0, ETH_ALEN);
+       if (ssid)
+               *ssid = NULL;
+       for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+               if (iface->wpa_state < WPA_ASSOCIATING ||
+                   iface->current_ssid == NULL || iface->assoc_freq == 0 ||
+                   !iface->current_ssid->p2p_group ||
+                   iface->current_ssid->mode != WPAS_MODE_INFRA)
                        continue;
+               if (ssid)
+                       *ssid = iface->current_ssid;
+               if (go_dev_addr)
+                       os_memcpy(go_dev_addr, iface->go_dev_addr, ETH_ALEN);
+               return iface->assoc_freq;
+       }
+       return 0;
+}
+
 
-               rn2 = ifs->driver->get_radio_name(ifs->drv_priv);
-               if (!rn2 || os_strcmp(rn, rn2) != 0)
+struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s,
+                                         int ndef)
+{
+       struct wpabuf *wsc, *p2p;
+       struct wpa_ssid *ssid;
+       u8 go_dev_addr[ETH_ALEN];
+       int cli_freq = wpas_p2p_cli_freq(wpa_s, &ssid, go_dev_addr);
+
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL) {
+               wpa_printf(MSG_DEBUG, "P2P: P2P disabled - cannot build handover request");
+               return NULL;
+       }
+
+       if (wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
+           wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+                          &wpa_s->conf->wps_nfc_dh_privkey) < 0) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "P2P: No DH key available for handover request");
+               return NULL;
+       }
+
+       if (cli_freq == 0) {
+               wsc = wps_build_nfc_handover_req_p2p(
+                       wpa_s->parent->wps, wpa_s->conf->wps_nfc_dh_pubkey);
+       } else
+               wsc = NULL;
+       p2p = p2p_build_nfc_handover_req(wpa_s->global->p2p, cli_freq,
+                                        go_dev_addr, ssid ? ssid->ssid : NULL,
+                                        ssid ? ssid->ssid_len : 0);
+
+       return wpas_p2p_nfc_handover(ndef, wsc, p2p);
+}
+
+
+struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+                                         int ndef, int tag)
+{
+       struct wpabuf *wsc, *p2p;
+       struct wpa_ssid *ssid;
+       u8 go_dev_addr[ETH_ALEN];
+       int cli_freq = wpas_p2p_cli_freq(wpa_s, &ssid, go_dev_addr);
+
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+               return NULL;
+
+       if (!tag && wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
+           wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+                          &wpa_s->conf->wps_nfc_dh_privkey) < 0)
+               return NULL;
+
+       if (cli_freq == 0) {
+               wsc = wps_build_nfc_handover_sel_p2p(
+                       wpa_s->parent->wps,
+                       tag ? wpa_s->conf->wps_nfc_dev_pw_id :
+                       DEV_PW_NFC_CONNECTION_HANDOVER,
+                       wpa_s->conf->wps_nfc_dh_pubkey,
+                       tag ? wpa_s->conf->wps_nfc_dev_pw : NULL);
+       } else
+               wsc = NULL;
+       p2p = p2p_build_nfc_handover_sel(wpa_s->global->p2p, cli_freq,
+                                        go_dev_addr, ssid ? ssid->ssid : NULL,
+                                        ssid ? ssid->ssid_len : 0);
+
+       return wpas_p2p_nfc_handover(ndef, wsc, p2p);
+}
+
+
+static int wpas_p2p_nfc_join_group(struct wpa_supplicant *wpa_s,
+                                  struct p2p_nfc_params *params)
+{
+       wpa_printf(MSG_DEBUG, "P2P: Initiate join-group based on NFC "
+                  "connection handover (freq=%d)",
+                  params->go_freq);
+
+       if (params->go_freq && params->go_ssid_len) {
+               wpa_s->p2p_wps_method = WPS_NFC;
+               wpa_s->pending_join_wps_method = WPS_NFC;
+               os_memset(wpa_s->pending_join_iface_addr, 0, ETH_ALEN);
+               os_memcpy(wpa_s->pending_join_dev_addr, params->go_dev_addr,
+                         ETH_ALEN);
+               return wpas_p2p_join_start(wpa_s, params->go_freq,
+                                          params->go_ssid,
+                                          params->go_ssid_len);
+       }
+
+       return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
+                               WPS_NFC, 0, 0, 1, 0, wpa_s->conf->p2p_go_intent,
+                               params->go_freq, -1, 0, 1, 1);
+}
+
+
+static int wpas_p2p_nfc_auth_join(struct wpa_supplicant *wpa_s,
+                                 struct p2p_nfc_params *params, int tag)
+{
+       int res, persistent;
+       struct wpa_ssid *ssid;
+
+       wpa_printf(MSG_DEBUG, "P2P: Authorize join-group based on NFC "
+                  "connection handover");
+       for (wpa_s = wpa_s->global->ifaces; wpa_s; wpa_s = wpa_s->next) {
+               ssid = wpa_s->current_ssid;
+               if (ssid == NULL)
                        continue;
-               if (ifs->wpa_state > WPA_SCANNING) {
-                       wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Use %u ms search "
-                               "delay due to concurrent operation on "
-                               "interface %s",
-                               P2P_CONCURRENT_SEARCH_DELAY, ifs->ifname);
-                       return P2P_CONCURRENT_SEARCH_DELAY;
+               if (ssid->mode != WPAS_MODE_P2P_GO)
+                       continue;
+               if (wpa_s->ap_iface == NULL)
+                       continue;
+               break;
+       }
+       if (wpa_s == NULL) {
+               wpa_printf(MSG_DEBUG, "P2P: Could not find GO interface");
+               return -1;
+       }
+
+       if (wpa_s->parent->p2p_oob_dev_pw_id !=
+           DEV_PW_NFC_CONNECTION_HANDOVER &&
+           !wpa_s->parent->p2p_oob_dev_pw) {
+               wpa_printf(MSG_DEBUG, "P2P: No NFC Dev Pw known");
+               return -1;
+       }
+       res = wpas_ap_wps_add_nfc_pw(
+               wpa_s, wpa_s->parent->p2p_oob_dev_pw_id,
+               wpa_s->parent->p2p_oob_dev_pw,
+               wpa_s->parent->p2p_peer_oob_pk_hash_known ?
+               wpa_s->parent->p2p_peer_oob_pubkey_hash : NULL);
+       if (res)
+               return res;
+
+       if (!tag) {
+               wpa_printf(MSG_DEBUG, "P2P: Negotiated handover - wait for peer to join without invitation");
+               return 0;
+       }
+
+       if (!params->peer ||
+           !(params->peer->dev_capab & P2P_DEV_CAPAB_INVITATION_PROCEDURE))
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "P2P: Static handover - invite peer " MACSTR
+                  " to join", MAC2STR(params->peer->p2p_device_addr));
+
+       wpa_s->global->p2p_invite_group = wpa_s;
+       persistent = ssid->p2p_persistent_group &&
+               wpas_p2p_get_persistent(wpa_s->parent,
+                                       params->peer->p2p_device_addr,
+                                       ssid->ssid, ssid->ssid_len);
+       wpa_s->parent->pending_invite_ssid_id = -1;
+
+       return p2p_invite(wpa_s->global->p2p, params->peer->p2p_device_addr,
+                         P2P_INVITE_ROLE_ACTIVE_GO, wpa_s->own_addr,
+                         ssid->ssid, ssid->ssid_len, ssid->frequency,
+                         wpa_s->global->p2p_dev_addr, persistent, 0,
+                         wpa_s->parent->p2p_oob_dev_pw_id);
+}
+
+
+static int wpas_p2p_nfc_init_go_neg(struct wpa_supplicant *wpa_s,
+                                   struct p2p_nfc_params *params,
+                                   int forced_freq)
+{
+       wpa_printf(MSG_DEBUG, "P2P: Initiate GO Negotiation based on NFC "
+                  "connection handover");
+       return wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
+                               WPS_NFC, 0, 0, 0, 0, wpa_s->conf->p2p_go_intent,
+                               forced_freq, -1, 0, 1, 1);
+}
+
+
+static int wpas_p2p_nfc_resp_go_neg(struct wpa_supplicant *wpa_s,
+                                   struct p2p_nfc_params *params,
+                                   int forced_freq)
+{
+       int res;
+
+       wpa_printf(MSG_DEBUG, "P2P: Authorize GO Negotiation based on NFC "
+                  "connection handover");
+       res = wpas_p2p_connect(wpa_s, params->peer->p2p_device_addr, NULL,
+                              WPS_NFC, 0, 0, 0, 1, wpa_s->conf->p2p_go_intent,
+                              forced_freq, -1, 0, 1, 1);
+       if (res)
+               return res;
+
+       res = wpas_p2p_listen(wpa_s, 60);
+       if (res) {
+               p2p_unauthorize(wpa_s->global->p2p,
+                               params->peer->p2p_device_addr);
+       }
+
+       return res;
+}
+
+
+static int wpas_p2p_nfc_connection_handover(struct wpa_supplicant *wpa_s,
+                                           const struct wpabuf *data,
+                                           int sel, int tag, int forced_freq)
+{
+       const u8 *pos, *end;
+       u16 len, id;
+       struct p2p_nfc_params params;
+       int res;
+
+       os_memset(&params, 0, sizeof(params));
+       params.sel = sel;
+
+       wpa_hexdump_buf(MSG_DEBUG, "P2P: Received NFC tag payload", data);
+
+       pos = wpabuf_head(data);
+       end = pos + wpabuf_len(data);
+
+       if (end - pos < 2) {
+               wpa_printf(MSG_DEBUG, "P2P: Not enough data for Length of WSC "
+                          "attributes");
+               return -1;
+       }
+       len = WPA_GET_BE16(pos);
+       pos += 2;
+       if (len > end - pos) {
+               wpa_printf(MSG_DEBUG, "P2P: Not enough data for WSC "
+                          "attributes");
+               return -1;
+       }
+       params.wsc_attr = pos;
+       params.wsc_len = len;
+       pos += len;
+
+       if (end - pos < 2) {
+               wpa_printf(MSG_DEBUG, "P2P: Not enough data for Length of P2P "
+                          "attributes");
+               return -1;
+       }
+       len = WPA_GET_BE16(pos);
+       pos += 2;
+       if (len > end - pos) {
+               wpa_printf(MSG_DEBUG, "P2P: Not enough data for P2P "
+                          "attributes");
+               return -1;
+       }
+       params.p2p_attr = pos;
+       params.p2p_len = len;
+       pos += len;
+
+       wpa_hexdump(MSG_DEBUG, "P2P: WSC attributes",
+                   params.wsc_attr, params.wsc_len);
+       wpa_hexdump(MSG_DEBUG, "P2P: P2P attributes",
+                   params.p2p_attr, params.p2p_len);
+       if (pos < end) {
+               wpa_hexdump(MSG_DEBUG,
+                           "P2P: Ignored extra data after P2P attributes",
+                           pos, end - pos);
+       }
+
+       res = p2p_process_nfc_connection_handover(wpa_s->global->p2p, &params);
+       if (res)
+               return res;
+
+       if (params.next_step == NO_ACTION)
+               return 0;
+
+       if (params.next_step == BOTH_GO) {
+               wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_BOTH_GO "peer=" MACSTR,
+                       MAC2STR(params.peer->p2p_device_addr));
+               return 0;
+       }
+
+       if (params.next_step == PEER_CLIENT) {
+               if (!is_zero_ether_addr(params.go_dev_addr)) {
+                       wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_PEER_CLIENT
+                               "peer=" MACSTR " freq=%d go_dev_addr=" MACSTR
+                               " ssid=\"%s\"",
+                               MAC2STR(params.peer->p2p_device_addr),
+                               params.go_freq,
+                               MAC2STR(params.go_dev_addr),
+                               wpa_ssid_txt(params.go_ssid,
+                                            params.go_ssid_len));
+               } else {
+                       wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_PEER_CLIENT
+                               "peer=" MACSTR " freq=%d",
+                               MAC2STR(params.peer->p2p_device_addr),
+                               params.go_freq);
                }
+               return 0;
+       }
+
+       if (wpas_p2p_cli_freq(wpa_s, NULL, NULL)) {
+               wpa_msg(wpa_s, MSG_INFO, P2P_EVENT_NFC_WHILE_CLIENT "peer="
+                       MACSTR, MAC2STR(params.peer->p2p_device_addr));
+               return 0;
        }
 
+       wpabuf_free(wpa_s->p2p_oob_dev_pw);
+       wpa_s->p2p_oob_dev_pw = NULL;
+
+       if (params.oob_dev_pw_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+               wpa_printf(MSG_DEBUG, "P2P: No peer OOB Dev Pw "
+                          "received");
+               return -1;
+       }
+
+       id = WPA_GET_BE16(params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN);
+       wpa_printf(MSG_DEBUG, "P2P: Peer OOB Dev Pw %u", id);
+       wpa_hexdump(MSG_DEBUG, "P2P: Peer OOB Public Key hash",
+                   params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN);
+       os_memcpy(wpa_s->p2p_peer_oob_pubkey_hash,
+                 params.oob_dev_pw, WPS_OOB_PUBKEY_HASH_LEN);
+       wpa_s->p2p_peer_oob_pk_hash_known = 1;
+
+       if (tag) {
+               if (id < 0x10) {
+                       wpa_printf(MSG_DEBUG, "P2P: Static handover - invalid "
+                                  "peer OOB Device Password Id %u", id);
+                       return -1;
+               }
+               wpa_printf(MSG_DEBUG, "P2P: Static handover - use peer OOB "
+                          "Device Password Id %u", id);
+               wpa_hexdump_key(MSG_DEBUG, "P2P: Peer OOB Device Password",
+                               params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN + 2,
+                               params.oob_dev_pw_len -
+                               WPS_OOB_PUBKEY_HASH_LEN - 2);
+               wpa_s->p2p_oob_dev_pw_id = id;
+               wpa_s->p2p_oob_dev_pw = wpabuf_alloc_copy(
+                       params.oob_dev_pw + WPS_OOB_PUBKEY_HASH_LEN + 2,
+                       params.oob_dev_pw_len -
+                       WPS_OOB_PUBKEY_HASH_LEN - 2);
+               if (wpa_s->p2p_oob_dev_pw == NULL)
+                       return -1;
+
+               if (wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
+                   wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+                                  &wpa_s->conf->wps_nfc_dh_privkey) < 0)
+                       return -1;
+       } else {
+               wpa_printf(MSG_DEBUG, "P2P: Using abbreviated WPS handshake "
+                          "without Device Password");
+               wpa_s->p2p_oob_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
+       }
+
+       switch (params.next_step) {
+       case NO_ACTION:
+       case BOTH_GO:
+       case PEER_CLIENT:
+               /* already covered above */
+               return 0;
+       case JOIN_GROUP:
+               return wpas_p2p_nfc_join_group(wpa_s, &params);
+       case AUTH_JOIN:
+               return wpas_p2p_nfc_auth_join(wpa_s, &params, tag);
+       case INIT_GO_NEG:
+               return wpas_p2p_nfc_init_go_neg(wpa_s, &params, forced_freq);
+       case RESP_GO_NEG:
+               /* TODO: use own OOB Dev Pw */
+               return wpas_p2p_nfc_resp_go_neg(wpa_s, &params, forced_freq);
+       }
+
+       return -1;
+}
+
+
+int wpas_p2p_nfc_tag_process(struct wpa_supplicant *wpa_s,
+                            const struct wpabuf *data, int forced_freq)
+{
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+               return -1;
+
+       return wpas_p2p_nfc_connection_handover(wpa_s, data, 1, 1, forced_freq);
+}
+
+
+int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init,
+                                const struct wpabuf *req,
+                                const struct wpabuf *sel, int forced_freq)
+{
+       struct wpabuf *tmp;
+       int ret;
+
+       if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "NFC: P2P connection handover reported");
+
+       wpa_hexdump_ascii(MSG_DEBUG, "NFC: Req",
+                         wpabuf_head(req), wpabuf_len(req));
+       wpa_hexdump_ascii(MSG_DEBUG, "NFC: Sel",
+                         wpabuf_head(sel), wpabuf_len(sel));
+       if (forced_freq)
+               wpa_printf(MSG_DEBUG, "NFC: Forced freq %d", forced_freq);
+       tmp = ndef_parse_p2p(init ? sel : req);
+       if (tmp == NULL) {
+               wpa_printf(MSG_DEBUG, "P2P: Could not parse NDEF");
+               return -1;
+       }
+
+       ret = wpas_p2p_nfc_connection_handover(wpa_s, tmp, init, 0,
+                                              forced_freq);
+       wpabuf_free(tmp);
+
+       return ret;
+}
+
+
+int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled)
+{
+       const u8 *if_addr;
+       int go_intent = wpa_s->conf->p2p_go_intent;
+       struct wpa_supplicant *iface;
+
+       if (wpa_s->global->p2p == NULL)
+               return -1;
+
+       if (!enabled) {
+               wpa_printf(MSG_DEBUG, "P2P: Disable use of own NFC Tag");
+               for (iface = wpa_s->global->ifaces; iface; iface = iface->next)
+               {
+                       if (!iface->ap_iface)
+                               continue;
+                       hostapd_wps_nfc_token_disable(iface->ap_iface->bss[0]);
+               }
+               p2p_set_authorized_oob_dev_pw_id(wpa_s->global->p2p, 0,
+                                                0, NULL);
+               if (wpa_s->p2p_nfc_tag_enabled)
+                       wpas_p2p_remove_pending_group_interface(wpa_s);
+               wpa_s->p2p_nfc_tag_enabled = 0;
+               return 0;
+       }
+
+       if (wpa_s->global->p2p_disabled)
+               return -1;
+
+       if (wpa_s->conf->wps_nfc_dh_pubkey == NULL ||
+           wpa_s->conf->wps_nfc_dh_privkey == NULL ||
+           wpa_s->conf->wps_nfc_dev_pw == NULL ||
+           wpa_s->conf->wps_nfc_dev_pw_id < 0x10) {
+               wpa_printf(MSG_DEBUG, "P2P: NFC password token not configured "
+                          "to allow static handover cases");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "P2P: Enable use of own NFC Tag");
+
+       wpa_s->p2p_oob_dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id;
+       wpabuf_free(wpa_s->p2p_oob_dev_pw);
+       wpa_s->p2p_oob_dev_pw = wpabuf_dup(wpa_s->conf->wps_nfc_dev_pw);
+       if (wpa_s->p2p_oob_dev_pw == NULL)
+               return -1;
+       wpa_s->p2p_peer_oob_pk_hash_known = 0;
+
+       if (wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_GO ||
+           wpa_s->p2p_group_interface == P2P_GROUP_INTERFACE_CLIENT) {
+               /*
+                * P2P Group Interface present and the command came on group
+                * interface, so enable the token for the current interface.
+                */
+               wpa_s->create_p2p_iface = 0;
+       } else {
+               wpa_s->create_p2p_iface = wpas_p2p_create_iface(wpa_s);
+       }
+
+       if (wpa_s->create_p2p_iface) {
+               enum wpa_driver_if_type iftype;
+               /* Prepare to add a new interface for the group */
+               iftype = WPA_IF_P2P_GROUP;
+               if (go_intent == 15)
+                       iftype = WPA_IF_P2P_GO;
+               if (wpas_p2p_add_group_interface(wpa_s, iftype) < 0) {
+                       wpa_printf(MSG_ERROR, "P2P: Failed to allocate a new "
+                                  "interface for the group");
+                       return -1;
+               }
+
+               if_addr = wpa_s->pending_interface_addr;
+       } else
+               if_addr = wpa_s->own_addr;
+
+       wpa_s->p2p_nfc_tag_enabled = enabled;
+
+       for (iface = wpa_s->global->ifaces; iface; iface = iface->next) {
+               struct hostapd_data *hapd;
+               if (iface->ap_iface == NULL)
+                       continue;
+               hapd = iface->ap_iface->bss[0];
+               wpabuf_free(hapd->conf->wps_nfc_dh_pubkey);
+               hapd->conf->wps_nfc_dh_pubkey =
+                       wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey);
+               wpabuf_free(hapd->conf->wps_nfc_dh_privkey);
+               hapd->conf->wps_nfc_dh_privkey =
+                       wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
+               wpabuf_free(hapd->conf->wps_nfc_dev_pw);
+               hapd->conf->wps_nfc_dev_pw =
+                       wpabuf_dup(wpa_s->conf->wps_nfc_dev_pw);
+               hapd->conf->wps_nfc_dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id;
+
+               if (hostapd_wps_nfc_token_enable(iface->ap_iface->bss[0]) < 0) {
+                       wpa_dbg(iface, MSG_DEBUG,
+                               "P2P: Failed to enable NFC Tag for GO");
+               }
+       }
+       p2p_set_authorized_oob_dev_pw_id(
+               wpa_s->global->p2p, wpa_s->conf->wps_nfc_dev_pw_id, go_intent,
+               if_addr);
+
        return 0;
 }
 
+#endif /* CONFIG_WPS_NFC */
+
 
-void wpas_p2p_continue_after_scan(struct wpa_supplicant *wpa_s)
+static void wpas_p2p_optimize_listen_channel(struct wpa_supplicant *wpa_s,
+                                            struct wpa_used_freq_data *freqs,
+                                            unsigned int num)
 {
-       wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Station mode scan operation not "
-               "pending anymore (sta_scan_pending=%d "
-               "p2p_cb_on_scan_complete=%d)", wpa_s->sta_scan_pending,
-               wpa_s->global->p2p_cb_on_scan_complete);
-       wpa_s->sta_scan_pending = 0;
+       u8 curr_chan, cand, chan;
+       unsigned int i;
 
-       if (!wpa_s->global->p2p_cb_on_scan_complete)
-               return;
-       wpa_s->global->p2p_cb_on_scan_complete = 0;
+       curr_chan = p2p_get_listen_channel(wpa_s->global->p2p);
+       for (i = 0, cand = 0; i < num; i++) {
+               ieee80211_freq_to_chan(freqs[i].freq, &chan);
+               if (curr_chan == chan) {
+                       cand = 0;
+                       break;
+               }
+
+               if (chan == 1 || chan == 6 || chan == 11)
+                       cand = chan;
+       }
+
+       if (cand) {
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "P2P: Update Listen channel to %u based on operating channel",
+                       cand);
+               p2p_set_listen_channel(wpa_s->global->p2p, 81, cand, 0);
+       }
+}
+
+
+void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_used_freq_data *freqs;
+       unsigned int num = wpa_s->num_multichan_concurrent;
 
        if (wpa_s->global->p2p_disabled || wpa_s->global->p2p == NULL)
                return;
 
-       if (p2p_other_scan_completed(wpa_s->global->p2p) == 1) {
-               wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Pending P2P operation "
-                       "continued after successful connection");
-               p2p_increase_search_delay(wpa_s->global->p2p,
-                                         wpas_p2p_search_delay(wpa_s));
+       /*
+        * If possible, optimize the Listen channel to be a channel that is
+        * already used by one of the other interfaces.
+        */
+       if (!wpa_s->conf->p2p_optimize_listen_chan)
+               return;
+
+       if (!wpa_s->current_ssid || wpa_s->wpa_state != WPA_COMPLETED)
+               return;
+
+       freqs = os_calloc(num, sizeof(struct wpa_used_freq_data));
+       if (!freqs)
+               return;
+
+       num = get_shared_radio_freqs_data(wpa_s, freqs, num);
+
+       wpas_p2p_optimize_listen_channel(wpa_s, freqs, num);
+       os_free(freqs);
+}
+
+
+void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disable P2P since removing "
+                       "the management interface is being removed");
+               wpas_p2p_deinit_global(wpa_s->global);
        }
 }
+
+
+void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->ap_iface->bss)
+               wpa_s->ap_iface->bss[0]->p2p_group = NULL;
+       wpas_p2p_group_deinit(wpa_s);
+}
old mode 100644 (file)
new mode 100755 (executable)
index 24c9de3..79d8e4c
@@ -15,43 +15,40 @@ enum p2p_send_action_result;
 struct p2p_peer_info;
 struct p2p_channels;
 struct wps_event_fail;
+struct p2ps_provision;
 
-int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s);
-void wpas_p2p_deinit(struct wpa_supplicant *wpa_s);
-void wpas_p2p_deinit_global(struct wpa_global *global);
-int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s);
+int wpas_p2p_add_p2pdev_interface(struct wpa_supplicant *wpa_s,
+                                 const char *conf_p2p_dev);
+struct wpa_supplicant * wpas_get_p2p_go_iface(struct wpa_supplicant *wpa_s,
+                                             const u8 *ssid, size_t ssid_len);
+struct wpa_supplicant * wpas_get_p2p_client_iface(struct wpa_supplicant *wpa_s,
+                                                 const u8 *peer_dev_addr);
 int wpas_p2p_connect(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
                     const char *pin, enum p2p_wps_method wps_method,
                     int persistent_group, int auto_join, int join,
                     int auth, int go_intent, int freq, int persistent_id,
-                    int pd, int ht40);
-#if defined(TIZEN_EXT_ENROLLEE) && defined(CONFIG_WPS)
-int wpas_p2p_wps_enrollee(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
-                   const char *pin, int wps_method);
-#endif
-void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
-                                  unsigned int freq, unsigned int duration);
-void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
-                                         unsigned int freq);
-int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname);
+                    int pd, int ht40, int vht);
+int wpas_p2p_handle_frequency_conflicts(struct wpa_supplicant *wpa_s,
+                                          int freq, struct wpa_ssid *ssid);
 int wpas_p2p_group_add(struct wpa_supplicant *wpa_s, int persistent_group,
-                      int freq, int ht40);
+                      int freq, int ht40, int vht, char *passphrase);
 int wpas_p2p_group_add_persistent(struct wpa_supplicant *wpa_s,
                                  struct wpa_ssid *ssid, int addr_allocated,
-                                 int freq, int ht40,
-                                 const struct p2p_channels *channels);
+                                 int force_freq, int neg_freq, int ht40,
+                                 int vht, const struct p2p_channels *channels,
+                                 int connection_timeout);
 struct p2p_group * wpas_p2p_group_init(struct wpa_supplicant *wpa_s,
                                       struct wpa_ssid *ssid);
-void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
-                         int registrar);
 enum wpas_p2p_prov_disc_use {
        WPAS_P2P_PD_FOR_GO_NEG,
        WPAS_P2P_PD_FOR_JOIN,
-       WPAS_P2P_PD_AUTO
+       WPAS_P2P_PD_AUTO,
+       WPAS_P2P_PD_FOR_ASP
 };
 int wpas_p2p_prov_disc(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
                       const char *config_method,
-                      enum wpas_p2p_prov_disc_use use);
+                      enum wpas_p2p_prov_disc_use use,
+                      struct p2ps_provision *p2ps_prov);
 void wpas_send_action_tx_status(struct wpa_supplicant *wpa_s, const u8 *dst,
                                const u8 *data, size_t data_len,
                                enum p2p_send_action_result result);
@@ -61,37 +58,19 @@ enum p2p_discovery_type;
 int wpas_p2p_find(struct wpa_supplicant *wpa_s, unsigned int timeout,
                  enum p2p_discovery_type type,
                  unsigned int num_req_dev_types, const u8 *req_dev_types,
-                 const u8 *dev_id, unsigned int search_delay);
+                 const u8 *dev_id, unsigned int search_delay,
+                 u8 seek_cnt, const char **seek_string, int freq);
 void wpas_p2p_stop_find(struct wpa_supplicant *wpa_s);
 int wpas_p2p_listen(struct wpa_supplicant *wpa_s, unsigned int timeout);
+int wpas_p2p_listen_start(struct wpa_supplicant *wpa_s, unsigned int timeout);
 int wpas_p2p_assoc_req_ie(struct wpa_supplicant *wpa_s, struct wpa_bss *bss,
                          u8 *buf, size_t len, int p2p_group);
-int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
-                         const u8 *dst, const u8 *bssid,
-                         const u8 *ie, size_t ie_len,
-                         int ssi_signal);
-void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
-                       const u8 *sa, const u8 *bssid,
-                       u8 category, const u8 *data, size_t len, int freq);
 void wpas_p2p_scan_ie(struct wpa_supplicant *wpa_s, struct wpabuf *ies);
-void wpas_p2p_group_deinit(struct wpa_supplicant *wpa_s);
-void wpas_dev_found(void *ctx, const u8 *addr,
-                   const struct p2p_peer_info *info,
-                   int new_device);
-void wpas_go_neg_completed(void *ctx, struct p2p_go_neg_results *res);
-void wpas_go_neg_req_rx(void *ctx, const u8 *src, u16 dev_passwd_id);
-void wpas_prov_disc_req(void *ctx, const u8 *peer, u16 config_methods,
-                       const u8 *dev_addr, const u8 *pri_dev_type,
-                       const char *dev_name, u16 supp_config_methods,
-                       u8 dev_capab, u8 group_capab, const u8 *group_id,
-                       size_t group_id_len);
-void wpas_prov_disc_resp(void *ctx, const u8 *peer, u16 config_methods);
-void wpas_sd_request(void *ctx, int freq, const u8 *sa, u8 dialog_token,
-                    u16 update_indic, const u8 *tlvs, size_t tlvs_len);
-void wpas_sd_response(void *ctx, const u8 *sa, u16 update_indic,
-                     const u8 *tlvs, size_t tlvs_len);
+void wpas_p2p_group_formation_failed(struct wpa_supplicant *wpa_s);
 u64 wpas_p2p_sd_request(struct wpa_supplicant *wpa_s, const u8 *dst,
                        const struct wpabuf *tlvs);
+u64 wpas_p2p_sd_request_asp(struct wpa_supplicant *wpa_s, const u8 *dst, u8 id,
+                           const char *svc_str, const char *info_substr);
 u64 wpas_p2p_sd_request_upnp(struct wpa_supplicant *wpa_s, const u8 *dst,
                             u8 version, const char *query);
 u64 wpas_p2p_sd_request_wifi_display(struct wpa_supplicant *wpa_s,
@@ -110,16 +89,17 @@ int wpas_p2p_service_add_upnp(struct wpa_supplicant *wpa_s, u8 version,
                              const char *service);
 int wpas_p2p_service_del_upnp(struct wpa_supplicant *wpa_s, u8 version,
                              const char *service);
+int wpas_p2p_service_add_asp(struct wpa_supplicant *wpa_s, int auto_accept,
+                            u32 adv_id, const char *adv_str, u8 svc_state,
+                            u16 config_methods, const char *svc_info);
+int wpas_p2p_service_del_asp(struct wpa_supplicant *wpa_s, u32 adv_id);
+int wpas_p2p_service_p2ps_id_exists(struct wpa_supplicant *wpa_s, u32 adv_id);
 int wpas_p2p_reject(struct wpa_supplicant *wpa_s, const u8 *addr);
-#ifdef TIZEN_EXT_P2P
-int wpas_p2p_reject_connection(struct wpa_supplicant *wpa_s, const u8 *addr);
-#endif
 int wpas_p2p_invite(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
                    struct wpa_ssid *ssid, const u8 *go_dev_addr, int freq,
-                   int ht40, int pref_freq);
+                   int ht40, int vht, int pref_freq);
 int wpas_p2p_invite_group(struct wpa_supplicant *wpa_s, const char *ifname,
                          const u8 *peer_addr, const u8 *go_dev_addr);
-void wpas_p2p_completed(struct wpa_supplicant *wpa_s);
 int wpas_p2p_presence_req(struct wpa_supplicant *wpa_s, u32 duration1,
                          u32 interval1, u32 duration2, u32 interval2);
 int wpas_p2p_ext_listen(struct wpa_supplicant *wpa_s, unsigned int period,
@@ -130,25 +110,12 @@ int wpas_p2p_deauth_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
 void wpas_p2p_disassoc_notif(struct wpa_supplicant *wpa_s, const u8 *bssid,
                             u16 reason_code, const u8 *ie, size_t ie_len,
                             int locally_generated);
-void wpas_p2p_update_config(struct wpa_supplicant *wpa_s);
 int wpas_p2p_set_noa(struct wpa_supplicant *wpa_s, u8 count, int start,
                     int duration);
 int wpas_p2p_set_cross_connect(struct wpa_supplicant *wpa_s, int enabled);
-void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s);
-void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s);
-int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s);
-void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s);
 int wpas_p2p_cancel(struct wpa_supplicant *wpa_s);
-void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s);
-void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
-                                  int freq_24, int freq_5, int freq_overall);
 int wpas_p2p_unauthorize(struct wpa_supplicant *wpa_s, const char *addr);
 int wpas_p2p_disconnect(struct wpa_supplicant *wpa_s);
-void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
-                        struct wps_event_fail *fail);
-int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s);
-void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
-                             struct wpa_ssid *ssid);
 struct wpa_ssid * wpas_p2p_get_persistent(struct wpa_supplicant *wpa_s,
                                          const u8 *addr, const u8 *ssid,
                                          size_t ssid_len);
@@ -157,14 +124,193 @@ void wpas_p2p_notify_ap_sta_authorized(struct wpa_supplicant *wpa_s,
 int wpas_p2p_scan_no_go_seen(struct wpa_supplicant *wpa_s);
 int wpas_p2p_get_ht40_mode(struct wpa_supplicant *wpa_s,
                           struct hostapd_hw_modes *mode, u8 channel);
+int wpas_p2p_get_vht80_center(struct wpa_supplicant *wpa_s,
+                             struct hostapd_hw_modes *mode, u8 channel);
 unsigned int wpas_p2p_search_delay(struct wpa_supplicant *wpa_s);
+void wpas_p2p_new_psk_cb(struct wpa_supplicant *wpa_s, const u8 *mac_addr,
+                        const u8 *p2p_dev_addr,
+                        const u8 *psk, size_t psk_len);
+void wpas_p2p_remove_client(struct wpa_supplicant *wpa_s, const u8 *peer,
+                           int iface_addr);
+struct wpabuf * wpas_p2p_nfc_handover_req(struct wpa_supplicant *wpa_s,
+                                         int ndef);
+struct wpabuf * wpas_p2p_nfc_handover_sel(struct wpa_supplicant *wpa_s,
+                                         int ndef, int tag);
+int wpas_p2p_nfc_tag_process(struct wpa_supplicant *wpa_s,
+                            const struct wpabuf *data, int forced_freq);
+int wpas_p2p_nfc_report_handover(struct wpa_supplicant *wpa_s, int init,
+                                const struct wpabuf *req,
+                                const struct wpabuf *sel, int forced_freq);
+int wpas_p2p_nfc_tag_enabled(struct wpa_supplicant *wpa_s, int enabled);
+void wpas_p2p_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx);
 
 #ifdef CONFIG_P2P
-void wpas_p2p_continue_after_scan(struct wpa_supplicant *wpa_s);
+
+int wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s);
+void wpas_p2p_deinit(struct wpa_supplicant *wpa_s);
+void wpas_p2p_completed(struct wpa_supplicant *wpa_s);
+void wpas_p2p_update_config(struct wpa_supplicant *wpa_s);
+int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s, const u8 *addr,
+                         const u8 *dst, const u8 *bssid,
+                         const u8 *ie, size_t ie_len,
+                         int ssi_signal);
+void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s, const u8 *peer_addr,
+                         int registrar);
+void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s);
+void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
+                                  int freq_24, int freq_5, int freq_overall);
+void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
+                       const u8 *sa, const u8 *bssid,
+                       u8 category, const u8 *data, size_t len, int freq);
+void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+                                  unsigned int freq, unsigned int duration);
+void wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+                                         unsigned int freq);
+void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s);
+void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s);
+void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s);
+int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s);
+int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s);
+void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s);
+void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s);
+void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s);
+void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s);
+void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
+                             struct wpa_ssid *ssid);
+int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s);
+int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s);
+void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+                        struct wps_event_fail *fail);
+int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s, const char *ifname);
+
 #else /* CONFIG_P2P */
-static inline void wpas_p2p_continue_after_scan(struct wpa_supplicant *wpa_s)
+
+static inline int
+wpas_p2p_init(struct wpa_global *global, struct wpa_supplicant *wpa_s)
+{
+       return 0;
+}
+
+static inline void wpas_p2p_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_completed(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_update_config(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline int wpas_p2p_probe_req_rx(struct wpa_supplicant *wpa_s,
+                                       const u8 *addr,
+                                       const u8 *dst, const u8 *bssid,
+                                       const u8 *ie, size_t ie_len,
+                                       int ssi_signal)
+{
+       return 0;
+}
+
+static inline void wpas_p2p_wps_success(struct wpa_supplicant *wpa_s,
+                                       const u8 *peer_addr, int registrar)
+{
+}
+
+static inline void wpas_p2p_update_channel_list(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_update_best_channels(struct wpa_supplicant *wpa_s,
+                                                int freq_24, int freq_5,
+                                                int freq_overall)
+{
+}
+
+static inline void wpas_p2p_rx_action(struct wpa_supplicant *wpa_s,
+                                     const u8 *da,
+                                     const u8 *sa, const u8 *bssid,
+                                     u8 category, const u8 *data, size_t len,
+                                     int freq)
+{
+}
+
+static inline void wpas_p2p_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+                                                unsigned int freq,
+                                                unsigned int duration)
+{
+}
+
+static inline void
+wpas_p2p_cancel_remain_on_channel_cb(struct wpa_supplicant *wpa_s,
+                                    unsigned int freq)
+{
+}
+
+static inline void wpas_p2p_interface_unavailable(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_notif_connected(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_notif_disconnected(struct wpa_supplicant *wpa_s)
 {
 }
+
+static inline int wpas_p2p_notif_pbc_overlap(struct wpa_supplicant *wpa_s)
+{
+       return 0;
+}
+
+static inline int wpas_p2p_4way_hs_failed(struct wpa_supplicant *wpa_s)
+{
+       return 0;
+}
+
+static inline void wpas_p2p_ap_setup_failed(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_indicate_state_change(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_deinit_iface(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_ap_deinit(struct wpa_supplicant *wpa_s)
+{
+}
+
+static inline void wpas_p2p_network_removed(struct wpa_supplicant *wpa_s,
+                                           struct wpa_ssid *ssid)
+{
+}
+
+static inline int wpas_p2p_in_progress(struct wpa_supplicant *wpa_s)
+{
+       return 0;
+}
+
+static inline int wpas_p2p_wps_eapol_cb(struct wpa_supplicant *wpa_s)
+{
+       return 0;
+}
+
+static inline void wpas_p2p_wps_failed(struct wpa_supplicant *wpa_s,
+                                      struct wps_event_fail *fail)
+{
+}
+
+static inline int wpas_p2p_group_remove(struct wpa_supplicant *wpa_s,
+                                       const char *ifname)
+{
+       return 0;
+}
+
 #endif /* CONFIG_P2P */
 
 #endif /* P2P_SUPPLICANT_H */
old mode 100644 (file)
new mode 100755 (executable)
index ff2ae74..ed57085
@@ -27,9 +27,6 @@
 #include "drivers/driver.h"
 
 
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-
 struct wpa_driver_ops *wpa_drivers[] = { NULL };
 
 
old mode 100644 (file)
new mode 100755 (executable)
index bdd6815..805891a
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - Scanning
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -22,6 +22,7 @@
 #include "notify.h"
 #include "bss.h"
 #include "scan.h"
+#include "mesh.h"
 
 
 static void wpa_supplicant_gen_assoc_event(struct wpa_supplicant *wpa_s)
@@ -95,6 +96,10 @@ int wpa_supplicant_enabled_networks(struct wpa_supplicant *wpa_s)
 {
        struct wpa_ssid *ssid = wpa_s->conf->ssid;
        int count = 0, disabled = 0;
+
+       if (wpa_s->p2p_mgmt)
+               return 0; /* no normal network profiles on p2p_mgmt interface */
+
        while (ssid) {
                if (!wpas_network_disabled(wpa_s, ssid))
                        count++;
@@ -141,71 +146,69 @@ static void wpa_supplicant_assoc_try(struct wpa_supplicant *wpa_s,
 }
 
 
-static int int_array_len(const int *a)
-{
-       int i;
-       for (i = 0; a && a[i]; i++)
-               ;
-       return i;
-}
-
-
-static void int_array_concat(int **res, const int *a)
+static void wpas_trigger_scan_cb(struct wpa_radio_work *work, int deinit)
 {
-       int reslen, alen, i;
-       int *n;
-
-       reslen = int_array_len(*res);
-       alen = int_array_len(a);
+       struct wpa_supplicant *wpa_s = work->wpa_s;
+       struct wpa_driver_scan_params *params = work->ctx;
+       int ret;
 
-       n = os_realloc_array(*res, reslen + alen + 1, sizeof(int));
-       if (n == NULL) {
-               os_free(*res);
-               *res = NULL;
+       if (deinit) {
+               if (!work->started) {
+                       wpa_scan_free_params(params);
+                       return;
+               }
+               wpa_supplicant_notify_scanning(wpa_s, 0);
+               wpas_notify_scan_done(wpa_s, 0);
+               wpa_s->scan_work = NULL;
                return;
        }
-       for (i = 0; i <= alen; i++)
-               n[reslen + i] = a[i];
-       *res = n;
-}
-
-
-static int freq_cmp(const void *a, const void *b)
-{
-       int _a = *(int *) a;
-       int _b = *(int *) b;
-
-       if (_a == 0)
-               return 1;
-       if (_b == 0)
-               return -1;
-       return _a - _b;
-}
 
+       if (wpas_update_random_addr_disassoc(wpa_s) < 0) {
+               wpa_msg(wpa_s, MSG_INFO,
+                       "Failed to assign random MAC address for a scan");
+               radio_work_done(work);
+               return;
+       }
 
-static void int_array_sort_unique(int *a)
-{
-       int alen;
-       int i, j;
+       wpa_supplicant_notify_scanning(wpa_s, 1);
 
-       if (a == NULL)
-               return;
+       if (wpa_s->clear_driver_scan_cache) {
+               wpa_printf(MSG_DEBUG,
+                          "Request driver to clear scan cache due to local BSS flush");
+               params->only_new_results = 1;
+       }
+       ret = wpa_drv_scan(wpa_s, params);
+       wpa_scan_free_params(params);
+       work->ctx = NULL;
+       if (ret) {
+               int retry = wpa_s->last_scan_req != MANUAL_SCAN_REQ;
 
-       alen = int_array_len(a);
-       qsort(a, alen, sizeof(int), freq_cmp);
+               if (wpa_s->disconnected)
+                       retry = 0;
 
-       i = 0;
-       j = 1;
-       while (a[i] && a[j]) {
-               if (a[i] == a[j]) {
-                       j++;
-                       continue;
+               wpa_supplicant_notify_scanning(wpa_s, 0);
+               wpas_notify_scan_done(wpa_s, 0);
+               if (wpa_s->wpa_state == WPA_SCANNING)
+                       wpa_supplicant_set_state(wpa_s,
+                                                wpa_s->scan_prev_wpa_state);
+               wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_SCAN_FAILED "ret=%d%s",
+                       ret, retry ? " retry=1" : "");
+               radio_work_done(work);
+
+               if (retry) {
+                       /* Restore scan_req since we will try to scan again */
+                       wpa_s->scan_req = wpa_s->last_scan_req;
+                       wpa_supplicant_req_scan(wpa_s, 1, 0);
                }
-               a[++i] = a[j++];
+               return;
        }
-       if (a[i])
-               i++;
-       a[i] = 0;
+
+       os_get_reltime(&wpa_s->scan_trigger_time);
+       wpa_s->scan_runs++;
+       wpa_s->normal_scans++;
+       wpa_s->own_scan_requested = 1;
+       wpa_s->clear_driver_scan_cache = 0;
+       wpa_s->scan_work = work;
 }
 
 
@@ -218,21 +221,24 @@ static void int_array_sort_unique(int *a)
 int wpa_supplicant_trigger_scan(struct wpa_supplicant *wpa_s,
                                struct wpa_driver_scan_params *params)
 {
-       int ret;
+       struct wpa_driver_scan_params *ctx;
 
-       wpa_supplicant_notify_scanning(wpa_s, 1);
+       if (wpa_s->scan_work) {
+               wpa_dbg(wpa_s, MSG_INFO, "Reject scan trigger since one is already pending");
+               return -1;
+       }
 
-       ret = wpa_drv_scan(wpa_s, params);
-       if (ret) {
-               wpa_supplicant_notify_scanning(wpa_s, 0);
-               wpas_notify_scan_done(wpa_s, 0);
-       } else {
-               os_get_time(&wpa_s->scan_trigger_time);
-               wpa_s->scan_runs++;
-               wpa_s->normal_scans++;
+       ctx = wpa_scan_clone_params(params);
+       if (ctx == NULL)
+               return -1;
+
+       if (radio_add_work(wpa_s, 0, "scan", 0, wpas_trigger_scan_cb, ctx) < 0)
+       {
+               wpa_scan_free_params(ctx);
+               return -1;
        }
 
-       return ret;
+       return 0;
 }
 
 
@@ -260,10 +266,9 @@ wpa_supplicant_sched_scan_timeout(void *eloop_ctx, void *timeout_ctx)
 }
 
 
-static int
-wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
-                               struct wpa_driver_scan_params *params,
-                               int interval)
+int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
+                                   struct wpa_driver_scan_params *params,
+                                   int interval)
 {
        int ret;
 
@@ -278,7 +283,7 @@ wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
 }
 
 
-static int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s)
+int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s)
 {
        int ret;
 
@@ -310,7 +315,7 @@ wpa_supplicant_build_filter_ssids(struct wpa_config *conf, size_t *num_ssids)
        }
        if (count == 0)
                return NULL;
-       ssids = os_zalloc(count * sizeof(struct wpa_driver_scan_filter));
+       ssids = os_calloc(count, sizeof(struct wpa_driver_scan_filter));
        if (ssids == NULL)
                return NULL;
 
@@ -338,7 +343,7 @@ static void wpa_supplicant_optimize_freqs(
                        wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO "
                                "preferred frequency %d MHz",
                                wpa_s->go_params->freq);
-                       params->freqs = os_zalloc(2 * sizeof(int));
+                       params->freqs = os_calloc(2, sizeof(int));
                        if (params->freqs)
                                params->freqs[0] = wpa_s->go_params->freq;
                } else if (wpa_s->p2p_in_provisioning < 8 &&
@@ -352,6 +357,32 @@ static void wpa_supplicant_optimize_freqs(
                }
                wpa_s->p2p_in_provisioning++;
        }
+
+       if (params->freqs == NULL && wpa_s->p2p_in_invitation) {
+               /*
+                * Optimize scan based on GO information during persistent
+                * group reinvocation
+                */
+               if (wpa_s->p2p_in_invitation < 5 &&
+                   wpa_s->p2p_invite_go_freq > 0) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only GO preferred frequency %d MHz during invitation",
+                               wpa_s->p2p_invite_go_freq);
+                       params->freqs = os_calloc(2, sizeof(int));
+                       if (params->freqs)
+                               params->freqs[0] = wpa_s->p2p_invite_go_freq;
+               }
+               wpa_s->p2p_in_invitation++;
+               if (wpa_s->p2p_in_invitation > 20) {
+                       /*
+                        * This should not really happen since the variable is
+                        * cleared on group removal, but if it does happen, make
+                        * sure we do not get stuck in special invitation scan
+                        * mode.
+                        */
+                       wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Clear p2p_in_invitation");
+                       wpa_s->p2p_in_invitation = 0;
+               }
+       }
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_WPS
@@ -362,18 +393,19 @@ static void wpa_supplicant_optimize_freqs(
                 */
                wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz "
                        "that was used during provisioning", wpa_s->wps_freq);
-               params->freqs = os_zalloc(2 * sizeof(int));
+               params->freqs = os_calloc(2, sizeof(int));
                if (params->freqs)
                        params->freqs[0] = wpa_s->wps_freq;
                wpa_s->after_wps--;
-       }
+       } else if (wpa_s->after_wps)
+               wpa_s->after_wps--;
 
        if (params->freqs == NULL && wpa_s->known_wps_freq && wpa_s->wps_freq)
        {
                /* Optimize provisioning scan based on already known channel */
                wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Scan only frequency %u MHz",
                        wpa_s->wps_freq);
-               params->freqs = os_zalloc(2 * sizeof(int));
+               params->freqs = os_calloc(2, sizeof(int));
                if (params->freqs)
                        params->freqs[0] = wpa_s->wps_freq;
                wpa_s->known_wps_freq = 0; /* only do this once */
@@ -390,11 +422,17 @@ static void wpas_add_interworking_elements(struct wpa_supplicant *wpa_s,
                return;
 
        wpabuf_put_u8(buf, WLAN_EID_EXT_CAPAB);
-       wpabuf_put_u8(buf, 4);
+       wpabuf_put_u8(buf, 6);
        wpabuf_put_u8(buf, 0x00);
        wpabuf_put_u8(buf, 0x00);
        wpabuf_put_u8(buf, 0x00);
        wpabuf_put_u8(buf, 0x80); /* Bit 31 - Interworking */
+       wpabuf_put_u8(buf, 0x00);
+#ifdef CONFIG_HS20
+       wpabuf_put_u8(buf, 0x40); /* Bit 46 - WNM-Notification */
+#else /* CONFIG_HS20 */
+       wpabuf_put_u8(buf, 0x00);
+#endif /* CONFIG_HS20 */
 
        wpabuf_put_u8(buf, WLAN_EID_INTERWORKING);
        wpabuf_put_u8(buf, is_zero_ether_addr(wpa_s->conf->hessid) ? 1 :
@@ -446,11 +484,13 @@ static struct wpabuf * wpa_supplicant_extra_ies(struct wpa_supplicant *wpa_s)
        }
 #endif /* CONFIG_P2P */
 
+       wpa_supplicant_mesh_add_scan_ie(wpa_s, &extra_ie);
+
 #endif /* CONFIG_WPS */
 
 #ifdef CONFIG_HS20
        if (wpa_s->conf->hs20 && wpabuf_resize(&extra_ie, 7) == 0)
-               wpas_hs20_add_indication(extra_ie);
+               wpas_hs20_add_indication(extra_ie, -1);
 #endif /* CONFIG_HS20 */
 
        return extra_ie;
@@ -483,44 +523,6 @@ static int non_p2p_network_enabled(struct wpa_supplicant *wpa_s)
 
 #endif /* CONFIG_P2P */
 
-/*
- * Find the operating frequency of any other virtual interface that is using
- * the same radio concurrently.
- */
-static int shared_vif_oper_freq(struct wpa_supplicant *wpa_s)
-{
-       const char *rn, *rn2;
-       struct wpa_supplicant *ifs;
-       u8 bssid[ETH_ALEN];
-
-       if (!wpa_s->driver->get_radio_name)
-               return -1;
-
-       rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv);
-       if (rn == NULL || rn[0] == '\0')
-               return -1;
-
-       for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
-               if (ifs == wpa_s || !ifs->driver->get_radio_name)
-                       continue;
-
-               rn2 = ifs->driver->get_radio_name(ifs->drv_priv);
-               if (!rn2 || os_strcmp(rn, rn2) != 0)
-                       continue;
-
-               if (ifs->current_ssid == NULL || ifs->assoc_freq == 0)
-                       continue;
-
-               if (ifs->current_ssid->mode == WPAS_MODE_AP ||
-                   ifs->current_ssid->mode == WPAS_MODE_P2P_GO)
-                       return ifs->current_ssid->frequency;
-               if (wpa_drv_get_bssid(ifs, bssid) == 0)
-                       return ifs->assoc_freq;
-       }
-
-       return 0;
-}
-
 
 static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
                                          u16 num_modes,
@@ -552,7 +554,7 @@ static void wpa_setband_scan_freqs_list(struct wpa_supplicant *wpa_s,
                return;
        }
 
-       params->freqs = os_zalloc((mode->num_channels + 1) * sizeof(int));
+       params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
        if (params->freqs == NULL)
                return;
        for (count = 0, i = 0; i < mode->num_channels; i++) {
@@ -579,28 +581,81 @@ static void wpa_setband_scan_freqs(struct wpa_supplicant *wpa_s,
 }
 
 
+static void wpa_set_scan_ssids(struct wpa_supplicant *wpa_s,
+                              struct wpa_driver_scan_params *params,
+                              size_t max_ssids)
+{
+       unsigned int i;
+       struct wpa_ssid *ssid;
+
+       for (i = 0; i < wpa_s->scan_id_count; i++) {
+               unsigned int j;
+
+               ssid = wpa_config_get_network(wpa_s->conf, wpa_s->scan_id[i]);
+               if (!ssid || !ssid->scan_ssid)
+                       continue;
+
+               for (j = 0; j < params->num_ssids; j++) {
+                       if (params->ssids[j].ssid_len == ssid->ssid_len &&
+                           params->ssids[j].ssid &&
+                           os_memcmp(params->ssids[j].ssid, ssid->ssid,
+                                     ssid->ssid_len) == 0)
+                               break;
+               }
+               if (j < params->num_ssids)
+                       continue; /* already in the list */
+
+               if (params->num_ssids + 1 > max_ssids) {
+                       wpa_printf(MSG_DEBUG,
+                                  "Over max scan SSIDs for manual request");
+                       break;
+               }
+
+               wpa_printf(MSG_DEBUG, "Scan SSID (manual request): %s",
+                          wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+               params->ssids[params->num_ssids].ssid = ssid->ssid;
+               params->ssids[params->num_ssids].ssid_len = ssid->ssid_len;
+               params->num_ssids++;
+       }
+
+       wpa_s->scan_id_count = 0;
+}
+
+
 static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
 {
        struct wpa_supplicant *wpa_s = eloop_ctx;
        struct wpa_ssid *ssid;
-       enum scan_req_type scan_req = NORMAL_SCAN_REQ;
-       int ret;
+       int ret, p2p_in_prog;
        struct wpabuf *extra_ie = NULL;
        struct wpa_driver_scan_params params;
        struct wpa_driver_scan_params *scan_params;
        size_t max_ssids;
-       enum wpa_states prev_state;
+       int connect_without_scan = 0;
+
+       if (wpa_s->pno || wpa_s->pno_sched_pending) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - PNO is in progress");
+               return;
+       }
 
        if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
                wpa_dbg(wpa_s, MSG_DEBUG, "Skip scan - interface disabled");
-               wpas_p2p_continue_after_scan(wpa_s);
                return;
        }
 
        if (wpa_s->disconnected && wpa_s->scan_req == NORMAL_SCAN_REQ) {
                wpa_dbg(wpa_s, MSG_DEBUG, "Disconnected - do not scan");
                wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
-               wpas_p2p_continue_after_scan(wpa_s);
+               return;
+       }
+
+       if (wpa_s->scanning) {
+               /*
+                * If we are already in scanning state, we shall reschedule the
+                * the incoming scan request.
+                */
+               wpa_dbg(wpa_s, MSG_DEBUG, "Already scanning - Reschedule the incoming scan req");
+               wpa_supplicant_req_scan(wpa_s, 1, 0);
                return;
        }
 
@@ -608,7 +663,6 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
            wpa_s->scan_req == NORMAL_SCAN_REQ) {
                wpa_dbg(wpa_s, MSG_DEBUG, "No enabled networks - do not scan");
                wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
-               wpas_p2p_continue_after_scan(wpa_s);
                return;
        }
 
@@ -625,22 +679,24 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
                return;
        }
 
-#ifdef CONFIG_P2P
-       if (wpas_p2p_in_progress(wpa_s) || wpas_wpa_is_in_progress(wpa_s)) {
-               if (wpa_s->sta_scan_pending &&
-                   wpas_p2p_in_progress(wpa_s) == 2 &&
-                   wpa_s->global->p2p_cb_on_scan_complete) {
-                       wpa_dbg(wpa_s, MSG_DEBUG, "Process pending station "
-                               "mode scan during P2P search");
-               } else {
-                       wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan "
-                               "while P2P operation is in progress");
-                       wpa_s->sta_scan_pending = 1;
-                       wpa_supplicant_req_scan(wpa_s, 5, 0);
-                       return;
+       ssid = NULL;
+       if (wpa_s->scan_req != MANUAL_SCAN_REQ &&
+           wpa_s->connect_without_scan) {
+               connect_without_scan = 1;
+               for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+                       if (ssid == wpa_s->connect_without_scan)
+                               break;
                }
        }
-#endif /* CONFIG_P2P */
+
+       p2p_in_prog = wpas_p2p_in_progress(wpa_s);
+       if (p2p_in_prog && p2p_in_prog != 2 &&
+           (!ssid ||
+            (ssid->mode != WPAS_MODE_AP && ssid->mode != WPAS_MODE_P2P_GO))) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Delay station mode scan while P2P operation is in progress");
+               wpa_supplicant_req_scan(wpa_s, 5, 0);
+               return;
+       }
 
        if (wpa_s->conf->ap_scan == 2)
                max_ssids = 1;
@@ -650,12 +706,22 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
                        max_ssids = WPAS_MAX_SCAN_SSIDS;
        }
 
-       scan_req = wpa_s->scan_req;
+       wpa_s->last_scan_req = wpa_s->scan_req;
        wpa_s->scan_req = NORMAL_SCAN_REQ;
 
+       if (connect_without_scan) {
+               wpa_s->connect_without_scan = NULL;
+               if (ssid) {
+                       wpa_printf(MSG_DEBUG, "Start a pre-selected network "
+                                  "without scan step");
+                       wpa_supplicant_associate(wpa_s, NULL, ssid);
+                       return;
+               }
+       }
+
        os_memset(&params, 0, sizeof(params));
 
-       prev_state = wpa_s->wpa_state;
+       wpa_s->scan_prev_wpa_state = wpa_s->wpa_state;
        if (wpa_s->wpa_state == WPA_DISCONNECTED ||
            wpa_s->wpa_state == WPA_INACTIVE)
                wpa_supplicant_set_state(wpa_s, WPA_SCANNING);
@@ -668,30 +734,30 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
                goto scan;
        }
 
-       if (scan_req != MANUAL_SCAN_REQ && wpa_s->connect_without_scan) {
-               for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
-                       if (ssid == wpa_s->connect_without_scan)
-                               break;
-               }
-               wpa_s->connect_without_scan = NULL;
-               if (ssid) {
-                       wpa_printf(MSG_DEBUG, "Start a pre-selected network "
-                                  "without scan step");
-                       wpa_supplicant_associate(wpa_s, NULL, ssid);
-                       return;
-               }
-       }
-
 #ifdef CONFIG_P2P
        if ((wpa_s->p2p_in_provisioning || wpa_s->show_group_started) &&
-           wpa_s->go_params) {
-               wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during "
-                          "P2P group formation");
+           wpa_s->go_params && !wpa_s->conf->passive_scan) {
+               wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during P2P group formation (p2p_in_provisioning=%d show_group_started=%d)",
+                          wpa_s->p2p_in_provisioning,
+                          wpa_s->show_group_started);
                params.ssids[0].ssid = wpa_s->go_params->ssid;
                params.ssids[0].ssid_len = wpa_s->go_params->ssid_len;
                params.num_ssids = 1;
                goto ssid_list_set;
        }
+
+       if (wpa_s->p2p_in_invitation) {
+               if (wpa_s->current_ssid) {
+                       wpa_printf(MSG_DEBUG, "P2P: Use specific SSID for scan during invitation");
+                       params.ssids[0].ssid = wpa_s->current_ssid->ssid;
+                       params.ssids[0].ssid_len =
+                               wpa_s->current_ssid->ssid_len;
+                       params.num_ssids = 1;
+               } else {
+                       wpa_printf(MSG_DEBUG, "P2P: No specific SSID known for scan during invitation");
+               }
+               goto ssid_list_set;
+       }
 #endif /* CONFIG_P2P */
 
        /* Find the starting point from which to continue scanning */
@@ -706,7 +772,8 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
                }
        }
 
-       if (scan_req != MANUAL_SCAN_REQ && wpa_s->conf->ap_scan == 2) {
+       if (wpa_s->last_scan_req != MANUAL_SCAN_REQ &&
+           wpa_s->conf->ap_scan == 2) {
                wpa_s->connect_without_scan = NULL;
                wpa_s->prev_scan_wildcard = 0;
                wpa_supplicant_assoc_try(wpa_s, ssid);
@@ -717,6 +784,36 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
                 * wildcard SSID.
                 */
                ssid = NULL;
+       } else if (wpa_s->reattach && wpa_s->current_ssid != NULL) {
+               /*
+                * Perform single-channel single-SSID scan for
+                * reassociate-to-same-BSS operation.
+                */
+               /* Setup SSID */
+               ssid = wpa_s->current_ssid;
+               wpa_hexdump_ascii(MSG_DEBUG, "Scan SSID",
+                                 ssid->ssid, ssid->ssid_len);
+               params.ssids[0].ssid = ssid->ssid;
+               params.ssids[0].ssid_len = ssid->ssid_len;
+               params.num_ssids = 1;
+
+               /*
+                * Allocate memory for frequency array, allocate one extra
+                * slot for the zero-terminator.
+                */
+               params.freqs = os_malloc(sizeof(int) * 2);
+               if (params.freqs == NULL) {
+                       wpa_dbg(wpa_s, MSG_ERROR, "Memory allocation failed");
+                       return;
+               }
+               params.freqs[0] = wpa_s->assoc_freq;
+               params.freqs[1] = 0;
+
+               /*
+                * Reset the reattach flag so that we fall back to full scan if
+                * this scan fails.
+                */
+               wpa_s->reattach = 0;
        } else {
                struct wpa_ssid *start = ssid, *tssid;
                int freqs_set = 0;
@@ -743,7 +840,13 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
                                ssid = wpa_s->conf->ssid;
                }
 
-               for (tssid = wpa_s->conf->ssid; tssid; tssid = tssid->next) {
+               if (wpa_s->scan_id_count &&
+                   wpa_s->last_scan_req == MANUAL_SCAN_REQ)
+                       wpa_set_scan_ssids(wpa_s, &params, max_ssids);
+
+               for (tssid = wpa_s->conf->ssid;
+                    wpa_s->last_scan_req != MANUAL_SCAN_REQ && tssid;
+                    tssid = tssid->next) {
                        if (wpas_network_disabled(wpa_s, tssid))
                                continue;
                        if ((params.freqs || !freqs_set) && tssid->scan_freq) {
@@ -784,6 +887,12 @@ static void wpa_supplicant_scan(void *eloop_ctx, void *timeout_ctx)
                wpa_dbg(wpa_s, MSG_DEBUG, "Include wildcard SSID in "
                        "the scan request");
                params.num_ssids++;
+       } else if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+                  wpa_s->manual_scan_passive && params.num_ssids == 0) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Use passive scan based on manual request");
+       } else if (wpa_s->conf->passive_scan) {
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "Use passive scan based on configuration");
        } else {
                wpa_s->prev_scan_ssid = WILDCARD_SSID_SCAN;
                params.num_ssids++;
@@ -797,6 +906,20 @@ ssid_list_set:
        wpa_supplicant_optimize_freqs(wpa_s, &params);
        extra_ie = wpa_supplicant_extra_ies(wpa_s);
 
+       if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+           wpa_s->manual_scan_only_new) {
+               wpa_printf(MSG_DEBUG,
+                          "Request driver to clear scan cache due to manual only_new=1 scan");
+               params.only_new_results = 1;
+       }
+
+       if (wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs == NULL &&
+           wpa_s->manual_scan_freqs) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Limit manual scan to specified channels");
+               params.freqs = wpa_s->manual_scan_freqs;
+               wpa_s->manual_scan_freqs = NULL;
+       }
+
        if (params.freqs == NULL && wpa_s->next_scan_freqs) {
                wpa_dbg(wpa_s, MSG_DEBUG, "Optimize scan based on previously "
                        "generated frequency list");
@@ -815,14 +938,19 @@ ssid_list_set:
 
        /* Use current associated channel? */
        if (wpa_s->conf->scan_cur_freq && !params.freqs) {
-               int freq = shared_vif_oper_freq(wpa_s);
-               if (freq > 0) {
-                       wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the current "
-                               "operating channel (%d MHz) since "
-                               "scan_cur_freq is enabled", freq);
-                       params.freqs = os_zalloc(sizeof(int) * 2);
-                       if (params.freqs)
-                               params.freqs[0] = freq;
+               unsigned int num = wpa_s->num_multichan_concurrent;
+
+               params.freqs = os_calloc(num + 1, sizeof(int));
+               if (params.freqs) {
+                       num = get_shared_radio_freqs(wpa_s, params.freqs, num);
+                       if (num > 0) {
+                               wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the "
+                                       "current operating channels since "
+                                       "scan_cur_freq is enabled");
+                       } else {
+                               os_free(params.freqs);
+                               params.freqs = NULL;
+                       }
                }
        }
 
@@ -834,7 +962,7 @@ ssid_list_set:
        }
 
 #ifdef CONFIG_P2P
-       if (wpa_s->p2p_in_provisioning ||
+       if (wpa_s->p2p_in_provisioning || wpa_s->p2p_in_invitation ||
            (wpa_s->show_group_started && wpa_s->go_params)) {
                /*
                 * The interface may not yet be in P2P mode, so we have to
@@ -844,6 +972,14 @@ ssid_list_set:
        }
 #endif /* CONFIG_P2P */
 
+       if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCAN) {
+               params.mac_addr_rand = 1;
+               if (wpa_s->mac_addr_scan) {
+                       params.mac_addr = wpa_s->mac_addr_scan;
+                       params.mac_addr_mask = wpa_s->mac_addr_scan + ETH_ALEN;
+               }
+       }
+
        scan_params = &params;
 
 scan:
@@ -857,47 +993,60 @@ scan:
         * station interface when we are not configured to prefer station
         * connection and a concurrent operation is already in process.
         */
-       if (wpa_s->scan_for_connection && scan_req == NORMAL_SCAN_REQ &&
+       if (wpa_s->scan_for_connection &&
+           wpa_s->last_scan_req == NORMAL_SCAN_REQ &&
            !scan_params->freqs && !params.freqs &&
            wpas_is_p2p_prioritized(wpa_s) &&
-           !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MULTI_CHANNEL_CONCURRENT) &&
            wpa_s->p2p_group_interface == NOT_P2P_GROUP_INTERFACE &&
            non_p2p_network_enabled(wpa_s)) {
-               int freq = shared_vif_oper_freq(wpa_s);
-               if (freq > 0) {
-                       wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Scan only the current "
-                               "operating channel (%d MHz) since driver does "
-                               "not support multi-channel concurrency", freq);
-                       params.freqs = os_zalloc(sizeof(int) * 2);
-                       if (params.freqs)
-                               params.freqs[0] = freq;
-                       scan_params->freqs = params.freqs;
+               unsigned int num = wpa_s->num_multichan_concurrent;
+
+               params.freqs = os_calloc(num + 1, sizeof(int));
+               if (params.freqs) {
+                       num = get_shared_radio_freqs(wpa_s, params.freqs, num);
+                       if (num > 0 && num == wpa_s->num_multichan_concurrent) {
+                               wpa_dbg(wpa_s, MSG_DEBUG, "Scan only the current operating channels since all channels are already used");
+                       } else {
+                               os_free(params.freqs);
+                               params.freqs = NULL;
+                       }
                }
        }
 #endif /* CONFIG_P2P */
 
        ret = wpa_supplicant_trigger_scan(wpa_s, scan_params);
 
+       if (ret && wpa_s->last_scan_req == MANUAL_SCAN_REQ && params.freqs &&
+           !wpa_s->manual_scan_freqs) {
+               /* Restore manual_scan_freqs for the next attempt */
+               wpa_s->manual_scan_freqs = params.freqs;
+               params.freqs = NULL;
+       }
+
        wpabuf_free(extra_ie);
        os_free(params.freqs);
        os_free(params.filter_ssids);
 
        if (ret) {
                wpa_msg(wpa_s, MSG_WARNING, "Failed to initiate AP scan");
-               if (prev_state != wpa_s->wpa_state)
-                       wpa_supplicant_set_state(wpa_s, prev_state);
+               if (wpa_s->scan_prev_wpa_state != wpa_s->wpa_state)
+                       wpa_supplicant_set_state(wpa_s,
+                                                wpa_s->scan_prev_wpa_state);
                /* Restore scan_req since we will try to scan again */
-               wpa_s->scan_req = scan_req;
+               wpa_s->scan_req = wpa_s->last_scan_req;
                wpa_supplicant_req_scan(wpa_s, 1, 0);
        } else {
                wpa_s->scan_for_connection = 0;
+#ifdef CONFIG_INTERWORKING
+               wpa_s->interworking_fast_assoc_tried = 0;
+#endif /* CONFIG_INTERWORKING */
        }
 }
 
 
 void wpa_supplicant_update_scan_int(struct wpa_supplicant *wpa_s, int sec)
 {
-       struct os_time remaining, new_int;
+       struct os_reltime remaining, new_int;
        int cancelled;
 
        cancelled = eloop_cancel_timeout_one(wpa_supplicant_scan, wpa_s, NULL,
@@ -905,13 +1054,15 @@ void wpa_supplicant_update_scan_int(struct wpa_supplicant *wpa_s, int sec)
 
        new_int.sec = sec;
        new_int.usec = 0;
-       if (cancelled && os_time_before(&remaining, &new_int)) {
+       if (cancelled && os_reltime_before(&remaining, &new_int)) {
                new_int.sec = remaining.sec;
                new_int.usec = remaining.usec;
        }
 
-       eloop_register_timeout(new_int.sec, new_int.usec, wpa_supplicant_scan,
-                              wpa_s, NULL);
+       if (cancelled) {
+               eloop_register_timeout(new_int.sec, new_int.usec,
+                                      wpa_supplicant_scan, wpa_s, NULL);
+       }
        wpa_s->scan_interval = sec;
 }
 
@@ -927,33 +1078,28 @@ void wpa_supplicant_update_scan_int(struct wpa_supplicant *wpa_s, int sec)
  */
 void wpa_supplicant_req_scan(struct wpa_supplicant *wpa_s, int sec, int usec)
 {
-       /* If there's at least one network that should be specifically scanned
-        * then don't cancel the scan and reschedule.  Some drivers do
-        * background scanning which generates frequent scan results, and that
-        * causes the specific SSID scan to get continually pushed back and
-        * never happen, which causes hidden APs to never get probe-scanned.
-        */
-       if (eloop_is_timeout_registered(wpa_supplicant_scan, wpa_s, NULL) &&
-           wpa_s->conf->ap_scan == 1) {
-               struct wpa_ssid *ssid = wpa_s->conf->ssid;
+       int res;
 
-               while (ssid) {
-                       if (!wpas_network_disabled(wpa_s, ssid) &&
-                           ssid->scan_ssid)
-                               break;
-                       ssid = ssid->next;
-               }
-               if (ssid) {
-                       wpa_dbg(wpa_s, MSG_DEBUG, "Not rescheduling scan to "
-                               "ensure that specific SSID scans occur");
-                       return;
-               }
+       if (wpa_s->p2p_mgmt) {
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "Ignore scan request (%d.%06d sec) on p2p_mgmt interface",
+                       sec, usec);
+               return;
        }
 
-       wpa_dbg(wpa_s, MSG_DEBUG, "Setting scan request: %d sec %d usec",
-               sec, usec);
-       eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
-       eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL);
+       res = eloop_deplete_timeout(sec, usec, wpa_supplicant_scan, wpa_s,
+                                   NULL);
+       if (res == 1) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Rescheduling scan request: %d.%06d sec",
+                       sec, usec);
+       } else if (res == 0) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Ignore new scan request for %d.%06d sec since an earlier request is scheduled to trigger sooner",
+                       sec, usec);
+       } else {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Setting scan request: %d.%06d sec",
+                       sec, usec);
+               eloop_register_timeout(sec, usec, wpa_supplicant_scan, wpa_s, NULL);
+       }
 }
 
 
@@ -1062,7 +1208,7 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
        os_memset(&params, 0, sizeof(params));
 
        /* If we can't allocate space for the filters, we just don't filter */
-       params.filter_ssids = os_zalloc(wpa_s->max_match_sets *
+       params.filter_ssids = os_calloc(wpa_s->max_match_sets,
                                        sizeof(struct wpa_driver_scan_filter));
 
        prev_state = wpa_s->wpa_state;
@@ -1165,6 +1311,16 @@ int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s)
                params.extra_ies_len = wpabuf_len(extra_ie);
        }
 
+       if (wpa_s->conf->filter_rssi)
+               params.filter_rssi = wpa_s->conf->filter_rssi;
+
+       /* See if user specified frequencies. If so, scan only those. */
+       if (wpa_s->conf->freq_list && !params.freqs) {
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "Optimize scan based on conf->freq_list");
+               int_array_concat(&params.freqs, wpa_s->conf->freq_list);
+       }
+
        scan_params = &params;
 
 scan:
@@ -1180,6 +1336,15 @@ scan:
 
        wpa_setband_scan_freqs(wpa_s, scan_params);
 
+       if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_SCHED_SCAN) {
+               params.mac_addr_rand = 1;
+               if (wpa_s->mac_addr_sched_scan) {
+                       params.mac_addr = wpa_s->mac_addr_sched_scan;
+                       params.mac_addr_mask = wpa_s->mac_addr_sched_scan +
+                               ETH_ALEN;
+               }
+       }
+
        ret = wpa_supplicant_start_sched_scan(wpa_s, scan_params,
                                              wpa_s->sched_scan_interval);
        wpabuf_free(extra_ie);
@@ -1225,7 +1390,23 @@ void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s)
 {
        wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling scan request");
        eloop_cancel_timeout(wpa_supplicant_scan, wpa_s, NULL);
-       wpas_p2p_continue_after_scan(wpa_s);
+}
+
+
+/**
+ * wpa_supplicant_cancel_delayed_sched_scan - Stop a delayed scheduled scan
+ * @wpa_s: Pointer to wpa_supplicant data
+ *
+ * This function is used to stop a delayed scheduled scan.
+ */
+void wpa_supplicant_cancel_delayed_sched_scan(struct wpa_supplicant *wpa_s)
+{
+       if (!wpa_s->sched_scan_supported)
+               return;
+
+       wpa_dbg(wpa_s, MSG_DEBUG, "Cancelling delayed sched scan");
+       eloop_cancel_timeout(wpa_supplicant_delayed_sched_scan_timeout,
+                            wpa_s, NULL);
 }
 
 
@@ -1347,6 +1528,43 @@ const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
 
 
 /**
+ * wpa_scan_get_vendor_ie_beacon - Fetch vendor information from a scan result
+ * @res: Scan result entry
+ * @vendor_type: Vendor type (four octets starting the IE payload)
+ * Returns: Pointer to the information element (id field) or %NULL if not found
+ *
+ * This function returns the first matching information element in the scan
+ * result.
+ *
+ * This function is like wpa_scan_get_vendor_ie(), but uses IE buffer only
+ * from Beacon frames instead of either Beacon or Probe Response frames.
+ */
+const u8 * wpa_scan_get_vendor_ie_beacon(const struct wpa_scan_res *res,
+                                        u32 vendor_type)
+{
+       const u8 *end, *pos;
+
+       if (res->beacon_ie_len == 0)
+               return NULL;
+
+       pos = (const u8 *) (res + 1);
+       pos += res->ie_len;
+       end = pos + res->beacon_ie_len;
+
+       while (pos + 1 < end) {
+               if (pos + 2 + pos[1] > end)
+                       break;
+               if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
+                   vendor_type == WPA_GET_BE32(&pos[2]))
+                       return pos;
+               pos += 2 + pos[1];
+       }
+
+       return NULL;
+}
+
+
+/**
  * wpa_scan_get_vendor_ie_multi - Fetch vendor IE data from a scan result
  * @res: Scan result entry
  * @vendor_type: Vendor type (four octets starting the IE payload)
@@ -1397,18 +1615,19 @@ struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
  */
 #define GREAT_SNR 30
 
+#define IS_5GHZ(n) (n > 4000)
+
 /* Compare function for sorting scan results. Return >0 if @b is considered
  * better. */
 static int wpa_scan_result_compar(const void *a, const void *b)
 {
-#define IS_5GHZ(n) (n > 4000)
 #define MIN(a,b) a < b ? a : b
        struct wpa_scan_res **_wa = (void *) a;
        struct wpa_scan_res **_wb = (void *) b;
        struct wpa_scan_res *wa = *_wa;
        struct wpa_scan_res *wb = *_wb;
-       int wpa_a, wpa_b, maxrate_a, maxrate_b;
-       int snr_a, snr_b;
+       int wpa_a, wpa_b;
+       int snr_a, snr_b, snr_a_full, snr_b_full;
 
        /* WPA/WPA2 support preferred */
        wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL ||
@@ -1429,37 +1648,34 @@ static int wpa_scan_result_compar(const void *a, const void *b)
            (wb->caps & IEEE80211_CAP_PRIVACY) == 0)
                return -1;
 
-       if ((wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) &&
-           !((wa->flags | wb->flags) & WPA_SCAN_NOISE_INVALID)) {
-               snr_a = MIN(wa->level - wa->noise, GREAT_SNR);
-               snr_b = MIN(wb->level - wb->noise, GREAT_SNR);
+       if (wa->flags & wb->flags & WPA_SCAN_LEVEL_DBM) {
+               snr_a_full = wa->snr;
+               snr_a = MIN(wa->snr, GREAT_SNR);
+               snr_b_full = wb->snr;
+               snr_b = MIN(wa->snr, GREAT_SNR);
        } else {
-               /* Not suitable information to calculate SNR, so use level */
-               snr_a = wa->level;
-               snr_b = wb->level;
+               /* Level is not in dBm, so we can't calculate
+                * SNR. Just use raw level (units unknown). */
+               snr_a = snr_a_full = wa->level;
+               snr_b = snr_b_full = wb->level;
        }
 
-       /* best/max rate preferred if SNR close enough */
-        if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) ||
+       /* if SNR is close, decide by max rate or frequency band */
+       if ((snr_a && snr_b && abs(snr_b - snr_a) < 5) ||
            (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) {
-               maxrate_a = wpa_scan_get_max_rate(wa);
-               maxrate_b = wpa_scan_get_max_rate(wb);
-               if (maxrate_a != maxrate_b)
-                       return maxrate_b - maxrate_a;
+               if (wa->est_throughput != wb->est_throughput)
+                       return wb->est_throughput - wa->est_throughput;
                if (IS_5GHZ(wa->freq) ^ IS_5GHZ(wb->freq))
                        return IS_5GHZ(wa->freq) ? -1 : 1;
        }
 
-       /* use freq for channel preference */
-
        /* all things being equal, use SNR; if SNRs are
         * identical, use quality values since some drivers may only report
         * that value and leave the signal level zero */
-       if (snr_b == snr_a)
+       if (snr_b_full == snr_a_full)
                return wb->qual - wa->qual;
-       return snr_b - snr_a;
+       return snr_b_full - snr_a_full;
 #undef MIN
-#undef IS_5GHZ
 }
 
 
@@ -1524,21 +1740,22 @@ static void dump_scan_res(struct wpa_scan_results *scan_res)
        for (i = 0; i < scan_res->num; i++) {
                struct wpa_scan_res *r = scan_res->res[i];
                u8 *pos;
-               if ((r->flags & (WPA_SCAN_LEVEL_DBM | WPA_SCAN_NOISE_INVALID))
-                   == WPA_SCAN_LEVEL_DBM) {
-                       int snr = r->level - r->noise;
+               if (r->flags & WPA_SCAN_LEVEL_DBM) {
+                       int noise_valid = !(r->flags & WPA_SCAN_NOISE_INVALID);
+
                        wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
-                                  "noise=%d level=%d snr=%d%s flags=0x%x "
-                                  "age=%u",
+                                  "noise=%d%s level=%d snr=%d%s flags=0x%x age=%u est=%u",
                                   MAC2STR(r->bssid), r->freq, r->qual,
-                                  r->noise, r->level, snr,
-                                  snr >= GREAT_SNR ? "*" : "", r->flags,
-                                  r->age);
+                                  r->noise, noise_valid ? "" : "~", r->level,
+                                  r->snr, r->snr >= GREAT_SNR ? "*" : "",
+                                  r->flags,
+                                  r->age, r->est_throughput);
                } else {
                        wpa_printf(MSG_EXCESSIVE, MACSTR " freq=%d qual=%d "
-                                  "noise=%d level=%d flags=0x%x age=%u",
+                                  "noise=%d level=%d flags=0x%x age=%u est=%u",
                                   MAC2STR(r->bssid), r->freq, r->qual,
-                                  r->noise, r->level, r->flags, r->age);
+                                  r->noise, r->level, r->flags, r->age,
+                                  r->est_throughput);
                }
                pos = (u8 *) (r + 1);
                if (r->ie_len)
@@ -1605,6 +1822,188 @@ static void filter_scan_res(struct wpa_supplicant *wpa_s,
 }
 
 
+/*
+ * Noise floor values to use when we have signal strength
+ * measurements, but no noise floor measurments. These values were
+ * measured in an office environment with many APs.
+ */
+#define DEFAULT_NOISE_FLOOR_2GHZ (-89)
+#define DEFAULT_NOISE_FLOOR_5GHZ (-92)
+
+static void scan_snr(struct wpa_scan_res *res)
+{
+       if (res->flags & WPA_SCAN_NOISE_INVALID) {
+               res->noise = IS_5GHZ(res->freq) ?
+                       DEFAULT_NOISE_FLOOR_5GHZ :
+                       DEFAULT_NOISE_FLOOR_2GHZ;
+       }
+
+       if (res->flags & WPA_SCAN_LEVEL_DBM) {
+               res->snr = res->level - res->noise;
+       } else {
+               /* Level is not in dBm, so we can't calculate
+                * SNR. Just use raw level (units unknown). */
+               res->snr = res->level;
+       }
+}
+
+
+static unsigned int max_ht20_rate(int snr)
+{
+       if (snr < 6)
+               return 6500; /* HT20 MCS0 */
+       if (snr < 8)
+               return 13000; /* HT20 MCS1 */
+       if (snr < 13)
+               return 19500; /* HT20 MCS2 */
+       if (snr < 17)
+               return 26000; /* HT20 MCS3 */
+       if (snr < 20)
+               return 39000; /* HT20 MCS4 */
+       if (snr < 23)
+               return 52000; /* HT20 MCS5 */
+       if (snr < 24)
+               return 58500; /* HT20 MCS6 */
+       return 65000; /* HT20 MCS7 */
+}
+
+
+static unsigned int max_ht40_rate(int snr)
+{
+       if (snr < 3)
+               return 13500; /* HT40 MCS0 */
+       if (snr < 6)
+               return 27000; /* HT40 MCS1 */
+       if (snr < 10)
+               return 40500; /* HT40 MCS2 */
+       if (snr < 15)
+               return 54000; /* HT40 MCS3 */
+       if (snr < 17)
+               return 81000; /* HT40 MCS4 */
+       if (snr < 22)
+               return 108000; /* HT40 MCS5 */
+       if (snr < 24)
+               return 121500; /* HT40 MCS6 */
+       return 135000; /* HT40 MCS7 */
+}
+
+
+static unsigned int max_vht80_rate(int snr)
+{
+       if (snr < 1)
+               return 0;
+       if (snr < 2)
+               return 29300; /* VHT80 MCS0 */
+       if (snr < 5)
+               return 58500; /* VHT80 MCS1 */
+       if (snr < 9)
+               return 87800; /* VHT80 MCS2 */
+       if (snr < 11)
+               return 117000; /* VHT80 MCS3 */
+       if (snr < 15)
+               return 175500; /* VHT80 MCS4 */
+       if (snr < 16)
+               return 234000; /* VHT80 MCS5 */
+       if (snr < 18)
+               return 263300; /* VHT80 MCS6 */
+       if (snr < 20)
+               return 292500; /* VHT80 MCS7 */
+       if (snr < 22)
+               return 351000; /* VHT80 MCS8 */
+       return 390000; /* VHT80 MCS9 */
+}
+
+
+static void scan_est_throughput(struct wpa_supplicant *wpa_s,
+                               struct wpa_scan_res *res)
+{
+       enum local_hw_capab capab = wpa_s->hw_capab;
+       int rate; /* max legacy rate in 500 kb/s units */
+       const u8 *ie;
+       unsigned int est, tmp;
+       int snr = res->snr;
+
+       if (res->est_throughput)
+               return;
+
+       /* Get maximum legacy rate */
+       rate = wpa_scan_get_max_rate(res);
+
+       /* Limit based on estimated SNR */
+       if (rate > 1 * 2 && snr < 1)
+               rate = 1 * 2;
+       else if (rate > 2 * 2 && snr < 4)
+               rate = 2 * 2;
+       else if (rate > 6 * 2 && snr < 5)
+               rate = 6 * 2;
+       else if (rate > 9 * 2 && snr < 6)
+               rate = 9 * 2;
+       else if (rate > 12 * 2 && snr < 7)
+               rate = 12 * 2;
+       else if (rate > 18 * 2 && snr < 10)
+               rate = 18 * 2;
+       else if (rate > 24 * 2 && snr < 11)
+               rate = 24 * 2;
+       else if (rate > 36 * 2 && snr < 15)
+               rate = 36 * 2;
+       else if (rate > 48 * 2 && snr < 19)
+               rate = 48 * 2;
+       else if (rate > 54 * 2 && snr < 21)
+               rate = 54 * 2;
+       est = rate * 500;
+
+       if (capab == CAPAB_HT || capab == CAPAB_HT40 || capab == CAPAB_VHT) {
+               ie = wpa_scan_get_ie(res, WLAN_EID_HT_CAP);
+               if (ie) {
+                       tmp = max_ht20_rate(snr);
+                       if (tmp > est)
+                               est = tmp;
+               }
+       }
+
+       if (capab == CAPAB_HT40 || capab == CAPAB_VHT) {
+               ie = wpa_scan_get_ie(res, WLAN_EID_HT_OPERATION);
+               if (ie && ie[1] >= 2 &&
+                   (ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) {
+                       tmp = max_ht40_rate(snr);
+                       if (tmp > est)
+                               est = tmp;
+               }
+       }
+
+       if (capab == CAPAB_VHT) {
+               /* Use +1 to assume VHT is always faster than HT */
+               ie = wpa_scan_get_ie(res, WLAN_EID_VHT_CAP);
+               if (ie) {
+                       tmp = max_ht20_rate(snr) + 1;
+                       if (tmp > est)
+                               est = tmp;
+
+                       ie = wpa_scan_get_ie(res, WLAN_EID_HT_OPERATION);
+                       if (ie && ie[1] >= 2 &&
+                           (ie[3] &
+                            HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK)) {
+                               tmp = max_ht40_rate(snr) + 1;
+                               if (tmp > est)
+                                       est = tmp;
+                       }
+
+                       ie = wpa_scan_get_ie(res, WLAN_EID_VHT_OPERATION);
+                       if (ie && ie[1] >= 1 &&
+                           (ie[2] & VHT_OPMODE_CHANNEL_WIDTH_MASK)) {
+                               tmp = max_vht80_rate(snr) + 1;
+                               if (tmp > est)
+                                       est = tmp;
+                       }
+               }
+       }
+
+       /* TODO: channel utilization and AP load (e.g., from AP Beacon) */
+
+       res->est_throughput = est;
+}
+
+
 /**
  * wpa_supplicant_get_scan_results - Get scan results
  * @wpa_s: Pointer to wpa_supplicant data
@@ -1634,12 +2033,19 @@ wpa_supplicant_get_scan_results(struct wpa_supplicant *wpa_s,
                 * Make sure we have a valid timestamp if the driver wrapper
                 * does not set this.
                 */
-               os_get_time(&scan_res->fetch_time);
+               os_get_reltime(&scan_res->fetch_time);
        }
        filter_scan_res(wpa_s, scan_res);
 
+       for (i = 0; i < scan_res->num; i++) {
+               struct wpa_scan_res *scan_res_item = scan_res->res[i];
+
+               scan_snr(scan_res_item);
+               scan_est_throughput(wpa_s, scan_res_item);
+       }
+
 #ifdef CONFIG_WPS
-       if (wpas_wps_in_progress(wpa_s)) {
+       if (wpas_wps_searching(wpa_s)) {
                wpa_dbg(wpa_s, MSG_DEBUG, "WPS: Order scan results with WPS "
                        "provisioning rules");
                compar = wpa_scan_result_wps_compar;
@@ -1691,9 +2097,21 @@ void scan_only_handler(struct wpa_supplicant *wpa_s,
                       struct wpa_scan_results *scan_res)
 {
        wpa_dbg(wpa_s, MSG_DEBUG, "Scan-only results received");
-       wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
+       if (wpa_s->last_scan_req == MANUAL_SCAN_REQ &&
+           wpa_s->manual_scan_use_id && wpa_s->own_scan_running) {
+               wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS "id=%u",
+                            wpa_s->manual_scan_id);
+               wpa_s->manual_scan_use_id = 0;
+       } else {
+               wpa_msg_ctrl(wpa_s, MSG_INFO, WPA_EVENT_SCAN_RESULTS);
+       }
        wpas_notify_scan_results(wpa_s);
        wpas_notify_scan_done(wpa_s, 1);
+       if (wpa_s->scan_work) {
+               struct wpa_radio_work *work = wpa_s->scan_work;
+               wpa_s->scan_work = NULL;
+               radio_work_done(work);
+       }
 }
 
 
@@ -1701,3 +2119,310 @@ int wpas_scan_scheduled(struct wpa_supplicant *wpa_s)
 {
        return eloop_is_timeout_registered(wpa_supplicant_scan, wpa_s, NULL);
 }
+
+
+struct wpa_driver_scan_params *
+wpa_scan_clone_params(const struct wpa_driver_scan_params *src)
+{
+       struct wpa_driver_scan_params *params;
+       size_t i;
+       u8 *n;
+
+       params = os_zalloc(sizeof(*params));
+       if (params == NULL)
+               return NULL;
+
+       for (i = 0; i < src->num_ssids; i++) {
+               if (src->ssids[i].ssid) {
+                       n = os_malloc(src->ssids[i].ssid_len);
+                       if (n == NULL)
+                               goto failed;
+                       os_memcpy(n, src->ssids[i].ssid,
+                                 src->ssids[i].ssid_len);
+                       params->ssids[i].ssid = n;
+                       params->ssids[i].ssid_len = src->ssids[i].ssid_len;
+               }
+       }
+       params->num_ssids = src->num_ssids;
+
+       if (src->extra_ies) {
+               n = os_malloc(src->extra_ies_len);
+               if (n == NULL)
+                       goto failed;
+               os_memcpy(n, src->extra_ies, src->extra_ies_len);
+               params->extra_ies = n;
+               params->extra_ies_len = src->extra_ies_len;
+       }
+
+       if (src->freqs) {
+               int len = int_array_len(src->freqs);
+               params->freqs = os_malloc((len + 1) * sizeof(int));
+               if (params->freqs == NULL)
+                       goto failed;
+               os_memcpy(params->freqs, src->freqs, (len + 1) * sizeof(int));
+       }
+
+       if (src->filter_ssids) {
+               params->filter_ssids = os_malloc(sizeof(*params->filter_ssids) *
+                                                src->num_filter_ssids);
+               if (params->filter_ssids == NULL)
+                       goto failed;
+               os_memcpy(params->filter_ssids, src->filter_ssids,
+                         sizeof(*params->filter_ssids) *
+                         src->num_filter_ssids);
+               params->num_filter_ssids = src->num_filter_ssids;
+       }
+
+       params->filter_rssi = src->filter_rssi;
+       params->p2p_probe = src->p2p_probe;
+       params->only_new_results = src->only_new_results;
+       params->low_priority = src->low_priority;
+
+       if (src->mac_addr_rand) {
+               params->mac_addr_rand = src->mac_addr_rand;
+
+               if (src->mac_addr && src->mac_addr_mask) {
+                       u8 *mac_addr;
+
+                       mac_addr = os_malloc(2 * ETH_ALEN);
+                       if (!mac_addr)
+                               goto failed;
+
+                       os_memcpy(mac_addr, src->mac_addr, ETH_ALEN);
+                       os_memcpy(mac_addr + ETH_ALEN, src->mac_addr_mask,
+                                 ETH_ALEN);
+                       params->mac_addr = mac_addr;
+                       params->mac_addr_mask = mac_addr + ETH_ALEN;
+               }
+       }
+       return params;
+
+failed:
+       wpa_scan_free_params(params);
+       return NULL;
+}
+
+
+void wpa_scan_free_params(struct wpa_driver_scan_params *params)
+{
+       size_t i;
+
+       if (params == NULL)
+               return;
+
+       for (i = 0; i < params->num_ssids; i++)
+               os_free((u8 *) params->ssids[i].ssid);
+       os_free((u8 *) params->extra_ies);
+       os_free(params->freqs);
+       os_free(params->filter_ssids);
+
+       /*
+        * Note: params->mac_addr_mask points to same memory allocation and
+        * must not be freed separately.
+        */
+       os_free((u8 *) params->mac_addr);
+
+       os_free(params);
+}
+
+
+int wpas_start_pno(struct wpa_supplicant *wpa_s)
+{
+       int ret, interval, prio;
+       size_t i, num_ssid, num_match_ssid;
+       struct wpa_ssid *ssid;
+       struct wpa_driver_scan_params params;
+
+       if (!wpa_s->sched_scan_supported)
+               return -1;
+
+       if (wpa_s->pno || wpa_s->pno_sched_pending)
+               return 0;
+
+       if ((wpa_s->wpa_state > WPA_SCANNING) &&
+           (wpa_s->wpa_state <= WPA_COMPLETED)) {
+               wpa_printf(MSG_ERROR, "PNO: In assoc process");
+               return -EAGAIN;
+       }
+
+       if (wpa_s->wpa_state == WPA_SCANNING) {
+               wpa_supplicant_cancel_scan(wpa_s);
+               if (wpa_s->sched_scanning) {
+                       wpa_printf(MSG_DEBUG, "Schedule PNO on completion of "
+                                  "ongoing sched scan");
+                       wpa_supplicant_cancel_sched_scan(wpa_s);
+                       wpa_s->pno_sched_pending = 1;
+                       return 0;
+               }
+       }
+
+       os_memset(&params, 0, sizeof(params));
+
+       num_ssid = num_match_ssid = 0;
+       ssid = wpa_s->conf->ssid;
+       while (ssid) {
+               if (!wpas_network_disabled(wpa_s, ssid)) {
+                       num_match_ssid++;
+                       if (ssid->scan_ssid)
+                               num_ssid++;
+               }
+               ssid = ssid->next;
+       }
+
+       if (num_match_ssid == 0) {
+               wpa_printf(MSG_DEBUG, "PNO: No configured SSIDs");
+               return -1;
+       }
+
+       if (num_match_ssid > num_ssid) {
+               params.num_ssids++; /* wildcard */
+               num_ssid++;
+       }
+
+       if (num_ssid > WPAS_MAX_SCAN_SSIDS) {
+               wpa_printf(MSG_DEBUG, "PNO: Use only the first %u SSIDs from "
+                          "%u", WPAS_MAX_SCAN_SSIDS, (unsigned int) num_ssid);
+               num_ssid = WPAS_MAX_SCAN_SSIDS;
+       }
+
+       if (num_match_ssid > wpa_s->max_match_sets) {
+               num_match_ssid = wpa_s->max_match_sets;
+               wpa_dbg(wpa_s, MSG_DEBUG, "PNO: Too many SSIDs to match");
+       }
+       params.filter_ssids = os_calloc(num_match_ssid,
+                                       sizeof(struct wpa_driver_scan_filter));
+       if (params.filter_ssids == NULL)
+               return -1;
+
+       i = 0;
+       prio = 0;
+       ssid = wpa_s->conf->pssid[prio];
+       while (ssid) {
+               if (!wpas_network_disabled(wpa_s, ssid)) {
+                       if (ssid->scan_ssid && params.num_ssids < num_ssid) {
+                               params.ssids[params.num_ssids].ssid =
+                                       ssid->ssid;
+                               params.ssids[params.num_ssids].ssid_len =
+                                        ssid->ssid_len;
+                               params.num_ssids++;
+                       }
+                       os_memcpy(params.filter_ssids[i].ssid, ssid->ssid,
+                                 ssid->ssid_len);
+                       params.filter_ssids[i].ssid_len = ssid->ssid_len;
+                       params.num_filter_ssids++;
+                       i++;
+                       if (i == num_match_ssid)
+                               break;
+               }
+               if (ssid->pnext)
+                       ssid = ssid->pnext;
+               else if (prio + 1 == wpa_s->conf->num_prio)
+                       break;
+               else
+                       ssid = wpa_s->conf->pssid[++prio];
+       }
+
+       if (wpa_s->conf->filter_rssi)
+               params.filter_rssi = wpa_s->conf->filter_rssi;
+
+       interval = wpa_s->conf->sched_scan_interval ?
+               wpa_s->conf->sched_scan_interval : 10;
+
+       if (params.freqs == NULL && wpa_s->manual_sched_scan_freqs) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Limit sched scan to specified channels");
+               params.freqs = wpa_s->manual_sched_scan_freqs;
+       }
+
+       if (wpa_s->mac_addr_rand_enable & MAC_ADDR_RAND_PNO) {
+               params.mac_addr_rand = 1;
+               if (wpa_s->mac_addr_pno) {
+                       params.mac_addr = wpa_s->mac_addr_pno;
+                       params.mac_addr_mask = wpa_s->mac_addr_pno + ETH_ALEN;
+               }
+       }
+
+       ret = wpa_supplicant_start_sched_scan(wpa_s, &params, interval);
+       os_free(params.filter_ssids);
+       if (ret == 0)
+               wpa_s->pno = 1;
+       else
+               wpa_msg(wpa_s, MSG_ERROR, "Failed to schedule PNO");
+       return ret;
+}
+
+
+int wpas_stop_pno(struct wpa_supplicant *wpa_s)
+{
+       int ret = 0;
+
+       if (!wpa_s->pno)
+               return 0;
+
+       ret = wpa_supplicant_stop_sched_scan(wpa_s);
+
+       wpa_s->pno = 0;
+       wpa_s->pno_sched_pending = 0;
+
+       if (wpa_s->wpa_state == WPA_SCANNING)
+               wpa_supplicant_req_scan(wpa_s, 0, 0);
+
+       return ret;
+}
+
+
+void wpas_mac_addr_rand_scan_clear(struct wpa_supplicant *wpa_s,
+                                   unsigned int type)
+{
+       type &= MAC_ADDR_RAND_ALL;
+       wpa_s->mac_addr_rand_enable &= ~type;
+
+       if (type & MAC_ADDR_RAND_SCAN) {
+               os_free(wpa_s->mac_addr_scan);
+               wpa_s->mac_addr_scan = NULL;
+       }
+
+       if (type & MAC_ADDR_RAND_SCHED_SCAN) {
+               os_free(wpa_s->mac_addr_sched_scan);
+               wpa_s->mac_addr_sched_scan = NULL;
+       }
+
+       if (type & MAC_ADDR_RAND_PNO) {
+               os_free(wpa_s->mac_addr_pno);
+               wpa_s->mac_addr_pno = NULL;
+       }
+}
+
+
+int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s,
+                               unsigned int type, const u8 *addr,
+                               const u8 *mask)
+{
+       u8 *tmp = NULL;
+
+       wpas_mac_addr_rand_scan_clear(wpa_s, type);
+
+       if (addr) {
+               tmp = os_malloc(2 * ETH_ALEN);
+               if (!tmp)
+                       return -1;
+               os_memcpy(tmp, addr, ETH_ALEN);
+               os_memcpy(tmp + ETH_ALEN, mask, ETH_ALEN);
+       }
+
+       if (type == MAC_ADDR_RAND_SCAN) {
+               wpa_s->mac_addr_scan = tmp;
+       } else if (type == MAC_ADDR_RAND_SCHED_SCAN) {
+               wpa_s->mac_addr_sched_scan = tmp;
+       } else if (type == MAC_ADDR_RAND_PNO) {
+               wpa_s->mac_addr_pno = tmp;
+       } else {
+               wpa_printf(MSG_INFO,
+                          "scan: Invalid MAC randomization type=0x%x",
+                          type);
+               os_free(tmp);
+               return -1;
+       }
+
+       wpa_s->mac_addr_rand_enable |= type;
+       return 0;
+}
old mode 100644 (file)
new mode 100755 (executable)
index e892479..7650f5a
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - Scanning
- * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -15,6 +15,7 @@ int wpa_supplicant_delayed_sched_scan(struct wpa_supplicant *wpa_s,
                                      int sec, int usec);
 int wpa_supplicant_req_sched_scan(struct wpa_supplicant *wpa_s);
 void wpa_supplicant_cancel_scan(struct wpa_supplicant *wpa_s);
+void wpa_supplicant_cancel_delayed_sched_scan(struct wpa_supplicant *wpa_s);
 void wpa_supplicant_cancel_sched_scan(struct wpa_supplicant *wpa_s);
 void wpa_supplicant_notify_scanning(struct wpa_supplicant *wpa_s,
                                    int scanning);
@@ -28,6 +29,8 @@ int wpa_supplicant_update_scan_results(struct wpa_supplicant *wpa_s);
 const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie);
 const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res,
                                  u32 vendor_type);
+const u8 * wpa_scan_get_vendor_ie_beacon(const struct wpa_scan_res *res,
+                                        u32 vendor_type);
 struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res,
                                             u32 vendor_type);
 int wpa_supplicant_filter_bssid_match(struct wpa_supplicant *wpa_s,
@@ -36,5 +39,20 @@ void wpa_supplicant_update_scan_int(struct wpa_supplicant *wpa_s, int sec);
 void scan_only_handler(struct wpa_supplicant *wpa_s,
                       struct wpa_scan_results *scan_res);
 int wpas_scan_scheduled(struct wpa_supplicant *wpa_s);
+int wpa_supplicant_start_sched_scan(struct wpa_supplicant *wpa_s,
+                                   struct wpa_driver_scan_params *params,
+                                   int interval);
+int wpa_supplicant_stop_sched_scan(struct wpa_supplicant *wpa_s);
+struct wpa_driver_scan_params *
+wpa_scan_clone_params(const struct wpa_driver_scan_params *src);
+void wpa_scan_free_params(struct wpa_driver_scan_params *params);
+int wpas_start_pno(struct wpa_supplicant *wpa_s);
+int wpas_stop_pno(struct wpa_supplicant *wpa_s);
+
+void wpas_mac_addr_rand_scan_clear(struct wpa_supplicant *wpa_s,
+                                  unsigned int type);
+int wpas_mac_addr_rand_scan_set(struct wpa_supplicant *wpa_s,
+                               unsigned int type, const u8 *addr,
+                               const u8 *mask);
 
 #endif /* SCAN_H */
old mode 100644 (file)
new mode 100755 (executable)
index 0371628..1788113
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant - SME
- * Copyright (c) 2009-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -46,7 +46,7 @@ static int index_within_array(const int *array, int idx)
 {
        int i;
        for (i = 0; i < idx; i++) {
-               if (array[i] == -1)
+               if (array[i] <= 0)
                        return 0;
        }
        return 1;
@@ -56,9 +56,9 @@ static int index_within_array(const int *array, int idx)
 static int sme_set_sae_group(struct wpa_supplicant *wpa_s)
 {
        int *groups = wpa_s->conf->sae_groups;
-       int default_groups[] = { 19, 20, 21, 25, 26 };
+       int default_groups[] = { 19, 20, 21, 25, 26, 0 };
 
-       if (!groups)
+       if (!groups || groups[0] <= 0)
                groups = default_groups;
 
        /* Configuration may have changed, so validate current index */
@@ -137,6 +137,60 @@ static struct wpabuf * sme_auth_build_sae_confirm(struct wpa_supplicant *wpa_s)
 #endif /* CONFIG_SAE */
 
 
+/**
+ * sme_auth_handle_rrm - Handle RRM aspects of current authentication attempt
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @bss: Pointer to the bss which is the target of authentication attempt
+ */
+static void sme_auth_handle_rrm(struct wpa_supplicant *wpa_s,
+                               struct wpa_bss *bss)
+{
+       const u8 rrm_ie_len = 5;
+       u8 *pos;
+       const u8 *rrm_ie;
+
+       wpa_s->rrm.rrm_used = 0;
+
+       wpa_printf(MSG_DEBUG,
+                  "RRM: Determining whether RRM can be used - device support: 0x%x",
+                  wpa_s->drv_rrm_flags);
+
+       rrm_ie = wpa_bss_get_ie(bss, WLAN_EID_RRM_ENABLED_CAPABILITIES);
+       if (!rrm_ie || !(bss->caps & IEEE80211_CAP_RRM)) {
+               wpa_printf(MSG_DEBUG, "RRM: No RRM in network");
+               return;
+       }
+
+       if (!(wpa_s->drv_rrm_flags &
+             WPA_DRIVER_FLAGS_DS_PARAM_SET_IE_IN_PROBES) ||
+           !(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_QUIET)) {
+               wpa_printf(MSG_DEBUG,
+                          "RRM: Insufficient RRM support in driver - do not use RRM");
+               return;
+       }
+
+       if (sizeof(wpa_s->sme.assoc_req_ie) <
+           wpa_s->sme.assoc_req_ie_len + rrm_ie_len + 2) {
+               wpa_printf(MSG_INFO,
+                          "RRM: Unable to use RRM, no room for RRM IE");
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "RRM: Adding RRM IE to Association Request");
+       pos = wpa_s->sme.assoc_req_ie + wpa_s->sme.assoc_req_ie_len;
+       os_memset(pos, 0, 2 + rrm_ie_len);
+       *pos++ = WLAN_EID_RRM_ENABLED_CAPABILITIES;
+       *pos++ = rrm_ie_len;
+
+       /* Set supported capabilites flags */
+       if (wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)
+               *pos |= WLAN_RRM_CAPS_LINK_MEASUREMENT;
+
+       wpa_s->sme.assoc_req_ie_len += rrm_ie_len + 2;
+       wpa_s->rrm.rrm_used = 1;
+}
+
+
 static void sme_send_authentication(struct wpa_supplicant *wpa_s,
                                    struct wpa_bss *bss, struct wpa_ssid *ssid,
                                    int start)
@@ -151,15 +205,19 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
 #endif /* CONFIG_IEEE80211R */
        int i, bssid_changed;
        struct wpabuf *resp = NULL;
-       u8 ext_capab[10];
+       u8 ext_capab[18];
        int ext_capab_len;
+       int skip_auth;
 
        if (bss == NULL) {
                wpa_msg(wpa_s, MSG_ERROR, "SME: No scan result available for "
                        "the network");
+               wpas_connect_work_done(wpa_s);
                return;
        }
 
+       skip_auth = wpa_s->conf->reassoc_same_bss_optim &&
+               wpa_s->reassoc_same_bss;
        wpa_s->current_bss = bss;
 
        os_memset(&params, 0, sizeof(params));
@@ -198,17 +256,22 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
                        "0x%x", params.auth_alg);
        }
 #ifdef CONFIG_SAE
+       wpa_s->sme.sae_pmksa_caching = 0;
        if (wpa_key_mgmt_sae(ssid->key_mgmt)) {
                const u8 *rsn;
                struct wpa_ie_data ied;
 
                rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
-               if (rsn &&
-                   wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0) {
-                       if (wpa_key_mgmt_sae(ied.key_mgmt)) {
-                               wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg");
-                               params.auth_alg = WPA_AUTH_ALG_SAE;
-                       }
+               if (!rsn) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "SAE enabled, but target BSS does not advertise RSN");
+               } else if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ied) == 0 &&
+                          wpa_key_mgmt_sae(ied.key_mgmt)) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "Using SAE auth_alg");
+                       params.auth_alg = WPA_AUTH_ALG_SAE;
+               } else {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "SAE enabled, but target BSS does not advertise SAE AKM for RSN");
                }
        }
 #endif /* CONFIG_SAE */
@@ -237,13 +300,14 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
                if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
                                            wpa_s->current_ssid,
                                            try_opportunistic) == 0)
-                       eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1);
+                       eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
                wpa_s->sme.assoc_req_ie_len = sizeof(wpa_s->sme.assoc_req_ie);
                if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
                                              wpa_s->sme.assoc_req_ie,
                                              &wpa_s->sme.assoc_req_ie_len)) {
                        wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
                                "key management and encryption suites");
+                       wpas_connect_work_done(wpa_s);
                        return;
                }
        } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) &&
@@ -263,6 +327,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
                        wpa_msg(wpa_s, MSG_WARNING, "SME: Failed to set WPA "
                                "key management and encryption suites (no "
                                "scan results)");
+                       wpas_connect_work_done(wpa_s);
                        return;
                }
 #ifdef CONFIG_WPS
@@ -322,8 +387,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
 #endif /* CONFIG_IEEE80211R */
 
 #ifdef CONFIG_IEEE80211W
-       wpa_s->sme.mfp = ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
-               wpa_s->conf->pmf : ssid->ieee80211w;
+       wpa_s->sme.mfp = wpas_get_ssid_pmf(wpa_s, ssid);
        if (wpa_s->sme.mfp != NO_MGMT_FRAME_PROTECTION) {
                const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
                struct wpa_ie_data _ie;
@@ -357,17 +421,25 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
                struct wpabuf *hs20;
                hs20 = wpabuf_alloc(20);
                if (hs20) {
-                       wpas_hs20_add_indication(hs20);
-                       os_memcpy(wpa_s->sme.assoc_req_ie +
-                                 wpa_s->sme.assoc_req_ie_len,
-                                 wpabuf_head(hs20), wpabuf_len(hs20));
-                       wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20);
+                       int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
+                       size_t len;
+
+                       wpas_hs20_add_indication(hs20, pps_mo_id);
+                       len = sizeof(wpa_s->sme.assoc_req_ie) -
+                               wpa_s->sme.assoc_req_ie_len;
+                       if (wpabuf_len(hs20) <= len) {
+                               os_memcpy(wpa_s->sme.assoc_req_ie +
+                                         wpa_s->sme.assoc_req_ie_len,
+                                         wpabuf_head(hs20), wpabuf_len(hs20));
+                               wpa_s->sme.assoc_req_ie_len += wpabuf_len(hs20);
+                       }
                        wpabuf_free(hs20);
                }
        }
 #endif /* CONFIG_HS20 */
 
-       ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab);
+       ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
+                                            sizeof(ext_capab));
        if (ext_capab_len > 0) {
                u8 *pos = wpa_s->sme.assoc_req_ie;
                if (wpa_s->sme.assoc_req_ie_len > 0 && pos[0] == WLAN_EID_RSN)
@@ -379,15 +451,42 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
                os_memcpy(pos, ext_capab, ext_capab_len);
        }
 
+       if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
+               struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
+               size_t len;
+
+               len = sizeof(wpa_s->sme.assoc_req_ie) -
+                       wpa_s->sme.assoc_req_ie_len;
+               if (wpabuf_len(buf) <= len) {
+                       os_memcpy(wpa_s->sme.assoc_req_ie +
+                                 wpa_s->sme.assoc_req_ie_len,
+                                 wpabuf_head(buf), wpabuf_len(buf));
+                       wpa_s->sme.assoc_req_ie_len += wpabuf_len(buf);
+               }
+       }
+
+       sme_auth_handle_rrm(wpa_s, bss);
+
 #ifdef CONFIG_SAE
-       if (params.auth_alg == WPA_AUTH_ALG_SAE) {
+       if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE &&
+           pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid, ssid, 0) == 0)
+       {
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "PMKSA cache entry found - try to use PMKSA caching instead of new SAE authentication");
+               params.auth_alg = WPA_AUTH_ALG_OPEN;
+               wpa_s->sme.sae_pmksa_caching = 1;
+       }
+
+       if (!skip_auth && params.auth_alg == WPA_AUTH_ALG_SAE) {
                if (start)
                        resp = sme_auth_build_sae_commit(wpa_s, ssid,
                                                         bss->bssid);
                else
                        resp = sme_auth_build_sae_confirm(wpa_s);
-               if (resp == NULL)
+               if (resp == NULL) {
+                       wpas_connection_failed(wpa_s, bss->bssid);
                        return;
+               }
                params.sae_data = wpabuf_head(resp);
                params.sae_data_len = wpabuf_len(resp);
                wpa_s->sme.sae.state = start ? SAE_COMMITTED : SAE_CONFIRMED;
@@ -410,6 +509,41 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
        if (old_ssid != wpa_s->current_ssid)
                wpas_notify_network_changed(wpa_s);
 
+#ifdef CONFIG_P2P
+       /*
+        * If multi-channel concurrency is not supported, check for any
+        * frequency conflict. In case of any frequency conflict, remove the
+        * least prioritized connection.
+        */
+       if (wpa_s->num_multichan_concurrent < 2) {
+               int freq, num;
+               num = get_shared_radio_freqs(wpa_s, &freq, 1);
+               if (num > 0 && freq > 0 && freq != params.freq) {
+                       wpa_printf(MSG_DEBUG,
+                                  "Conflicting frequency found (%d != %d)",
+                                  freq, params.freq);
+                       if (wpas_p2p_handle_frequency_conflicts(wpa_s,
+                                                               params.freq,
+                                                               ssid) < 0) {
+                               wpas_connection_failed(wpa_s, bss->bssid);
+                               wpa_supplicant_mark_disassoc(wpa_s);
+                               wpabuf_free(resp);
+                               wpas_connect_work_done(wpa_s);
+                               return;
+                       }
+               }
+       }
+#endif /* CONFIG_P2P */
+
+       if (skip_auth) {
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "SME: Skip authentication step on reassoc-to-same-BSS");
+               wpabuf_free(resp);
+               sme_associate(wpa_s, ssid->mode, bss->bssid, WLAN_AUTH_OPEN);
+               return;
+       }
+
+
        wpa_s->sme.auth_alg = params.auth_alg;
        if (wpa_drv_authenticate(wpa_s, &params) < 0) {
                wpa_msg(wpa_s, MSG_INFO, "SME: Authentication request to the "
@@ -417,6 +551,7 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
                wpas_connection_failed(wpa_s, bss->bssid);
                wpa_supplicant_mark_disassoc(wpa_s);
                wpabuf_free(resp);
+               wpas_connect_work_done(wpa_s);
                return;
        }
 
@@ -432,14 +567,71 @@ static void sme_send_authentication(struct wpa_supplicant *wpa_s,
 }
 
 
+static void sme_auth_start_cb(struct wpa_radio_work *work, int deinit)
+{
+       struct wpa_connect_work *cwork = work->ctx;
+       struct wpa_supplicant *wpa_s = work->wpa_s;
+
+       if (deinit) {
+               if (work->started)
+                       wpa_s->connect_work = NULL;
+
+               wpas_connect_work_free(cwork);
+               return;
+       }
+
+       wpa_s->connect_work = work;
+
+       if (cwork->bss_removed ||
+           !wpas_valid_bss_ssid(wpa_s, cwork->bss, cwork->ssid)) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "SME: BSS/SSID entry for authentication not valid anymore - drop connection attempt");
+               wpas_connect_work_done(wpa_s);
+               return;
+       }
+
+       sme_send_authentication(wpa_s, cwork->bss, cwork->ssid, 1);
+}
+
+
 void sme_authenticate(struct wpa_supplicant *wpa_s,
                      struct wpa_bss *bss, struct wpa_ssid *ssid)
 {
+       struct wpa_connect_work *cwork;
+
+       if (bss == NULL || ssid == NULL)
+               return;
+       if (wpa_s->connect_work) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "SME: Reject sme_authenticate() call since connect_work exist");
+               return;
+       }
+
+       if (radio_work_pending(wpa_s, "sme-connect")) {
+               /*
+                * The previous sme-connect work might no longer be valid due to
+                * the fact that the BSS list was updated. In addition, it makes
+                * sense to adhere to the 'newer' decision.
+                */
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "SME: Remove previous pending sme-connect");
+               radio_remove_works(wpa_s, "sme-connect", 0);
+       }
+
+       cwork = os_zalloc(sizeof(*cwork));
+       if (cwork == NULL)
+               return;
+       cwork->bss = bss;
+       cwork->ssid = ssid;
+       cwork->sme = 1;
+
 #ifdef CONFIG_SAE
        wpa_s->sme.sae.state = SAE_NOTHING;
        wpa_s->sme.sae.send_confirm = 0;
+       wpa_s->sme.sae_group_index = 0;
 #endif /* CONFIG_SAE */
-       sme_send_authentication(wpa_s, bss, ssid, 1);
+
+       if (radio_add_work(wpa_s, bss->freq, "sme-connect", 1,
+                          sme_auth_start_cb, cwork) < 0)
+               wpas_connect_work_free(cwork);
 }
 
 
@@ -448,6 +640,8 @@ void sme_authenticate(struct wpa_supplicant *wpa_s,
 static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
                        u16 status_code, const u8 *data, size_t len)
 {
+       int *groups;
+
        wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE authentication transaction %u "
                "status code %u", auth_transaction, status_code);
 
@@ -455,10 +649,32 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
            status_code == WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ &&
            wpa_s->sme.sae.state == SAE_COMMITTED &&
            wpa_s->current_bss && wpa_s->current_ssid) {
-               wpa_dbg(wpa_s, MSG_DEBUG, "SME: SAE anti-clogging token "
-                       "requested");
+               int default_groups[] = { 19, 20, 21, 25, 26, 0 };
+               u16 group;
+
+               groups = wpa_s->conf->sae_groups;
+               if (!groups || groups[0] <= 0)
+                       groups = default_groups;
+
+               if (len < sizeof(le16)) {
+                       wpa_dbg(wpa_s, MSG_DEBUG,
+                               "SME: Too short SAE anti-clogging token request");
+                       return -1;
+               }
+               group = WPA_GET_LE16(data);
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "SME: SAE anti-clogging token requested (group %u)",
+                       group);
+               if (sae_group_allowed(&wpa_s->sme.sae, groups, group) !=
+                   WLAN_STATUS_SUCCESS) {
+                       wpa_dbg(wpa_s, MSG_ERROR,
+                               "SME: SAE group %u of anti-clogging request is invalid",
+                               group);
+                       return -1;
+               }
                wpabuf_free(wpa_s->sme.sae_token);
-               wpa_s->sme.sae_token = wpabuf_alloc_copy(data, len);
+               wpa_s->sme.sae_token = wpabuf_alloc_copy(data + sizeof(le16),
+                                                        len - sizeof(le16));
                sme_send_authentication(wpa_s, wpa_s->current_bss,
                                        wpa_s->current_ssid, 1);
                return 0;
@@ -482,15 +698,18 @@ static int sme_sae_auth(struct wpa_supplicant *wpa_s, u16 auth_transaction,
                return -1;
 
        if (auth_transaction == 1) {
+               groups = wpa_s->conf->sae_groups;
+
                wpa_dbg(wpa_s, MSG_DEBUG, "SME SAE commit");
                if (wpa_s->current_bss == NULL ||
                    wpa_s->current_ssid == NULL)
                        return -1;
                if (wpa_s->sme.sae.state != SAE_COMMITTED)
                        return -1;
+               if (groups && groups[0] <= 0)
+                       groups = NULL;
                if (sae_parse_commit(&wpa_s->sme.sae, data, len, NULL, NULL,
-                                    wpa_s->conf->sae_groups) !=
-                   WLAN_STATUS_SUCCESS)
+                                    groups) != WLAN_STATUS_SUCCESS)
                        return -1;
 
                if (sae_process_commit(&wpa_s->sme.sae) < 0) {
@@ -568,7 +787,8 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
 
                wpa_printf(MSG_DEBUG, "SME: SAE completed - setting PMK for "
                           "4-way handshake");
-               wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN);
+               wpa_sm_set_pmk(wpa_s->wpa, wpa_s->sme.sae.pmk, PMK_LEN,
+                              wpa_s->pending_bssid);
        }
 #endif /* CONFIG_SAE */
 
@@ -585,6 +805,8 @@ void sme_event_auth(struct wpa_supplicant *wpa_s, union wpa_event_data *data)
                        return;
                }
 
+               wpas_connect_work_done(wpa_s);
+
                switch (data->auth.auth_type) {
                case WLAN_AUTH_OPEN:
                        wpa_s->current_ssid->auth_alg = WPA_AUTH_ALG_SHARED;
@@ -641,15 +863,16 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
        params.bssid = bssid;
        params.ssid = wpa_s->sme.ssid;
        params.ssid_len = wpa_s->sme.ssid_len;
-       params.freq = wpa_s->sme.freq;
+       params.freq.freq = wpa_s->sme.freq;
        params.bg_scan_period = wpa_s->current_ssid ?
                wpa_s->current_ssid->bg_scan_period : -1;
        params.wpa_ie = wpa_s->sme.assoc_req_ie_len ?
                wpa_s->sme.assoc_req_ie : NULL;
        params.wpa_ie_len = wpa_s->sme.assoc_req_ie_len;
-       params.pairwise_suite =
-               wpa_cipher_to_suite_driver(wpa_s->pairwise_cipher);
-       params.group_suite = wpa_cipher_to_suite_driver(wpa_s->group_cipher);
+       params.pairwise_suite = wpa_s->pairwise_cipher;
+       params.group_suite = wpa_s->group_cipher;
+       params.key_mgmt_suite = wpa_s->key_mgmt;
+       params.wpa_proto = wpa_s->wpa_proto;
 #ifdef CONFIG_HT_OVERRIDES
        os_memset(&htcaps, 0, sizeof(htcaps));
        os_memset(&htcaps_mask, 0, sizeof(htcaps_mask));
@@ -672,13 +895,14 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
 #endif /* CONFIG_IEEE80211R */
        params.mode = mode;
        params.mgmt_frame_protection = wpa_s->sme.mfp;
+       params.rrm_used = wpa_s->rrm.rrm_used;
        if (wpa_s->sme.prev_bssid_set)
                params.prev_bssid = wpa_s->sme.prev_bssid;
 
        wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR
                " (SSID='%s' freq=%d MHz)", MAC2STR(params.bssid),
                params.ssid ? wpa_ssid_txt(params.ssid, params.ssid_len) : "",
-               params.freq);
+               params.freq.freq);
 
        wpa_supplicant_set_state(wpa_s, WPA_ASSOCIATING);
 
@@ -696,6 +920,10 @@ void sme_associate(struct wpa_supplicant *wpa_s, enum wpas_mode mode,
                params.wpa_proto = WPA_PROTO_WPA;
                wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.wpa_ie - 2,
                                        elems.wpa_ie_len + 2);
+       } else if (elems.osen) {
+               params.wpa_proto = WPA_PROTO_OSEN;
+               wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, elems.osen - 2,
+                                       elems.osen_len + 2);
        } else
                wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
        if (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group)
@@ -775,6 +1003,27 @@ void sme_event_assoc_reject(struct wpa_supplicant *wpa_s,
 
        eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
 
+#ifdef CONFIG_SAE
+       if (wpa_s->sme.sae_pmksa_caching && wpa_s->current_ssid &&
+           wpa_key_mgmt_sae(wpa_s->current_ssid->key_mgmt)) {
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "PMKSA caching attempt rejected - drop PMKSA cache entry and fall back to SAE authentication");
+               wpa_sm_aborted_cached(wpa_s->wpa);
+               wpa_sm_pmksa_cache_flush(wpa_s->wpa, wpa_s->current_ssid);
+               if (wpa_s->current_bss) {
+                       struct wpa_bss *bss = wpa_s->current_bss;
+                       struct wpa_ssid *ssid = wpa_s->current_ssid;
+
+                       wpa_drv_deauthenticate(wpa_s, wpa_s->pending_bssid,
+                                              WLAN_REASON_DEAUTH_LEAVING);
+                       wpas_connect_work_done(wpa_s);
+                       wpa_supplicant_mark_disassoc(wpa_s);
+                       wpa_supplicant_connect(wpa_s, bss, ssid);
+                       return;
+               }
+       }
+#endif /* CONFIG_SAE */
+
        /*
         * For now, unconditionally terminate the previous authentication. In
         * theory, this should not be needed, but mac80211 gets quite confused
@@ -805,7 +1054,7 @@ void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s,
 
 
 void sme_event_disassoc(struct wpa_supplicant *wpa_s,
-                       union wpa_event_data *data)
+                       struct disassoc_info *info)
 {
        wpa_dbg(wpa_s, MSG_DEBUG, "SME: Disassociation event received");
        if (wpa_s->sme.prev_bssid_set) {
@@ -875,6 +1124,21 @@ void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
 }
 
 
+void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s)
+{
+       wpa_s->sme.prev_bssid_set = 0;
+#ifdef CONFIG_SAE
+       wpabuf_free(wpa_s->sme.sae_token);
+       wpa_s->sme.sae_token = NULL;
+       sae_clear_data(&wpa_s->sme.sae);
+#endif /* CONFIG_SAE */
+#ifdef CONFIG_IEEE80211R
+       if (wpa_s->sme.ft_ies)
+               sme_update_ft_ies(wpa_s, NULL, NULL, 0);
+#endif /* CONFIG_IEEE80211R */
+}
+
+
 void sme_deinit(struct wpa_supplicant *wpa_s)
 {
        os_free(wpa_s->sme.ft_ies);
@@ -883,11 +1147,7 @@ void sme_deinit(struct wpa_supplicant *wpa_s)
 #ifdef CONFIG_IEEE80211W
        sme_stop_sa_query(wpa_s);
 #endif /* CONFIG_IEEE80211W */
-#ifdef CONFIG_SAE
-       wpabuf_free(wpa_s->sme.sae_token);
-       wpa_s->sme.sae_token = NULL;
-       sae_clear_data(&wpa_s->sme.sae);
-#endif /* CONFIG_SAE */
+       sme_clear_on_disassoc(wpa_s);
 
        eloop_cancel_timeout(sme_assoc_timer, wpa_s, NULL);
        eloop_cancel_timeout(sme_auth_timer, wpa_s, NULL);
@@ -903,8 +1163,11 @@ static void sme_send_2040_bss_coex(struct wpa_supplicant *wpa_s,
        struct ieee80211_2040_intol_chan_report *ic_report;
        struct wpabuf *buf;
 
-       wpa_printf(MSG_DEBUG, "SME: Send 20/40 BSS Coexistence to " MACSTR,
-                  MAC2STR(wpa_s->bssid));
+       wpa_printf(MSG_DEBUG, "SME: Send 20/40 BSS Coexistence to " MACSTR
+                  " (num_channels=%u num_intol=%u)",
+                  MAC2STR(wpa_s->bssid), num_channels, num_intol);
+       wpa_hexdump(MSG_DEBUG, "SME: 20/40 BSS Intolerant Channels",
+                   chan_list, num_channels);
 
        buf = wpabuf_alloc(2 + /* action.category + action_code */
                           sizeof(struct ieee80211_2040_bss_coex_ie) +
@@ -986,8 +1249,14 @@ int sme_proc_obss_scan(struct wpa_supplicant *wpa_s)
 
                ie = wpa_bss_get_ie(bss, WLAN_EID_HT_CAP);
                ht_cap = (ie && (ie[1] == 26)) ? WPA_GET_LE16(ie + 2) : 0;
+               wpa_printf(MSG_DEBUG, "SME OBSS scan BSS " MACSTR
+                          " freq=%u chan=%u ht_cap=0x%x",
+                          MAC2STR(bss->bssid), bss->freq, channel, ht_cap);
 
                if (!ht_cap || (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)) {
+                       if (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)
+                               num_intol++;
+
                        /* Check whether the channel is already considered */
                        for (i = 0; i < num_channels; i++) {
                                if (channel == chan_list[i])
@@ -996,9 +1265,6 @@ int sme_proc_obss_scan(struct wpa_supplicant *wpa_s)
                        if (i != num_channels)
                                continue;
 
-                       if (ht_cap & HT_CAP_INFO_40MHZ_INTOLERANT)
-                               num_intol++;
-
                        chan_list[num_channels++] = channel;
                }
        }
@@ -1023,28 +1289,72 @@ static struct hostapd_hw_modes * get_mode(struct hostapd_hw_modes *modes,
 }
 
 
-static void wpa_setband_scan_freqs_list(struct wpa_supplicant *wpa_s,
-                                       enum hostapd_hw_mode band,
-                                       struct wpa_driver_scan_params *params)
+static void wpa_obss_scan_freqs_list(struct wpa_supplicant *wpa_s,
+                                    struct wpa_driver_scan_params *params)
 {
-       /* Include only supported channels for the specified band */
+       /* Include only affected channels */
        struct hostapd_hw_modes *mode;
        int count, i;
+       int start, end;
 
-       mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, band);
+       mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes,
+                       HOSTAPD_MODE_IEEE80211G);
        if (mode == NULL) {
                /* No channels supported in this band - use empty list */
                params->freqs = os_zalloc(sizeof(int));
                return;
        }
 
+       if (wpa_s->sme.ht_sec_chan == HT_SEC_CHAN_UNKNOWN &&
+           wpa_s->current_bss) {
+               const u8 *ie;
+
+               ie = wpa_bss_get_ie(wpa_s->current_bss, WLAN_EID_HT_OPERATION);
+               if (ie && ie[1] >= 2) {
+                       u8 o;
+
+                       o = ie[3] & HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK;
+                       if (o == HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE)
+                               wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_ABOVE;
+                       else if (o == HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW)
+                               wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_BELOW;
+               }
+       }
+
+       start = wpa_s->assoc_freq - 10;
+       end = wpa_s->assoc_freq + 10;
+       switch (wpa_s->sme.ht_sec_chan) {
+       case HT_SEC_CHAN_UNKNOWN:
+               /* HT40+ possible on channels 1..9 */
+               if (wpa_s->assoc_freq <= 2452)
+                       start -= 20;
+               /* HT40- possible on channels 5-13 */
+               if (wpa_s->assoc_freq >= 2432)
+                       end += 20;
+               break;
+       case HT_SEC_CHAN_ABOVE:
+               end += 20;
+               break;
+       case HT_SEC_CHAN_BELOW:
+               start -= 20;
+               break;
+       }
+       wpa_printf(MSG_DEBUG,
+                  "OBSS: assoc_freq %d possible affected range %d-%d",
+                  wpa_s->assoc_freq, start, end);
+
        params->freqs = os_calloc(mode->num_channels + 1, sizeof(int));
        if (params->freqs == NULL)
                return;
        for (count = 0, i = 0; i < mode->num_channels; i++) {
+               int freq;
+
                if (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)
                        continue;
-               params->freqs[count++] = mode->channels[i].freq;
+               freq = mode->channels[i].freq;
+               if (freq - 10 >= end || freq + 10 <= start)
+                       continue; /* not affected */
+               params->freqs[count++] = freq;
        }
 }
 
@@ -1060,7 +1370,8 @@ static void sme_obss_scan_timeout(void *eloop_ctx, void *timeout_ctx)
        }
 
        os_memset(&params, 0, sizeof(params));
-       wpa_setband_scan_freqs_list(wpa_s, HOSTAPD_MODE_IEEE80211G, &params);
+       wpa_obss_scan_freqs_list(wpa_s, &params);
+       params.low_priority = 1;
        wpa_printf(MSG_DEBUG, "SME OBSS: Request an OBSS scan");
 
        if (wpa_supplicant_trigger_scan(wpa_s, &params))
@@ -1084,6 +1395,7 @@ void sme_sched_obss_scan(struct wpa_supplicant *wpa_s, int enable)
 
        eloop_cancel_timeout(sme_obss_scan_timeout, wpa_s, NULL);
        wpa_s->sme.sched_obss_scan = 0;
+       wpa_s->sme.ht_sec_chan = HT_SEC_CHAN_UNKNOWN;
        if (!enable)
                return;
 
@@ -1147,9 +1459,9 @@ static const unsigned int sa_query_retry_timeout = 201;
 static int sme_check_sa_query_timeout(struct wpa_supplicant *wpa_s)
 {
        u32 tu;
-       struct os_time now, passed;
-       os_get_time(&now);
-       os_time_sub(&now, &wpa_s->sme.sa_query_start, &passed);
+       struct os_reltime now, passed;
+       os_get_reltime(&now);
+       os_reltime_sub(&now, &wpa_s->sme.sa_query_start, &passed);
        tu = (passed.sec * 1000000 + passed.usec) / 1024;
        if (sa_query_max_timeout < tu) {
                wpa_dbg(wpa_s, MSG_DEBUG, "SME: SA Query timed out");
@@ -1199,13 +1511,16 @@ static void sme_sa_query_timer(void *eloop_ctx, void *timeout_ctx)
                return;
        if (wpa_s->sme.sa_query_count == 0) {
                /* Starting a new SA Query procedure */
-               os_get_time(&wpa_s->sme.sa_query_start);
+               os_get_reltime(&wpa_s->sme.sa_query_start);
        }
        trans_id = nbuf + wpa_s->sme.sa_query_count * WLAN_SA_QUERY_TR_ID_LEN;
        wpa_s->sme.sa_query_trans_id = nbuf;
        wpa_s->sme.sa_query_count++;
 
-       os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN);
+       if (os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN) < 0) {
+               wpa_printf(MSG_DEBUG, "Could not generate SA Query ID");
+               return;
+       }
 
        timeout = sa_query_retry_timeout;
        sec = ((timeout / 1000) * 1024) / 1000;
@@ -1238,13 +1553,12 @@ void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa,
                                 const u8 *da, u16 reason_code)
 {
        struct wpa_ssid *ssid;
+       struct os_reltime now;
 
        if (wpa_s->wpa_state != WPA_COMPLETED)
                return;
        ssid = wpa_s->current_ssid;
-       if (ssid == NULL ||
-           (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
-            wpa_s->conf->pmf : ssid->ieee80211w) == NO_MGMT_FRAME_PROTECTION)
+       if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION)
                return;
        if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0)
                return;
@@ -1254,6 +1568,12 @@ void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa,
        if (wpa_s->sme.sa_query_count > 0)
                return;
 
+       os_get_reltime(&now);
+       if (wpa_s->sme.last_unprot_disconnect.sec &&
+           !os_reltime_expired(&now, &wpa_s->sme.last_unprot_disconnect, 10))
+               return; /* limit SA Query procedure frequency */
+       wpa_s->sme.last_unprot_disconnect = now;
+
        wpa_dbg(wpa_s, MSG_DEBUG, "SME: Unprotected disconnect dropped - "
                "possible AP/STA state mismatch - trigger SA Query");
        sme_start_sa_query(wpa_s);
old mode 100644 (file)
new mode 100755 (executable)
index a7cc507..fd5c3b4
@@ -25,7 +25,7 @@ void sme_event_auth_timed_out(struct wpa_supplicant *wpa_s,
 void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s,
                               union wpa_event_data *data);
 void sme_event_disassoc(struct wpa_supplicant *wpa_s,
-                       union wpa_event_data *data);
+                       struct disassoc_info *info);
 void sme_event_unprot_disconnect(struct wpa_supplicant *wpa_s, const u8 *sa,
                                 const u8 *da, u16 reason_code);
 void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa,
@@ -33,6 +33,7 @@ void sme_sa_query_rx(struct wpa_supplicant *wpa_s, const u8 *sa,
 void sme_state_changed(struct wpa_supplicant *wpa_s);
 void sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
                                       const u8 *prev_pending_bssid);
+void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s);
 void sme_deinit(struct wpa_supplicant *wpa_s);
 
 int sme_proc_obss_scan(struct wpa_supplicant *wpa_s);
@@ -74,7 +75,7 @@ static inline void sme_event_assoc_timed_out(struct wpa_supplicant *wpa_s,
 }
 
 static inline void sme_event_disassoc(struct wpa_supplicant *wpa_s,
-                                     union wpa_event_data *data)
+                                     struct disassoc_info *info)
 {
 }
 
@@ -94,6 +95,10 @@ sme_disassoc_while_authenticating(struct wpa_supplicant *wpa_s,
 {
 }
 
+static inline void sme_clear_on_disassoc(struct wpa_supplicant *wpa_s)
+{
+}
+
 static inline void sme_deinit(struct wpa_supplicant *wpa_s)
 {
 }
index ba2be6f..39971f2 100644 (file)
 #include "ap/wpa_auth.h"
 
 
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-
-
 struct wpa {
        u8 auth_addr[ETH_ALEN];
        u8 supp_addr[ETH_ALEN];
@@ -298,7 +294,7 @@ static int auth_init_group(struct wpa *wpa)
 
 static int auth_init(struct wpa *wpa)
 {
-       wpa->auth = wpa_auth_sta_init(wpa->auth_group, wpa->supp_addr);
+       wpa->auth = wpa_auth_sta_init(wpa->auth_group, wpa->supp_addr, NULL);
        if (wpa->auth == NULL) {
                wpa_printf(MSG_DEBUG, "AUTH: wpa_auth_sta_init() failed");
                return -1;
old mode 100644 (file)
new mode 100755 (executable)
index b84cccc..4c9f98e
@@ -5,8 +5,6 @@ To do:
   authentication has been completed (cache scard data based on serial#(?)
   and try to optimize next connection if the same card is present for next
   auth)
-- on disconnect event, could try to associate with another AP if one is
-  present in scan results; would need to update scan results periodically..
 - if driver/hw is not WPA2 capable, must remove WPA_PROTO_RSN flag from
   ssid->proto fields to avoid detecting downgrade attacks when the driver
   is not reporting RSN IE, but msg 3/4 has one
@@ -24,14 +22,12 @@ To do:
   RFC 3748 Sect. 4.2
 - test compilation with gcc -W options (more warnings?)
   (Done once; number of unused function arguments still present)
-- add proper support for using dot11RSNAConfigSATimeout
-- ctrl_iface: get/set/remove blob
+- ctrl_iface: get/remove blob
 - use doc/docbook/*.sgml and docbook2{txt,html,pdf} to replace README and
   web pages including the same information.. i.e., have this information only
   in one page; how to build a PDF file with all the SGML included?
 - EAP-POTP/RSA SecurID profile (RFC 4793)
 - document wpa_gui build and consider adding it to 'make install'
-- test madwifi with pairwise=TKIP group=WEP104
 - consider merging hostapd and wpa_supplicant PMKSA cache implementations
 - consider redesigning pending EAP requests (identity/password/otp from
   ctrl_iface) by moving the retrying of the previous request into EAP
@@ -57,14 +53,11 @@ To do:
 - try to work around race in configuring PTK and sending msg 4/4 (some NDIS
   drivers with ndiswrapper end up not being able to complete 4-way handshake
   in some cases; extra delay before setting the key seems to help)
-- add wpa_secure_memzero() macro and secure implementation (volatile u8*) to
-  clear memory; this would be used to clear temporary buffers containing
-  private data (e.g., keys); the macro can be defined to NOP in order to save
-  space (i.e., no code should depend on the macro doing something)
 - make sure that TLS session cache is not shared between EAP types or if it
   is, that the cache entries are bound to only one EAP type; e.g., cache entry
   created with EAP-TLS must not be allowed to do fast re-auth with EAP-TTLS
-- consider moving eap_tls_build_ack() call into eap_tls_process_helper()
+- consider moving eap_peer_tls_build_ack() call into
+  eap_peer_tls_process_helper()
   (it seems to be called always if helper returns 1)
   * could need to modify eap_{ttls,peap,fast}_decrypt to do same
 - add support for fetching full user cert chain from Windows certificate
old mode 100644 (file)
new mode 100755 (executable)
index 92ca536..c363b21
@@ -16,6 +16,9 @@
 #include "wifi_display.h"
 
 
+#define WIFI_DISPLAY_SUBELEM_HEADER_LEN 3
+
+
 int wifi_display_init(struct wpa_global *global)
 {
        global->wifi_display = 1;
@@ -33,11 +36,42 @@ void wifi_display_deinit(struct wpa_global *global)
 }
 
 
+struct wpabuf * wifi_display_get_wfd_ie(struct wpa_global *global)
+{
+       struct wpabuf *ie;
+       size_t len;
+       int i;
+
+       if (global->p2p == NULL)
+               return NULL;
+
+       len = 0;
+       for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
+               if (global->wfd_subelem[i])
+                       len += wpabuf_len(global->wfd_subelem[i]);
+       }
+
+       ie = wpabuf_alloc(len);
+       if (ie == NULL)
+               return NULL;
+
+       for (i = 0; i < MAX_WFD_SUBELEMS; i++) {
+               if (global->wfd_subelem[i])
+                       wpabuf_put_buf(ie, global->wfd_subelem[i]);
+       }
+
+       return ie;
+}
+
+
 static int wifi_display_update_wfd_ie(struct wpa_global *global)
 {
        struct wpabuf *ie, *buf;
        size_t len, plen;
 
+       if (global->p2p == NULL)
+               return 0;
+
        wpa_printf(MSG_DEBUG, "WFD: Update WFD IE");
 
        if (!global->wifi_display) {
@@ -199,15 +233,31 @@ int wifi_display_subelem_set(struct wpa_global *global, char *cmd)
        if (pos == NULL)
                return -1;
        *pos++ = '\0';
-       subelem = atoi(cmd);
-       if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
-               return -1;
 
        len = os_strlen(pos);
        if (len & 1)
                return -1;
        len /= 2;
 
+       if (os_strcmp(cmd, "all") == 0) {
+               int res;
+
+               e = wpabuf_alloc(len);
+               if (e == NULL)
+                       return -1;
+               if (hexstr2bin(pos, wpabuf_put(e, len), len) < 0) {
+                       wpabuf_free(e);
+                       return -1;
+               }
+               res = wifi_display_subelem_set_from_ies(global, e);
+               wpabuf_free(e);
+               return res;
+       }
+
+       subelem = atoi(cmd);
+       if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
+               return -1;
+
        if (len == 0) {
                /* Clear subelement */
                e = NULL;
@@ -232,11 +282,78 @@ int wifi_display_subelem_set(struct wpa_global *global, char *cmd)
 }
 
 
+int wifi_display_subelem_set_from_ies(struct wpa_global *global,
+                                     struct wpabuf *ie)
+{
+       int subelements[MAX_WFD_SUBELEMS] = {};
+       const u8 *pos, *end;
+       unsigned int len, subelem;
+       struct wpabuf *e;
+
+       wpa_printf(MSG_DEBUG, "WFD IEs set: %p - %lu",
+                  ie, ie ? (unsigned long) wpabuf_len(ie) : 0);
+
+       if (ie == NULL || wpabuf_len(ie) < 6)
+               return -1;
+
+       pos = wpabuf_head(ie);
+       end = pos + wpabuf_len(ie);
+
+       while (end > pos) {
+               if (pos + 3 > end)
+                       break;
+
+               len = WPA_GET_BE16(pos + 1) + 3;
+
+               wpa_printf(MSG_DEBUG, "WFD Sub-Element ID %d - len %d",
+                          *pos, len - 3);
+
+               if (len > (unsigned int) (end - pos))
+                       break;
+
+               subelem = *pos;
+               if (subelem < MAX_WFD_SUBELEMS && subelements[subelem] == 0) {
+                       e = wpabuf_alloc_copy(pos, len);
+                       if (e == NULL)
+                               return -1;
+
+                       wpabuf_free(global->wfd_subelem[subelem]);
+                       global->wfd_subelem[subelem] = e;
+                       subelements[subelem] = 1;
+               }
+
+               pos += len;
+       }
+
+       for (subelem = 0; subelem < MAX_WFD_SUBELEMS; subelem++) {
+               if (subelements[subelem] == 0) {
+                       wpabuf_free(global->wfd_subelem[subelem]);
+                       global->wfd_subelem[subelem] = NULL;
+               }
+       }
+
+       return wifi_display_update_wfd_ie(global);
+}
+
+
 int wifi_display_subelem_get(struct wpa_global *global, char *cmd,
                             char *buf, size_t buflen)
 {
        int subelem;
 
+       if (os_strcmp(cmd, "all") == 0) {
+               struct wpabuf *ie;
+               int res;
+
+               ie = wifi_display_get_wfd_ie(global);
+               if (ie == NULL)
+                       return 0;
+               res = wpa_snprintf_hex(buf, buflen, wpabuf_head(ie),
+                                      wpabuf_len(ie));
+               wpabuf_free(ie);
+               return res;
+       }
+
        subelem = atoi(cmd);
        if (subelem < 0 || subelem >= MAX_WFD_SUBELEMS)
                return -1;
@@ -249,3 +366,53 @@ int wifi_display_subelem_get(struct wpa_global *global, char *cmd,
                                1,
                                wpabuf_len(global->wfd_subelem[subelem]) - 1);
 }
+
+
+char * wifi_display_subelem_hex(const struct wpabuf *wfd_subelems, u8 id)
+{
+       char *subelem = NULL;
+       const u8 *buf;
+       size_t buflen;
+       size_t i = 0;
+       u16 elen;
+
+       if (!wfd_subelems)
+               return NULL;
+
+       buf = wpabuf_head_u8(wfd_subelems);
+       if (!buf)
+               return NULL;
+
+       buflen = wpabuf_len(wfd_subelems);
+
+       while (i + WIFI_DISPLAY_SUBELEM_HEADER_LEN < buflen) {
+               elen = WPA_GET_BE16(buf + i + 1);
+               if (i + WIFI_DISPLAY_SUBELEM_HEADER_LEN + elen > buflen)
+                       break; /* truncated subelement */
+
+               if (buf[i] == id) {
+                       /*
+                        * Limit explicitly to an arbitrary length to avoid
+                        * unnecessarily large allocations. In practice, this
+                        * is limited to maximum frame length anyway, so the
+                        * maximum memory allocation here is not really that
+                        * large. Anyway, the Wi-Fi Display subelements that
+                        * are fetched with this function are even shorter.
+                        */
+                       if (elen > 1000)
+                               break;
+                       subelem = os_zalloc(2 * elen + 1);
+                       if (!subelem)
+                               return NULL;
+                       wpa_snprintf_hex(subelem, 2 * elen + 1,
+                                        buf + i +
+                                        WIFI_DISPLAY_SUBELEM_HEADER_LEN,
+                                        elen);
+                       break;
+               }
+
+               i += elen + WIFI_DISPLAY_SUBELEM_HEADER_LEN;
+       }
+
+       return subelem;
+}
old mode 100644 (file)
new mode 100755 (executable)
index b75d4f2..0966bdb
 int wifi_display_init(struct wpa_global *global);
 void wifi_display_deinit(struct wpa_global *global);
 void wifi_display_enable(struct wpa_global *global, int enabled);
+struct wpabuf *wifi_display_get_wfd_ie(struct wpa_global *global);
 int wifi_display_subelem_set(struct wpa_global *global, char *cmd);
+int wifi_display_subelem_set_from_ies(struct wpa_global *global,
+                                     struct wpabuf *ie);
 int wifi_display_subelem_get(struct wpa_global *global, char *cmd,
                             char *buf, size_t buflen);
+char * wifi_display_subelem_hex(const struct wpabuf *wfd_subelems, u8 id);
 
 #endif /* WIFI_DISPLAY_H */
old mode 100644 (file)
new mode 100755 (executable)
diff --git a/wpa_supplicant/wmm_ac.c b/wpa_supplicant/wmm_ac.c
new file mode 100755 (executable)
index 0000000..5625d36
--- /dev/null
@@ -0,0 +1,995 @@
+/*
+ * Wi-Fi Multimedia Admission Control (WMM-AC)
+ * Copyright(c) 2014, Intel Mobile Communication GmbH.
+ * Copyright(c) 2014, Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "utils/common.h"
+#include "utils/list.h"
+#include "utils/eloop.h"
+#include "common/ieee802_11_common.h"
+#include "wpa_supplicant_i.h"
+#include "bss.h"
+#include "driver_i.h"
+#include "wmm_ac.h"
+
+static void wmm_ac_addts_req_timeout(void *eloop_ctx, void *timeout_ctx);
+
+static const enum wmm_ac up_to_ac[8] = {
+       WMM_AC_BK,
+       WMM_AC_BE,
+       WMM_AC_BE,
+       WMM_AC_BK,
+       WMM_AC_VI,
+       WMM_AC_VI,
+       WMM_AC_VO,
+       WMM_AC_VO
+};
+
+
+static inline u8 wmm_ac_get_tsid(const struct wmm_tspec_element *tspec)
+{
+       return (tspec->ts_info[0] >> 1) & 0x0f;
+}
+
+
+static u8 wmm_ac_get_direction(const struct wmm_tspec_element *tspec)
+{
+       return (tspec->ts_info[0] >> 5) & 0x03;
+}
+
+
+static u8 wmm_ac_get_user_priority(const struct wmm_tspec_element *tspec)
+{
+       return (tspec->ts_info[1] >> 3) & 0x07;
+}
+
+
+static u8 wmm_ac_direction_to_idx(u8 direction)
+{
+       switch (direction) {
+       case WMM_AC_DIR_UPLINK:
+               return TS_DIR_IDX_UPLINK;
+       case WMM_AC_DIR_DOWNLINK:
+               return TS_DIR_IDX_DOWNLINK;
+       case WMM_AC_DIR_BIDIRECTIONAL:
+               return TS_DIR_IDX_BIDI;
+       default:
+               wpa_printf(MSG_ERROR, "Invalid direction: %d", direction);
+               return WMM_AC_DIR_UPLINK;
+       }
+}
+
+
+static int wmm_ac_add_ts(struct wpa_supplicant *wpa_s, const u8 *addr,
+                        const struct wmm_tspec_element *tspec)
+{
+       struct wmm_tspec_element *_tspec;
+       int ret;
+       u16 admitted_time = le_to_host16(tspec->medium_time);
+       u8 up = wmm_ac_get_user_priority(tspec);
+       u8 ac = up_to_ac[up];
+       u8 dir = wmm_ac_get_direction(tspec);
+       u8 tsid = wmm_ac_get_tsid(tspec);
+       enum ts_dir_idx idx = wmm_ac_direction_to_idx(dir);
+
+       /* should have been verified before, but double-check here */
+       if (wpa_s->tspecs[ac][idx]) {
+               wpa_printf(MSG_ERROR,
+                          "WMM AC: tspec (ac=%d, dir=%d) already exists!",
+                          ac, dir);
+               return -1;
+       }
+
+       /* copy tspec */
+       _tspec = os_malloc(sizeof(*_tspec));
+       if (!_tspec)
+               return -1;
+
+       /* store the admitted TSPEC */
+       os_memcpy(_tspec, tspec, sizeof(*_tspec));
+
+       if (dir != WMM_AC_DIR_DOWNLINK) {
+               ret = wpa_drv_add_ts(wpa_s, tsid, addr, up, admitted_time);
+               wpa_printf(MSG_DEBUG,
+                          "WMM AC: Add TS: addr=" MACSTR
+                          " TSID=%u admitted time=%u, ret=%d",
+                          MAC2STR(addr), tsid, admitted_time, ret);
+               if (ret < 0) {
+                       os_free(_tspec);
+                       return -1;
+               }
+       }
+
+       wpa_s->tspecs[ac][idx] = _tspec;
+
+       wpa_printf(MSG_DEBUG, "Traffic stream was created successfully");
+
+       wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_ADDED
+               "tsid=%d addr=" MACSTR " admitted_time=%d",
+               tsid, MAC2STR(addr), admitted_time);
+
+       return 0;
+}
+
+
+static void wmm_ac_del_ts_idx(struct wpa_supplicant *wpa_s, u8 ac,
+                             enum ts_dir_idx dir)
+{
+       struct wmm_tspec_element *tspec = wpa_s->tspecs[ac][dir];
+       u8 tsid;
+
+       if (!tspec)
+               return;
+
+       tsid = wmm_ac_get_tsid(tspec);
+       wpa_printf(MSG_DEBUG, "WMM AC: Del TS ac=%d tsid=%d", ac, tsid);
+
+       /* update the driver in case of uplink/bidi */
+       if (wmm_ac_get_direction(tspec) != WMM_AC_DIR_DOWNLINK)
+               wpa_drv_del_ts(wpa_s, tsid, wpa_s->bssid);
+
+       wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REMOVED
+               "tsid=%d addr=" MACSTR, tsid, MAC2STR(wpa_s->bssid));
+
+       os_free(wpa_s->tspecs[ac][dir]);
+       wpa_s->tspecs[ac][dir] = NULL;
+}
+
+
+static void wmm_ac_del_req(struct wpa_supplicant *wpa_s, int failed)
+{
+       struct wmm_ac_addts_request *req = wpa_s->addts_request;
+
+       if (!req)
+               return;
+
+       if (failed)
+               wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REQ_FAILED
+                       "tsid=%u", wmm_ac_get_tsid(&req->tspec));
+
+       eloop_cancel_timeout(wmm_ac_addts_req_timeout, wpa_s, req);
+       wpa_s->addts_request = NULL;
+       os_free(req);
+}
+
+
+static void wmm_ac_addts_req_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       struct wmm_ac_addts_request *addts_req = timeout_ctx;
+
+       wpa_printf(MSG_DEBUG,
+                  "Timeout getting ADDTS response (tsid=%d up=%d)",
+                  wmm_ac_get_tsid(&addts_req->tspec),
+                  wmm_ac_get_user_priority(&addts_req->tspec));
+
+       wmm_ac_del_req(wpa_s, 1);
+}
+
+
+static int wmm_ac_send_addts_request(struct wpa_supplicant *wpa_s,
+                                    const struct wmm_ac_addts_request *req)
+{
+       struct wpabuf *buf;
+       int ret;
+
+       wpa_printf(MSG_DEBUG, "Sending ADDTS Request to " MACSTR,
+                  MAC2STR(req->address));
+
+       /* category + action code + dialog token + status + sizeof(tspec) */
+       buf = wpabuf_alloc(4 + sizeof(req->tspec));
+       if (!buf) {
+               wpa_printf(MSG_ERROR, "WMM AC: Allocation error");
+               return -1;
+       }
+
+       wpabuf_put_u8(buf, WLAN_ACTION_WMM);
+       wpabuf_put_u8(buf, WMM_ACTION_CODE_ADDTS_REQ);
+       wpabuf_put_u8(buf, req->dialog_token);
+       wpabuf_put_u8(buf, 0); /* status code */
+       wpabuf_put_data(buf, &req->tspec, sizeof(req->tspec));
+
+       ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, req->address,
+                               wpa_s->own_addr, wpa_s->bssid,
+                               wpabuf_head(buf), wpabuf_len(buf), 0);
+       if (ret) {
+               wpa_printf(MSG_WARNING,
+                          "WMM AC: Failed to send ADDTS Request");
+       }
+
+       wpabuf_free(buf);
+       return ret;
+}
+
+
+static int wmm_ac_send_delts(struct wpa_supplicant *wpa_s,
+                            const struct wmm_tspec_element *tspec,
+                            const u8 *address)
+{
+       struct wpabuf *buf;
+       int ret;
+
+       /* category + action code + dialog token + status + sizeof(tspec) */
+       buf = wpabuf_alloc(4 + sizeof(*tspec));
+       if (!buf)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "Sending DELTS to " MACSTR, MAC2STR(address));
+
+       /* category + action code + dialog token + status + sizeof(tspec) */
+       wpabuf_put_u8(buf, WLAN_ACTION_WMM);
+       wpabuf_put_u8(buf, WMM_ACTION_CODE_DELTS);
+       wpabuf_put_u8(buf, 0); /* Dialog Token (not used) */
+       wpabuf_put_u8(buf, 0); /* Status Code (not used) */
+       wpabuf_put_data(buf, tspec, sizeof(*tspec));
+
+       ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, address,
+                                 wpa_s->own_addr, wpa_s->bssid,
+                                 wpabuf_head(buf), wpabuf_len(buf), 0);
+       if (ret)
+               wpa_printf(MSG_WARNING, "Failed to send DELTS frame");
+
+       wpabuf_free(buf);
+       return ret;
+}
+
+
+/* return the AC using the given TSPEC tid */
+static int wmm_ac_find_tsid(struct wpa_supplicant *wpa_s, u8 tsid,
+                           enum ts_dir_idx *dir)
+{
+       int ac;
+       enum ts_dir_idx idx;
+
+       for (ac = 0; ac < WMM_AC_NUM; ac++) {
+               for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) {
+                       if (wpa_s->tspecs[ac][idx] &&
+                           wmm_ac_get_tsid(wpa_s->tspecs[ac][idx]) == tsid) {
+                               if (dir)
+                                       *dir = idx;
+                               return ac;
+                       }
+               }
+       }
+
+       return -1;
+}
+
+
+static struct wmm_ac_addts_request *
+wmm_ac_build_addts_req(struct wpa_supplicant *wpa_s,
+                      const struct wmm_ac_ts_setup_params *params,
+                      const u8 *address)
+{
+       struct wmm_ac_addts_request *addts_req;
+       struct wmm_tspec_element *tspec;
+       u8 ac = up_to_ac[params->user_priority];
+       u8 uapsd = wpa_s->wmm_ac_assoc_info->ac_params[ac].uapsd;
+
+       addts_req = os_zalloc(sizeof(*addts_req));
+       if (!addts_req)
+               return NULL;
+
+       tspec = &addts_req->tspec;
+       os_memcpy(addts_req->address, address, ETH_ALEN);
+
+       /* The dialog token cannot be zero */
+       if (++wpa_s->wmm_ac_last_dialog_token == 0)
+               wpa_s->wmm_ac_last_dialog_token++;
+
+       addts_req->dialog_token = wpa_s->wmm_ac_last_dialog_token;
+       tspec->eid = WLAN_EID_VENDOR_SPECIFIC;
+       tspec->length = sizeof(*tspec) - 2; /* reduce eid and length */
+       tspec->oui[0] = 0x00;
+       tspec->oui[1] = 0x50;
+       tspec->oui[2] = 0xf2;
+       tspec->oui_type = WMM_OUI_TYPE;
+       tspec->oui_subtype = WMM_OUI_SUBTYPE_TSPEC_ELEMENT;
+       tspec->version = WMM_VERSION;
+
+       tspec->ts_info[0] = params->tsid << 1;
+       tspec->ts_info[0] |= params->direction << 5;
+       tspec->ts_info[0] |= WMM_AC_ACCESS_POLICY_EDCA << 7;
+       tspec->ts_info[1] = uapsd << 2;
+       tspec->ts_info[1] |= params->user_priority << 3;
+       tspec->ts_info[2] = 0;
+
+       tspec->nominal_msdu_size = host_to_le16(params->nominal_msdu_size);
+       if (params->fixed_nominal_msdu)
+               tspec->nominal_msdu_size |=
+                       host_to_le16(WMM_AC_FIXED_MSDU_SIZE);
+
+       tspec->mean_data_rate = host_to_le32(params->mean_data_rate);
+       tspec->minimum_phy_rate = host_to_le32(params->minimum_phy_rate);
+       tspec->surplus_bandwidth_allowance =
+               host_to_le16(params->surplus_bandwidth_allowance);
+
+       return addts_req;
+}
+
+
+static int param_in_range(const char *name, long value,
+                         long min_val, long max_val)
+{
+       if (value < min_val || (max_val >= 0 && value > max_val)) {
+               wpa_printf(MSG_DEBUG,
+                          "WMM AC: param %s (%ld) is out of range (%ld-%ld)",
+                          name, value, min_val, max_val);
+               return 0;
+       }
+
+       return 1;
+}
+
+
+static int wmm_ac_should_replace_ts(struct wpa_supplicant *wpa_s,
+                                   u8 tsid, u8 ac, u8 dir)
+{
+       enum ts_dir_idx idx;
+       int cur_ac, existing_ts = 0, replace_ts = 0;
+
+       cur_ac = wmm_ac_find_tsid(wpa_s, tsid, &idx);
+       if (cur_ac >= 0) {
+               if (cur_ac != ac) {
+                       wpa_printf(MSG_DEBUG,
+                                  "WMM AC: TSID %i already exists on different ac (%d)",
+                                  tsid, cur_ac);
+                       return -1;
+               }
+
+               /* same tsid - this tspec will replace the current one */
+               replace_ts |= BIT(idx);
+       }
+
+       for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) {
+               if (wpa_s->tspecs[ac][idx])
+                       existing_ts |= BIT(idx);
+       }
+
+       switch (dir) {
+       case WMM_AC_DIR_UPLINK:
+               /* replace existing uplink/bidi tspecs */
+               replace_ts |= existing_ts & (BIT(TS_DIR_IDX_UPLINK) |
+                                            BIT(TS_DIR_IDX_BIDI));
+               break;
+       case WMM_AC_DIR_DOWNLINK:
+               /* replace existing downlink/bidi tspecs */
+               replace_ts |= existing_ts & (BIT(TS_DIR_IDX_DOWNLINK) |
+                                            BIT(TS_DIR_IDX_BIDI));
+               break;
+       case WMM_AC_DIR_BIDIRECTIONAL:
+               /* replace all existing tspecs */
+               replace_ts |= existing_ts;
+               break;
+       default:
+               return -1;
+       }
+
+       return replace_ts;
+}
+
+
+static int wmm_ac_ts_req_is_valid(struct wpa_supplicant *wpa_s,
+                                 const struct wmm_ac_ts_setup_params *params)
+{
+       enum wmm_ac req_ac;
+
+#define PARAM_IN_RANGE(field, min_value, max_value) \
+       param_in_range(#field, params->field, min_value, max_value)
+
+       if (!PARAM_IN_RANGE(tsid, 0, WMM_AC_MAX_TID) ||
+           !PARAM_IN_RANGE(user_priority, 0, WMM_AC_MAX_USER_PRIORITY) ||
+           !PARAM_IN_RANGE(nominal_msdu_size, 1, WMM_AC_MAX_NOMINAL_MSDU) ||
+           !PARAM_IN_RANGE(mean_data_rate, 1, -1) ||
+           !PARAM_IN_RANGE(minimum_phy_rate, 1, -1) ||
+           !PARAM_IN_RANGE(surplus_bandwidth_allowance, WMM_AC_MIN_SBA_UNITY,
+                           -1))
+               return 0;
+#undef PARAM_IN_RANGE
+
+       if (!(params->direction == WMM_TSPEC_DIRECTION_UPLINK ||
+             params->direction == WMM_TSPEC_DIRECTION_DOWNLINK ||
+             params->direction == WMM_TSPEC_DIRECTION_BI_DIRECTIONAL)) {
+               wpa_printf(MSG_DEBUG, "WMM AC: invalid TS direction: %d",
+                          params->direction);
+               return 0;
+       }
+
+       req_ac = up_to_ac[params->user_priority];
+
+       /* Requested accesss category must have acm */
+       if (!wpa_s->wmm_ac_assoc_info->ac_params[req_ac].acm) {
+               wpa_printf(MSG_DEBUG, "WMM AC: AC %d is not ACM", req_ac);
+               return 0;
+       }
+
+       if (wmm_ac_should_replace_ts(wpa_s, params->tsid, req_ac,
+                                    params->direction) < 0)
+               return 0;
+
+       return 1;
+}
+
+
+static struct wmm_ac_assoc_data *
+wmm_ac_process_param_elem(struct wpa_supplicant *wpa_s, const u8 *ies,
+                         size_t ies_len)
+{
+       struct ieee802_11_elems elems;
+       struct wmm_parameter_element *wmm_params;
+       struct wmm_ac_assoc_data *assoc_data;
+       int i;
+
+       /* Parsing WMM Parameter Element */
+       if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) {
+               wpa_printf(MSG_DEBUG, "WMM AC: could not parse assoc ies");
+               return NULL;
+       }
+
+       if (!elems.wmm) {
+               wpa_printf(MSG_DEBUG, "WMM AC: No WMM IE");
+               return NULL;
+       }
+
+       if (elems.wmm_len != sizeof(*wmm_params)) {
+               wpa_printf(MSG_DEBUG, "WMM AC: Invalid WMM ie length");
+               return NULL;
+       }
+
+       wmm_params = (struct wmm_parameter_element *)(elems.wmm);
+
+       assoc_data = os_zalloc(sizeof(*assoc_data));
+       if (!assoc_data)
+               return NULL;
+
+       for (i = 0; i < WMM_AC_NUM; i++)
+               assoc_data->ac_params[i].acm =
+                       !!(wmm_params->ac[i].aci_aifsn & WMM_AC_ACM);
+
+       wpa_printf(MSG_DEBUG,
+                  "WMM AC: AC mandatory: AC_BE=%u AC_BK=%u AC_VI=%u AC_VO=%u",
+                  assoc_data->ac_params[WMM_AC_BE].acm,
+                  assoc_data->ac_params[WMM_AC_BK].acm,
+                  assoc_data->ac_params[WMM_AC_VI].acm,
+                  assoc_data->ac_params[WMM_AC_VO].acm);
+
+       return assoc_data;
+}
+
+
+static int wmm_ac_init(struct wpa_supplicant *wpa_s, const u8 *ies,
+                      size_t ies_len, const struct wmm_params *wmm_params)
+{
+       struct wmm_ac_assoc_data *assoc_data;
+       u8 ac;
+
+       if (wpa_s->wmm_ac_assoc_info) {
+               wpa_printf(MSG_ERROR, "WMM AC: Already initialized");
+               return -1;
+       }
+
+       if (!ies) {
+               wpa_printf(MSG_ERROR, "WMM AC: Missing IEs");
+               return -1;
+       }
+
+       if (!(wmm_params->info_bitmap & WMM_PARAMS_UAPSD_QUEUES_INFO)) {
+               wpa_printf(MSG_DEBUG, "WMM AC: Missing U-APSD configuration");
+               return -1;
+       }
+
+       os_memset(wpa_s->tspecs, 0, sizeof(wpa_s->tspecs));
+       wpa_s->wmm_ac_last_dialog_token = 0;
+       wpa_s->addts_request = NULL;
+
+       assoc_data = wmm_ac_process_param_elem(wpa_s, ies, ies_len);
+       if (!assoc_data)
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "WMM AC: U-APSD queues=0x%x",
+                  wmm_params->uapsd_queues);
+
+       for (ac = 0; ac < WMM_AC_NUM; ac++) {
+               assoc_data->ac_params[ac].uapsd =
+                       !!(wmm_params->uapsd_queues & BIT(ac));
+       }
+
+       wpa_s->wmm_ac_assoc_info = assoc_data;
+       return 0;
+}
+
+
+static void wmm_ac_del_ts(struct wpa_supplicant *wpa_s, u8 ac, int dir_bitmap)
+{
+       enum ts_dir_idx idx;
+
+       for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) {
+               if (!(dir_bitmap & BIT(idx)))
+                       continue;
+
+               wmm_ac_del_ts_idx(wpa_s, ac, idx);
+       }
+}
+
+
+static void wmm_ac_deinit(struct wpa_supplicant *wpa_s)
+{
+       int i;
+
+       for (i = 0; i < WMM_AC_NUM; i++)
+               wmm_ac_del_ts(wpa_s, i, TS_DIR_IDX_ALL);
+
+       /* delete pending add_ts requset */
+       wmm_ac_del_req(wpa_s, 1);
+
+       os_free(wpa_s->wmm_ac_assoc_info);
+       wpa_s->wmm_ac_assoc_info = NULL;
+}
+
+
+void wmm_ac_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *ies,
+                        size_t ies_len, const struct wmm_params *wmm_params)
+{
+       if (wmm_ac_init(wpa_s, ies, ies_len, wmm_params))
+               return;
+
+       wpa_printf(MSG_DEBUG,
+                  "WMM AC: Valid WMM association, WMM AC is enabled");
+}
+
+
+void wmm_ac_notify_disassoc(struct wpa_supplicant *wpa_s)
+{
+       if (!wpa_s->wmm_ac_assoc_info)
+               return;
+
+       wmm_ac_deinit(wpa_s);
+       wpa_printf(MSG_DEBUG, "WMM AC: WMM AC is disabled");
+}
+
+
+int wpas_wmm_ac_delts(struct wpa_supplicant *wpa_s, u8 tsid)
+{
+       struct wmm_tspec_element tspec;
+       int ac;
+       enum ts_dir_idx dir;
+
+       if (!wpa_s->wmm_ac_assoc_info) {
+               wpa_printf(MSG_DEBUG,
+                          "WMM AC: Failed to delete TS, WMM AC is disabled");
+               return -1;
+       }
+
+       ac = wmm_ac_find_tsid(wpa_s, tsid, &dir);
+       if (ac < 0) {
+               wpa_printf(MSG_DEBUG, "WMM AC: TS does not exist");
+               return -1;
+       }
+
+       tspec = *wpa_s->tspecs[ac][dir];
+
+       wmm_ac_del_ts_idx(wpa_s, ac, dir);
+
+       wmm_ac_send_delts(wpa_s, &tspec, wpa_s->bssid);
+
+       return 0;
+}
+
+
+int wpas_wmm_ac_addts(struct wpa_supplicant *wpa_s,
+                     struct wmm_ac_ts_setup_params *params)
+{
+       struct wmm_ac_addts_request *addts_req;
+
+       if (!wpa_s->wmm_ac_assoc_info) {
+               wpa_printf(MSG_DEBUG,
+                          "WMM AC: Cannot add TS - missing assoc data");
+               return -1;
+       }
+
+       if (wpa_s->addts_request) {
+               wpa_printf(MSG_DEBUG,
+                          "WMM AC: can't add TS - ADDTS request is already pending");
+               return -1;
+       }
+
+       /*
+        * we can setup downlink TS even without driver support.
+        * however, we need driver support for the other directions.
+        */
+       if (params->direction != WMM_AC_DIR_DOWNLINK &&
+           !wpa_s->wmm_ac_supported) {
+               wpa_printf(MSG_DEBUG,
+                          "Cannot set uplink/bidi TS without driver support");
+               return -1;
+       }
+
+       if (!wmm_ac_ts_req_is_valid(wpa_s, params))
+               return -1;
+
+       wpa_printf(MSG_DEBUG, "WMM AC: TS setup request (addr=" MACSTR
+                  " tsid=%u user priority=%u direction=%d)",
+                  MAC2STR(wpa_s->bssid), params->tsid,
+                  params->user_priority, params->direction);
+
+       addts_req = wmm_ac_build_addts_req(wpa_s, params, wpa_s->bssid);
+       if (!addts_req)
+               return -1;
+
+       if (wmm_ac_send_addts_request(wpa_s, addts_req))
+               goto err;
+
+       /* save as pending and set ADDTS resp timeout to 1 second */
+       wpa_s->addts_request = addts_req;
+       eloop_register_timeout(1, 0, wmm_ac_addts_req_timeout,
+                              wpa_s, addts_req);
+       return 0;
+err:
+       os_free(addts_req);
+       return -1;
+}
+
+
+static void wmm_ac_handle_delts(struct wpa_supplicant *wpa_s, const u8 *sa,
+                               const struct wmm_tspec_element *tspec)
+{
+       int ac;
+       u8 tsid;
+       enum ts_dir_idx idx;
+
+       tsid = wmm_ac_get_tsid(tspec);
+
+       wpa_printf(MSG_DEBUG,
+                  "WMM AC: DELTS frame has been received TSID=%u addr="
+                  MACSTR, tsid, MAC2STR(sa));
+
+       ac = wmm_ac_find_tsid(wpa_s, tsid, &idx);
+       if (ac < 0) {
+               wpa_printf(MSG_DEBUG,
+                          "WMM AC: Ignoring DELTS frame - TSID does not exist");
+               return;
+       }
+
+       wmm_ac_del_ts_idx(wpa_s, ac, idx);
+
+       wpa_printf(MSG_DEBUG,
+                  "TS was deleted successfully (tsid=%u address=" MACSTR ")",
+                  tsid, MAC2STR(sa));
+}
+
+
+static void wmm_ac_handle_addts_resp(struct wpa_supplicant *wpa_s, const u8 *sa,
+               const u8 resp_dialog_token, const u8 status_code,
+               const struct wmm_tspec_element *tspec)
+{
+       struct wmm_ac_addts_request *req = wpa_s->addts_request;
+       u8 ac, tsid, up, dir;
+       int replace_tspecs;
+
+       tsid = wmm_ac_get_tsid(tspec);
+       dir = wmm_ac_get_direction(tspec);
+       up = wmm_ac_get_user_priority(tspec);
+       ac = up_to_ac[up];
+
+       /* make sure we have a matching addts request */
+       if (!req || req->dialog_token != resp_dialog_token) {
+               wpa_printf(MSG_DEBUG,
+                          "WMM AC: no req with dialog=%u, ignoring frame",
+                          resp_dialog_token);
+               return;
+       }
+
+       /* make sure the params are the same */
+       if (os_memcmp(req->address, sa, ETH_ALEN) != 0 ||
+           tsid != wmm_ac_get_tsid(&req->tspec) ||
+           up != wmm_ac_get_user_priority(&req->tspec) ||
+           dir != wmm_ac_get_direction(&req->tspec)) {
+               wpa_printf(MSG_DEBUG,
+                          "WMM AC: ADDTS params do not match, ignoring frame");
+               return;
+       }
+
+       /* delete pending request */
+       wmm_ac_del_req(wpa_s, 0);
+
+       wpa_printf(MSG_DEBUG,
+                  "ADDTS response status=%d tsid=%u up=%u direction=%u",
+                  status_code, tsid, up, dir);
+
+       if (status_code != WMM_ADDTS_STATUS_ADMISSION_ACCEPTED) {
+               wpa_printf(MSG_INFO, "WMM AC: ADDTS request was rejected");
+               goto err_msg;
+       }
+
+       replace_tspecs = wmm_ac_should_replace_ts(wpa_s, tsid, ac, dir);
+       if (replace_tspecs < 0)
+               goto err_delts;
+
+       wpa_printf(MSG_DEBUG, "ts idx replace bitmap: 0x%x", replace_tspecs);
+
+       /* when replacing tspecs - delete first */
+       wmm_ac_del_ts(wpa_s, ac, replace_tspecs);
+
+       /* Creating a new traffic stream */
+       wpa_printf(MSG_DEBUG,
+                  "WMM AC: adding a new TS with TSID=%u address="MACSTR
+                  " medium time=%u access category=%d dir=%d ",
+                  tsid, MAC2STR(sa),
+                  le_to_host16(tspec->medium_time), ac, dir);
+
+       if (wmm_ac_add_ts(wpa_s, sa, tspec))
+               goto err_delts;
+
+       return;
+
+err_delts:
+       /* ask the ap to delete the tspec */
+       wmm_ac_send_delts(wpa_s, tspec, sa);
+err_msg:
+       wpa_msg(wpa_s, MSG_INFO, WMM_AC_EVENT_TSPEC_REQ_FAILED "tsid=%u",
+               tsid);
+}
+
+
+void wmm_ac_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
+                       const u8 *sa, const u8 *data, size_t len)
+{
+       u8 action;
+       u8 dialog_token;
+       u8 status_code;
+       struct ieee802_11_elems elems;
+       struct wmm_tspec_element *tspec;
+
+       if (wpa_s->wmm_ac_assoc_info == NULL) {
+               wpa_printf(MSG_DEBUG,
+                          "WMM AC: WMM AC is disabled, ignoring action frame");
+               return;
+       }
+
+       action = data[0];
+
+       if (action != WMM_ACTION_CODE_ADDTS_RESP &&
+           action != WMM_ACTION_CODE_DELTS) {
+               wpa_printf(MSG_DEBUG,
+                          "WMM AC: Unknown action (%d), ignoring action frame",
+                          action);
+               return;
+       }
+
+       /* WMM AC action frame */
+       if (os_memcmp(da, wpa_s->own_addr, ETH_ALEN) != 0) {
+               wpa_printf(MSG_DEBUG, "WMM AC: frame destination addr="MACSTR
+                          " is other than ours, ignoring frame", MAC2STR(da));
+               return;
+       }
+
+       if (os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) {
+               wpa_printf(MSG_DEBUG, "WMM AC: ignore frame with sa " MACSTR
+                          " different other than our bssid", MAC2STR(da));
+               return;
+       }
+
+       if (len < 2 + sizeof(struct wmm_tspec_element)) {
+               wpa_printf(MSG_DEBUG,
+                          "WMM AC: Short ADDTS response ignored (len=%lu)",
+                          (unsigned long) len);
+               return;
+       }
+
+       data++;
+       len--;
+       dialog_token = data[0];
+       status_code = data[1];
+
+       if (ieee802_11_parse_elems(data + 2, len - 2, &elems, 1) != ParseOK) {
+               wpa_printf(MSG_DEBUG,
+                          "WMM AC: Could not parse WMM AC action from " MACSTR,
+                          MAC2STR(sa));
+               return;
+       }
+
+       /* the struct also contains the type and value, so decrease it */
+       if (elems.wmm_tspec_len != sizeof(struct wmm_tspec_element) - 2) {
+               wpa_printf(MSG_DEBUG, "WMM AC: missing or wrong length TSPEC");
+               return;
+       }
+
+       tspec = (struct wmm_tspec_element *)(elems.wmm_tspec - 2);
+
+       wpa_printf(MSG_DEBUG, "WMM AC: RX WMM AC Action from " MACSTR,
+                  MAC2STR(sa));
+       wpa_hexdump(MSG_MSGDUMP, "WMM AC: WMM AC Action content", data, len);
+
+       switch (action) {
+       case WMM_ACTION_CODE_ADDTS_RESP:
+               wmm_ac_handle_addts_resp(wpa_s, sa, dialog_token, status_code,
+                                        tspec);
+               break;
+       case WMM_ACTION_CODE_DELTS:
+               wmm_ac_handle_delts(wpa_s, sa, tspec);
+               break;
+       default:
+               break;
+       }
+}
+
+
+static const char * get_ac_str(u8 ac)
+{
+       switch (ac) {
+       case WMM_AC_BE:
+               return "BE";
+       case WMM_AC_BK:
+               return "BK";
+       case WMM_AC_VI:
+               return "VI";
+       case WMM_AC_VO:
+               return "VO";
+       default:
+               return "N/A";
+       }
+}
+
+
+static const char * get_direction_str(u8 direction)
+{
+       switch (direction) {
+       case WMM_AC_DIR_DOWNLINK:
+               return "Downlink";
+       case WMM_AC_DIR_UPLINK:
+               return "Uplink";
+       case WMM_AC_DIR_BIDIRECTIONAL:
+               return "Bi-directional";
+       default:
+               return "N/A";
+       }
+}
+
+
+int wpas_wmm_ac_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen)
+{
+       struct wmm_ac_assoc_data *assoc_info = wpa_s->wmm_ac_assoc_info;
+       enum ts_dir_idx idx;
+       int pos = 0;
+       u8 ac, up;
+
+       if (!assoc_info) {
+               return wpa_scnprintf(buf, buflen - pos,
+                                    "Not associated to a WMM AP, WMM AC is Disabled\n");
+       }
+
+       pos += wpa_scnprintf(buf + pos, buflen - pos, "WMM AC is Enabled\n");
+
+       for (ac = 0; ac < WMM_AC_NUM; ac++) {
+               int ts_count = 0;
+
+               pos += wpa_scnprintf(buf + pos, buflen - pos,
+                                    "%s: acm=%d uapsd=%d\n",
+                                    get_ac_str(ac),
+                                    assoc_info->ac_params[ac].acm,
+                                    assoc_info->ac_params[ac].uapsd);
+
+               for (idx = 0; idx < TS_DIR_IDX_COUNT; idx++) {
+                       struct wmm_tspec_element *tspec;
+                       u8 dir, tsid;
+                       const char *dir_str;
+
+                       tspec = wpa_s->tspecs[ac][idx];
+                       if (!tspec)
+                               continue;
+
+                       ts_count++;
+
+                       dir = wmm_ac_get_direction(tspec);
+                       dir_str = get_direction_str(dir);
+                       tsid = wmm_ac_get_tsid(tspec);
+                       up = wmm_ac_get_user_priority(tspec);
+
+                       pos += wpa_scnprintf(buf + pos, buflen - pos,
+                                            "\tTSID=%u UP=%u\n"
+                                            "\tAddress = "MACSTR"\n"
+                                            "\tWMM AC dir = %s\n"
+                                            "\tTotal admitted time = %u\n\n",
+                                            tsid, up,
+                                            MAC2STR(wpa_s->bssid),
+                                            dir_str,
+                                            le_to_host16(tspec->medium_time));
+               }
+
+               if (!ts_count) {
+                       pos += wpa_scnprintf(buf + pos, buflen - pos,
+                                            "\t(No Traffic Stream)\n\n");
+               }
+       }
+
+       return pos;
+}
+
+
+static u8 wmm_ac_get_tspecs_count(struct wpa_supplicant *wpa_s)
+{
+       int ac, dir, tspecs_count = 0;
+
+       for (ac = 0; ac < WMM_AC_NUM; ac++) {
+               for (dir = 0; dir < TS_DIR_IDX_COUNT; dir++) {
+                       if (wpa_s->tspecs[ac][dir])
+                               tspecs_count++;
+               }
+       }
+
+       return tspecs_count;
+}
+
+
+void wmm_ac_save_tspecs(struct wpa_supplicant *wpa_s)
+{
+       int ac, dir, tspecs_count;
+
+       wpa_printf(MSG_DEBUG, "WMM AC: Save last configured tspecs");
+
+       if (!wpa_s->wmm_ac_assoc_info)
+               return;
+
+       tspecs_count = wmm_ac_get_tspecs_count(wpa_s);
+       if (!tspecs_count) {
+               wpa_printf(MSG_DEBUG, "WMM AC: No configured TSPECs");
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "WMM AC: Saving tspecs");
+
+       wmm_ac_clear_saved_tspecs(wpa_s);
+       wpa_s->last_tspecs = os_calloc(tspecs_count,
+                                      sizeof(*wpa_s->last_tspecs));
+       if (!wpa_s->last_tspecs) {
+               wpa_printf(MSG_ERROR, "WMM AC: Failed to save tspecs!");
+               return;
+       }
+
+       for (ac = 0; ac < WMM_AC_NUM; ac++) {
+               for (dir = 0; dir < TS_DIR_IDX_COUNT; dir++) {
+                       if (!wpa_s->tspecs[ac][dir])
+                               continue;
+
+                       wpa_s->last_tspecs[wpa_s->last_tspecs_count++] =
+                               *wpa_s->tspecs[ac][dir];
+               }
+       }
+
+       wpa_printf(MSG_DEBUG, "WMM AC: Successfully saved %d TSPECs",
+                  wpa_s->last_tspecs_count);
+}
+
+
+void wmm_ac_clear_saved_tspecs(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->last_tspecs) {
+               wpa_printf(MSG_DEBUG, "WMM AC: Clear saved tspecs");
+               os_free(wpa_s->last_tspecs);
+               wpa_s->last_tspecs = NULL;
+               wpa_s->last_tspecs_count = 0;
+       }
+}
+
+
+int wmm_ac_restore_tspecs(struct wpa_supplicant *wpa_s)
+{
+       unsigned int i;
+
+       if (!wpa_s->wmm_ac_assoc_info || !wpa_s->last_tspecs_count)
+               return 0;
+
+       wpa_printf(MSG_DEBUG, "WMM AC: Restore %u saved tspecs",
+                  wpa_s->last_tspecs_count);
+
+       for (i = 0; i < wpa_s->last_tspecs_count; i++)
+               wmm_ac_add_ts(wpa_s, wpa_s->bssid, &wpa_s->last_tspecs[i]);
+
+       return 0;
+}
diff --git a/wpa_supplicant/wmm_ac.h b/wpa_supplicant/wmm_ac.h
new file mode 100755 (executable)
index 0000000..5171b16
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * Wi-Fi Multimedia Admission Control (WMM-AC)
+ * Copyright(c) 2014, Intel Mobile Communication GmbH.
+ * Copyright(c) 2014, Intel Corporation. All rights reserved.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WMM_AC_H
+#define WMM_AC_H
+
+#include "common/ieee802_11_defs.h"
+#include "drivers/driver.h"
+
+struct wpa_supplicant;
+
+#define WMM_AC_ACCESS_POLICY_EDCA 1
+#define WMM_AC_FIXED_MSDU_SIZE BIT(15)
+
+#define WMM_AC_MAX_TID 7
+#define WMM_AC_MAX_USER_PRIORITY 7
+#define WMM_AC_MIN_SBA_UNITY 0x2000
+#define WMM_AC_MAX_NOMINAL_MSDU 32767
+
+/**
+ * struct wmm_ac_assoc_data - WMM Admission Control Association Data
+ *
+ * This struct will store any relevant WMM association data needed by WMM AC.
+ * In case there is a valid WMM association, an instance of this struct will be
+ * created. In case there is no instance of this struct, the station is not
+ * associated to a valid WMM BSS and hence, WMM AC will not be used.
+ */
+struct wmm_ac_assoc_data {
+       struct {
+               /*
+                * acm - Admission Control Mandatory
+                * In case an access category is ACM, the traffic will have
+                * to be admitted by WMM-AC's admission mechanism before use.
+                */
+               unsigned int acm:1;
+
+               /*
+                * uapsd_queues - Unscheduled Automatic Power Save Delivery
+                *                queues.
+                * Indicates whether ACs are configured for U-APSD (or legacy
+                * PS). Storing this value is necessary in order to set the
+                * Power Save Bit (PSB) in ADDTS request Action frames (if not
+                * given).
+                */
+               unsigned int uapsd:1;
+       } ac_params[WMM_AC_NUM];
+};
+
+/**
+ * wmm_ac_dir - WMM Admission Control Direction
+ */
+enum wmm_ac_dir {
+       WMM_AC_DIR_UPLINK = 0,
+       WMM_AC_DIR_DOWNLINK = 1,
+       WMM_AC_DIR_BIDIRECTIONAL = 3
+};
+
+/**
+ * ts_dir_idx - indices of internally saved tspecs
+ *
+ * we can have multiple tspecs (downlink + uplink) per ac.
+ * save them in array, and use the enum to directly access
+ * the respective tspec slot (according to the direction).
+ */
+enum ts_dir_idx {
+       TS_DIR_IDX_UPLINK,
+       TS_DIR_IDX_DOWNLINK,
+       TS_DIR_IDX_BIDI,
+
+       TS_DIR_IDX_COUNT
+};
+#define TS_DIR_IDX_ALL (BIT(TS_DIR_IDX_COUNT) - 1)
+
+/**
+ * struct wmm_ac_addts_request - ADDTS Request Information
+ *
+ * The last sent ADDTS request(s) will be saved as element(s) of this struct in
+ * order to be compared with the received ADDTS response in ADDTS response
+ * action frame handling and should be stored until that point.
+ * In case a new traffic stream will be created/replaced/updated, only its
+ * relevant traffic stream information will be stored as a wmm_ac_ts struct.
+ */
+struct wmm_ac_addts_request {
+       /*
+        * dialog token - Used to link the recived ADDTS response with this
+        * saved ADDTS request when ADDTS response is being handled
+        */
+       u8 dialog_token;
+
+       /*
+        * address - The alleged traffic stream's receiver/transmitter address
+        * Address and TID are used to identify the TS (TID is contained in
+        * TSPEC)
+        */
+       u8 address[ETH_ALEN];
+
+       /*
+        * tspec - Traffic Stream Specification, will be used to compare the
+        * sent TSPEC in ADDTS request to the received TSPEC in ADDTS response
+        * and act accordingly in ADDTS response handling
+        */
+       struct wmm_tspec_element tspec;
+};
+
+
+/**
+ * struct wmm_ac_ts_setup_params - TS setup parameters
+ *
+ * This struct holds parameters which should be provided
+ * to wmm_ac_ts_setup in order to setup a traffic stream
+ */
+struct wmm_ac_ts_setup_params {
+       /*
+        * tsid - Traffic ID
+        * TID and address are used to identify the TS
+        */
+       int tsid;
+
+       /*
+        * direction - Traffic Stream's direction
+        */
+       enum wmm_ac_dir direction;
+
+       /*
+        * user_priority - Traffic Stream's user priority
+        */
+       int user_priority;
+
+       /*
+        * nominal_msdu_size - Nominal MAC service data unit size
+        */
+       int nominal_msdu_size;
+
+       /*
+        * fixed_nominal_msdu - Whether the size is fixed
+        * 0 = Nominal MSDU size is not fixed
+        * 1 = Nominal MSDU size is fixed
+        */
+       int fixed_nominal_msdu;
+
+       /*
+        * surplus_bandwidth_allowance - Specifies excess time allocation
+        */
+       int mean_data_rate;
+
+       /*
+        * minimum_phy_rate - Specifies the minimum supported PHY rate in bps
+        */
+       int minimum_phy_rate;
+
+       /*
+        * surplus_bandwidth_allowance - Specifies excess time allocation
+        */
+       int surplus_bandwidth_allowance;
+};
+
+void wmm_ac_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *ies,
+                        size_t ies_len, const struct wmm_params *wmm_params);
+void wmm_ac_notify_disassoc(struct wpa_supplicant *wpa_s);
+int wpas_wmm_ac_addts(struct wpa_supplicant *wpa_s,
+                     struct wmm_ac_ts_setup_params *params);
+int wpas_wmm_ac_delts(struct wpa_supplicant *wpa_s, u8 tsid);
+void wmm_ac_rx_action(struct wpa_supplicant *wpa_s, const u8 *da,
+                       const u8 *sa, const u8 *data, size_t len);
+int wpas_wmm_ac_status(struct wpa_supplicant *wpa_s, char *buf, size_t buflen);
+void wmm_ac_save_tspecs(struct wpa_supplicant *wpa_s);
+void wmm_ac_clear_saved_tspecs(struct wpa_supplicant *wpa_s);
+int wmm_ac_restore_tspecs(struct wpa_supplicant *wpa_s);
+
+#endif /* WMM_AC_H */
old mode 100644 (file)
new mode 100755 (executable)
index 4f8d895..954de67
@@ -10,6 +10,7 @@
 
 #include "utils/common.h"
 #include "common/ieee802_11_defs.h"
+#include "common/ieee802_11_common.h"
 #include "common/wpa_ctrl.h"
 #include "rsn_supp/wpa.h"
 #include "wpa_supplicant_i.h"
@@ -18,6 +19,7 @@
 #include "ctrl_iface.h"
 #include "bss.h"
 #include "wnm_sta.h"
+#include "hs20_supplicant.h"
 
 #define MAX_TFS_IE_LEN  1024
 #define WNM_MAX_NEIGHBOR_REPORT 10
@@ -181,7 +183,7 @@ static void wnm_sleep_mode_exit_success(struct wpa_supplicant *wpa_s,
        /* Install GTK/IGTK */
 
        /* point to key data field */
-       ptr = (u8 *) frm + 1 + 1 + 2;
+       ptr = (u8 *) frm + 1 + 2;
        end = ptr + key_len_total;
        wpa_hexdump_key(MSG_DEBUG, "WNM: Key Data", ptr, key_len_total);
 
@@ -234,23 +236,29 @@ static void ieee802_11_rx_wnmsleep_resp(struct wpa_supplicant *wpa_s,
                                        const u8 *frm, int len)
 {
        /*
-        * Action [1] | Diaglog Token [1] | Key Data Len [2] | Key Data |
+        * Action [1] | Dialog Token [1] | Key Data Len [2] | Key Data |
         * WNM-Sleep Mode IE | TFS Response IE
         */
-       u8 *pos = (u8 *) frm; /* point to action field */
-       u16 key_len_total = le_to_host16(*((u16 *)(frm+2)));
+       u8 *pos = (u8 *) frm; /* point to payload after the action field */
+       u16 key_len_total;
        struct wnm_sleep_element *wnmsleep_ie = NULL;
        /* multiple TFS Resp IE (assuming consecutive) */
        u8 *tfsresp_ie_start = NULL;
        u8 *tfsresp_ie_end = NULL;
+       size_t left;
 
-       wpa_printf(MSG_DEBUG, "action=%d token = %d key_len_total = %d",
-                  frm[0], frm[1], key_len_total);
-       pos += 4 + key_len_total;
-       if (pos > frm + len) {
+       if (len < 3)
+               return;
+       key_len_total = WPA_GET_LE16(frm + 1);
+
+       wpa_printf(MSG_DEBUG, "WNM-Sleep Mode Response token=%u key_len_total=%d",
+                  frm[0], key_len_total);
+       left = len - 3;
+       if (key_len_total > left) {
                wpa_printf(MSG_INFO, "WNM: Too short frame for Key Data field");
                return;
        }
+       pos += 3 + key_len_total;
        while (pos - frm < len) {
                u8 ie_len = *(pos + 1);
                if (pos + 2 + ie_len > frm + len) {
@@ -304,16 +312,11 @@ void wnm_deallocate_memory(struct wpa_supplicant *wpa_s)
        int i;
 
        for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
-               os_free(wpa_s->wnm_neighbor_report_elements[i].tsf_info);
-               os_free(wpa_s->wnm_neighbor_report_elements[i].con_coun_str);
-               os_free(wpa_s->wnm_neighbor_report_elements[i].bss_tran_can);
-               os_free(wpa_s->wnm_neighbor_report_elements[i].bss_term_dur);
-               os_free(wpa_s->wnm_neighbor_report_elements[i].bearing);
                os_free(wpa_s->wnm_neighbor_report_elements[i].meas_pilot);
-               os_free(wpa_s->wnm_neighbor_report_elements[i].rrm_cap);
                os_free(wpa_s->wnm_neighbor_report_elements[i].mul_bssid);
        }
 
+       wpa_s->wnm_num_neighbor_report = 0;
        os_free(wpa_s->wnm_neighbor_report_elements);
        wpa_s->wnm_neighbor_report_elements = NULL;
 }
@@ -328,12 +331,9 @@ static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep,
                        wpa_printf(MSG_DEBUG, "WNM: Too short TSF");
                        break;
                }
-               rep->tsf_info = os_zalloc(sizeof(struct tsf_info));
-               if (rep->tsf_info == NULL)
-                       break;
-               rep->tsf_info->present = 1;
-               os_memcpy(rep->tsf_info->tsf_offset, pos, 2);
-               os_memcpy(rep->tsf_info->beacon_interval, pos + 2, 2);
+               rep->tsf_offset = WPA_GET_LE16(pos);
+               rep->beacon_int = WPA_GET_LE16(pos + 2);
+               rep->tsf_present = 1;
                break;
        case WNM_NEIGHBOR_CONDENSED_COUNTRY_STRING:
                if (elen < 2) {
@@ -341,12 +341,8 @@ static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep,
                                   "country string");
                        break;
                }
-               rep->con_coun_str =
-                       os_zalloc(sizeof(struct condensed_country_string));
-               if (rep->con_coun_str == NULL)
-                       break;
-               rep->con_coun_str->present = 1;
-               os_memcpy(rep->con_coun_str->country_string, pos, 2);
+               os_memcpy(rep->country, pos, 2);
+               rep->country_present = 1;
                break;
        case WNM_NEIGHBOR_BSS_TRANSITION_CANDIDATE:
                if (elen < 1) {
@@ -354,25 +350,13 @@ static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep,
                                   "candidate");
                        break;
                }
-               rep->bss_tran_can =
-                       os_zalloc(sizeof(struct bss_transition_candidate));
-               if (rep->bss_tran_can == NULL)
-                       break;
-               rep->bss_tran_can->present = 1;
-               rep->bss_tran_can->preference = pos[0];
+               rep->preference = pos[0];
+               rep->preference_present = 1;
                break;
        case WNM_NEIGHBOR_BSS_TERMINATION_DURATION:
-               if (elen < 12) {
-                       wpa_printf(MSG_DEBUG, "WNM: Too short BSS termination "
-                                  "duration");
-                       break;
-               }
-               rep->bss_term_dur =
-                       os_zalloc(sizeof(struct bss_termination_duration));
-               if (rep->bss_term_dur == NULL)
-                       break;
-               rep->bss_term_dur->present = 1;
-               os_memcpy(rep->bss_term_dur->duration, pos, 12);
+               rep->bss_term_tsf = WPA_GET_LE64(pos);
+               rep->bss_term_dur = WPA_GET_LE16(pos + 8);
+               rep->bss_term_present = 1;
                break;
        case WNM_NEIGHBOR_BEARING:
                if (elen < 8) {
@@ -380,56 +364,67 @@ static void wnm_parse_neighbor_report_elem(struct neighbor_report *rep,
                                   "bearing");
                        break;
                }
-               rep->bearing = os_zalloc(sizeof(struct bearing));
-               if (rep->bearing == NULL)
-                       break;
-               rep->bearing->present = 1;
-               os_memcpy(rep->bearing->bearing, pos, 8);
+               rep->bearing = WPA_GET_LE16(pos);
+               rep->distance = WPA_GET_LE32(pos + 2);
+               rep->rel_height = WPA_GET_LE16(pos + 2 + 4);
+               rep->bearing_present = 1;
                break;
        case WNM_NEIGHBOR_MEASUREMENT_PILOT:
-               if (elen < 2) {
+               if (elen < 1) {
                        wpa_printf(MSG_DEBUG, "WNM: Too short measurement "
                                   "pilot");
                        break;
                }
+               os_free(rep->meas_pilot);
                rep->meas_pilot = os_zalloc(sizeof(struct measurement_pilot));
                if (rep->meas_pilot == NULL)
                        break;
-               rep->meas_pilot->present = 1;
                rep->meas_pilot->measurement_pilot = pos[0];
-               rep->meas_pilot->num_vendor_specific = pos[1];
-               os_memcpy(rep->meas_pilot->vendor_specific, pos + 2, elen - 2);
+               rep->meas_pilot->subelem_len = elen - 1;
+               os_memcpy(rep->meas_pilot->subelems, pos + 1, elen - 1);
                break;
        case WNM_NEIGHBOR_RRM_ENABLED_CAPABILITIES:
-               if (elen < 4) {
+               if (elen < 5) {
                        wpa_printf(MSG_DEBUG, "WNM: Too short RRM enabled "
                                   "capabilities");
                        break;
                }
-               rep->rrm_cap =
-                       os_zalloc(sizeof(struct rrm_enabled_capabilities));
-               if (rep->rrm_cap == NULL)
-                       break;
-               rep->rrm_cap->present = 1;
-               os_memcpy(rep->rrm_cap->capabilities, pos, 4);
+               os_memcpy(rep->rm_capab, pos, 5);
+               rep->rm_capab_present = 1;
                break;
        case WNM_NEIGHBOR_MULTIPLE_BSSID:
-               if (elen < 2) {
+               if (elen < 1) {
                        wpa_printf(MSG_DEBUG, "WNM: Too short multiple BSSID");
                        break;
                }
+               os_free(rep->mul_bssid);
                rep->mul_bssid = os_zalloc(sizeof(struct multiple_bssid));
                if (rep->mul_bssid == NULL)
                        break;
-               rep->mul_bssid->present = 1;
                rep->mul_bssid->max_bssid_indicator = pos[0];
-               rep->mul_bssid->num_vendor_specific = pos[1];
-               os_memcpy(rep->mul_bssid->vendor_specific, pos + 2, elen - 2);
+               rep->mul_bssid->subelem_len = elen - 1;
+               os_memcpy(rep->mul_bssid->subelems, pos + 1, elen - 1);
                break;
        }
 }
 
 
+static int wnm_nei_get_chan(struct wpa_supplicant *wpa_s, u8 op_class, u8 chan)
+{
+       struct wpa_bss *bss = wpa_s->current_bss;
+       const char *country = NULL;
+
+       if (bss) {
+               const u8 *elem = wpa_bss_get_ie(bss, WLAN_EID_COUNTRY);
+
+               if (elem && elem[1] >= 2)
+                       country = (const char *) (elem + 2);
+       }
+
+       return ieee80211_chan_to_freq(country, op_class, chan);
+}
+
+
 static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
                                      const u8 *pos, u8 len,
                                      struct neighbor_report *rep)
@@ -442,7 +437,7 @@ static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
        }
 
        os_memcpy(rep->bssid, pos, ETH_ALEN);
-       os_memcpy(rep->bssid_information, pos + ETH_ALEN, 4);
+       rep->bssid_info = WPA_GET_LE32(pos + ETH_ALEN);
        rep->regulatory_class = *(pos + 10);
        rep->channel_number = *(pos + 11);
        rep->phy_type = *(pos + 12);
@@ -455,40 +450,89 @@ static void wnm_parse_neighbor_report(struct wpa_supplicant *wpa_s,
 
                id = *pos++;
                elen = *pos++;
+               wpa_printf(MSG_DEBUG, "WNM: Subelement id=%u len=%u", id, elen);
+               left -= 2;
+               if (elen > left) {
+                       wpa_printf(MSG_DEBUG,
+                                  "WNM: Truncated neighbor report subelement");
+                       break;
+               }
                wnm_parse_neighbor_report_elem(rep, id, elen, pos);
-               left -= 2 + elen;
+               left -= elen;
                pos += elen;
        }
+
+       rep->freq = wnm_nei_get_chan(wpa_s, rep->regulatory_class,
+                                    rep->channel_number);
 }
 
 
-static int compare_scan_neighbor_results(struct wpa_supplicant *wpa_s,
-                                        struct wpa_scan_results *scan_res,
-                                        struct neighbor_report *neigh_rep,
-                                        u8 num_neigh_rep, u8 *bssid_to_connect)
+static struct wpa_bss *
+compare_scan_neighbor_results(struct wpa_supplicant *wpa_s)
 {
 
-       u8 i, j;
+       u8 i;
+       struct wpa_bss *bss = wpa_s->current_bss;
+       struct wpa_bss *target;
 
-       if (scan_res == NULL || num_neigh_rep == 0)
+       if (!bss)
                return 0;
 
-       for (i = 0; i < num_neigh_rep; i++) {
-               for (j = 0; j < scan_res->num; j++) {
-                       /* Check for a better RSSI AP */
-                       if (os_memcmp(scan_res->res[j]->bssid,
-                                     neigh_rep[i].bssid, ETH_ALEN) == 0 &&
-                           scan_res->res[j]->level >
-                           wpa_s->current_bss->level) {
-                               /* Got a BSSID with better RSSI value */
-                               os_memcpy(bssid_to_connect, neigh_rep[i].bssid,
-                                         ETH_ALEN);
-                               return 1;
-                       }
+       wpa_printf(MSG_DEBUG, "WNM: Current BSS " MACSTR " RSSI %d",
+                  MAC2STR(wpa_s->bssid), bss->level);
+
+       for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+               struct neighbor_report *nei;
+
+               nei = &wpa_s->wnm_neighbor_report_elements[i];
+               if (nei->preference_present && nei->preference == 0) {
+                       wpa_printf(MSG_DEBUG, "Skip excluded BSS " MACSTR,
+                                  MAC2STR(nei->bssid));
+                       continue;
+               }
+
+               target = wpa_bss_get_bssid(wpa_s, nei->bssid);
+               if (!target) {
+                       wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
+                                  " (pref %d) not found in scan results",
+                                  MAC2STR(nei->bssid),
+                                  nei->preference_present ? nei->preference :
+                                  -1);
+                       continue;
                }
+
+               if (bss->ssid_len != target->ssid_len ||
+                   os_memcmp(bss->ssid, target->ssid, bss->ssid_len) != 0) {
+                       /*
+                        * TODO: Could consider allowing transition to another
+                        * ESS if PMF was enabled for the association.
+                        */
+                       wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
+                                  " (pref %d) in different ESS",
+                                  MAC2STR(nei->bssid),
+                                  nei->preference_present ? nei->preference :
+                                  -1);
+                       continue;
+               }
+
+               if (target->level < bss->level && target->level < -80) {
+                       wpa_printf(MSG_DEBUG, "Candidate BSS " MACSTR
+                                  " (pref %d) does not have sufficient signal level (%d)",
+                                  MAC2STR(nei->bssid),
+                                  nei->preference_present ? nei->preference :
+                                  -1,
+                                  target->level);
+                       continue;
+               }
+
+               wpa_printf(MSG_DEBUG,
+                          "WNM: Found an acceptable preferred transition candidate BSS "
+                          MACSTR " (RSSI %d)",
+                          MAC2STR(nei->bssid), target->level);
+               return target;
        }
 
-       return 0;
+       return NULL;
 }
 
 
@@ -500,10 +544,16 @@ static void wnm_send_bss_transition_mgmt_resp(
        u8 buf[1000], *pos;
        struct ieee80211_mgmt *mgmt;
        size_t len;
+       int res;
 
        wpa_printf(MSG_DEBUG, "WNM: Send BSS Transition Management Response "
                   "to " MACSTR " dialog_token=%u status=%u delay=%d",
                   MAC2STR(wpa_s->bssid), dialog_token, status, delay);
+       if (!wpa_s->current_bss) {
+               wpa_printf(MSG_DEBUG,
+                          "WNM: Current BSS not known - drop response");
+               return;
+       }
 
        mgmt = (struct ieee80211_mgmt *) buf;
        os_memset(&buf, 0, sizeof(buf));
@@ -521,66 +571,215 @@ static void wnm_send_bss_transition_mgmt_resp(
        if (target_bssid) {
                os_memcpy(pos, target_bssid, ETH_ALEN);
                pos += ETH_ALEN;
+       } else if (status == WNM_BSS_TM_ACCEPT) {
+               /*
+                * P802.11-REVmc clarifies that the Target BSSID field is always
+                * present when status code is zero, so use a fake value here if
+                * no BSSID is yet known.
+                */
+               os_memset(pos, 0, ETH_ALEN);
+               pos += ETH_ALEN;
        }
 
        len = pos - (u8 *) &mgmt->u.action.category;
 
-       wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
-                           wpa_s->own_addr, wpa_s->bssid,
-                           &mgmt->u.action.category, len, 0);
+       res = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+                                 wpa_s->own_addr, wpa_s->bssid,
+                                 &mgmt->u.action.category, len, 0);
+       if (res < 0) {
+               wpa_printf(MSG_DEBUG,
+                          "WNM: Failed to send BSS Transition Management Response");
+       }
 }
 
 
-void wnm_scan_response(struct wpa_supplicant *wpa_s,
-                      struct wpa_scan_results *scan_res)
+int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail)
 {
-       u8 bssid[ETH_ALEN];
+       struct wpa_bss *bss;
+       struct wpa_ssid *ssid = wpa_s->current_ssid;
+       enum bss_trans_mgmt_status_code status = WNM_BSS_TM_REJECT_UNSPECIFIED;
 
-       if (scan_res == NULL) {
-               wpa_printf(MSG_ERROR, "Scan result is NULL");
-               goto send_bss_resp_fail;
+       if (!wpa_s->wnm_neighbor_report_elements)
+               return 0;
+
+       if (os_reltime_before(&wpa_s->wnm_cand_valid_until,
+                             &wpa_s->scan_trigger_time)) {
+               wpa_printf(MSG_DEBUG, "WNM: Previously stored BSS transition candidate list is not valid anymore - drop it");
+               wnm_deallocate_memory(wpa_s);
+               return 0;
+       }
+
+       if (!wpa_s->current_bss ||
+           os_memcmp(wpa_s->wnm_cand_from_bss, wpa_s->current_bss->bssid,
+                     ETH_ALEN) != 0) {
+               wpa_printf(MSG_DEBUG, "WNM: Stored BSS transition candidate list not from the current BSS - ignore it");
+               return 0;
        }
 
        /* Compare the Neighbor Report and scan results */
-       if (compare_scan_neighbor_results(wpa_s, scan_res,
-                                         wpa_s->wnm_neighbor_report_elements,
-                                         wpa_s->wnm_num_neighbor_report,
-                                         bssid) == 1) {
-               /* Associate to the network */
-               struct wpa_bss *bss;
-               struct wpa_ssid *ssid = wpa_s->current_ssid;
-
-               bss = wpa_bss_get_bssid(wpa_s, bssid);
-               if (!bss) {
-                       wpa_printf(MSG_DEBUG, "WNM: Target AP not found from "
-                                  "BSS table");
-                       goto send_bss_resp_fail;
-               }
+       bss = compare_scan_neighbor_results(wpa_s);
+       if (!bss) {
+               wpa_printf(MSG_DEBUG, "WNM: No BSS transition candidate match found");
+               status = WNM_BSS_TM_REJECT_NO_SUITABLE_CANDIDATES;
+               goto send_bss_resp_fail;
+       }
 
-               /* Send the BSS Management Response - Accept */
-               if (wpa_s->wnm_reply) {
-                       wnm_send_bss_transition_mgmt_resp(wpa_s,
+       /* Associate to the network */
+       /* Send the BSS Management Response - Accept */
+       if (wpa_s->wnm_reply) {
+               wpa_s->wnm_reply = 0;
+               wnm_send_bss_transition_mgmt_resp(wpa_s,
                                                  wpa_s->wnm_dialog_token,
                                                  WNM_BSS_TM_ACCEPT,
-                                                 0, NULL);
-               }
+                                                 0, bss->bssid);
+       }
 
-               wpa_s->reassociate = 1;
-               wpa_supplicant_connect(wpa_s, bss, ssid);
-               wnm_deallocate_memory(wpa_s);
-               return;
+       if (bss == wpa_s->current_bss) {
+               wpa_printf(MSG_DEBUG,
+                          "WNM: Already associated with the preferred candidate");
+               return 1;
        }
 
-       /* Send reject response for all the failures */
-send_bss_resp_fail:
+       wpa_s->reassociate = 1;
+       wpa_supplicant_connect(wpa_s, bss, ssid);
        wnm_deallocate_memory(wpa_s);
+       return 1;
+
+send_bss_resp_fail:
+       if (!reply_on_fail)
+               return 0;
+
+       /* Send reject response for all the failures */
+
        if (wpa_s->wnm_reply) {
+               wpa_s->wnm_reply = 0;
                wnm_send_bss_transition_mgmt_resp(wpa_s,
                                                  wpa_s->wnm_dialog_token,
-                                                 WNM_BSS_TM_REJECT_UNSPECIFIED,
-                                                 0, NULL);
+                                                 status, 0, NULL);
+       }
+       wnm_deallocate_memory(wpa_s);
+
+       return 0;
+}
+
+
+static int cand_pref_compar(const void *a, const void *b)
+{
+       const struct neighbor_report *aa = a;
+       const struct neighbor_report *bb = b;
+
+       if (!aa->preference_present && !bb->preference_present)
+               return 0;
+       if (!aa->preference_present)
+               return 1;
+       if (!bb->preference_present)
+               return -1;
+       if (bb->preference > aa->preference)
+               return 1;
+       if (bb->preference < aa->preference)
+               return -1;
+       return 0;
+}
+
+
+static void wnm_sort_cand_list(struct wpa_supplicant *wpa_s)
+{
+       if (!wpa_s->wnm_neighbor_report_elements)
+               return;
+       qsort(wpa_s->wnm_neighbor_report_elements,
+             wpa_s->wnm_num_neighbor_report, sizeof(struct neighbor_report),
+             cand_pref_compar);
+}
+
+
+static void wnm_dump_cand_list(struct wpa_supplicant *wpa_s)
+{
+       unsigned int i;
+
+       wpa_printf(MSG_DEBUG, "WNM: BSS Transition Candidate List");
+       if (!wpa_s->wnm_neighbor_report_elements)
+               return;
+       for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+               struct neighbor_report *nei;
+
+               nei = &wpa_s->wnm_neighbor_report_elements[i];
+               wpa_printf(MSG_DEBUG, "%u: " MACSTR
+                          " info=0x%x op_class=%u chan=%u phy=%u pref=%d freq=%d",
+                          i, MAC2STR(nei->bssid), nei->bssid_info,
+                          nei->regulatory_class,
+                          nei->channel_number, nei->phy_type,
+                          nei->preference_present ? nei->preference : -1,
+                          nei->freq);
+       }
+}
+
+
+static int chan_supported(struct wpa_supplicant *wpa_s, int freq)
+{
+       unsigned int i;
+
+       for (i = 0; i < wpa_s->hw.num_modes; i++) {
+               struct hostapd_hw_modes *mode = &wpa_s->hw.modes[i];
+               int j;
+
+               for (j = 0; j < mode->num_channels; j++) {
+                       struct hostapd_channel_data *chan;
+
+                       chan = &mode->channels[j];
+                       if (chan->freq == freq &&
+                           !(chan->flag & HOSTAPD_CHAN_DISABLED))
+                               return 1;
+               }
+       }
+
+       return 0;
+}
+
+
+static void wnm_set_scan_freqs(struct wpa_supplicant *wpa_s)
+{
+       int *freqs;
+       int num_freqs = 0;
+       unsigned int i;
+
+       if (!wpa_s->wnm_neighbor_report_elements)
+               return;
+
+       if (wpa_s->hw.modes == NULL)
+               return;
+
+       os_free(wpa_s->next_scan_freqs);
+       wpa_s->next_scan_freqs = NULL;
+
+       freqs = os_calloc(wpa_s->wnm_num_neighbor_report + 1, sizeof(int));
+       if (freqs == NULL)
+               return;
+
+       for (i = 0; i < wpa_s->wnm_num_neighbor_report; i++) {
+               struct neighbor_report *nei;
+
+               nei = &wpa_s->wnm_neighbor_report_elements[i];
+               if (nei->freq <= 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "WNM: Unknown neighbor operating frequency for "
+                                  MACSTR " - scan all channels",
+                                  MAC2STR(nei->bssid));
+                       os_free(freqs);
+                       return;
+               }
+               if (chan_supported(wpa_s, nei->freq))
+                       add_freq(freqs, &num_freqs, nei->freq);
+       }
+
+       if (num_freqs == 0) {
+               os_free(freqs);
+               return;
        }
-       return;
+
+       wpa_printf(MSG_DEBUG,
+                  "WNM: Scan %d frequencies based on transition candidate list",
+                  num_freqs);
+       wpa_s->next_scan_freqs = freqs;
 }
 
 
@@ -588,20 +787,28 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
                                             const u8 *pos, const u8 *end,
                                             int reply)
 {
+       unsigned int beacon_int;
+       u8 valid_int;
+
        if (pos + 5 > end)
                return;
 
+       if (wpa_s->current_bss)
+               beacon_int = wpa_s->current_bss->beacon_int;
+       else
+               beacon_int = 100; /* best guess */
+
        wpa_s->wnm_dialog_token = pos[0];
        wpa_s->wnm_mode = pos[1];
        wpa_s->wnm_dissoc_timer = WPA_GET_LE16(pos + 2);
-       wpa_s->wnm_validity_interval = pos[4];
+       valid_int = pos[4];
        wpa_s->wnm_reply = reply;
 
        wpa_printf(MSG_DEBUG, "WNM: BSS Transition Management Request: "
                   "dialog_token=%u request_mode=0x%x "
                   "disassoc_timer=%u validity_interval=%u",
                   wpa_s->wnm_dialog_token, wpa_s->wnm_mode,
-                  wpa_s->wnm_dissoc_timer, wpa_s->wnm_validity_interval);
+                  wpa_s->wnm_dissoc_timer, valid_int);
 
        pos += 5;
 
@@ -616,7 +823,6 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
 
        if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_ESS_DISASSOC_IMMINENT) {
                char url[256];
-               unsigned int beacon_int;
 
                if (pos + 1 > end || pos + 1 + pos[0] > end) {
                        wpa_printf(MSG_DEBUG, "WNM: Invalid BSS Transition "
@@ -627,11 +833,6 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
                url[pos[0]] = '\0';
                pos += 1 + pos[0];
 
-               if (wpa_s->current_bss)
-                       beacon_int = wpa_s->current_bss->beacon_int;
-               else
-                       beacon_int = 100; /* best guess */
-
                wpa_msg(wpa_s, MSG_INFO, ESS_DISASSOC_IMMINENT "%d %u %s",
                        wpa_sm_pmf_enabled(wpa_s->wpa),
                        wpa_s->wnm_dissoc_timer * beacon_int * 128 / 125, url);
@@ -649,11 +850,12 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
        }
 
        if (wpa_s->wnm_mode & WNM_BSS_TM_REQ_PREF_CAND_LIST_INCLUDED) {
+               unsigned int valid_ms;
+
                wpa_msg(wpa_s, MSG_INFO, "WNM: Preferred List Available");
-               wpa_s->wnm_num_neighbor_report = 0;
-               os_free(wpa_s->wnm_neighbor_report_elements);
-               wpa_s->wnm_neighbor_report_elements = os_zalloc(
-                       WNM_MAX_NEIGHBOR_REPORT *
+               wnm_deallocate_memory(wpa_s);
+               wpa_s->wnm_neighbor_report_elements = os_calloc(
+                       WNM_MAX_NEIGHBOR_REPORT,
                        sizeof(struct neighbor_report));
                if (wpa_s->wnm_neighbor_report_elements == NULL)
                        return;
@@ -670,16 +872,44 @@ static void ieee802_11_rx_bss_trans_mgmt_req(struct wpa_supplicant *wpa_s,
                                wpa_printf(MSG_DEBUG, "WNM: Truncated request");
                                return;
                        }
-                       wnm_parse_neighbor_report(
-                               wpa_s, pos, len,
-                               &wpa_s->wnm_neighbor_report_elements[
-                                       wpa_s->wnm_num_neighbor_report]);
+                       if (tag == WLAN_EID_NEIGHBOR_REPORT) {
+                               struct neighbor_report *rep;
+                               rep = &wpa_s->wnm_neighbor_report_elements[
+                                       wpa_s->wnm_num_neighbor_report];
+                               wnm_parse_neighbor_report(wpa_s, pos, len, rep);
+                       }
 
                        pos += len;
                        wpa_s->wnm_num_neighbor_report++;
                }
+               wnm_sort_cand_list(wpa_s);
+               wnm_dump_cand_list(wpa_s);
+               valid_ms = valid_int * beacon_int * 128 / 125;
+               wpa_printf(MSG_DEBUG, "WNM: Candidate list valid for %u ms",
+                          valid_ms);
+               os_get_reltime(&wpa_s->wnm_cand_valid_until);
+               wpa_s->wnm_cand_valid_until.sec += valid_ms / 1000;
+               wpa_s->wnm_cand_valid_until.usec += (valid_ms % 1000) * 1000;
+               wpa_s->wnm_cand_valid_until.sec +=
+                       wpa_s->wnm_cand_valid_until.usec / 1000000;
+               wpa_s->wnm_cand_valid_until.usec %= 1000000;
+               os_memcpy(wpa_s->wnm_cand_from_bss, wpa_s->bssid, ETH_ALEN);
+
+               if (wpa_s->last_scan_res_used > 0) {
+                       struct os_reltime now;
+
+                       os_get_reltime(&now);
+                       if (!os_reltime_expired(&now, &wpa_s->last_scan, 10)) {
+                               wpa_printf(MSG_DEBUG,
+                                          "WNM: Try to use recent scan results");
+                               if (wnm_scan_process(wpa_s, 0) > 0)
+                                       return;
+                               wpa_printf(MSG_DEBUG,
+                                          "WNM: No match in previous scan results - try a new scan");
+                       }
+               }
 
-               wpa_s->scan_res_handler = wnm_scan_response;
+               wnm_set_scan_freqs(wpa_s);
                wpa_supplicant_req_scan(wpa_s, 0, 0);
        } else if (reply) {
                enum bss_trans_mgmt_status_code status;
@@ -717,7 +947,7 @@ int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
                                           WLAN_FC_STYPE_ACTION);
        mgmt->u.action.category = WLAN_ACTION_WNM;
        mgmt->u.action.u.bss_tm_query.action = WNM_BSS_TRANS_MGMT_QUERY;
-       mgmt->u.action.u.bss_tm_query.dialog_token = 0;
+       mgmt->u.action.u.bss_tm_query.dialog_token = 1;
        mgmt->u.action.u.bss_tm_query.query_reason = query_reason;
        pos = mgmt->u.action.u.bss_tm_query.variable;
 
@@ -731,23 +961,170 @@ int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
 }
 
 
+static void ieee802_11_rx_wnm_notif_req_wfa(struct wpa_supplicant *wpa_s,
+                                           const u8 *sa, const u8 *data,
+                                           int len)
+{
+       const u8 *pos, *end, *next;
+       u8 ie, ie_len;
+
+       pos = data;
+       end = data + len;
+
+       while (pos + 1 < end) {
+               ie = *pos++;
+               ie_len = *pos++;
+               wpa_printf(MSG_DEBUG, "WNM: WFA subelement %u len %u",
+                          ie, ie_len);
+               if (ie_len > end - pos) {
+                       wpa_printf(MSG_DEBUG, "WNM: Not enough room for "
+                                  "subelement");
+                       break;
+               }
+               next = pos + ie_len;
+               if (ie_len < 4) {
+                       pos = next;
+                       continue;
+               }
+               wpa_printf(MSG_DEBUG, "WNM: Subelement OUI %06x type %u",
+                          WPA_GET_BE24(pos), pos[3]);
+
+#ifdef CONFIG_HS20
+               if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 5 &&
+                   WPA_GET_BE24(pos) == OUI_WFA &&
+                   pos[3] == HS20_WNM_SUB_REM_NEEDED) {
+                       /* Subscription Remediation subelement */
+                       const u8 *ie_end;
+                       u8 url_len;
+                       char *url;
+                       u8 osu_method;
+
+                       wpa_printf(MSG_DEBUG, "WNM: Subscription Remediation "
+                                  "subelement");
+                       ie_end = pos + ie_len;
+                       pos += 4;
+                       url_len = *pos++;
+                       if (url_len == 0) {
+                               wpa_printf(MSG_DEBUG, "WNM: No Server URL included");
+                               url = NULL;
+                               osu_method = 1;
+                       } else {
+                               if (pos + url_len + 1 > ie_end) {
+                                       wpa_printf(MSG_DEBUG, "WNM: Not enough room for Server URL (len=%u) and Server Method (left %d)",
+                                                  url_len,
+                                                  (int) (ie_end - pos));
+                                       break;
+                               }
+                               url = os_malloc(url_len + 1);
+                               if (url == NULL)
+                                       break;
+                               os_memcpy(url, pos, url_len);
+                               url[url_len] = '\0';
+                               osu_method = pos[url_len];
+                       }
+                       hs20_rx_subscription_remediation(wpa_s, url,
+                                                        osu_method);
+                       os_free(url);
+                       pos = next;
+                       continue;
+               }
+
+               if (ie == WLAN_EID_VENDOR_SPECIFIC && ie_len >= 8 &&
+                   WPA_GET_BE24(pos) == OUI_WFA &&
+                   pos[3] == HS20_WNM_DEAUTH_IMMINENT_NOTICE) {
+                       const u8 *ie_end;
+                       u8 url_len;
+                       char *url;
+                       u8 code;
+                       u16 reauth_delay;
+
+                       ie_end = pos + ie_len;
+                       pos += 4;
+                       code = *pos++;
+                       reauth_delay = WPA_GET_LE16(pos);
+                       pos += 2;
+                       url_len = *pos++;
+                       wpa_printf(MSG_DEBUG, "WNM: HS 2.0 Deauthentication "
+                                  "Imminent - Reason Code %u   "
+                                  "Re-Auth Delay %u  URL Length %u",
+                                  code, reauth_delay, url_len);
+                       if (pos + url_len > ie_end)
+                               break;
+                       url = os_malloc(url_len + 1);
+                       if (url == NULL)
+                               break;
+                       os_memcpy(url, pos, url_len);
+                       url[url_len] = '\0';
+                       hs20_rx_deauth_imminent_notice(wpa_s, code,
+                                                      reauth_delay, url);
+                       os_free(url);
+                       pos = next;
+                       continue;
+               }
+#endif /* CONFIG_HS20 */
+
+               pos = next;
+       }
+}
+
+
+static void ieee802_11_rx_wnm_notif_req(struct wpa_supplicant *wpa_s,
+                                       const u8 *sa, const u8 *frm, int len)
+{
+       const u8 *pos, *end;
+       u8 dialog_token, type;
+
+       /* Dialog Token [1] | Type [1] | Subelements */
+
+       if (len < 2 || sa == NULL)
+               return;
+       end = frm + len;
+       pos = frm;
+       dialog_token = *pos++;
+       type = *pos++;
+
+       wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Received WNM-Notification Request "
+               "(dialog_token %u type %u sa " MACSTR ")",
+               dialog_token, type, MAC2STR(sa));
+       wpa_hexdump(MSG_DEBUG, "WNM-Notification Request subelements",
+                   pos, end - pos);
+
+       if (wpa_s->wpa_state != WPA_COMPLETED ||
+           os_memcmp(sa, wpa_s->bssid, ETH_ALEN) != 0) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "WNM: WNM-Notification frame not "
+                       "from our AP - ignore it");
+               return;
+       }
+
+       switch (type) {
+       case 1:
+               ieee802_11_rx_wnm_notif_req_wfa(wpa_s, sa, pos, end - pos);
+               break;
+       default:
+               wpa_dbg(wpa_s, MSG_DEBUG, "WNM: Ignore unknown "
+                       "WNM-Notification type %u", type);
+               break;
+       }
+}
+
+
 void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
-                             struct rx_action *action)
+                             const struct ieee80211_mgmt *mgmt, size_t len)
 {
        const u8 *pos, *end;
        u8 act;
 
-       if (action->data == NULL || action->len == 0)
+       if (len < IEEE80211_HDRLEN + 2)
                return;
 
-       pos = action->data;
-       end = pos + action->len;
+       pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 1;
        act = *pos++;
+       end = ((const u8 *) mgmt) + len;
 
        wpa_printf(MSG_DEBUG, "WNM: RX action %u from " MACSTR,
-                  act, MAC2STR(action->sa));
+                  act, MAC2STR(mgmt->sa));
        if (wpa_s->wpa_state < WPA_ASSOCIATED ||
-           os_memcmp(action->sa, wpa_s->bssid, ETH_ALEN) != 0) {
+           os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) {
                wpa_printf(MSG_DEBUG, "WNM: Ignore unexpected WNM Action "
                           "frame");
                return;
@@ -756,10 +1133,13 @@ void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
        switch (act) {
        case WNM_BSS_TRANS_MGMT_REQ:
                ieee802_11_rx_bss_trans_mgmt_req(wpa_s, pos, end,
-                                                !(action->da[0] & 0x01));
+                                                !(mgmt->da[0] & 0x01));
                break;
        case WNM_SLEEP_MODE_RESP:
-               ieee802_11_rx_wnmsleep_resp(wpa_s, action->data, action->len);
+               ieee802_11_rx_wnmsleep_resp(wpa_s, pos, end - pos);
+               break;
+       case WNM_NOTIFICATION_REQ:
+               ieee802_11_rx_wnm_notif_req(wpa_s, mgmt->sa, pos, end - pos);
                break;
        default:
                wpa_printf(MSG_ERROR, "WNM: Unknown request");
old mode 100644 (file)
new mode 100755 (executable)
index 2933926..8de4348
@@ -9,68 +9,43 @@
 #ifndef WNM_STA_H
 #define WNM_STA_H
 
-struct rx_action;
-struct wpa_supplicant;
-
-struct tsf_info {
-       u8 present;
-       u8 tsf_offset[2];
-       u8 beacon_interval[2];
-};
-
-struct condensed_country_string {
-       u8 present;
-       u8 country_string[2];
-};
-
-struct bss_transition_candidate {
-       u8 present;
-       u8 preference;
-};
-
-struct bss_termination_duration {
-       u8 present;
-       u8 duration[12];
-};
-
-struct bearing {
-       u8 present;
-       u8 bearing[8];
-};
-
 struct measurement_pilot {
-       u8 present;
        u8 measurement_pilot;
-       u8 num_vendor_specific;
-       u8 vendor_specific[255];
-};
-
-struct rrm_enabled_capabilities {
-       u8 present;
-       u8 capabilities[4];
+       u8 subelem_len;
+       u8 subelems[255];
 };
 
 struct multiple_bssid {
-       u8 present;
        u8 max_bssid_indicator;
-       u8 num_vendor_specific;
-       u8 vendor_specific[255];
+       u8 subelem_len;
+       u8 subelems[255];
 };
 
 struct neighbor_report {
        u8 bssid[ETH_ALEN];
-       u8 bssid_information[4];
+       u32 bssid_info;
        u8 regulatory_class;
        u8 channel_number;
        u8 phy_type;
-       struct tsf_info *tsf_info;
-       struct condensed_country_string *con_coun_str;
-       struct bss_transition_candidate *bss_tran_can;
-       struct bss_termination_duration *bss_term_dur;
-       struct bearing *bearing;
+       u8 preference; /* valid if preference_present=1 */
+       u16 tsf_offset; /* valid if tsf_present=1 */
+       u16 beacon_int; /* valid if tsf_present=1 */
+       char country[2]; /* valid if country_present=1 */
+       u8 rm_capab[5]; /* valid if rm_capab_present=1 */
+       u16 bearing; /* valid if bearing_present=1 */
+       u16 rel_height; /* valid if bearing_present=1 */
+       u32 distance; /* valid if bearing_present=1 */
+       u64 bss_term_tsf; /* valid if bss_term_present=1 */
+       u16 bss_term_dur; /* valid if bss_term_present=1 */
+       unsigned int preference_present:1;
+       unsigned int tsf_present:1;
+       unsigned int country_present:1;
+       unsigned int rm_capab_present:1;
+       unsigned int bearing_present:1;
+       unsigned int bss_term_present:1;
        struct measurement_pilot *meas_pilot;
-       struct rrm_enabled_capabilities *rrm_cap;
        struct multiple_bssid *mul_bssid;
+       int freq;
 };
 
 
@@ -78,13 +53,25 @@ int ieee802_11_send_wnmsleep_req(struct wpa_supplicant *wpa_s,
                                 u8 action, u16 intval, struct wpabuf *tfs_req);
 
 void ieee802_11_rx_wnm_action(struct wpa_supplicant *wpa_s,
-                             struct rx_action *action);
-
-void wnm_scan_response(struct wpa_supplicant *wpa_s,
-                      struct wpa_scan_results *scan_res);
+                             const struct ieee80211_mgmt *mgmt, size_t len);
 
 int wnm_send_bss_transition_mgmt_query(struct wpa_supplicant *wpa_s,
                                       u8 query_reason);
 void wnm_deallocate_memory(struct wpa_supplicant *wpa_s);
 
+
+#ifdef CONFIG_WNM
+
+int wnm_scan_process(struct wpa_supplicant *wpa_s, int reply_on_fail);
+
+#else /* CONFIG_WNM */
+
+static inline int wnm_scan_process(struct wpa_supplicant *wpa_s,
+                                  int reply_on_fail)
+{
+       return 0;
+}
+
+#endif /* CONFIG_WNM */
+
 #endif /* WNM_STA_H */
old mode 100644 (file)
new mode 100755 (executable)
index a77e767..5a0af0d
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - command line interface for wpa_supplicant daemon
- * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -28,7 +28,7 @@
 
 static const char *wpa_cli_version =
 "wpa_cli v" VERSION_STR "\n"
-"Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi> and contributors";
 
 
 static const char *wpa_cli_license =
@@ -70,7 +70,7 @@ static struct wpa_ctrl *ctrl_conn;
 static struct wpa_ctrl *mon_conn;
 static int wpa_cli_quit = 0;
 static int wpa_cli_attached = 0;
-static int wpa_cli_connected = 0;
+static int wpa_cli_connected = -1;
 static int wpa_cli_last_id = 0;
 #ifndef CONFIG_CTRL_IFACE_DIR
 #define CONFIG_CTRL_IFACE_DIR "/var/run/wpa_supplicant"
@@ -167,6 +167,7 @@ static void cli_txt_list_del_addr(struct dl_list *txt_list, const char *txt)
 }
 
 
+#ifdef CONFIG_P2P
 static void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt)
 {
        const char *end;
@@ -180,6 +181,7 @@ static void cli_txt_list_del_word(struct dl_list *txt_list, const char *txt)
        cli_txt_list_del(txt_list, buf);
        os_free(buf);
 }
+#endif /* CONFIG_P2P */
 
 
 static int cli_txt_list_add(struct dl_list *txt_list, const char *txt)
@@ -201,6 +203,7 @@ static int cli_txt_list_add(struct dl_list *txt_list, const char *txt)
 }
 
 
+#ifdef CONFIG_P2P
 static int cli_txt_list_add_addr(struct dl_list *txt_list, const char *txt)
 {
        u8 addr[ETH_ALEN];
@@ -227,6 +230,7 @@ static int cli_txt_list_add_word(struct dl_list *txt_list, const char *txt)
        os_free(buf);
        return ret;
 }
+#endif /* CONFIG_P2P */
 
 
 static char ** cli_txt_list_array(struct dl_list *txt_list)
@@ -329,7 +333,7 @@ static int wpa_cli_open_connection(const char *ifname, int attach)
                        return -1;
                res = os_snprintf(cfile, flen, "%s/%s", ctrl_iface_dir,
                                  ifname);
-               if (res < 0 || res >= flen) {
+               if (os_snprintf_error(flen, res)) {
                        os_free(cfile);
                        return -1;
                }
@@ -444,13 +448,13 @@ static int write_cmd(char *buf, size_t buflen, const char *cmd, int argc,
        end = buf + buflen;
 
        res = os_snprintf(pos, end - pos, "%s", cmd);
-       if (res < 0 || res >= end - pos)
+       if (os_snprintf_error(end - pos, res))
                goto fail;
        pos += res;
 
        for (i = 0; i < argc; i++) {
                res = os_snprintf(pos, end - pos, " %s", argv[i]);
-               if (res < 0 || res >= end - pos)
+               if (os_snprintf_error(end - pos, res))
                        goto fail;
                pos += res;
        }
@@ -492,6 +496,8 @@ static int wpa_cli_cmd_status(struct wpa_ctrl *ctrl, int argc, char *argv[])
                return wpa_ctrl_command(ctrl, "STATUS-VERBOSE");
        if (argc > 0 && os_strcmp(argv[0], "wps") == 0)
                return wpa_ctrl_command(ctrl, "STATUS-WPS");
+       if (argc > 0 && os_strcmp(argv[0], "driver") == 0)
+               return wpa_ctrl_command(ctrl, "STATUS-DRIVER");
        return wpa_ctrl_command(ctrl, "STATUS");
 }
 
@@ -526,6 +532,13 @@ static int wpa_cli_cmd_pmksa(struct wpa_ctrl *ctrl, int argc, char *argv[])
 }
 
 
+static int wpa_cli_cmd_pmksa_flush(struct wpa_ctrl *ctrl, int argc,
+                                  char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "PMKSA_FLUSH");
+}
+
+
 static int wpa_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
        print_help(argc > 0 ? argv[0] : NULL);
@@ -571,7 +584,7 @@ static int wpa_cli_cmd_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
 
        if (argc == 1) {
                res = os_snprintf(cmd, sizeof(cmd), "SET %s ", argv[0]);
-               if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+               if (os_snprintf_error(sizeof(cmd), res)) {
                        printf("Too long SET command.\n");
                        return -1;
                }
@@ -597,7 +610,8 @@ static char ** wpa_cli_complete_set(const char *str, int pos)
                /* global configuration parameters */
                "eapol_version", "ap_scan", "disable_scan_offload",
                "fast_reauth", "opensc_engine_path", "pkcs11_engine_path",
-               "pkcs11_module_path", "pcsc_reader", "pcsc_pin",
+               "pkcs11_module_path", "openssl_ciphers",
+               "pcsc_reader", "pcsc_pin",
                "driver_param", "dot11RSNAConfigPMKLifetime",
                "dot11RSNAConfigPMKReauthThreshold",
                "dot11RSNAConfigSATimeout",
@@ -609,7 +623,9 @@ static char ** wpa_cli_complete_set(const char *str, int pos)
                "p2p_oper_reg_class", "p2p_oper_channel",
                "p2p_go_intent", "p2p_ssid_postfix", "persistent_reconnect",
                "p2p_intra_bss", "p2p_group_idle", "p2p_pref_chan",
+               "p2p_no_go_freq",
                "p2p_go_ht40", "p2p_disabled", "p2p_no_group_iface",
+               "p2p_go_vht",
                "p2p_ignore_shared_freq", "country", "bss_max_count",
                "bss_expiration_age", "bss_expiration_scan_count",
                "filter_ssids", "filter_rssi", "max_num_sta",
@@ -619,9 +635,10 @@ static char ** wpa_cli_complete_set(const char *str, int pos)
                "wps_nfc_dev_pw", "ext_password_backend",
                "p2p_go_max_inactivity", "auto_interworking", "okc", "pmf",
                "sae_groups", "dtim_period", "beacon_int", "ap_vendor_elements",
-               "ignore_old_scan_res", "freq_list"
+               "ignore_old_scan_res", "freq_list", "external_sim",
+               "tdls_external_control", "p2p_search_delay"
        };
-       int i, num_fields = sizeof(fields) / sizeof(fields[0]);
+       int i, num_fields = ARRAY_SIZE(fields);
 
        if (arg == 1) {
                char **res = os_calloc(num_fields + 1, sizeof(char *));
@@ -641,6 +658,11 @@ static char ** wpa_cli_complete_set(const char *str, int pos)
        return NULL;
 }
 
+static int wpa_cli_cmd_dump(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "DUMP");
+}
+
 
 static int wpa_cli_cmd_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
@@ -667,6 +689,12 @@ static int wpa_cli_cmd_reassociate(struct wpa_ctrl *ctrl, int argc,
 }
 
 
+static int wpa_cli_cmd_reattach(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "REATTACH");
+}
+
+
 static int wpa_cli_cmd_preauthenticate(struct wpa_ctrl *ctrl, int argc,
                                       char *argv[])
 {
@@ -710,7 +738,7 @@ static int wpa_cli_cmd_bss_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
                res = os_snprintf(cmd, sizeof(cmd), "BSS_FLUSH 0");
        else
                res = os_snprintf(cmd, sizeof(cmd), "BSS_FLUSH %s", argv[0]);
-       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+       if (os_snprintf_error(sizeof(cmd), res)) {
                printf("Too long BSS_FLUSH command.\n");
                return -1;
        }
@@ -827,58 +855,6 @@ static int wpa_cli_cmd_nfc_get_handover_sel(struct wpa_ctrl *ctrl, int argc,
 }
 
 
-static int wpa_cli_cmd_nfc_rx_handover_req(struct wpa_ctrl *ctrl, int argc,
-                                          char *argv[])
-{
-       int ret;
-       char *buf;
-       size_t buflen;
-
-       if (argc != 1) {
-               printf("Invalid 'nfc_rx_handover_req' command - one argument "
-                      "is required.\n");
-               return -1;
-       }
-
-       buflen = 21 + os_strlen(argv[0]);
-       buf = os_malloc(buflen);
-       if (buf == NULL)
-               return -1;
-       os_snprintf(buf, buflen, "NFC_RX_HANDOVER_REQ %s", argv[0]);
-
-       ret = wpa_ctrl_command(ctrl, buf);
-       os_free(buf);
-
-       return ret;
-}
-
-
-static int wpa_cli_cmd_nfc_rx_handover_sel(struct wpa_ctrl *ctrl, int argc,
-                                          char *argv[])
-{
-       int ret;
-       char *buf;
-       size_t buflen;
-
-       if (argc != 1) {
-               printf("Invalid 'nfc_rx_handover_sel' command - one argument "
-                      "is required.\n");
-               return -1;
-       }
-
-       buflen = 21 + os_strlen(argv[0]);
-       buf = os_malloc(buflen);
-       if (buf == NULL)
-               return -1;
-       os_snprintf(buf, buflen, "NFC_RX_HANDOVER_SEL %s", argv[0]);
-
-       ret = wpa_ctrl_command(ctrl, buf);
-       os_free(buf);
-
-       return ret;
-}
-
-
 static int wpa_cli_cmd_nfc_report_handover(struct wpa_ctrl *ctrl, int argc,
                                           char *argv[])
 {
@@ -937,7 +913,7 @@ static int wpa_cli_cmd_wps_reg(struct wpa_ctrl *ctrl, int argc, char *argv[])
                return -1;
        }
 
-       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+       if (os_snprintf_error(sizeof(cmd), res)) {
                printf("Too long WPS_REG command.\n");
                return -1;
        }
@@ -1062,7 +1038,7 @@ static int wpa_cli_cmd_wps_er_config(struct wpa_ctrl *ctrl, int argc,
                return -1;
        }
 
-       if (res < 0 || (size_t) res >= sizeof(cmd) - 1) {
+       if (os_snprintf_error(sizeof(cmd), res)) {
                printf("Too long WPS_ER_CONFIG command.\n");
                return -1;
        }
@@ -1114,14 +1090,14 @@ static int wpa_cli_cmd_identity(struct wpa_ctrl *ctrl, int argc, char *argv[])
        pos = cmd;
        ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "IDENTITY-%s:%s",
                          argv[0], argv[1]);
-       if (ret < 0 || ret >= end - pos) {
+       if (os_snprintf_error(end - pos, ret)) {
                printf("Too long IDENTITY command.\n");
                return -1;
        }
        pos += ret;
        for (i = 2; i < argc; i++) {
                ret = os_snprintf(pos, end - pos, " %s", argv[i]);
-               if (ret < 0 || ret >= end - pos) {
+               if (os_snprintf_error(end - pos, ret)) {
                        printf("Too long IDENTITY command.\n");
                        return -1;
                }
@@ -1147,14 +1123,14 @@ static int wpa_cli_cmd_password(struct wpa_ctrl *ctrl, int argc, char *argv[])
        pos = cmd;
        ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSWORD-%s:%s",
                          argv[0], argv[1]);
-       if (ret < 0 || ret >= end - pos) {
+       if (os_snprintf_error(end - pos, ret)) {
                printf("Too long PASSWORD command.\n");
                return -1;
        }
        pos += ret;
        for (i = 2; i < argc; i++) {
                ret = os_snprintf(pos, end - pos, " %s", argv[i]);
-               if (ret < 0 || ret >= end - pos) {
+               if (os_snprintf_error(end - pos, ret)) {
                        printf("Too long PASSWORD command.\n");
                        return -1;
                }
@@ -1181,14 +1157,14 @@ static int wpa_cli_cmd_new_password(struct wpa_ctrl *ctrl, int argc,
        pos = cmd;
        ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "NEW_PASSWORD-%s:%s",
                          argv[0], argv[1]);
-       if (ret < 0 || ret >= end - pos) {
+       if (os_snprintf_error(end - pos, ret)) {
                printf("Too long NEW_PASSWORD command.\n");
                return -1;
        }
        pos += ret;
        for (i = 2; i < argc; i++) {
                ret = os_snprintf(pos, end - pos, " %s", argv[i]);
-               if (ret < 0 || ret >= end - pos) {
+               if (os_snprintf_error(end - pos, ret)) {
                        printf("Too long NEW_PASSWORD command.\n");
                        return -1;
                }
@@ -1214,14 +1190,14 @@ static int wpa_cli_cmd_pin(struct wpa_ctrl *ctrl, int argc, char *argv[])
        pos = cmd;
        ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PIN-%s:%s",
                          argv[0], argv[1]);
-       if (ret < 0 || ret >= end - pos) {
+       if (os_snprintf_error(end - pos, ret)) {
                printf("Too long PIN command.\n");
                return -1;
        }
        pos += ret;
        for (i = 2; i < argc; i++) {
                ret = os_snprintf(pos, end - pos, " %s", argv[i]);
-               if (ret < 0 || ret >= end - pos) {
+               if (os_snprintf_error(end - pos, ret)) {
                        printf("Too long PIN command.\n");
                        return -1;
                }
@@ -1246,14 +1222,14 @@ static int wpa_cli_cmd_otp(struct wpa_ctrl *ctrl, int argc, char *argv[])
        pos = cmd;
        ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "OTP-%s:%s",
                          argv[0], argv[1]);
-       if (ret < 0 || ret >= end - pos) {
+       if (os_snprintf_error(end - pos, ret)) {
                printf("Too long OTP command.\n");
                return -1;
        }
        pos += ret;
        for (i = 2; i < argc; i++) {
                ret = os_snprintf(pos, end - pos, " %s", argv[i]);
-               if (ret < 0 || ret >= end - pos) {
+               if (os_snprintf_error(end - pos, ret)) {
                        printf("Too long OTP command.\n");
                        return -1;
                }
@@ -1264,6 +1240,38 @@ static int wpa_cli_cmd_otp(struct wpa_ctrl *ctrl, int argc, char *argv[])
 }
 
 
+static int wpa_cli_cmd_sim(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       char cmd[256], *pos, *end;
+       int i, ret;
+
+       if (argc < 2) {
+               printf("Invalid SIM command: needs two arguments "
+                      "(network id and SIM operation response)\n");
+               return -1;
+       }
+
+       end = cmd + sizeof(cmd);
+       pos = cmd;
+       ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "SIM-%s:%s",
+                         argv[0], argv[1]);
+       if (os_snprintf_error(end - pos, ret)) {
+               printf("Too long SIM command.\n");
+               return -1;
+       }
+       pos += ret;
+       for (i = 2; i < argc; i++) {
+               ret = os_snprintf(pos, end - pos, " %s", argv[i]);
+               if (os_snprintf_error(end - pos, ret)) {
+                       printf("Too long SIM command.\n");
+                       return -1;
+               }
+               pos += ret;
+       }
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
 static int wpa_cli_cmd_passphrase(struct wpa_ctrl *ctrl, int argc,
                                  char *argv[])
 {
@@ -1280,14 +1288,14 @@ static int wpa_cli_cmd_passphrase(struct wpa_ctrl *ctrl, int argc,
        pos = cmd;
        ret = os_snprintf(pos, end - pos, WPA_CTRL_RSP "PASSPHRASE-%s:%s",
                          argv[0], argv[1]);
-       if (ret < 0 || ret >= end - pos) {
+       if (os_snprintf_error(end - pos, ret)) {
                printf("Too long PASSPHRASE command.\n");
                return -1;
        }
        pos += ret;
        for (i = 2; i < argc; i++) {
                ret = os_snprintf(pos, end - pos, " %s", argv[i]);
-               if (ret < 0 || ret >= end - pos) {
+               if (os_snprintf_error(end - pos, ret)) {
                        printf("Too long PASSPHRASE command.\n");
                        return -1;
                }
@@ -1421,6 +1429,24 @@ static int wpa_cli_cmd_get_network(struct wpa_ctrl *ctrl, int argc,
 }
 
 
+static int wpa_cli_cmd_dup_network(struct wpa_ctrl *ctrl, int argc,
+                                  char *argv[])
+{
+       if (argc == 0) {
+               wpa_cli_show_network_variables();
+               return 0;
+       }
+
+       if (argc < 3) {
+               printf("Invalid DUP_NETWORK command: needs three arguments\n"
+                      "(src netid, dest netid, and variable name)\n");
+               return -1;
+       }
+
+       return wpa_cli_cmd(ctrl, "DUP_NETWORK", 3, argc, argv);
+}
+
+
 static int wpa_cli_cmd_list_creds(struct wpa_ctrl *ctrl, int argc,
                                  char *argv[])
 {
@@ -1453,6 +1479,18 @@ static int wpa_cli_cmd_set_cred(struct wpa_ctrl *ctrl, int argc, char *argv[])
 }
 
 
+static int wpa_cli_cmd_get_cred(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       if (argc != 2) {
+               printf("Invalid GET_CRED command: needs two arguments\n"
+                      "(cred id, variable name)\n");
+               return -1;
+       }
+
+       return wpa_cli_cmd(ctrl, "GET_CRED", 2, argc, argv);
+}
+
+
 static int wpa_cli_cmd_disconnect(struct wpa_ctrl *ctrl, int argc,
                                  char *argv[])
 {
@@ -1544,8 +1582,12 @@ static int wpa_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc, char *argv[])
        wpa_cli_close_connection();
        os_free(ctrl_ifname);
        ctrl_ifname = os_strdup(argv[0]);
+       if (!ctrl_ifname) {
+               printf("Failed to allocate memory\n");
+               return 0;
+       }
 
-       if (wpa_cli_open_connection(ctrl_ifname, 1)) {
+       if (wpa_cli_open_connection(ctrl_ifname, 1) == 0) {
                printf("Connected to interface '%s.\n", ctrl_ifname);
        } else {
                printf("Could not connect to interface '%s' - re-trying\n",
@@ -1593,7 +1635,7 @@ static int wpa_cli_cmd_interface_add(struct wpa_ctrl *ctrl, int argc,
                          argc > 1 ? argv[1] : "", argc > 2 ? argv[2] : "",
                          argc > 3 ? argv[3] : "", argc > 4 ? argv[4] : "",
                          argc > 5 ? argv[5] : "");
-       if (res < 0 || (size_t) res >= sizeof(cmd))
+       if (os_snprintf_error(sizeof(cmd), res))
                return -1;
        cmd[sizeof(cmd) - 1] = '\0';
        return wpa_ctrl_command(ctrl, cmd);
@@ -1683,6 +1725,13 @@ static int wpa_cli_cmd_disassociate(struct wpa_ctrl *ctrl, int argc,
 {
        return wpa_cli_cmd(ctrl, "DISASSOCIATE", 1, argc, argv);
 }
+
+static int wpa_cli_cmd_chanswitch(struct wpa_ctrl *ctrl, int argc,
+                                   char *argv[])
+{
+       return wpa_cli_cmd(ctrl, "CHAN_SWITCH", 2, argc, argv);
+}
+
 #endif /* CONFIG_AP */
 
 
@@ -1698,10 +1747,12 @@ static int wpa_cli_cmd_resume(struct wpa_ctrl *ctrl, int argc, char *argv[])
 }
 
 
+#ifdef CONFIG_TESTING_OPTIONS
 static int wpa_cli_cmd_drop_sa(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
        return wpa_ctrl_command(ctrl, "DROP_SA");
 }
+#endif /* CONFIG_TESTING_OPTIONS */
 
 
 static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[])
@@ -1710,6 +1761,32 @@ static int wpa_cli_cmd_roam(struct wpa_ctrl *ctrl, int argc, char *argv[])
 }
 
 
+#ifdef CONFIG_MESH
+
+static int wpa_cli_cmd_mesh_interface_add(struct wpa_ctrl *ctrl, int argc,
+                                         char *argv[])
+{
+       return wpa_cli_cmd(ctrl, "MESH_INTERFACE_ADD", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_mesh_group_add(struct wpa_ctrl *ctrl, int argc,
+                                     char *argv[])
+{
+       return wpa_cli_cmd(ctrl, "MESH_GROUP_ADD", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_mesh_group_remove(struct wpa_ctrl *ctrl, int argc,
+                                        char *argv[])
+{
+       return wpa_cli_cmd(ctrl, "MESH_GROUP_REMOVE", 1, argc, argv);
+}
+
+#endif /* CONFIG_MESH */
+
+
+#ifdef CONFIG_P2P
 
 static int wpa_cli_cmd_p2p_find(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
@@ -1753,6 +1830,20 @@ static int wpa_cli_cmd_p2p_stop_find(struct wpa_ctrl *ctrl, int argc,
 }
 
 
+static int wpa_cli_cmd_p2p_asp_provision(struct wpa_ctrl *ctrl, int argc,
+                                        char *argv[])
+{
+       return wpa_cli_cmd(ctrl, "P2P_ASP_PROVISION", 3, argc, argv);
+}
+
+
+static int wpa_cli_cmd_p2p_asp_provision_resp(struct wpa_ctrl *ctrl, int argc,
+                                             char *argv[])
+{
+       return wpa_cli_cmd(ctrl, "P2P_ASP_PROVISION_RESP", 2, argc, argv);
+}
+
+
 static int wpa_cli_cmd_p2p_connect(struct wpa_ctrl *ctrl, int argc,
                                   char *argv[])
 {
@@ -1837,11 +1928,9 @@ static int wpa_cli_cmd_p2p_serv_disc_req(struct wpa_ctrl *ctrl, int argc,
 {
        char cmd[4096];
 
-       if (argc != 2 && argc != 4) {
+       if (argc < 2) {
                printf("Invalid P2P_SERV_DISC_REQ command: needs two "
-                      "arguments (address and TLVs) or four arguments "
-                      "(address, \"upnp\", version, search target "
-                      "(SSDP ST:)\n");
+                      "or more arguments (address and TLVs)\n");
                return -1;
        }
 
@@ -1872,7 +1961,7 @@ static int wpa_cli_cmd_p2p_serv_disc_resp(struct wpa_ctrl *ctrl, int argc,
 
        res = os_snprintf(cmd, sizeof(cmd), "P2P_SERV_DISC_RESP %s %s %s %s",
                          argv[0], argv[1], argv[2], argv[3]);
-       if (res < 0 || (size_t) res >= sizeof(cmd))
+       if (os_snprintf_error(sizeof(cmd), res))
                return -1;
        cmd[sizeof(cmd) - 1] = '\0';
        return wpa_ctrl_command(ctrl, cmd);
@@ -1903,27 +1992,25 @@ static int wpa_cli_cmd_p2p_service_flush(struct wpa_ctrl *ctrl, int argc,
 static int wpa_cli_cmd_p2p_service_add(struct wpa_ctrl *ctrl, int argc,
                                       char *argv[])
 {
-       char cmd[4096];
-       int res;
+       if (argc < 3) {
+               printf("Invalid P2P_SERVICE_ADD command: needs 3-6 arguments\n");
+               return -1;
+       }
+
+       return wpa_cli_cmd(ctrl, "P2P_SERVICE_ADD", 3, argc, argv);
+}
+
 
-       if (argc != 3 && argc != 4) {
-               printf("Invalid P2P_SERVICE_ADD command: needs three or four "
+static int wpa_cli_cmd_p2p_service_rep(struct wpa_ctrl *ctrl, int argc,
+                                      char *argv[])
+{
+       if (argc < 5 || argc > 6) {
+               printf("Invalid P2P_SERVICE_REP command: needs 5-6 "
                       "arguments\n");
                return -1;
        }
 
-       if (argc == 4)
-               res = os_snprintf(cmd, sizeof(cmd),
-                                 "P2P_SERVICE_ADD %s %s %s %s",
-                                 argv[0], argv[1], argv[2], argv[3]);
-       else
-               res = os_snprintf(cmd, sizeof(cmd),
-                                 "P2P_SERVICE_ADD %s %s %s",
-                                 argv[0], argv[1], argv[2]);
-       if (res < 0 || (size_t) res >= sizeof(cmd))
-               return -1;
-       cmd[sizeof(cmd) - 1] = '\0';
-       return wpa_ctrl_command(ctrl, cmd);
+       return wpa_cli_cmd(ctrl, "P2P_SERVICE_REP", 5, argc, argv);
 }
 
 
@@ -1947,7 +2034,7 @@ static int wpa_cli_cmd_p2p_service_del(struct wpa_ctrl *ctrl, int argc,
                res = os_snprintf(cmd, sizeof(cmd),
                                  "P2P_SERVICE_DEL %s %s",
                                  argv[0], argv[1]);
-       if (res < 0 || (size_t) res >= sizeof(cmd))
+       if (os_snprintf_error(sizeof(cmd), res))
                return -1;
        cmd[sizeof(cmd) - 1] = '\0';
        return wpa_ctrl_command(ctrl, cmd);
@@ -2050,6 +2137,50 @@ static int wpa_cli_cmd_p2p_set(struct wpa_ctrl *ctrl, int argc, char *argv[])
 }
 
 
+static char ** wpa_cli_complete_p2p_set(const char *str, int pos)
+{
+       int arg = get_cmd_arg_num(str, pos);
+       const char *fields[] = {
+               "discoverability",
+               "managed",
+               "listen_channel",
+               "ssid_postfix",
+               "noa",
+               "ps",
+               "oppps",
+               "ctwindow",
+               "disabled",
+               "conc_pref",
+               "force_long_sd",
+               "peer_filter",
+               "cross_connect",
+               "go_apsd",
+               "client_apsd",
+               "disallow_freq",
+               "disc_int",
+               "per_sta_psk",
+       };
+       int i, num_fields = ARRAY_SIZE(fields);
+
+       if (arg == 1) {
+               char **res = os_calloc(num_fields + 1, sizeof(char *));
+               if (res == NULL)
+                       return NULL;
+               for (i = 0; i < num_fields; i++) {
+                       res[i] = os_strdup(fields[i]);
+                       if (res[i] == NULL)
+                               return res;
+               }
+               return res;
+       }
+
+       if (arg == 2 && os_strncasecmp(str, "p2p_set peer_filter ", 20) == 0)
+               return cli_txt_list_array(&p2p_peers);
+
+       return NULL;
+}
+
+
 static int wpa_cli_cmd_p2p_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
        return wpa_ctrl_command(ctrl, "P2P_FLUSH");
@@ -2101,6 +2232,14 @@ static int wpa_cli_cmd_p2p_ext_listen(struct wpa_ctrl *ctrl, int argc,
 }
 
 
+static int wpa_cli_cmd_p2p_remove_client(struct wpa_ctrl *ctrl, int argc,
+                                        char *argv[])
+{
+       return wpa_cli_cmd(ctrl, "P2P_REMOVE_CLIENT", 1, argc, argv);
+}
+
+#endif /* CONFIG_P2P */
+
 #ifdef CONFIG_WIFI_DISPLAY
 
 static int wpa_cli_cmd_wfd_subelem_set(struct wpa_ctrl *ctrl, int argc,
@@ -2117,7 +2256,7 @@ static int wpa_cli_cmd_wfd_subelem_set(struct wpa_ctrl *ctrl, int argc,
 
        res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_SET %s %s",
                          argv[0], argc > 1 ? argv[1] : "");
-       if (res < 0 || (size_t) res >= sizeof(cmd))
+       if (os_snprintf_error(sizeof(cmd), res))
                return -1;
        cmd[sizeof(cmd) - 1] = '\0';
        return wpa_ctrl_command(ctrl, cmd);
@@ -2138,7 +2277,7 @@ static int wpa_cli_cmd_wfd_subelem_get(struct wpa_ctrl *ctrl, int argc,
 
        res = os_snprintf(cmd, sizeof(cmd), "WFD_SUBELEM_GET %s",
                          argv[0]);
-       if (res < 0 || (size_t) res >= sizeof(cmd))
+       if (os_snprintf_error(sizeof(cmd), res))
                return -1;
        cmd[sizeof(cmd) - 1] = '\0';
        return wpa_ctrl_command(ctrl, cmd);
@@ -2175,6 +2314,13 @@ static int wpa_cli_cmd_interworking_connect(struct wpa_ctrl *ctrl, int argc,
 }
 
 
+static int wpa_cli_cmd_interworking_add_network(struct wpa_ctrl *ctrl, int argc,
+                                               char *argv[])
+{
+       return wpa_cli_cmd(ctrl, "INTERWORKING_ADD_NETWORK", 1, argc, argv);
+}
+
+
 static int wpa_cli_cmd_anqp_get(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
        return wpa_cli_cmd(ctrl, "ANQP_GET", 2, argc, argv);
@@ -2223,6 +2369,37 @@ static int wpa_cli_cmd_get_nai_home_realm_list(struct wpa_ctrl *ctrl, int argc,
        return wpa_ctrl_command(ctrl, cmd);
 }
 
+
+static int wpa_cli_cmd_hs20_icon_request(struct wpa_ctrl *ctrl, int argc,
+                                        char *argv[])
+{
+       char cmd[512];
+
+       if (argc < 2) {
+               printf("Command needs two arguments (dst mac addr and "
+                      "icon name)\n");
+               return -1;
+       }
+
+       if (write_cmd(cmd, sizeof(cmd), "HS20_ICON_REQUEST", argc, argv) < 0)
+               return -1;
+
+       return wpa_ctrl_command(ctrl, cmd);
+}
+
+
+static int wpa_cli_cmd_fetch_osu(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "FETCH_OSU");
+}
+
+
+static int wpa_cli_cmd_cancel_fetch_osu(struct wpa_ctrl *ctrl, int argc,
+                                       char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "CANCEL_FETCH_OSU");
+}
+
 #endif /* CONFIG_HS20 */
 
 
@@ -2254,6 +2431,41 @@ static int wpa_cli_cmd_tdls_teardown(struct wpa_ctrl *ctrl, int argc,
 }
 
 
+static int wpa_cli_cmd_wmm_ac_addts(struct wpa_ctrl *ctrl, int argc,
+                                   char *argv[])
+{
+       return wpa_cli_cmd(ctrl, "WMM_AC_ADDTS", 3, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wmm_ac_delts(struct wpa_ctrl *ctrl, int argc,
+                                   char *argv[])
+{
+       return wpa_cli_cmd(ctrl, "WMM_AC_DELTS", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_wmm_ac_status(struct wpa_ctrl *ctrl, int argc,
+                                   char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "WMM_AC_STATUS");
+}
+
+
+static int wpa_cli_cmd_tdls_chan_switch(struct wpa_ctrl *ctrl, int argc,
+                                       char *argv[])
+{
+       return wpa_cli_cmd(ctrl, "TDLS_CHAN_SWITCH", 2, argc, argv);
+}
+
+
+static int wpa_cli_cmd_tdls_cancel_chan_switch(struct wpa_ctrl *ctrl, int argc,
+                                              char *argv[])
+{
+       return wpa_cli_cmd(ctrl, "TDLS_CANCEL_CHAN_SWITCH", 1, argc, argv);
+}
+
+
 static int wpa_cli_cmd_signal_poll(struct wpa_ctrl *ctrl, int argc,
                                   char *argv[])
 {
@@ -2312,12 +2524,52 @@ static int wpa_cli_cmd_raw(struct wpa_ctrl *ctrl, int argc, char *argv[])
 }
 
 
+#ifdef ANDROID
+static int wpa_cli_cmd_driver(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       return wpa_cli_cmd(ctrl, "DRIVER", 1, argc, argv);
+}
+#endif /* ANDROID */
+
+
+static int wpa_cli_cmd_vendor(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       return wpa_cli_cmd(ctrl, "VENDOR", 1, argc, argv);
+}
+
+
 static int wpa_cli_cmd_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
 {
        return wpa_ctrl_command(ctrl, "FLUSH");
 }
 
 
+static int wpa_cli_cmd_radio_work(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       return wpa_cli_cmd(ctrl, "RADIO_WORK", 1, argc, argv);
+}
+
+
+static int wpa_cli_cmd_neighbor_rep_request(struct wpa_ctrl *ctrl, int argc,
+                                           char *argv[])
+{
+       return wpa_cli_cmd(ctrl, "NEIGHBOR_REP_REQUEST", 0, argc, argv);
+}
+
+
+static int wpa_cli_cmd_erp_flush(struct wpa_ctrl *ctrl, int argc, char *argv[])
+{
+       return wpa_ctrl_command(ctrl, "ERP_FLUSH");
+}
+
+
+static int wpa_cli_cmd_mac_rand_scan(struct wpa_ctrl *ctrl, int argc,
+                                    char *argv[])
+{
+       return wpa_cli_cmd(ctrl, "MAC_RAND_SCAN", 1, argc, argv);
+}
+
+
 enum wpa_cli_cmd_flags {
        cli_cmd_flag_none               = 0x00,
        cli_cmd_flag_sensitive          = 0x01
@@ -2369,6 +2621,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
          cli_cmd_flag_none,
          "= set variables (shows list of variables when run without "
          "arguments)" },
+       { "dump", wpa_cli_cmd_dump, NULL,
+         cli_cmd_flag_none,
+         "= dump config variables" },
        { "get", wpa_cli_cmd_get, NULL,
          cli_cmd_flag_none,
          "<name> = get information" },
@@ -2381,9 +2636,15 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
        { "pmksa", wpa_cli_cmd_pmksa, NULL,
          cli_cmd_flag_none,
          "= show PMKSA cache" },
+       { "pmksa_flush", wpa_cli_cmd_pmksa_flush, NULL,
+         cli_cmd_flag_none,
+         "= flush PMKSA cache entries" },
        { "reassociate", wpa_cli_cmd_reassociate, NULL,
          cli_cmd_flag_none,
          "= force reassociation" },
+       { "reattach", wpa_cli_cmd_reattach, NULL,
+         cli_cmd_flag_none,
+         "= force reassociation back to the same BSS" },
        { "preauthenticate", wpa_cli_cmd_preauthenticate, wpa_cli_complete_bss,
          cli_cmd_flag_none,
          "<BSSID> = force preauthentication" },
@@ -2407,6 +2668,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
          cli_cmd_flag_sensitive,
          "<network id> <passphrase> = configure private key passphrase\n"
          "  for an SSID" },
+       { "sim", wpa_cli_cmd_sim, NULL,
+         cli_cmd_flag_sensitive,
+         "<network id> <pin> = report SIM operation result" },
        { "bssid", wpa_cli_cmd_bssid, NULL,
          cli_cmd_flag_none,
          "<network id> <BSSID> = set preferred BSSID for an SSID" },
@@ -2444,6 +2708,10 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
        { "get_network", wpa_cli_cmd_get_network, NULL,
          cli_cmd_flag_none,
          "<network id> <variable> = get network variables" },
+       { "dup_network", wpa_cli_cmd_dup_network, NULL,
+         cli_cmd_flag_none,
+         "<src network id> <dst network id> <variable> = duplicate network variables"
+       },
        { "list_creds", wpa_cli_cmd_list_creds, NULL,
          cli_cmd_flag_none,
          "= list configured credentials" },
@@ -2456,6 +2724,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
        { "set_cred", wpa_cli_cmd_set_cred, NULL,
          cli_cmd_flag_sensitive,
          "<cred id> <variable> <value> = set credential variables" },
+       { "get_cred", wpa_cli_cmd_get_cred, NULL,
+         cli_cmd_flag_none,
+         "<cred id> <variable> = get credential variables" },
        { "save_config", wpa_cli_cmd_save_config, NULL,
          cli_cmd_flag_none,
          "= save the current configuration" },
@@ -2549,12 +2820,6 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
        { "nfc_get_handover_sel", wpa_cli_cmd_nfc_get_handover_sel, NULL,
          cli_cmd_flag_none,
          "<NDEF> <WPS> = create NFC handover select" },
-       { "nfc_rx_handover_req", wpa_cli_cmd_nfc_rx_handover_req, NULL,
-         cli_cmd_flag_none,
-         "<hexdump of payload> = report received NFC handover request" },
-       { "nfc_rx_handover_sel", wpa_cli_cmd_nfc_rx_handover_sel, NULL,
-         cli_cmd_flag_none,
-         "<hexdump of payload> = report received NFC handover select" },
        { "nfc_report_handover", wpa_cli_cmd_nfc_report_handover, NULL,
          cli_cmd_flag_none,
          "<role> <type> <hexdump of req> <hexdump of sel> = report completed "
@@ -2608,21 +2873,46 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
        { "disassociate", wpa_cli_cmd_disassociate, NULL,
          cli_cmd_flag_none,
          "<addr> = disassociate a station" },
+       { "chan_switch", wpa_cli_cmd_chanswitch, NULL,
+         cli_cmd_flag_none,
+         "<cs_count> <freq> [sec_channel_offset=] [center_freq1=]"
+         " [center_freq2=] [bandwidth=] [blocktx] [ht|vht]"
+         " = CSA parameters" },
 #endif /* CONFIG_AP */
        { "suspend", wpa_cli_cmd_suspend, NULL, cli_cmd_flag_none,
          "= notification of suspend/hibernate" },
        { "resume", wpa_cli_cmd_resume, NULL, cli_cmd_flag_none,
          "= notification of resume/thaw" },
+#ifdef CONFIG_TESTING_OPTIONS
        { "drop_sa", wpa_cli_cmd_drop_sa, NULL, cli_cmd_flag_none,
          "= drop SA without deauth/disassoc (test command)" },
+#endif /* CONFIG_TESTING_OPTIONS */
        { "roam", wpa_cli_cmd_roam, wpa_cli_complete_bss,
          cli_cmd_flag_none,
          "<addr> = roam to the specified BSS" },
+#ifdef CONFIG_MESH
+       { "mesh_interface_add", wpa_cli_cmd_mesh_interface_add, NULL,
+         cli_cmd_flag_none,
+         "[ifname] = Create a new mesh interface" },
+       { "mesh_group_add", wpa_cli_cmd_mesh_group_add, NULL,
+         cli_cmd_flag_none,
+         "<network id> = join a mesh network (disable others)" },
+       { "mesh_group_remove", wpa_cli_cmd_mesh_group_remove, NULL,
+         cli_cmd_flag_none,
+         "<ifname> = Remove mesh group interface" },
+#endif /* CONFIG_MESH */
+#ifdef CONFIG_P2P
        { "p2p_find", wpa_cli_cmd_p2p_find, wpa_cli_complete_p2p_find,
          cli_cmd_flag_none,
          "[timeout] [type=*] = find P2P Devices for up-to timeout seconds" },
        { "p2p_stop_find", wpa_cli_cmd_p2p_stop_find, NULL, cli_cmd_flag_none,
          "= stop P2P Devices search" },
+       { "p2p_asp_provision", wpa_cli_cmd_p2p_asp_provision, NULL,
+         cli_cmd_flag_none,
+         "<addr> adv_id=<adv_id> conncap=<conncap> [info=<infodata>] = provision with a P2P ASP Device" },
+       { "p2p_asp_provision_resp", wpa_cli_cmd_p2p_asp_provision_resp, NULL,
+         cli_cmd_flag_none,
+         "<addr> adv_id=<adv_id> [role<conncap>] [info=<infodata>] = provision with a P2P ASP Device" },
        { "p2p_connect", wpa_cli_cmd_p2p_connect, wpa_cli_complete_p2p_connect,
          cli_cmd_flag_none,
          "<addr> <\"pbc\"|PIN> [ht40] = connect to a P2P Device" },
@@ -2659,8 +2949,12 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
          "= remove all stored service entries" },
        { "p2p_service_add", wpa_cli_cmd_p2p_service_add, NULL,
          cli_cmd_flag_none,
-         "<bonjour|upnp> <query|version> <response|service> = add a local "
+         "<bonjour|upnp|asp> <query|version> <response|service> = add a local "
          "service" },
+       { "p2p_service_rep", wpa_cli_cmd_p2p_service_rep, NULL,
+         cli_cmd_flag_none,
+         "asp <auto> <adv_id> <svc_state> <svc_string> [<svc_info>] = replace "
+         "local ASP service" },
        { "p2p_service_del", wpa_cli_cmd_p2p_service_del, NULL,
          cli_cmd_flag_none,
          "<bonjour|upnp> <query|version> [|service] = remove a local "
@@ -2677,7 +2971,8 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
        { "p2p_peer", wpa_cli_cmd_p2p_peer, wpa_cli_complete_p2p_peer,
          cli_cmd_flag_none,
          "<address> = show information about known P2P peer" },
-       { "p2p_set", wpa_cli_cmd_p2p_set, NULL, cli_cmd_flag_none,
+       { "p2p_set", wpa_cli_cmd_p2p_set, wpa_cli_complete_p2p_set,
+         cli_cmd_flag_none,
          "<field> <value> = set a P2P parameter" },
        { "p2p_flush", wpa_cli_cmd_p2p_flush, NULL, cli_cmd_flag_none,
          "= flush P2P state" },
@@ -2693,6 +2988,10 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
        { "p2p_ext_listen", wpa_cli_cmd_p2p_ext_listen, NULL,
          cli_cmd_flag_none,
          "[<period> <interval>] = set extended listen timing" },
+       { "p2p_remove_client", wpa_cli_cmd_p2p_remove_client,
+         wpa_cli_complete_p2p_peer, cli_cmd_flag_none,
+         "<address|iface=address> = remove a peer from all groups" },
+#endif /* CONFIG_P2P */
 #ifdef CONFIG_WIFI_DISPLAY
        { "wfd_subelem_set", wpa_cli_cmd_wfd_subelem_set, NULL,
          cli_cmd_flag_none,
@@ -2713,6 +3012,9 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
        { "interworking_connect", wpa_cli_cmd_interworking_connect,
          wpa_cli_complete_bss, cli_cmd_flag_none,
          "<BSSID> = connect using Interworking credentials" },
+       { "interworking_add_network", wpa_cli_cmd_interworking_add_network,
+         wpa_cli_complete_bss, cli_cmd_flag_none,
+         "<BSSID> = connect using Interworking credentials" },
        { "anqp_get", wpa_cli_cmd_anqp_get, wpa_cli_complete_bss,
          cli_cmd_flag_none,
          "<addr> <info id>[,<info id>]... = request ANQP information" },
@@ -2731,6 +3033,14 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
        { "nai_home_realm_list", wpa_cli_cmd_get_nai_home_realm_list,
          wpa_cli_complete_bss, cli_cmd_flag_none,
          "<addr> <home realm> = get HS20 nai home realm list" },
+       { "hs20_icon_request", wpa_cli_cmd_hs20_icon_request,
+         wpa_cli_complete_bss, cli_cmd_flag_none,
+         "<addr> <icon name> = get Hotspot 2.0 OSU icon" },
+       { "fetch_osu", wpa_cli_cmd_fetch_osu, NULL, cli_cmd_flag_none,
+         "= fetch OSU provider information from all APs" },
+       { "cancel_fetch_osu", wpa_cli_cmd_cancel_fetch_osu, NULL,
+         cli_cmd_flag_none,
+         "= cancel fetch_osu command" },
 #endif /* CONFIG_HS20 */
        { "sta_autoconnect", wpa_cli_cmd_sta_autoconnect, NULL,
          cli_cmd_flag_none,
@@ -2744,6 +3054,25 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
        { "tdls_teardown", wpa_cli_cmd_tdls_teardown, NULL,
          cli_cmd_flag_none,
          "<addr> = tear down TDLS with <addr>" },
+       { "wmm_ac_addts", wpa_cli_cmd_wmm_ac_addts, NULL,
+         cli_cmd_flag_none,
+         "<uplink/downlink/bidi> <tsid=0..7> <up=0..7> [nominal_msdu_size=#] "
+         "[mean_data_rate=#] [min_phy_rate=#] [sba=#] [fixed_nominal_msdu] "
+         "= add WMM-AC traffic stream" },
+       { "wmm_ac_delts", wpa_cli_cmd_wmm_ac_delts, NULL,
+         cli_cmd_flag_none,
+         "<tsid> = delete WMM-AC traffic stream" },
+       { "wmm_ac_status", wpa_cli_cmd_wmm_ac_status, NULL,
+         cli_cmd_flag_none,
+         "= show status for Wireless Multi-Media Admission-Control" },
+       { "tdls_chan_switch", wpa_cli_cmd_tdls_chan_switch, NULL,
+         cli_cmd_flag_none,
+         "<addr> <oper class> <freq> [sec_channel_offset=] [center_freq1=] "
+         "[center_freq2=] [bandwidth=] [ht|vht] = enable channel switching "
+         "with TDLS peer" },
+       { "tdls_cancel_chan_switch", wpa_cli_cmd_tdls_cancel_chan_switch, NULL,
+         cli_cmd_flag_none,
+         "<addr> = disable channel switching with TDLS peer <addr>" },
        { "signal_poll", wpa_cli_cmd_signal_poll, NULL,
          cli_cmd_flag_none,
          "= get signal parameters" },
@@ -2767,6 +3096,27 @@ static struct wpa_cli_cmd wpa_cli_commands[] = {
          "<params..> = Sent unprocessed command" },
        { "flush", wpa_cli_cmd_flush, NULL, cli_cmd_flag_none,
          "= flush wpa_supplicant state" },
+#ifdef ANDROID
+       { "driver", wpa_cli_cmd_driver, NULL, cli_cmd_flag_none,
+         "<command> = driver private commands" },
+#endif /* ANDROID */
+       { "radio_work", wpa_cli_cmd_radio_work, NULL, cli_cmd_flag_none,
+         "= radio_work <show/add/done>" },
+       { "vendor", wpa_cli_cmd_vendor, NULL, cli_cmd_flag_none,
+         "<vendor id> <command id> [<hex formatted command argument>] = Send vendor command"
+       },
+       { "neighbor_rep_request",
+         wpa_cli_cmd_neighbor_rep_request, NULL, cli_cmd_flag_none,
+         "[ssid=<SSID>] = Trigger request to AP for neighboring AP report "
+         "(with optional given SSID, default: current SSID)"
+       },
+       { "erp_flush", wpa_cli_cmd_erp_flush, NULL, cli_cmd_flag_none,
+         "= flush ERP keys" },
+       { "mac_rand_scan",
+         wpa_cli_cmd_mac_rand_scan, NULL, cli_cmd_flag_none,
+         "<scan|sched|pno|all> enable=<0/1> [addr=mac-address "
+         "mask=mac-address-mask] = scan MAC randomization"
+       },
        { NULL, NULL, NULL, cli_cmd_flag_none, NULL }
 };
 
@@ -2827,7 +3177,7 @@ static char ** wpa_list_cmd_list(void)
        int i, count;
        struct cli_txt_entry *e;
 
-       count = sizeof(wpa_cli_commands) / sizeof(wpa_cli_commands[0]);
+       count = ARRAY_SIZE(wpa_cli_commands);
        count += dl_list_len(&p2p_groups);
        count += dl_list_len(&ifnames);
        res = os_calloc(count + 1, sizeof(char *));
@@ -2974,28 +3324,19 @@ static int str_match(const char *a, const char *b)
 static int wpa_cli_exec(const char *program, const char *arg1,
                        const char *arg2)
 {
-       char *cmd;
+       char *arg;
        size_t len;
        int res;
-       int ret = 0;
 
-       len = os_strlen(program) + os_strlen(arg1) + os_strlen(arg2) + 3;
-       cmd = os_malloc(len);
-       if (cmd == NULL)
-               return -1;
-       res = os_snprintf(cmd, len, "%s %s %s", program, arg1, arg2);
-       if (res < 0 || (size_t) res >= len) {
-               os_free(cmd);
+       len = os_strlen(arg1) + os_strlen(arg2) + 2;
+       arg = os_malloc(len);
+       if (arg == NULL)
                return -1;
-       }
-       cmd[len - 1] = '\0';
-#ifndef _WIN32_WCE
-       if (system(cmd) < 0)
-               ret = -1;
-#endif /* _WIN32_WCE */
-       os_free(cmd);
+       os_snprintf(arg, len, "%s %s", arg1, arg2);
+       res = os_exec(program, arg, 1);
+       os_free(arg);
 
-       return ret;
+       return res;
 }
 
 
@@ -3003,15 +3344,29 @@ static void wpa_cli_action_process(const char *msg)
 {
        const char *pos;
        char *copy = NULL, *id, *pos2;
+       const char *ifname = ctrl_ifname;
+       char ifname_buf[100];
 
        pos = msg;
+       if (os_strncmp(pos, "IFNAME=", 7) == 0) {
+               const char *end;
+               end = os_strchr(pos + 7, ' ');
+               if (end && (unsigned int) (end - pos) < sizeof(ifname_buf)) {
+                       pos += 7;
+                       os_memcpy(ifname_buf, pos, end - pos);
+                       ifname_buf[end - pos] = '\0';
+                       ifname = ifname_buf;
+                       pos = end + 1;
+               }
+       }
        if (*pos == '<') {
+               const char *prev = pos;
                /* skip priority */
                pos = os_strchr(pos, '>');
                if (pos)
                        pos++;
                else
-                       pos = msg;
+                       pos = prev;
        }
 
        if (str_match(pos, WPA_EVENT_CONNECTED)) {
@@ -3045,36 +3400,48 @@ static void wpa_cli_action_process(const char *msg)
 
                os_setenv("WPA_CTRL_DIR", ctrl_iface_dir, 1);
 
-               if (!wpa_cli_connected || new_id != wpa_cli_last_id) {
+               if (wpa_cli_connected <= 0 || new_id != wpa_cli_last_id) {
                        wpa_cli_connected = 1;
                        wpa_cli_last_id = new_id;
-                       wpa_cli_exec(action_file, ctrl_ifname, "CONNECTED");
+                       wpa_cli_exec(action_file, ifname, "CONNECTED");
                }
        } else if (str_match(pos, WPA_EVENT_DISCONNECTED)) {
                if (wpa_cli_connected) {
                        wpa_cli_connected = 0;
-                       wpa_cli_exec(action_file, ctrl_ifname, "DISCONNECTED");
+                       wpa_cli_exec(action_file, ifname, "DISCONNECTED");
                }
-       } else if (str_match(pos, P2P_EVENT_GROUP_STARTED)) {
+       } else if (str_match(pos, MESH_GROUP_STARTED)) {
                wpa_cli_exec(action_file, ctrl_ifname, pos);
-       } else if (str_match(pos, P2P_EVENT_GROUP_REMOVED)) {
+       } else if (str_match(pos, MESH_GROUP_REMOVED)) {
                wpa_cli_exec(action_file, ctrl_ifname, pos);
-       } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_ENABLE)) {
+       } else if (str_match(pos, MESH_PEER_CONNECTED)) {
                wpa_cli_exec(action_file, ctrl_ifname, pos);
-       } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_DISABLE)) {
+       } else if (str_match(pos, MESH_PEER_DISCONNECTED)) {
                wpa_cli_exec(action_file, ctrl_ifname, pos);
+       } else if (str_match(pos, P2P_EVENT_GROUP_STARTED)) {
+               wpa_cli_exec(action_file, ifname, pos);
+       } else if (str_match(pos, P2P_EVENT_GROUP_REMOVED)) {
+               wpa_cli_exec(action_file, ifname, pos);
+       } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_ENABLE)) {
+               wpa_cli_exec(action_file, ifname, pos);
+       } else if (str_match(pos, P2P_EVENT_CROSS_CONNECT_DISABLE)) {
+               wpa_cli_exec(action_file, ifname, pos);
        } else if (str_match(pos, P2P_EVENT_GO_NEG_FAILURE)) {
-               wpa_cli_exec(action_file, ctrl_ifname, pos);
+               wpa_cli_exec(action_file, ifname, pos);
        } else if (str_match(pos, WPS_EVENT_SUCCESS)) {
-               wpa_cli_exec(action_file, ctrl_ifname, pos);
+               wpa_cli_exec(action_file, ifname, pos);
        } else if (str_match(pos, WPS_EVENT_FAIL)) {
-               wpa_cli_exec(action_file, ctrl_ifname, pos);
+               wpa_cli_exec(action_file, ifname, pos);
        } else if (str_match(pos, AP_STA_CONNECTED)) {
-               wpa_cli_exec(action_file, ctrl_ifname, pos);
+               wpa_cli_exec(action_file, ifname, pos);
        } else if (str_match(pos, AP_STA_DISCONNECTED)) {
-               wpa_cli_exec(action_file, ctrl_ifname, pos);
+               wpa_cli_exec(action_file, ifname, pos);
        } else if (str_match(pos, ESS_DISASSOC_IMMINENT)) {
-               wpa_cli_exec(action_file, ctrl_ifname, pos);
+               wpa_cli_exec(action_file, ifname, pos);
+       } else if (str_match(pos, HS20_SUBSCRIPTION_REMEDIATION)) {
+               wpa_cli_exec(action_file, ifname, pos);
+       } else if (str_match(pos, HS20_DEAUTH_IMMINENT_NOTICE)) {
+               wpa_cli_exec(action_file, ifname, pos);
        } else if (str_match(pos, WPA_EVENT_TERMINATING)) {
                printf("wpa_supplicant is terminating - stop monitoring\n");
                wpa_cli_quit = 1;
@@ -3136,6 +3503,7 @@ static void cli_event(const char *str)
                return;
        }
 
+#ifdef CONFIG_P2P
        if (str_starts(start, P2P_EVENT_DEVICE_FOUND)) {
                s = os_strstr(start, " p2p_dev_addr=");
                if (s == NULL)
@@ -3167,6 +3535,7 @@ static void cli_event(const char *str)
                cli_txt_list_del_word(&p2p_groups, s + 1);
                return;
        }
+#endif /* CONFIG_P2P */
 }
 
 
@@ -3204,7 +3573,7 @@ static void wpa_cli_recv_pending(struct wpa_ctrl *ctrl, int action_monitor)
                return;
        }
        while (wpa_ctrl_pending(ctrl) > 0) {
-               char buf[256];
+               char buf[4096];
                size_t len = sizeof(buf) - 1;
                if (wpa_ctrl_recv(ctrl, buf, &len) == 0) {
                        buf[len] = '\0';
@@ -3268,10 +3637,18 @@ static int tokenize_cmd(char *cmd, char *argv[])
 
 static void wpa_cli_ping(void *eloop_ctx, void *timeout_ctx)
 {
-       if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) {
-               printf("Connection to wpa_supplicant lost - trying to "
-                      "reconnect\n");
-               wpa_cli_close_connection();
+       if (ctrl_conn) {
+               int res;
+               char *prefix = ifname_prefix;
+
+               ifname_prefix = NULL;
+               res = _wpa_ctrl_command(ctrl_conn, "PING", 0);
+               ifname_prefix = prefix;
+               if (res) {
+                       printf("Connection to wpa_supplicant lost - trying to "
+                              "reconnect\n");
+                       wpa_cli_close_connection();
+               }
        }
        if (!ctrl_conn)
                wpa_cli_reconnect();
@@ -3390,7 +3767,7 @@ static void update_ifnames(struct wpa_ctrl *ctrl)
                        break;
                *end = '\0';
                ret = os_snprintf(txt, sizeof(txt), "ifname=%s", pos);
-               if (ret > 0 && ret < (int) sizeof(txt))
+               if (!os_snprintf_error(sizeof(txt), ret))
                        cli_txt_list_add(&ifnames, txt);
                pos = end + 1;
        }
@@ -3408,7 +3785,8 @@ static void try_connection(void *eloop_ctx, void *timeout_ctx)
        if (!wpa_cli_open_connection(ctrl_ifname, 1) == 0) {
                if (!warning_displayed) {
                        printf("Could not connect to wpa_supplicant: "
-                              "%s - re-trying\n", ctrl_ifname);
+                              "%s - re-trying\n",
+                              ctrl_ifname ? ctrl_ifname : "(nil)");
                        warning_displayed = 1;
                }
                eloop_register_timeout(1, 0, try_connection, NULL, NULL);
@@ -3669,7 +4047,8 @@ int main(int argc, char *argv[])
                    wpa_cli_open_connection(ctrl_ifname, 0) < 0) {
                        fprintf(stderr, "Failed to connect to non-global "
                                "ctrl_ifname: %s  error: %s\n",
-                               ctrl_ifname, strerror(errno));
+                               ctrl_ifname ? ctrl_ifname : "(nil)",
+                               strerror(errno));
                        return -1;
                }
 
index 063347e..ae0c240 100644 (file)
@@ -12,6 +12,7 @@
 #include "signalbar.h"
 #include "wpagui.h"
 #include "networkconfig.h"
+#include "scanresultsitem.h"
 
 
 ScanResults::ScanResults(QWidget *parent, const char *, bool, Qt::WFlags)
@@ -95,7 +96,7 @@ void ScanResults::updateResults()
                                ssid = (*it).mid(pos);
                }
 
-               QTreeWidgetItem *item = new QTreeWidgetItem(scanResultsWidget);
+               ScanResultsItem *item = new ScanResultsItem(scanResultsWidget);
                if (item) {
                        item->setText(0, ssid);
                        item->setText(1, bssid);
index 3c81929..69bc0f6 100644 (file)
@@ -34,6 +34,7 @@ HEADERS       += wpamsg.h \
        wpagui.h \
        eventhistory.h \
        scanresults.h \
+       scanresultsitem.h \
        signalbar.h \
        userdatarequest.h \
        networkconfig.h \
@@ -45,6 +46,7 @@ SOURCES       += main.cpp \
        wpagui.cpp \
        eventhistory.cpp \
        scanresults.cpp \
+       scanresultsitem.cpp \
        signalbar.cpp \
        userdatarequest.cpp \
        networkconfig.cpp \
old mode 100644 (file)
new mode 100755 (executable)
index 6bba8d2..bc6fa7f
 #include "userdatarequest.h"
 #include "networkconfig.h"
 
-#if 1
-/* Silence stdout */
-#define printf wpagui_printf
-static int wpagui_printf(const char *, ...)
-{
-       return 0;
-}
+
+#ifndef QT_NO_DEBUG
+#define debug(M, ...) qDebug("DEBUG %d: " M, __LINE__, ##__VA_ARGS__)
+#else
+#define debug(M, ...) do {} while (0)
 #endif
 
+
 WpaGui::WpaGui(QApplication *_app, QWidget *parent, const char *, Qt::WFlags)
        : QMainWindow(parent), app(_app)
 {
        setupUi(this);
+       this->setWindowFlags(Qt::Dialog);
 
 #ifdef CONFIG_NATIVE_WINDOWS
        fileStopServiceAction = new QAction(this);
@@ -129,6 +129,7 @@ WpaGui::WpaGui(QApplication *_app, QWidget *parent, const char *, Qt::WFlags)
        udr = NULL;
        tray_icon = NULL;
        startInTray = false;
+       quietMode = false;
        ctrl_iface = NULL;
        ctrl_conn = NULL;
        monitor_conn = NULL;
@@ -161,8 +162,8 @@ WpaGui::WpaGui(QApplication *_app, QWidget *parent, const char *, Qt::WFlags)
        timer->start(1000);
 
        if (openCtrlConnection(ctrl_iface) < 0) {
-               printf("Failed to open control connection to "
-                      "wpa_supplicant.\n");
+               debug("Failed to open control connection to "
+                     "wpa_supplicant.");
        }
 
        updateStatus();
@@ -233,7 +234,7 @@ void WpaGui::parse_argv()
 {
        int c;
        for (;;) {
-               c = getopt(qApp->argc(), qApp->argv(), "i:p:t");
+               c = getopt(qApp->argc(), qApp->argv(), "i:p:tq");
                if (c < 0)
                        break;
                switch (c) {
@@ -248,6 +249,9 @@ void WpaGui::parse_argv()
                case 't':
                        startInTray = true;
                        break;
+               case 'q':
+                       quietMode = true;
+                       break;
                }
        }
 }
@@ -290,8 +294,8 @@ int WpaGui::openCtrlConnection(const char *ifname)
                                if (strcmp(dent->d_name, ".") == 0 ||
                                    strcmp(dent->d_name, "..") == 0)
                                        continue;
-                               printf("Selected interface '%s'\n",
-                                      dent->d_name);
+                               debug("Selected interface '%s'",
+                                     dent->d_name);
                                ctrl_iface = strdup(dent->d_name);
                                break;
                        }
@@ -368,7 +372,7 @@ int WpaGui::openCtrlConnection(const char *ifname)
                monitor_conn = NULL;
        }
 
-       printf("Trying to connect to '%s'\n", cfile);
+       debug("Trying to connect to '%s'", cfile);
        ctrl_conn = wpa_ctrl_open(cfile);
        if (ctrl_conn == NULL) {
                free(cfile);
@@ -381,7 +385,7 @@ int WpaGui::openCtrlConnection(const char *ifname)
                return -1;
        }
        if (wpa_ctrl_attach(monitor_conn)) {
-               printf("Failed to attach to wpa_supplicant\n");
+               debug("Failed to attach to wpa_supplicant");
                wpa_ctrl_close(monitor_conn);
                monitor_conn = NULL;
                wpa_ctrl_close(ctrl_conn);
@@ -442,9 +446,9 @@ int WpaGui::ctrlRequest(const char *cmd, char *buf, size_t *buflen)
                return -3;
        ret = wpa_ctrl_request(ctrl_conn, cmd, strlen(cmd), buf, buflen, NULL);
        if (ret == -2)
-               printf("'%s' command timed out.\n", cmd);
+               debug("'%s' command timed out.", cmd);
        else if (ret < 0)
-               printf("'%s' command failed.\n", cmd);
+               debug("'%s' command failed.", cmd);
 
        return ret;
 }
@@ -491,6 +495,7 @@ void WpaGui::updateStatus()
                textSsid->clear();
                textBssid->clear();
                textIpAddress->clear();
+               updateTrayToolTip(tr("no status information"));
 
 #ifdef CONFIG_NATIVE_WINDOWS
                static bool first = true;
@@ -538,6 +543,7 @@ void WpaGui::updateStatus()
                        } else if (strcmp(start, "ssid") == 0) {
                                ssid_updated = true;
                                textSsid->setText(pos);
+                               updateTrayToolTip(pos + tr(" (associated)"));
                        } else if (strcmp(start, "ip_address") == 0) {
                                ipaddr_updated = true;
                                textIpAddress->setText(pos);
@@ -585,8 +591,10 @@ void WpaGui::updateStatus()
                textStatus->clear();
        if (!auth_updated)
                textAuthentication->clear();
-       if (!ssid_updated)
+       if (!ssid_updated) {
                textSsid->clear();
+               updateTrayToolTip(tr("(not-associated)"));
+       }
        if (!bssid_updated)
                textBssid->clear();
        if (!ipaddr_updated)
@@ -696,13 +704,13 @@ void WpaGui::updateNetworks()
 
 void WpaGui::helpIndex()
 {
-       printf("helpIndex\n");
+       debug("helpIndex");
 }
 
 
 void WpaGui::helpContents()
 {
-       printf("helpContents\n");
+       debug("helpContents");
 }
 
 
@@ -797,9 +805,9 @@ void WpaGui::ping()
 
        len = sizeof(buf) - 1;
        if (ctrlRequest("PING", buf, &len) < 0) {
-               printf("PING failed - trying to reconnect\n");
+               debug("PING failed - trying to reconnect");
                if (openCtrlConnection(ctrl_iface) >= 0) {
-                       printf("Reconnected successfully\n");
+                       debug("Reconnected successfully");
                        pingsToStatusUpdate = 0;
                }
        }
@@ -993,8 +1001,8 @@ void WpaGui::enableNetwork(const QString &sel)
        if (cmd.contains(QRegExp("^\\d+:")))
                cmd.truncate(cmd.indexOf(':'));
        else if (!cmd.startsWith("all")) {
-               printf("Invalid editNetwork '%s'\n",
-                      cmd.toAscii().constData());
+               debug("Invalid editNetwork '%s'",
+                     cmd.toAscii().constData());
                return;
        }
        cmd.prepend("ENABLE_NETWORK ");
@@ -1012,8 +1020,8 @@ void WpaGui::disableNetwork(const QString &sel)
        if (cmd.contains(QRegExp("^\\d+:")))
                cmd.truncate(cmd.indexOf(':'));
        else if (!cmd.startsWith("all")) {
-               printf("Invalid editNetwork '%s'\n",
-                      cmd.toAscii().constData());
+               debug("Invalid editNetwork '%s'",
+                     cmd.toAscii().constData());
                return;
        }
        cmd.prepend("DISABLE_NETWORK ");
@@ -1102,8 +1110,8 @@ void WpaGui::removeNetwork(const QString &sel)
        if (cmd.contains(QRegExp("^\\d+:")))
                cmd.truncate(cmd.indexOf(':'));
        else if (!cmd.startsWith("all")) {
-               printf("Invalid editNetwork '%s'\n",
-                      cmd.toAscii().constData());
+               debug("Invalid editNetwork '%s'",
+                     cmd.toAscii().constData());
                return;
        }
        cmd.prepend("REMOVE_NETWORK ");
@@ -1166,8 +1174,8 @@ int WpaGui::getNetworkDisabled(const QString &sel)
        size_t reply_len = sizeof(reply) - 1;
        int pos = cmd.indexOf(':');
        if (pos < 0) {
-               printf("Invalid getNetworkDisabled '%s'\n",
-                      cmd.toAscii().constData());
+               debug("Invalid getNetworkDisabled '%s'",
+                     cmd.toAscii().constData());
                return -1;
        }
        cmd.truncate(pos);
@@ -1258,8 +1266,8 @@ void WpaGui::saveConfig()
 void WpaGui::selectAdapter( const QString & sel )
 {
        if (openCtrlConnection(sel.toAscii().constData()) < 0)
-               printf("Failed to open control connection to "
-                      "wpa_supplicant.\n");
+               debug("Failed to open control connection to "
+                     "wpa_supplicant.");
        updateStatus();
        updateNetworks();
 }
@@ -1270,7 +1278,6 @@ void WpaGui::createTrayIcon(bool trayOnly)
        QApplication::setQuitOnLastWindowClosed(false);
 
        tray_icon = new QSystemTrayIcon(this);
-       tray_icon->setToolTip(qAppName() + tr(" - wpa_supplicant user interface"));
        if (QImageReader::supportedImageFormats().contains(QByteArray("svg")))
                tray_icon->setIcon(QIcon(":/icons/wpa_gui.svg"));
        else
@@ -1332,7 +1339,7 @@ void WpaGui::showTrayMessage(QSystemTrayIcon::MessageIcon type, int sec,
        if (!QSystemTrayIcon::supportsMessages())
                return;
 
-       if (isVisible() || !tray_icon || !tray_icon->isVisible())
+       if (isVisible() || !tray_icon || !tray_icon->isVisible() || quietMode)
                return;
 
        tray_icon->showMessage(qAppName(), msg, type, sec * 1000);
@@ -1407,6 +1414,13 @@ void WpaGui::showTrayStatus()
 }
 
 
+void WpaGui::updateTrayToolTip(const QString &msg)
+{
+       if (tray_icon)
+               tray_icon->setToolTip(msg);
+}
+
+
 void WpaGui::closeEvent(QCloseEvent *event)
 {
        if (eh) {
@@ -1685,13 +1699,13 @@ bool WpaGui::serviceRunning()
 
        scm = OpenSCManager(0, 0, SC_MANAGER_CONNECT);
        if (!scm) {
-               printf("OpenSCManager failed: %d\n", (int) GetLastError());
+               debug("OpenSCManager failed: %d", (int) GetLastError());
                return false;
        }
 
        svc = OpenService(scm, WPASVC_NAME, SERVICE_QUERY_STATUS);
        if (!svc) {
-               printf("OpenService failed: %d\n\n", (int) GetLastError());
+               debug("OpenService failed: %d", (int) GetLastError());
                CloseServiceHandle(scm);
                return false;
        }
index 340286c..026eacb 100644 (file)
@@ -70,6 +70,7 @@ public slots:
        virtual void showTrayMessage(QSystemTrayIcon::MessageIcon type,
                                     int sec, const QString &msg);
        virtual void showTrayStatus();
+       virtual void updateTrayToolTip(const QString &msg);
        virtual void wpsDialog();
        virtual void peersDialog();
        virtual void tabChanged(int index);
@@ -116,6 +117,7 @@ private:
        void createTrayIcon(bool);
        bool ackTrayIcon;
        bool startInTray;
+       bool quietMode;
 
        int openCtrlConnection(const char *ifname);
 
old mode 100644 (file)
new mode 100755 (executable)
old mode 100644 (file)
new mode 100755 (executable)
index 4afaae9..ac38d69
@@ -202,7 +202,9 @@ static void wpa_priv_cmd_associate(struct wpa_priv_interface *iface,
        if (assoc->ssid_len > 32)
                return;
        params.ssid_len = assoc->ssid_len;
-       params.freq = assoc->freq;
+       params.freq.mode = assoc->hwmode;
+       params.freq.freq = assoc->freq;
+       params.freq.channel = assoc->channel;
        if (assoc->wpa_ie_len) {
                params.wpa_ie = (u8 *) (assoc + 1);
                params.wpa_ie_len = assoc->wpa_ie_len;
@@ -333,7 +335,7 @@ static void wpa_priv_l2_rx(void *ctx, const u8 *src_addr, const u8 *buf,
        msg.msg_namelen = sizeof(iface->l2_addr);
 
        if (sendmsg(iface->fd, &msg, 0) < 0) {
-               perror("sendmsg(l2 rx)");
+               wpa_printf(MSG_ERROR, "sendmsg(l2 rx): %s", strerror(errno));
        }
 }
 
@@ -465,7 +467,7 @@ static void wpa_priv_receive(int sock, void *eloop_ctx, void *sock_ctx)
        res = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *) &from,
                       &fromlen);
        if (res < 0) {
-               perror("recvfrom");
+               wpa_printf(MSG_ERROR, "recvfrom: %s", strerror(errno));
                return;
        }
 
@@ -552,8 +554,6 @@ static void wpa_priv_interface_deinit(struct wpa_priv_interface *iface)
 }
 
 
-extern struct wpa_driver_ops *wpa_drivers[];
-
 static struct wpa_priv_interface *
 wpa_priv_interface_init(const char *dir, const char *params)
 {
@@ -615,7 +615,7 @@ wpa_priv_interface_init(const char *dir, const char *params)
 
        iface->fd = socket(PF_UNIX, SOCK_DGRAM, 0);
        if (iface->fd < 0) {
-               perror("socket(PF_UNIX)");
+               wpa_printf(MSG_ERROR, "socket(PF_UNIX): %s", strerror(errno));
                wpa_priv_interface_deinit(iface);
                return NULL;
        }
@@ -633,15 +633,16 @@ wpa_priv_interface_init(const char *dir, const char *params)
                                   "allow connections - assuming it was "
                                   "leftover from forced program termination");
                        if (unlink(iface->sock_name) < 0) {
-                               perror("unlink[ctrl_iface]");
-                               wpa_printf(MSG_ERROR, "Could not unlink "
-                                          "existing ctrl_iface socket '%s'",
-                                          iface->sock_name);
+                               wpa_printf(MSG_ERROR,
+                                          "Could not unlink existing ctrl_iface socket '%s': %s",
+                                          iface->sock_name, strerror(errno));
                                goto fail;
                        }
                        if (bind(iface->fd, (struct sockaddr *) &addr,
                                 sizeof(addr)) < 0) {
-                               perror("wpa-priv-iface-init: bind(PF_UNIX)");
+                               wpa_printf(MSG_ERROR,
+                                          "wpa-priv-iface-init: bind(PF_UNIX): %s",
+                                          strerror(errno));
                                goto fail;
                        }
                        wpa_printf(MSG_DEBUG, "Successfully replaced leftover "
@@ -656,7 +657,7 @@ wpa_priv_interface_init(const char *dir, const char *params)
        }
 
        if (chmod(iface->sock_name, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
-               perror("chmod");
+               wpa_printf(MSG_ERROR, "chmod: %s", strerror(errno));
                goto fail;
        }
 
@@ -688,7 +689,8 @@ static int wpa_priv_send_event(struct wpa_priv_interface *iface, int event,
        msg.msg_namelen = sizeof(iface->drv_addr);
 
        if (sendmsg(iface->fd, &msg, 0) < 0) {
-               perror("sendmsg(wpas_socket)");
+               wpa_printf(MSG_ERROR, "sendmsg(wpas_socket): %s",
+                          strerror(errno));
                return -1;
        }
 
@@ -903,7 +905,8 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
        msg.msg_namelen = sizeof(iface->drv_addr);
 
        if (sendmsg(iface->fd, &msg, 0) < 0)
-               perror("sendmsg(wpas_socket)");
+               wpa_printf(MSG_ERROR, "sendmsg(wpas_socket): %s",
+                          strerror(errno));
 }
 
 
@@ -946,8 +949,6 @@ static void usage(void)
 }
 
 
-extern int wpa_debug_level;
-
 int main(int argc, char *argv[])
 {
        int c, i;
old mode 100644 (file)
new mode 100755 (executable)
index d9f8616..87676da
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -33,6 +33,7 @@
 #include "rsn_supp/pmksa_cache.h"
 #include "common/wpa_ctrl.h"
 #include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
 #include "p2p/p2p.h"
 #include "blacklist.h"
 #include "wpas_glue.h"
 #include "offchannel.h"
 #include "hs20_supplicant.h"
 #include "wnm_sta.h"
+#include "wpas_kay.h"
+#include "mesh.h"
 
 const char *wpa_supplicant_version =
 "wpa_supplicant v" VERSION_STR "\n"
-"Copyright (c) 2003-2013, Jouni Malinen <j@w1.fi> and contributors";
+"Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi> and contributors";
 
 const char *wpa_supplicant_license =
 "This software may be distributed under the terms of the BSD license.\n"
@@ -104,11 +107,6 @@ const char *wpa_supplicant_full_license5 =
 "\n";
 #endif /* CONFIG_NO_STDOUT_DEBUG */
 
-extern int wpa_debug_level;
-extern int wpa_debug_show_keys;
-extern int wpa_debug_timestamp;
-extern struct wpa_driver_ops *wpa_drivers[];
-
 /* Configure default/group WEP keys for static WEP */
 int wpa_set_wep_keys(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
 {
@@ -135,6 +133,7 @@ int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
        size_t keylen;
        enum wpa_alg alg;
        u8 seq[6] = { 0 };
+       int ret;
 
        /* IBSS/WPA-None uses only one key (Group) for both receiving and
         * sending unicast and multicast packets. */
@@ -178,7 +177,9 @@ int wpa_supplicant_set_wpa_none_key(struct wpa_supplicant *wpa_s,
        /* TODO: should actually remember the previously used seq#, both for TX
         * and RX from each STA.. */
 
-       return wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen);
+       ret = wpa_drv_set_key(wpa_s, alg, NULL, 0, 1, seq, 6, key, keylen);
+       os_memset(key, 0, sizeof(key));
+       return ret;
 }
 
 
@@ -200,8 +201,6 @@ static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx)
         * So, wait a second until scanning again.
         */
        wpa_supplicant_req_scan(wpa_s, 1, 0);
-
-       wpas_p2p_continue_after_scan(wpa_s);
 }
 
 
@@ -217,7 +216,7 @@ static void wpa_supplicant_timeout(void *eloop_ctx, void *timeout_ctx)
 void wpa_supplicant_req_auth_timeout(struct wpa_supplicant *wpa_s,
                                     int sec, int usec)
 {
-       if (wpa_s->conf && wpa_s->conf->ap_scan == 0 &&
+       if (wpa_s->conf->ap_scan == 0 &&
            (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED))
                return;
 
@@ -293,17 +292,37 @@ void wpa_supplicant_initiate_eapol(struct wpa_supplicant *wpa_s)
                                EAPOL_REQUIRE_KEY_BROADCAST;
                }
 
-               if (wpa_s->conf && (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED))
+               if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_WIRED)
                        eapol_conf.required_keys = 0;
        }
-       if (wpa_s->conf)
-               eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
+       eapol_conf.fast_reauth = wpa_s->conf->fast_reauth;
        eapol_conf.workaround = ssid->eap_workaround;
        eapol_conf.eap_disabled =
                !wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt) &&
                wpa_s->key_mgmt != WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
                wpa_s->key_mgmt != WPA_KEY_MGMT_WPS;
+       eapol_conf.external_sim = wpa_s->conf->external_sim;
+
+#ifdef CONFIG_WPS
+       if (wpa_s->key_mgmt == WPA_KEY_MGMT_WPS) {
+               eapol_conf.wps |= EAPOL_LOCAL_WPS_IN_USE;
+               if (wpa_s->current_bss) {
+                       struct wpabuf *ie;
+                       ie = wpa_bss_get_vendor_ie_multi(wpa_s->current_bss,
+                                                        WPS_IE_VENDOR_TYPE);
+                       if (ie) {
+                               if (wps_is_20(ie))
+                                       eapol_conf.wps |=
+                                               EAPOL_PEER_IS_WPS20_AP;
+                               wpabuf_free(ie);
+                       }
+               }
+       }
+#endif /* CONFIG_WPS */
+
        eapol_sm_notify_config(wpa_s->eapol, &ssid->eap, &eapol_conf);
+
+       ieee802_1x_alloc_kay_sm(wpa_s, ssid);
 #endif /* IEEE8021X_EAPOL */
 }
 
@@ -379,6 +398,8 @@ void free_hw_features(struct wpa_supplicant *wpa_s)
 
 static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
 {
+       int i;
+
        bgscan_deinit(wpa_s);
        autoscan_deinit(wpa_s);
        scard_deinit(wpa_s->scard);
@@ -391,6 +412,10 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
                l2_packet_deinit(wpa_s->l2_br);
                wpa_s->l2_br = NULL;
        }
+#ifdef CONFIG_TESTING_OPTIONS
+       l2_packet_deinit(wpa_s->l2_test);
+       wpa_s->l2_test = NULL;
+#endif /* CONFIG_TESTING_OPTIONS */
 
        if (wpa_s->conf != NULL) {
                struct wpa_ssid *ssid;
@@ -414,6 +439,7 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
        wpa_tdls_deinit(wpa_s->wpa);
 #endif /* CONFIG_TDLS */
 
+       wmm_ac_clear_saved_tspecs(wpa_s);
        pmksa_candidate_free(wpa_s->wpa);
        wpa_sm_deinit(wpa_s->wpa);
        wpa_s->wpa = NULL;
@@ -421,6 +447,7 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
 
        wpa_bss_deinit(wpa_s);
 
+       wpa_supplicant_cancel_delayed_sched_scan(wpa_s);
        wpa_supplicant_cancel_scan(wpa_s);
        wpa_supplicant_cancel_auth_timeout(wpa_s);
        eloop_cancel_timeout(wpa_supplicant_stop_countermeasures, wpa_s, NULL);
@@ -445,9 +472,7 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
        wpa_supplicant_ap_deinit(wpa_s);
 #endif /* CONFIG_AP */
 
-#ifdef CONFIG_P2P
        wpas_p2p_deinit(wpa_s);
-#endif /* CONFIG_P2P */
 
 #ifdef CONFIG_OFFCHANNEL
        offchannel_deinit(wpa_s);
@@ -458,11 +483,21 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
        os_free(wpa_s->next_scan_freqs);
        wpa_s->next_scan_freqs = NULL;
 
+       os_free(wpa_s->manual_scan_freqs);
+       wpa_s->manual_scan_freqs = NULL;
+
+       os_free(wpa_s->manual_sched_scan_freqs);
+       wpa_s->manual_sched_scan_freqs = NULL;
+
+       wpas_mac_addr_rand_scan_clear(wpa_s, MAC_ADDR_RAND_ALL);
+
        gas_query_deinit(wpa_s->gas);
        wpa_s->gas = NULL;
 
        free_hw_features(wpa_s);
 
+       ieee802_1x_dealloc_kay_sm(wpa_s);
+
        os_free(wpa_s->bssid_filter);
        wpa_s->bssid_filter = NULL;
 
@@ -480,9 +515,23 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
        wpa_s->ext_pw = NULL;
 
        wpabuf_free(wpa_s->last_gas_resp);
+       wpa_s->last_gas_resp = NULL;
+       wpabuf_free(wpa_s->prev_gas_resp);
+       wpa_s->prev_gas_resp = NULL;
 
        os_free(wpa_s->last_scan_res);
        wpa_s->last_scan_res = NULL;
+
+#ifdef CONFIG_HS20
+       hs20_deinit(wpa_s);
+#endif /* CONFIG_HS20 */
+
+       for (i = 0; i < NUM_VENDOR_ELEM_FRAMES; i++) {
+               wpabuf_free(wpa_s->vendor_elem[i]);
+               wpa_s->vendor_elem[i] = NULL;
+       }
+
+       wmm_ac_notify_disassoc(wpa_s);
 }
 
 
@@ -496,29 +545,23 @@ static void wpa_supplicant_cleanup(struct wpa_supplicant *wpa_s)
  */
 void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
 {
-       if (wpa_s->keys_cleared) {
-               /* Some drivers (e.g., ndiswrapper & NDIS drivers) seem to have
-                * timing issues with keys being cleared just before new keys
-                * are set or just after association or something similar. This
-                * shows up in group key handshake failing often because of the
-                * client not receiving the first encrypted packets correctly.
-                * Skipping some of the extra key clearing steps seems to help
-                * in completing group key handshake more reliably. */
-               wpa_dbg(wpa_s, MSG_DEBUG, "No keys have been configured - "
-                       "skip key clearing");
-               return;
-       }
+       int i, max;
 
-       /* MLME-DELETEKEYS.request */
-       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 0, 0, NULL, 0, NULL, 0);
-       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 1, 0, NULL, 0, NULL, 0);
-       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 2, 0, NULL, 0, NULL, 0);
-       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 3, 0, NULL, 0, NULL, 0);
 #ifdef CONFIG_IEEE80211W
-       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 4, 0, NULL, 0, NULL, 0);
-       wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, 5, 0, NULL, 0, NULL, 0);
+       max = 6;
+#else /* CONFIG_IEEE80211W */
+       max = 4;
 #endif /* CONFIG_IEEE80211W */
-       if (addr) {
+
+       /* MLME-DELETEKEYS.request */
+       for (i = 0; i < max; i++) {
+               if (wpa_s->keys_cleared & BIT(i))
+                       continue;
+               wpa_drv_set_key(wpa_s, WPA_ALG_NONE, NULL, i, 0, NULL, 0,
+                               NULL, 0);
+       }
+       if (!(wpa_s->keys_cleared & BIT(0)) && addr &&
+           !is_zero_ether_addr(addr)) {
                wpa_drv_set_key(wpa_s, WPA_ALG_NONE, addr, 0, 0, NULL, 0, NULL,
                                0);
                /* MLME-SETPROTECTION.request(None) */
@@ -527,7 +570,7 @@ void wpa_clear_keys(struct wpa_supplicant *wpa_s, const u8 *addr)
                        MLME_SETPROTECTION_PROTECT_TYPE_NONE,
                        MLME_SETPROTECTION_KEY_TYPE_PAIRWISE);
        }
-       wpa_s->keys_cleared = 1;
+       wpa_s->keys_cleared = (u32) -1;
 }
 
 
@@ -569,14 +612,26 @@ const char * wpa_supplicant_state_txt(enum wpa_states state)
 
 static void wpa_supplicant_start_bgscan(struct wpa_supplicant *wpa_s)
 {
+       const char *name;
+
+       if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan)
+               name = wpa_s->current_ssid->bgscan;
+       else
+               name = wpa_s->conf->bgscan;
+       if (name == NULL || name[0] == '\0')
+               return;
        if (wpas_driver_bss_selection(wpa_s))
                return;
        if (wpa_s->current_ssid == wpa_s->bgscan_ssid)
                return;
+#ifdef CONFIG_P2P
+       if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE)
+               return;
+#endif /* CONFIG_P2P */
 
        bgscan_deinit(wpa_s);
-       if (wpa_s->current_ssid && wpa_s->current_ssid->bgscan) {
-               if (bgscan_init(wpa_s, wpa_s->current_ssid)) {
+       if (wpa_s->current_ssid) {
+               if (bgscan_init(wpa_s, wpa_s->current_ssid, name)) {
                        wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
                                "bgscan");
                        /*
@@ -650,6 +705,17 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
                wpa_supplicant_state_txt(wpa_s->wpa_state),
                wpa_supplicant_state_txt(state));
 
+       if (state == WPA_INTERFACE_DISABLED) {
+               /* Assure normal scan when interface is restored */
+               wpa_s->normal_scans = 0;
+       }
+
+       if (state == WPA_COMPLETED) {
+               wpas_connect_work_done(wpa_s);
+               /* Reinitialize normal_scan counter */
+               wpa_s->normal_scans = 0;
+       }
+
        if (state != WPA_SCANNING)
                wpa_supplicant_notify_scanning(wpa_s, 0);
 
@@ -659,9 +725,8 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
 #ifdef TIZEN_EXT_P2P
                if (wpa_s->ifname && (os_strcmp(wpa_s->ifname , "p2p-wlan0-0") == 0))
                        wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to "
-                               MACSTR " completed %s [id=%d id_str=%s]",
-                               MAC2STR(wpa_s->bssid), wpa_s->reassociated_connection ?
-                               "(reauth)" : "(auth)",
+                               MACSTR " completed [id=%d id_str=%s]",
+                               MAC2STR(wpa_s->bssid),
                                ssid ? ssid->id : -1,
                                ssid && ssid->id_str ? ssid->id_str : "");
                        wpa_msg(wpa_s->parent, MSG_INFO, WPA_EVENT_CONNECTED "- Connection to "
@@ -681,9 +746,8 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
                wpa_drv_set_supp_port(wpa_s, 1);
 #endif /* IEEE8021X_EAPOL */
                wpa_s->after_wps = 0;
-#ifdef CONFIG_P2P
+               wpa_s->known_wps_freq = 0;
                wpas_p2p_completed(wpa_s);
-#endif /* CONFIG_P2P */
 
                sme_sched_obss_scan(wpa_s, 1);
        } else if (state == WPA_DISCONNECTED || state == WPA_ASSOCIATING ||
@@ -701,9 +765,8 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
        if (state == WPA_DISCONNECTED) {
                struct wpa_ssid *ssid = wpa_s->current_ssid;
                wpa_msg(wpa_s->parent, MSG_INFO, WPA_EVENT_DISCONNECTED "- Disconnection to "
-                               MACSTR " completed %s [id=%d id_str=%s]",
-                               MAC2STR(wpa_s->bssid), wpa_s->reassociated_connection ?
-                               "(reauth)" : "(auth)",
+                               MACSTR " completed [id=%d id_str=%s]",
+                               MAC2STR(wpa_s->bssid),
                                ssid ? ssid->id : -1,
                                ssid && ssid->id_str ? ssid->id_str : "");
        }
@@ -711,7 +774,7 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
 #ifdef CONFIG_BGSCAN
        if (state == WPA_COMPLETED)
                wpa_supplicant_start_bgscan(wpa_s);
-       else
+       else if (state < WPA_ASSOCIATED)
                wpa_supplicant_stop_bgscan(wpa_s);
 #endif /* CONFIG_BGSCAN */
 
@@ -721,9 +784,18 @@ void wpa_supplicant_set_state(struct wpa_supplicant *wpa_s,
        if (state == WPA_DISCONNECTED || state == WPA_INACTIVE)
                wpa_supplicant_start_autoscan(wpa_s);
 
+       if (old_state >= WPA_ASSOCIATED && wpa_s->wpa_state < WPA_ASSOCIATED)
+               wmm_ac_notify_disassoc(wpa_s);
+
        if (wpa_s->wpa_state != old_state) {
                wpas_notify_state_changed(wpa_s, wpa_s->wpa_state, old_state);
 
+               /*
+                * Notify the P2P Device interface about a state change in one
+                * of the interfaces.
+                */
+               wpas_p2p_indicate_state_change(wpa_s);
+
                if (wpa_s->wpa_state == WPA_COMPLETED ||
                    old_state == WPA_COMPLETED)
                        wpas_notify_auth_changed(wpa_s);
@@ -737,9 +809,15 @@ void wpa_supplicant_terminate_proc(struct wpa_global *global)
 #ifdef CONFIG_WPS
        struct wpa_supplicant *wpa_s = global->ifaces;
        while (wpa_s) {
+               struct wpa_supplicant *next = wpa_s->next;
                if (wpas_wps_terminate_pending(wpa_s) == 1)
                        pending = 1;
-               wpa_s = wpa_s->next;
+#ifdef CONFIG_P2P
+               if (wpa_s->p2p_group_interface != NOT_P2P_GROUP_INTERFACE ||
+                   (wpa_s->current_ssid && wpa_s->current_ssid->p2p_group))
+                       wpas_p2p_disconnect(wpa_s);
+#endif /* CONFIG_P2P */
+               wpa_s = next;
        }
 #endif /* CONFIG_WPS */
        if (pending)
@@ -812,13 +890,14 @@ int wpa_supplicant_reload_configuration(struct wpa_supplicant *wpa_s)
 
        eapol_sm_invalidate_cached_session(wpa_s->eapol);
        if (wpa_s->current_ssid) {
+               wpa_s->own_disconnect_req = 1;
                wpa_supplicant_deauthenticate(wpa_s,
                                              WLAN_REASON_DEAUTH_LEAVING);
        }
 
        /*
         * TODO: should notify EAPOL SM about changes in opensc_engine_path,
-        * pkcs11_engine_path, pkcs11_module_path.
+        * pkcs11_engine_path, pkcs11_module_path, openssl_ciphers.
         */
        if (wpa_key_mgmt_wpa_psk(wpa_s->key_mgmt)) {
                /*
@@ -868,34 +947,6 @@ static void wpa_supplicant_reconfig(int sig, void *signal_ctx)
 }
 
 
-enum wpa_key_mgmt key_mgmt2driver(int key_mgmt)
-{
-       switch (key_mgmt) {
-       case WPA_KEY_MGMT_NONE:
-               return KEY_MGMT_NONE;
-       case WPA_KEY_MGMT_IEEE8021X_NO_WPA:
-               return KEY_MGMT_802_1X_NO_WPA;
-       case WPA_KEY_MGMT_IEEE8021X:
-               return KEY_MGMT_802_1X;
-       case WPA_KEY_MGMT_WPA_NONE:
-               return KEY_MGMT_WPA_NONE;
-       case WPA_KEY_MGMT_FT_IEEE8021X:
-               return KEY_MGMT_FT_802_1X;
-       case WPA_KEY_MGMT_FT_PSK:
-               return KEY_MGMT_FT_PSK;
-       case WPA_KEY_MGMT_IEEE8021X_SHA256:
-               return KEY_MGMT_802_1X_SHA256;
-       case WPA_KEY_MGMT_PSK_SHA256:
-               return KEY_MGMT_PSK_SHA256;
-       case WPA_KEY_MGMT_WPS:
-               return KEY_MGMT_WPS;
-       case WPA_KEY_MGMT_PSK:
-       default:
-               return KEY_MGMT_PSK;
-       }
-}
-
-
 static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s,
                                         struct wpa_ssid *ssid,
                                         struct wpa_ie_data *ie)
@@ -932,9 +983,7 @@ static int wpa_supplicant_suites_from_ai(struct wpa_supplicant *wpa_s,
 
 #ifdef CONFIG_IEEE80211W
        if (!(ie->capabilities & WPA_CAPABILITY_MFPC) &&
-           (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
-            wpa_s->conf->pmf : ssid->ieee80211w) ==
-           MGMT_FRAME_PROTECTION_REQUIRED) {
+           wpas_get_ssid_pmf(wpa_s, ssid) == MGMT_FRAME_PROTECTION_REQUIRED) {
                wpa_msg(wpa_s, MSG_INFO, "WPA: Driver associated with an AP "
                        "that does not support management frame protection - "
                        "reject");
@@ -966,13 +1015,14 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 {
        struct wpa_ie_data ie;
        int sel, proto;
-       const u8 *bss_wpa, *bss_rsn;
+       const u8 *bss_wpa, *bss_rsn, *bss_osen;
 
        if (bss) {
                bss_wpa = wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE);
                bss_rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
+               bss_osen = wpa_bss_get_vendor_ie(bss, OSEN_IE_VENDOR_TYPE);
        } else
-               bss_wpa = bss_rsn = NULL;
+               bss_wpa = bss_rsn = bss_osen = NULL;
 
        if (bss_rsn && (ssid->proto & WPA_PROTO_RSN) &&
            wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie) == 0 &&
@@ -982,17 +1032,63 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
                wpa_dbg(wpa_s, MSG_DEBUG, "RSN: using IEEE 802.11i/D9.0");
                proto = WPA_PROTO_RSN;
        } else if (bss_wpa && (ssid->proto & WPA_PROTO_WPA) &&
-                  wpa_parse_wpa_ie(bss_wpa, 2 +bss_wpa[1], &ie) == 0 &&
+                  wpa_parse_wpa_ie(bss_wpa, 2 + bss_wpa[1], &ie) == 0 &&
                   (ie.group_cipher & ssid->group_cipher) &&
                   (ie.pairwise_cipher & ssid->pairwise_cipher) &&
                   (ie.key_mgmt & ssid->key_mgmt)) {
                wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using IEEE 802.11i/D3.0");
                proto = WPA_PROTO_WPA;
+#ifdef CONFIG_HS20
+       } else if (bss_osen && (ssid->proto & WPA_PROTO_OSEN)) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using OSEN");
+               /* TODO: parse OSEN element */
+               os_memset(&ie, 0, sizeof(ie));
+               ie.group_cipher = WPA_CIPHER_CCMP;
+               ie.pairwise_cipher = WPA_CIPHER_CCMP;
+               ie.key_mgmt = WPA_KEY_MGMT_OSEN;
+               proto = WPA_PROTO_OSEN;
+#endif /* CONFIG_HS20 */
        } else if (bss) {
                wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select WPA/RSN");
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "WPA: ssid proto=0x%x pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x",
+                       ssid->proto, ssid->pairwise_cipher, ssid->group_cipher,
+                       ssid->key_mgmt);
+               wpa_dbg(wpa_s, MSG_DEBUG, "WPA: BSS " MACSTR " ssid='%s'%s%s%s",
+                       MAC2STR(bss->bssid),
+                       wpa_ssid_txt(bss->ssid, bss->ssid_len),
+                       bss_wpa ? " WPA" : "",
+                       bss_rsn ? " RSN" : "",
+                       bss_osen ? " OSEN" : "");
+               if (bss_rsn) {
+                       wpa_hexdump(MSG_DEBUG, "RSN", bss_rsn, 2 + bss_rsn[1]);
+                       if (wpa_parse_wpa_ie(bss_rsn, 2 + bss_rsn[1], &ie)) {
+                               wpa_dbg(wpa_s, MSG_DEBUG,
+                                       "Could not parse RSN element");
+                       } else {
+                               wpa_dbg(wpa_s, MSG_DEBUG,
+                                       "RSN: pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x",
+                                       ie.pairwise_cipher, ie.group_cipher,
+                                       ie.key_mgmt);
+                       }
+               }
+               if (bss_wpa) {
+                       wpa_hexdump(MSG_DEBUG, "WPA", bss_wpa, 2 + bss_wpa[1]);
+                       if (wpa_parse_wpa_ie(bss_wpa, 2 + bss_wpa[1], &ie)) {
+                               wpa_dbg(wpa_s, MSG_DEBUG,
+                                       "Could not parse WPA element");
+                       } else {
+                               wpa_dbg(wpa_s, MSG_DEBUG,
+                                       "WPA: pairwise_cipher=0x%x group_cipher=0x%x key_mgmt=0x%x",
+                                       ie.pairwise_cipher, ie.group_cipher,
+                                       ie.key_mgmt);
+                       }
+               }
                return -1;
        } else {
-               if (ssid->proto & WPA_PROTO_RSN)
+               if (ssid->proto & WPA_PROTO_OSEN)
+                       proto = WPA_PROTO_OSEN;
+               else if (ssid->proto & WPA_PROTO_RSN)
                        proto = WPA_PROTO_RSN;
                else
                        proto = WPA_PROTO_WPA;
@@ -1025,7 +1121,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
        wpa_s->wpa_proto = proto;
        wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_PROTO, proto);
        wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_RSN_ENABLED,
-                        !!(ssid->proto & WPA_PROTO_RSN));
+                        !!(ssid->proto & (WPA_PROTO_RSN | WPA_PROTO_OSEN)));
 
        if (bss || !wpa_s->ap_ies_from_associnfo) {
                if (wpa_sm_set_ap_wpa_ie(wpa_s->wpa, bss_wpa,
@@ -1061,6 +1157,18 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
                sel &= ~(WPA_KEY_MGMT_SAE | WPA_KEY_MGMT_FT_SAE);
 #endif /* CONFIG_SAE */
        if (0) {
+#ifdef CONFIG_SUITEB192
+       } else if (sel & WPA_KEY_MGMT_IEEE8021X_SUITE_B_192) {
+               wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "WPA: using KEY_MGMT 802.1X with Suite B (192-bit)");
+#endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_SUITEB
+       } else if (sel & WPA_KEY_MGMT_IEEE8021X_SUITE_B) {
+               wpa_s->key_mgmt = WPA_KEY_MGMT_IEEE8021X_SUITE_B;
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "WPA: using KEY_MGMT 802.1X with Suite B");
+#endif /* CONFIG_SUITEB */
 #ifdef CONFIG_IEEE80211R
        } else if (sel & WPA_KEY_MGMT_FT_IEEE8021X) {
                wpa_s->key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X;
@@ -1096,6 +1204,11 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
        } else if (sel & WPA_KEY_MGMT_WPA_NONE) {
                wpa_s->key_mgmt = WPA_KEY_MGMT_WPA_NONE;
                wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using KEY_MGMT WPA-NONE");
+#ifdef CONFIG_HS20
+       } else if (sel & WPA_KEY_MGMT_OSEN) {
+               wpa_s->key_mgmt = WPA_KEY_MGMT_OSEN;
+               wpa_dbg(wpa_s, MSG_DEBUG, "HS 2.0: using KEY_MGMT OSEN");
+#endif /* CONFIG_HS20 */
        } else {
                wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to select "
                        "authenticated key management type");
@@ -1109,14 +1222,25 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
 
 #ifdef CONFIG_IEEE80211W
        sel = ie.mgmt_group_cipher;
-       if ((ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
-            wpa_s->conf->pmf : ssid->ieee80211w) == NO_MGMT_FRAME_PROTECTION ||
+       if (wpas_get_ssid_pmf(wpa_s, ssid) == NO_MGMT_FRAME_PROTECTION ||
            !(ie.capabilities & WPA_CAPABILITY_MFPC))
                sel = 0;
        if (sel & WPA_CIPHER_AES_128_CMAC) {
                wpa_s->mgmt_group_cipher = WPA_CIPHER_AES_128_CMAC;
                wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
                        "AES-128-CMAC");
+       } else if (sel & WPA_CIPHER_BIP_GMAC_128) {
+               wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_128;
+               wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
+                       "BIP-GMAC-128");
+       } else if (sel & WPA_CIPHER_BIP_GMAC_256) {
+               wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_GMAC_256;
+               wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
+                       "BIP-GMAC-256");
+       } else if (sel & WPA_CIPHER_BIP_CMAC_256) {
+               wpa_s->mgmt_group_cipher = WPA_CIPHER_BIP_CMAC_256;
+               wpa_dbg(wpa_s, MSG_DEBUG, "WPA: using MGMT group cipher "
+                       "BIP-CMAC-256");
        } else {
                wpa_s->mgmt_group_cipher = 0;
                wpa_dbg(wpa_s, MSG_DEBUG, "WPA: not using MGMT group cipher");
@@ -1124,8 +1248,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
        wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MGMT_GROUP,
                         wpa_s->mgmt_group_cipher);
        wpa_sm_set_param(wpa_s->wpa, WPA_PARAM_MFP,
-                        (ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
-                         wpa_s->conf->pmf : ssid->ieee80211w));
+                        wpas_get_ssid_pmf(wpa_s, ssid));
 #endif /* CONFIG_IEEE80211W */
 
        if (wpa_sm_set_assoc_wpa_ie_default(wpa_s->wpa, wpa_ie, wpa_ie_len)) {
@@ -1134,7 +1257,7 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
        }
 
        if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) {
-               wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN);
+               wpa_sm_set_pmk(wpa_s->wpa, ssid->psk, PMK_LEN, NULL);
 #ifndef CONFIG_NO_PBKDF2
                if (bss && ssid->bssid_set && ssid->ssid_len == 0 &&
                    ssid->passphrase) {
@@ -1143,7 +1266,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
                                    4096, psk, PMK_LEN);
                        wpa_hexdump_key(MSG_MSGDUMP, "PSK (from passphrase)",
                                        psk, PMK_LEN);
-                       wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN);
+                       wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+                       os_memset(psk, 0, sizeof(psk));
                }
 #endif /* CONFIG_NO_PBKDF2 */
 #ifdef CONFIG_EXT_PASSWORD
@@ -1179,7 +1303,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
                                wpa_hexdump_key(MSG_MSGDUMP, "PSK (from "
                                                "external passphrase)",
                                                psk, PMK_LEN);
-                               wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN);
+                               wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+                               os_memset(psk, 0, sizeof(psk));
                        } else
 #endif /* CONFIG_NO_PBKDF2 */
                        if (wpabuf_len(pw) == 2 * PMK_LEN) {
@@ -1190,7 +1315,8 @@ int wpa_supplicant_set_suites(struct wpa_supplicant *wpa_s,
                                        ext_password_free(pw);
                                        return -1;
                                }
-                               wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN);
+                               wpa_sm_set_pmk(wpa_s->wpa, psk, PMK_LEN, NULL);
+                               os_memset(psk, 0, sizeof(psk));
                        } else {
                                wpa_msg(wpa_s, MSG_INFO, "EXT PW: No suitable "
                                        "PSK available");
@@ -1235,8 +1361,16 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx)
 #endif /* CONFIG_INTERWORKING */
                break;
        case 4: /* Bits 32-39 */
+#ifdef CONFIG_INTERWORKING
+               if (wpa_s->drv_flags / WPA_DRIVER_FLAGS_QOS_MAPPING)
+                       *pos |= 0x01; /* Bit 32 - QoS Map */
+#endif /* CONFIG_INTERWORKING */
                break;
        case 5: /* Bits 40-47 */
+#ifdef CONFIG_HS20
+               if (wpa_s->conf->hs20)
+                       *pos |= 0x40; /* Bit 46 - WNM-Notification */
+#endif /* CONFIG_HS20 */
                break;
        case 6: /* Bits 48-55 */
                break;
@@ -1244,13 +1378,18 @@ static void wpas_ext_capab_byte(struct wpa_supplicant *wpa_s, u8 *pos, int idx)
 }
 
 
-int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf)
+int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen)
 {
        u8 *pos = buf;
-       u8 len = 4, i;
+       u8 len = 6, i;
 
        if (len < wpa_s->extended_capa_len)
                len = wpa_s->extended_capa_len;
+       if (buflen < (size_t) len + 2) {
+               wpa_printf(MSG_INFO,
+                          "Not enough room for building extended capabilities element");
+               return -1;
+       }
 
        *pos++ = WLAN_EID_EXT_CAPAB;
        *pos++ = len;
@@ -1274,6 +1413,132 @@ int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf)
 }
 
 
+static int wpas_valid_bss(struct wpa_supplicant *wpa_s,
+                         struct wpa_bss *test_bss)
+{
+       struct wpa_bss *bss;
+
+       dl_list_for_each(bss, &wpa_s->bss, struct wpa_bss, list) {
+               if (bss == test_bss)
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+static int wpas_valid_ssid(struct wpa_supplicant *wpa_s,
+                          struct wpa_ssid *test_ssid)
+{
+       struct wpa_ssid *ssid;
+
+       for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
+               if (ssid == test_ssid)
+                       return 1;
+       }
+
+       return 0;
+}
+
+
+int wpas_valid_bss_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *test_bss,
+                       struct wpa_ssid *test_ssid)
+{
+       if (test_bss && !wpas_valid_bss(wpa_s, test_bss))
+               return 0;
+
+       return test_ssid == NULL || wpas_valid_ssid(wpa_s, test_ssid);
+}
+
+
+void wpas_connect_work_free(struct wpa_connect_work *cwork)
+{
+       if (cwork == NULL)
+               return;
+       os_free(cwork);
+}
+
+
+void wpas_connect_work_done(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_connect_work *cwork;
+       struct wpa_radio_work *work = wpa_s->connect_work;
+
+       if (!work)
+               return;
+
+       wpa_s->connect_work = NULL;
+       cwork = work->ctx;
+       work->ctx = NULL;
+       wpas_connect_work_free(cwork);
+       radio_work_done(work);
+}
+
+
+int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style)
+{
+       struct os_reltime now;
+       u8 addr[ETH_ALEN];
+
+       os_get_reltime(&now);
+       if (wpa_s->last_mac_addr_style == style &&
+           wpa_s->last_mac_addr_change.sec != 0 &&
+           !os_reltime_expired(&now, &wpa_s->last_mac_addr_change,
+                               wpa_s->conf->rand_addr_lifetime)) {
+               wpa_msg(wpa_s, MSG_DEBUG,
+                       "Previously selected random MAC address has not yet expired");
+               return 0;
+       }
+
+       switch (style) {
+       case 1:
+               if (random_mac_addr(addr) < 0)
+                       return -1;
+               break;
+       case 2:
+               os_memcpy(addr, wpa_s->perm_addr, ETH_ALEN);
+               if (random_mac_addr_keep_oui(addr) < 0)
+                       return -1;
+               break;
+       default:
+               return -1;
+       }
+
+       if (wpa_drv_set_mac_addr(wpa_s, addr) < 0) {
+               wpa_msg(wpa_s, MSG_INFO,
+                       "Failed to set random MAC address");
+               return -1;
+       }
+
+       os_get_reltime(&wpa_s->last_mac_addr_change);
+       wpa_s->mac_addr_changed = 1;
+       wpa_s->last_mac_addr_style = style;
+
+       if (wpa_supplicant_update_mac_addr(wpa_s) < 0) {
+               wpa_msg(wpa_s, MSG_INFO,
+                       "Could not update MAC address information");
+               return -1;
+       }
+
+       wpa_msg(wpa_s, MSG_DEBUG, "Using random MAC address " MACSTR,
+               MAC2STR(addr));
+
+       return 0;
+}
+
+
+int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s)
+{
+       if (wpa_s->wpa_state >= WPA_AUTHENTICATING ||
+           !wpa_s->conf->preassoc_mac_addr)
+               return 0;
+
+       return wpas_update_random_addr(wpa_s, wpa_s->conf->preassoc_mac_addr);
+}
+
+
+static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit);
+
 /**
  * wpa_supplicant_associate - Request association
  * @wpa_s: Pointer to wpa_supplicant data
@@ -1285,21 +1550,42 @@ int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf)
 void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                              struct wpa_bss *bss, struct wpa_ssid *ssid)
 {
-       u8 wpa_ie[200];
-       size_t wpa_ie_len;
-       int use_crypt, ret, i, bssid_changed;
-       int algs = WPA_AUTH_ALG_OPEN;
-       enum wpa_cipher cipher_pairwise, cipher_group;
-       struct wpa_driver_associate_params params;
-       int wep_keys_set = 0;
-       int assoc_failed = 0;
-       struct wpa_ssid *old_ssid;
-       u8 ext_capab[10];
-       int ext_capab_len;
-#ifdef CONFIG_HT_OVERRIDES
-       struct ieee80211_ht_capabilities htcaps;
-       struct ieee80211_ht_capabilities htcaps_mask;
-#endif /* CONFIG_HT_OVERRIDES */
+       struct wpa_connect_work *cwork;
+       int rand_style;
+
+       if (ssid->mac_addr == -1)
+               rand_style = wpa_s->conf->mac_addr;
+       else
+               rand_style = ssid->mac_addr;
+
+       wmm_ac_clear_saved_tspecs(wpa_s);
+       wpa_s->reassoc_same_bss = 0;
+
+       if (wpa_s->last_ssid == ssid) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Re-association to the same ESS");
+               if (wpa_s->current_bss && wpa_s->current_bss == bss) {
+                       wmm_ac_save_tspecs(wpa_s);
+                       wpa_s->reassoc_same_bss = 1;
+               }
+       } else if (rand_style > 0) {
+               if (wpas_update_random_addr(wpa_s, rand_style) < 0)
+                       return;
+               wpa_sm_pmksa_cache_flush(wpa_s->wpa, ssid);
+       } else if (wpa_s->mac_addr_changed) {
+               if (wpa_drv_set_mac_addr(wpa_s, NULL) < 0) {
+                       wpa_msg(wpa_s, MSG_INFO,
+                               "Could not restore permanent MAC address");
+                       return;
+               }
+               wpa_s->mac_addr_changed = 0;
+               if (wpa_supplicant_update_mac_addr(wpa_s) < 0) {
+                       wpa_msg(wpa_s, MSG_INFO,
+                               "Could not update MAC address information");
+                       return;
+               }
+               wpa_msg(wpa_s, MSG_DEBUG, "Using permanent MAC address");
+       }
+       wpa_s->last_ssid = ssid;
 
 #ifdef CONFIG_IBSS_RSN
        ibss_rsn_deinit(wpa_s->ibss_rsn);
@@ -1316,6 +1602,8 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                }
                if (wpa_supplicant_create_ap(wpa_s, ssid) < 0) {
                        wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+                       if (ssid->mode == WPAS_MODE_P2P_GROUP_FORMATION)
+                               wpas_p2p_ap_setup_failed(wpa_s);
                        return;
                }
                wpa_s->current_bss = bss;
@@ -1326,6 +1614,31 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                return;
        }
 
+       if (ssid->mode == WPAS_MODE_MESH) {
+#ifdef CONFIG_MESH
+               if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_MESH)) {
+                       wpa_msg(wpa_s, MSG_INFO,
+                               "Driver does not support mesh mode");
+                       return;
+               }
+               if (bss)
+                       ssid->frequency = bss->freq;
+               if (wpa_supplicant_join_mesh(wpa_s, ssid) < 0) {
+                       wpa_msg(wpa_s, MSG_ERROR, "Could not join mesh");
+                       return;
+               }
+               wpa_s->current_bss = bss;
+               wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_GROUP_STARTED
+                            "ssid=\"%s\" id=%d",
+                            wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
+                            ssid->id);
+#else /* CONFIG_MESH */
+               wpa_msg(wpa_s, MSG_ERROR,
+                       "mesh mode support not included in the build");
+#endif /* CONFIG_MESH */
+               return;
+       }
+
 #ifdef CONFIG_TDLS
        if (bss)
                wpa_tdls_ap_ies(wpa_s->wpa, (const u8 *) (bss + 1),
@@ -1338,86 +1651,379 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                return;
        }
 
-       os_memset(&params, 0, sizeof(params));
-       wpa_s->reassociate = 0;
-       if (bss && !wpas_driver_bss_selection(wpa_s)) {
-#ifdef CONFIG_IEEE80211R
-               const u8 *ie, *md = NULL;
-#endif /* CONFIG_IEEE80211R */
-               wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR
-                       " (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid),
-                       wpa_ssid_txt(bss->ssid, bss->ssid_len), bss->freq);
-               bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
-               os_memset(wpa_s->bssid, 0, ETH_ALEN);
-               os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
-               if (bssid_changed)
-                       wpas_notify_bssid_changed(wpa_s);
-#ifdef CONFIG_IEEE80211R
-               ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
-               if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN)
-                       md = ie + 2;
-               wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0);
-               if (md) {
-                       /* Prepare for the next transition */
-                       wpa_ft_prepare_auth_request(wpa_s->wpa, ie);
-               }
-#endif /* CONFIG_IEEE80211R */
-#ifdef CONFIG_WPS
-       } else if ((ssid->ssid == NULL || ssid->ssid_len == 0) &&
-                  wpa_s->conf->ap_scan == 2 &&
-                  (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
-               /* Use ap_scan==1 style network selection to find the network
-                */
-               wpa_s->scan_req = MANUAL_SCAN_REQ;
-               wpa_s->reassociate = 1;
-               wpa_supplicant_req_scan(wpa_s, 0, 0);
+       if (wpa_s->connect_work) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since connect_work exist");
                return;
-#endif /* CONFIG_WPS */
-       } else {
-               wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'",
-                       wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
-               os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
        }
-       wpa_supplicant_cancel_sched_scan(wpa_s);
-       wpa_supplicant_cancel_scan(wpa_s);
-
-       /* Starting new association, so clear the possibly used WPA IE from the
-        * previous association. */
-       wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
 
-#ifdef IEEE8021X_EAPOL
-       if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
-               if (ssid->leap) {
-                       if (ssid->non_leap == 0)
-                               algs = WPA_AUTH_ALG_LEAP;
-                       else
-                               algs |= WPA_AUTH_ALG_LEAP;
-               }
+       if (radio_work_pending(wpa_s, "connect")) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Reject wpa_supplicant_associate() call since pending work exist");
+               return;
        }
-#endif /* IEEE8021X_EAPOL */
-       wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
-       if (ssid->auth_alg) {
-               algs = ssid->auth_alg;
-               wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: "
-                       "0x%x", algs);
+
+       cwork = os_zalloc(sizeof(*cwork));
+       if (cwork == NULL)
+               return;
+
+       cwork->bss = bss;
+       cwork->ssid = ssid;
+
+       if (radio_add_work(wpa_s, bss ? bss->freq : 0, "connect", 1,
+                          wpas_start_assoc_cb, cwork) < 0) {
+               os_free(cwork);
        }
+}
 
-       if (bss && (wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) ||
-                   wpa_bss_get_ie(bss, WLAN_EID_RSN)) &&
-           wpa_key_mgmt_wpa(ssid->key_mgmt)) {
-               int try_opportunistic;
-               try_opportunistic = (ssid->proactive_key_caching < 0 ?
-                                    wpa_s->conf->okc :
-                                    ssid->proactive_key_caching) &&
-                       (ssid->proto & WPA_PROTO_RSN);
-               if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
-                                           ssid, try_opportunistic) == 0)
-                       eapol_sm_notify_pmkid_attempt(wpa_s->eapol, 1);
+
+static int bss_is_ibss(struct wpa_bss *bss)
+{
+       return (bss->caps & (IEEE80211_CAP_ESS | IEEE80211_CAP_IBSS)) ==
+               IEEE80211_CAP_IBSS;
+}
+
+
+void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
+                         const struct wpa_ssid *ssid,
+                         struct hostapd_freq_params *freq)
+{
+       enum hostapd_hw_mode hw_mode;
+       struct hostapd_hw_modes *mode = NULL;
+       int ht40plus[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157,
+                          184, 192 };
+       int vht80[] = { 36, 52, 100, 116, 132, 149 };
+       struct hostapd_channel_data *pri_chan = NULL, *sec_chan = NULL;
+       u8 channel;
+       int i, chan_idx, ht40 = -1, res, obss_scan = 1;
+       unsigned int j;
+       struct hostapd_freq_params vht_freq;
+
+       freq->freq = ssid->frequency;
+
+       for (j = 0; j < wpa_s->last_scan_res_used; j++) {
+               struct wpa_bss *bss = wpa_s->last_scan_res[j];
+
+               if (ssid->mode != WPAS_MODE_IBSS)
+                       break;
+
+               /* Don't adjust control freq in case of fixed_freq */
+               if (ssid->fixed_freq)
+                       break;
+
+               if (!bss_is_ibss(bss))
+                       continue;
+
+               if (ssid->ssid_len == bss->ssid_len &&
+                   os_memcmp(ssid->ssid, bss->ssid, bss->ssid_len) == 0) {
+                       wpa_printf(MSG_DEBUG,
+                                  "IBSS already found in scan results, adjust control freq: %d",
+                                  bss->freq);
+                       freq->freq = bss->freq;
+                       obss_scan = 0;
+                       break;
+               }
+       }
+
+       /* For IBSS check HT_IBSS flag */
+       if (ssid->mode == WPAS_MODE_IBSS &&
+           !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_HT_IBSS))
+               return;
+
+       if (wpa_s->group_cipher == WPA_CIPHER_WEP40 ||
+           wpa_s->group_cipher == WPA_CIPHER_WEP104 ||
+           wpa_s->pairwise_cipher == WPA_CIPHER_TKIP) {
+               wpa_printf(MSG_DEBUG,
+                          "IBSS: WEP/TKIP detected, do not try to enable HT");
+               return;
+       }
+
+       hw_mode = ieee80211_freq_to_chan(freq->freq, &channel);
+       for (i = 0; wpa_s->hw.modes && i < wpa_s->hw.num_modes; i++) {
+               if (wpa_s->hw.modes[i].mode == hw_mode) {
+                       mode = &wpa_s->hw.modes[i];
+                       break;
+               }
+       }
+
+       if (!mode)
+               return;
+
+       freq->ht_enabled = ht_supported(mode);
+       if (!freq->ht_enabled)
+               return;
+
+       /* Setup higher BW only for 5 GHz */
+       if (mode->mode != HOSTAPD_MODE_IEEE80211A)
+               return;
+
+       for (chan_idx = 0; chan_idx < mode->num_channels; chan_idx++) {
+               pri_chan = &mode->channels[chan_idx];
+               if (pri_chan->chan == channel)
+                       break;
+               pri_chan = NULL;
+       }
+       if (!pri_chan)
+               return;
+
+       /* Check primary channel flags */
+       if (pri_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
+               return;
+
+       /* Check/setup HT40+/HT40- */
+       for (j = 0; j < ARRAY_SIZE(ht40plus); j++) {
+               if (ht40plus[j] == channel) {
+                       ht40 = 1;
+                       break;
+               }
+       }
+
+       /* Find secondary channel */
+       for (i = 0; i < mode->num_channels; i++) {
+               sec_chan = &mode->channels[i];
+               if (sec_chan->chan == channel + ht40 * 4)
+                       break;
+               sec_chan = NULL;
+       }
+       if (!sec_chan)
+               return;
+
+       /* Check secondary channel flags */
+       if (sec_chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
+               return;
+
+       freq->channel = pri_chan->chan;
+
+       switch (ht40) {
+       case -1:
+               if (!(pri_chan->flag & HOSTAPD_CHAN_HT40MINUS))
+                       return;
+               freq->sec_channel_offset = -1;
+               break;
+       case 1:
+               if (!(pri_chan->flag & HOSTAPD_CHAN_HT40PLUS))
+                       return;
+               freq->sec_channel_offset = 1;
+               break;
+       default:
+               break;
+       }
+
+       if (freq->sec_channel_offset && obss_scan) {
+               struct wpa_scan_results *scan_res;
+
+               scan_res = wpa_supplicant_get_scan_results(wpa_s, NULL, 0);
+               if (scan_res == NULL) {
+                       /* Back to HT20 */
+                       freq->sec_channel_offset = 0;
+                       return;
+               }
+
+               res = check_40mhz_5g(mode, scan_res, pri_chan->chan,
+                                    sec_chan->chan);
+               switch (res) {
+               case 0:
+                       /* Back to HT20 */
+                       freq->sec_channel_offset = 0;
+                       break;
+               case 1:
+                       /* Configuration allowed */
+                       break;
+               case 2:
+                       /* Switch pri/sec channels */
+                       freq->freq = hw_get_freq(mode, sec_chan->chan);
+                       freq->sec_channel_offset = -freq->sec_channel_offset;
+                       freq->channel = sec_chan->chan;
+                       break;
+               default:
+                       freq->sec_channel_offset = 0;
+                       break;
+               }
+
+               wpa_scan_results_free(scan_res);
+       }
+
+       wpa_printf(MSG_DEBUG,
+                  "IBSS/mesh: setup freq channel %d, sec_channel_offset %d",
+                  freq->channel, freq->sec_channel_offset);
+
+       /* Not sure if mesh is ready for VHT */
+       if (ssid->mode != WPAS_MODE_IBSS)
+               return;
+
+       /* For IBSS check VHT_IBSS flag */
+       if (!(wpa_s->drv_flags & WPA_DRIVER_FLAGS_VHT_IBSS))
+               return;
+
+       vht_freq = *freq;
+
+       vht_freq.vht_enabled = vht_supported(mode);
+       if (!vht_freq.vht_enabled)
+               return;
+
+       /* setup center_freq1, bandwidth */
+       for (j = 0; j < ARRAY_SIZE(vht80); j++) {
+               if (freq->channel >= vht80[j] &&
+                   freq->channel < vht80[j] + 16)
+                       break;
+       }
+
+       if (j == ARRAY_SIZE(vht80))
+               return;
+
+       for (i = vht80[j]; i < vht80[j] + 16; i += 4) {
+               struct hostapd_channel_data *chan;
+
+               chan = hw_get_channel_chan(mode, i, NULL);
+               if (!chan)
+                       return;
+
+               /* Back to HT configuration if channel not usable */
+               if (chan->flag & (HOSTAPD_CHAN_DISABLED | HOSTAPD_CHAN_NO_IR))
+                       return;
+       }
+
+       if (hostapd_set_freq_params(&vht_freq, mode->mode, freq->freq,
+                                   freq->channel, freq->ht_enabled,
+                                   vht_freq.vht_enabled,
+                                   freq->sec_channel_offset,
+                                   VHT_CHANWIDTH_80MHZ,
+                                   vht80[j] + 6, 0, 0) != 0)
+               return;
+
+       *freq = vht_freq;
+
+       wpa_printf(MSG_DEBUG, "IBSS: VHT setup freq cf1 %d, cf2 %d, bw %d",
+                  freq->center_freq1, freq->center_freq2, freq->bandwidth);
+}
+
+
+static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
+{
+       struct wpa_connect_work *cwork = work->ctx;
+       struct wpa_bss *bss = cwork->bss;
+       struct wpa_ssid *ssid = cwork->ssid;
+       struct wpa_supplicant *wpa_s = work->wpa_s;
+       u8 wpa_ie[200];
+       size_t wpa_ie_len;
+       int use_crypt, ret, i, bssid_changed;
+       int algs = WPA_AUTH_ALG_OPEN;
+       unsigned int cipher_pairwise, cipher_group;
+       struct wpa_driver_associate_params params;
+       int wep_keys_set = 0;
+       int assoc_failed = 0;
+       struct wpa_ssid *old_ssid;
+#ifdef CONFIG_HT_OVERRIDES
+       struct ieee80211_ht_capabilities htcaps;
+       struct ieee80211_ht_capabilities htcaps_mask;
+#endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+       struct ieee80211_vht_capabilities vhtcaps;
+       struct ieee80211_vht_capabilities vhtcaps_mask;
+#endif /* CONFIG_VHT_OVERRIDES */
+
+       if (deinit) {
+               if (work->started) {
+                       wpa_s->connect_work = NULL;
+
+                       /* cancel possible auth. timeout */
+                       eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s,
+                                            NULL);
+               }
+               wpas_connect_work_free(cwork);
+               return;
+       }
+
+       wpa_s->connect_work = work;
+
+       if (cwork->bss_removed || !wpas_valid_bss_ssid(wpa_s, bss, ssid)) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "BSS/SSID entry for association not valid anymore - drop connection attempt");
+               wpas_connect_work_done(wpa_s);
+               return;
+       }
+
+       os_memset(&params, 0, sizeof(params));
+       wpa_s->reassociate = 0;
+       wpa_s->eap_expected_failure = 0;
+       if (bss &&
+           (!wpas_driver_bss_selection(wpa_s) || wpas_wps_searching(wpa_s))) {
+#ifdef CONFIG_IEEE80211R
+               const u8 *ie, *md = NULL;
+#endif /* CONFIG_IEEE80211R */
+               wpa_msg(wpa_s, MSG_INFO, "Trying to associate with " MACSTR
+                       " (SSID='%s' freq=%d MHz)", MAC2STR(bss->bssid),
+                       wpa_ssid_txt(bss->ssid, bss->ssid_len), bss->freq);
+               bssid_changed = !is_zero_ether_addr(wpa_s->bssid);
+               os_memset(wpa_s->bssid, 0, ETH_ALEN);
+               os_memcpy(wpa_s->pending_bssid, bss->bssid, ETH_ALEN);
+               if (bssid_changed)
+                       wpas_notify_bssid_changed(wpa_s);
+#ifdef CONFIG_IEEE80211R
+               ie = wpa_bss_get_ie(bss, WLAN_EID_MOBILITY_DOMAIN);
+               if (ie && ie[1] >= MOBILITY_DOMAIN_ID_LEN)
+                       md = ie + 2;
+               wpa_sm_set_ft_params(wpa_s->wpa, ie, ie ? 2 + ie[1] : 0);
+               if (md) {
+                       /* Prepare for the next transition */
+                       wpa_ft_prepare_auth_request(wpa_s->wpa, ie);
+               }
+#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_WPS
+       } else if ((ssid->ssid == NULL || ssid->ssid_len == 0) &&
+                  wpa_s->conf->ap_scan == 2 &&
+                  (ssid->key_mgmt & WPA_KEY_MGMT_WPS)) {
+               /* Use ap_scan==1 style network selection to find the network
+                */
+               wpas_connect_work_done(wpa_s);
+               wpa_s->scan_req = MANUAL_SCAN_REQ;
+               wpa_s->reassociate = 1;
+               wpa_supplicant_req_scan(wpa_s, 0, 0);
+               return;
+#endif /* CONFIG_WPS */
+       } else {
+               wpa_msg(wpa_s, MSG_INFO, "Trying to associate with SSID '%s'",
+                       wpa_ssid_txt(ssid->ssid, ssid->ssid_len));
+               os_memset(wpa_s->pending_bssid, 0, ETH_ALEN);
+       }
+       wpa_supplicant_cancel_sched_scan(wpa_s);
+       wpa_supplicant_cancel_scan(wpa_s);
+
+       /* Starting new association, so clear the possibly used WPA IE from the
+        * previous association. */
+       wpa_sm_set_assoc_wpa_ie(wpa_s->wpa, NULL, 0);
+
+#ifdef IEEE8021X_EAPOL
+       if (ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
+               if (ssid->leap) {
+                       if (ssid->non_leap == 0)
+                               algs = WPA_AUTH_ALG_LEAP;
+                       else
+                               algs |= WPA_AUTH_ALG_LEAP;
+               }
+       }
+#endif /* IEEE8021X_EAPOL */
+       wpa_dbg(wpa_s, MSG_DEBUG, "Automatic auth_alg selection: 0x%x", algs);
+       if (ssid->auth_alg) {
+               algs = ssid->auth_alg;
+               wpa_dbg(wpa_s, MSG_DEBUG, "Overriding auth_alg selection: "
+                       "0x%x", algs);
+       }
+
+       if (bss && (wpa_bss_get_vendor_ie(bss, WPA_IE_VENDOR_TYPE) ||
+                   wpa_bss_get_ie(bss, WLAN_EID_RSN)) &&
+           wpa_key_mgmt_wpa(ssid->key_mgmt)) {
+               int try_opportunistic;
+               try_opportunistic = (ssid->proactive_key_caching < 0 ?
+                                    wpa_s->conf->okc :
+                                    ssid->proactive_key_caching) &&
+                       (ssid->proto & WPA_PROTO_RSN);
+               if (pmksa_cache_set_current(wpa_s->wpa, NULL, bss->bssid,
+                                           ssid, try_opportunistic) == 0)
+                       eapol_sm_notify_pmkid_attempt(wpa_s->eapol);
                wpa_ie_len = sizeof(wpa_ie);
                if (wpa_supplicant_set_suites(wpa_s, bss, ssid,
                                              wpa_ie, &wpa_ie_len)) {
                        wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
                                "key management and encryption suites");
+                       wpas_connect_work_done(wpa_s);
                        return;
                }
        } else if ((ssid->key_mgmt & WPA_KEY_MGMT_IEEE8021X_NO_WPA) && bss &&
@@ -1437,6 +2043,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                        wpa_msg(wpa_s, MSG_WARNING, "WPA: Failed to set WPA "
                                "key management and encryption suites (no "
                                "scan results)");
+                       wpas_connect_work_done(wpa_s);
                        return;
                }
 #ifdef CONFIG_WPS
@@ -1489,6 +2096,8 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                                "disallows" : "allows");
                }
        }
+
+       os_memset(wpa_s->p2p_ip_addr_info, 0, sizeof(wpa_s->p2p_ip_addr_info));
 #endif /* CONFIG_P2P */
 
 #ifdef CONFIG_HS20
@@ -1496,30 +2105,61 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                struct wpabuf *hs20;
                hs20 = wpabuf_alloc(20);
                if (hs20) {
-                       wpas_hs20_add_indication(hs20);
-                       os_memcpy(wpa_ie + wpa_ie_len, wpabuf_head(hs20),
-                                 wpabuf_len(hs20));
-                       wpa_ie_len += wpabuf_len(hs20);
+                       int pps_mo_id = hs20_get_pps_mo_id(wpa_s, ssid);
+                       size_t len;
+
+                       wpas_hs20_add_indication(hs20, pps_mo_id);
+                       len = sizeof(wpa_ie) - wpa_ie_len;
+                       if (wpabuf_len(hs20) <= len) {
+                               os_memcpy(wpa_ie + wpa_ie_len,
+                                         wpabuf_head(hs20), wpabuf_len(hs20));
+                               wpa_ie_len += wpabuf_len(hs20);
+                       }
                        wpabuf_free(hs20);
                }
        }
 #endif /* CONFIG_HS20 */
 
-       ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab);
-       if (ext_capab_len > 0) {
-               u8 *pos = wpa_ie;
-               if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN)
-                       pos += 2 + pos[1];
-               os_memmove(pos + ext_capab_len, pos,
-                          wpa_ie_len - (pos - wpa_ie));
-               wpa_ie_len += ext_capab_len;
-               os_memcpy(pos, ext_capab, ext_capab_len);
+       /*
+        * Workaround: Add Extended Capabilities element only if the AP
+        * included this element in Beacon/Probe Response frames. Some older
+        * APs seem to have interoperability issues if this element is
+        * included, so while the standard may require us to include the
+        * element in all cases, it is justifiable to skip it to avoid
+        * interoperability issues.
+        */
+       if (!bss || wpa_bss_get_ie(bss, WLAN_EID_EXT_CAPAB)) {
+               u8 ext_capab[18];
+               int ext_capab_len;
+               ext_capab_len = wpas_build_ext_capab(wpa_s, ext_capab,
+                                                    sizeof(ext_capab));
+               if (ext_capab_len > 0) {
+                       u8 *pos = wpa_ie;
+                       if (wpa_ie_len > 0 && pos[0] == WLAN_EID_RSN)
+                               pos += 2 + pos[1];
+                       os_memmove(pos + ext_capab_len, pos,
+                                  wpa_ie_len - (pos - wpa_ie));
+                       wpa_ie_len += ext_capab_len;
+                       os_memcpy(pos, ext_capab, ext_capab_len);
+               }
+       }
+
+       if (wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ]) {
+               struct wpabuf *buf = wpa_s->vendor_elem[VENDOR_ELEM_ASSOC_REQ];
+               size_t len;
+
+               len = sizeof(wpa_ie) - wpa_ie_len;
+               if (wpabuf_len(buf) <= len) {
+                       os_memcpy(wpa_ie + wpa_ie_len,
+                                 wpabuf_head(buf), wpabuf_len(buf));
+                       wpa_ie_len += wpabuf_len(buf);
+               }
        }
 
        wpa_clear_keys(wpa_s, bss ? bss->bssid : NULL);
        use_crypt = 1;
-       cipher_pairwise = wpa_cipher_to_suite_driver(wpa_s->pairwise_cipher);
-       cipher_group = wpa_cipher_to_suite_driver(wpa_s->group_cipher);
+       cipher_pairwise = wpa_s->pairwise_cipher;
+       cipher_group = wpa_s->group_cipher;
        if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE ||
            wpa_s->key_mgmt == WPA_KEY_MGMT_IEEE8021X_NO_WPA) {
                if (wpa_s->key_mgmt == WPA_KEY_MGMT_NONE)
@@ -1543,7 +2183,7 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                        /* Assume that dynamic WEP-104 keys will be used and
                         * set cipher suites in order for drivers to expect
                         * encryption. */
-                       cipher_pairwise = cipher_group = CIPHER_WEP104;
+                       cipher_pairwise = cipher_group = WPA_CIPHER_WEP104;
                }
        }
 #endif /* IEEE8021X_EAPOL */
@@ -1564,8 +2204,10 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                                   MAC2STR(bss->bssid), bss->freq,
                                   ssid->bssid_set);
                        params.bssid = bss->bssid;
-                       params.freq = bss->freq;
+                       params.freq.freq = bss->freq;
                }
+               params.bssid_hint = bss->bssid;
+               params.freq_hint = bss->freq;
        } else {
                params.ssid = ssid->ssid;
                params.ssid_len = ssid->ssid_len;
@@ -1577,14 +2219,24 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
                params.fixed_bssid = 1;
        }
 
-       if (ssid->mode == WPAS_MODE_IBSS && ssid->frequency > 0 &&
-           params.freq == 0)
-               params.freq = ssid->frequency; /* Initial channel for IBSS */
+       /* Initial frequency for IBSS/mesh */
+       if ((ssid->mode == WPAS_MODE_IBSS || ssid->mode == WPAS_MODE_MESH) &&
+           ssid->frequency > 0 && params.freq.freq == 0)
+               ibss_mesh_setup_freq(wpa_s, ssid, &params.freq);
+
+       if (ssid->mode == WPAS_MODE_IBSS) {
+               params.fixed_freq = ssid->fixed_freq;
+               if (ssid->beacon_int)
+                       params.beacon_int = ssid->beacon_int;
+               else
+                       params.beacon_int = wpa_s->conf->beacon_int;
+       }
+
        params.wpa_ie = wpa_ie;
        params.wpa_ie_len = wpa_ie_len;
        params.pairwise_suite = cipher_pairwise;
        params.group_suite = cipher_group;
-       params.key_mgmt_suite = key_mgmt2driver(wpa_s->key_mgmt);
+       params.key_mgmt_suite = wpa_s->key_mgmt;
        params.wpa_proto = wpa_s->wpa_proto;
        params.auth_alg = algs;
        params.mode = ssid->mode;
@@ -1597,19 +2249,35 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
        params.wep_tx_keyidx = ssid->wep_tx_keyidx;
 
        if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
-           (params.key_mgmt_suite == KEY_MGMT_PSK ||
-            params.key_mgmt_suite == KEY_MGMT_FT_PSK)) {
+           (params.key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+            params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK)) {
                params.passphrase = ssid->passphrase;
                if (ssid->psk_set)
                        params.psk = ssid->psk;
        }
 
+       if (wpa_s->conf->key_mgmt_offload) {
+               if (params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
+                   params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
+                   params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
+                   params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
+                       params.req_key_mgmt_offload =
+                               ssid->proactive_key_caching < 0 ?
+                               wpa_s->conf->okc : ssid->proactive_key_caching;
+               else
+                       params.req_key_mgmt_offload = 1;
+
+               if ((params.key_mgmt_suite == WPA_KEY_MGMT_PSK ||
+                    params.key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
+                    params.key_mgmt_suite == WPA_KEY_MGMT_FT_PSK) &&
+                   ssid->psk_set)
+                       params.psk = ssid->psk;
+       }
+
        params.drop_unencrypted = use_crypt;
 
 #ifdef CONFIG_IEEE80211W
-       params.mgmt_frame_protection =
-               ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT ?
-               wpa_s->conf->pmf : ssid->ieee80211w;
+       params.mgmt_frame_protection = wpas_get_ssid_pmf(wpa_s, ssid);
        if (params.mgmt_frame_protection != NO_MGMT_FRAME_PROTECTION && bss) {
                const u8 *rsn = wpa_bss_get_ie(bss, WLAN_EID_RSN);
                struct wpa_ie_data ie;
@@ -1638,6 +2306,35 @@ void wpa_supplicant_associate(struct wpa_supplicant *wpa_s,
        params.htcaps_mask = (u8 *) &htcaps_mask;
        wpa_supplicant_apply_ht_overrides(wpa_s, ssid, &params);
 #endif /* CONFIG_HT_OVERRIDES */
+#ifdef CONFIG_VHT_OVERRIDES
+       os_memset(&vhtcaps, 0, sizeof(vhtcaps));
+       os_memset(&vhtcaps_mask, 0, sizeof(vhtcaps_mask));
+       params.vhtcaps = &vhtcaps;
+       params.vhtcaps_mask = &vhtcaps_mask;
+       wpa_supplicant_apply_vht_overrides(wpa_s, ssid, &params);
+#endif /* CONFIG_VHT_OVERRIDES */
+
+#ifdef CONFIG_P2P
+       /*
+        * If multi-channel concurrency is not supported, check for any
+        * frequency conflict. In case of any frequency conflict, remove the
+        * least prioritized connection.
+        */
+       if (wpa_s->num_multichan_concurrent < 2) {
+               int freq, num;
+               num = get_shared_radio_freqs(wpa_s, &freq, 1);
+               if (num > 0 && freq > 0 && freq != params.freq.freq) {
+                       wpa_printf(MSG_DEBUG,
+                                  "Assoc conflicting freq found (%d != %d)",
+                                  freq, params.freq.freq);
+                       if (wpas_p2p_handle_frequency_conflicts(
+                                   wpa_s, params.freq.freq, ssid) < 0) {
+                               wpas_connect_work_done(wpa_s);
+                               return;
+                       }
+               }
+       }
+#endif /* CONFIG_P2P */
 
        ret = wpa_drv_associate(wpa_s, &params);
        if (ret < 0) {
@@ -1719,6 +2416,7 @@ static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s,
 {
        struct wpa_ssid *old_ssid;
 
+       wpas_connect_work_done(wpa_s);
        wpa_clear_keys(wpa_s, addr);
        old_ssid = wpa_s->current_ssid;
        wpa_supplicant_mark_disassoc(wpa_s);
@@ -1730,30 +2428,6 @@ static void wpa_supplicant_clear_connection(struct wpa_supplicant *wpa_s,
 }
 
 
-#ifdef TIZEN_EXT
-/**
- * wpa_supplicant_disassociate - Disassociate the current connection
- * @wpa_s: Pointer to wpa_supplicant data
- * @reason_code: IEEE 802.11 reason code for the disassociate frame
- *
- * This function is used to request %wpa_supplicant to disassociate with the
- * current AP.
- */
-void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s,
-                                int reason_code)
-{
-       u8 *addr = NULL;
-
-       if (!is_zero_ether_addr(wpa_s->bssid)) {
-               wpa_drv_disassociate(wpa_s, wpa_s->bssid, reason_code);
-               addr = wpa_s->bssid;
-       }
-
-       wpa_supplicant_clear_connection(wpa_s, addr);
-}
-#endif /* TIZEN_EXT */
-
-
 /**
  * wpa_supplicant_deauthenticate - Deauthenticate the current connection
  * @wpa_s: Pointer to wpa_supplicant data
@@ -1795,6 +2469,14 @@ void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
        wpa_tdls_teardown_peers(wpa_s->wpa);
 #endif /* CONFIG_TDLS */
 
+#ifdef CONFIG_MESH
+       if (wpa_s->ifmsh) {
+               wpa_msg_ctrl(wpa_s, MSG_INFO, MESH_GROUP_REMOVED "%s",
+                            wpa_s->ifname);
+               wpa_supplicant_leave_mesh(wpa_s);
+       }
+#endif /* CONFIG_MESH */
+
        if (addr) {
                wpa_drv_deauthenticate(wpa_s, addr, reason_code);
                os_memset(&event, 0, sizeof(event));
@@ -1822,7 +2504,7 @@ static void wpa_supplicant_enable_one_network(struct wpa_supplicant *wpa_s,
         * Try to reassociate since there is no current configuration and a new
         * network was made available.
         */
-       if (!wpa_s->current_ssid)
+       if (!wpa_s->current_ssid && !wpa_s->disconnected)
                wpa_s->reassociate = 1;
 }
 
@@ -1843,7 +2525,7 @@ void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
        } else
                wpa_supplicant_enable_one_network(wpa_s, ssid);
 
-       if (wpa_s->reassociate) {
+       if (wpa_s->reassociate && !wpa_s->disconnected) {
                if (wpa_s->sched_scanning) {
                        wpa_printf(MSG_DEBUG, "Stop ongoing sched_scan to add "
                                   "new network to scan filters");
@@ -1924,6 +2606,7 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
        int disconnected = 0;
 
        if (ssid && ssid != wpa_s->current_ssid && wpa_s->current_ssid) {
+               wpa_s->own_disconnect_req = 1;
                wpa_supplicant_deauthenticate(
                        wpa_s, WLAN_REASON_DEAUTH_LEAVING);
                disconnected = 1;
@@ -1957,13 +2640,20 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
                return;
        }
 
-       if (ssid)
+       if (ssid) {
                wpa_s->current_ssid = ssid;
-       wpa_s->connect_without_scan = NULL;
+               eapol_sm_notify_config(wpa_s->eapol, NULL, NULL);
+               wpa_s->connect_without_scan =
+                       (ssid->mode == WPAS_MODE_MESH) ? ssid : NULL;
+       } else {
+               wpa_s->connect_without_scan = NULL;
+       }
+
        wpa_s->disconnected = 0;
        wpa_s->reassociate = 1;
 
-       if (wpa_supplicant_fast_associate(wpa_s) != 1)
+       if (wpa_s->connect_without_scan ||
+           wpa_supplicant_fast_associate(wpa_s) != 1)
                wpa_supplicant_req_scan(wpa_s, 0, disconnected ? 100000 : 0);
 
        if (ssid)
@@ -1972,6 +2662,59 @@ void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
 
 
 /**
+ * wpas_set_pkcs11_engine_and_module_path - Set PKCS #11 engine and module path
+ * @wpa_s: wpa_supplicant structure for a network interface
+ * @pkcs11_engine_path: PKCS #11 engine path or NULL
+ * @pkcs11_module_path: PKCS #11 module path or NULL
+ * Returns: 0 on success; -1 on failure
+ *
+ * Sets the PKCS #11 engine and module path. Both have to be NULL or a valid
+ * path. If resetting the EAPOL state machine with the new PKCS #11 engine and
+ * module path fails the paths will be reset to the default value (NULL).
+ */
+int wpas_set_pkcs11_engine_and_module_path(struct wpa_supplicant *wpa_s,
+                                          const char *pkcs11_engine_path,
+                                          const char *pkcs11_module_path)
+{
+       char *pkcs11_engine_path_copy = NULL;
+       char *pkcs11_module_path_copy = NULL;
+
+       if (pkcs11_engine_path != NULL) {
+               pkcs11_engine_path_copy = os_strdup(pkcs11_engine_path);
+               if (pkcs11_engine_path_copy == NULL)
+                       return -1;
+       }
+       if (pkcs11_module_path != NULL) {
+               pkcs11_module_path_copy = os_strdup(pkcs11_module_path);
+               if (pkcs11_module_path_copy == NULL) {
+                       os_free(pkcs11_engine_path_copy);
+                       return -1;
+               }
+       }
+
+       os_free(wpa_s->conf->pkcs11_engine_path);
+       os_free(wpa_s->conf->pkcs11_module_path);
+       wpa_s->conf->pkcs11_engine_path = pkcs11_engine_path_copy;
+       wpa_s->conf->pkcs11_module_path = pkcs11_module_path_copy;
+
+       wpa_sm_set_eapol(wpa_s->wpa, NULL);
+       eapol_sm_deinit(wpa_s->eapol);
+       wpa_s->eapol = NULL;
+       if (wpa_supplicant_init_eapol(wpa_s)) {
+               /* Error -> Reset paths to the default value (NULL) once. */
+               if (pkcs11_engine_path != NULL && pkcs11_module_path != NULL)
+                       wpas_set_pkcs11_engine_and_module_path(wpa_s, NULL,
+                                                              NULL);
+
+               return -1;
+       }
+       wpa_sm_set_eapol(wpa_s->wpa, wpa_s->eapol);
+
+       return 0;
+}
+
+
+/**
  * wpa_supplicant_set_ap_scan - Set AP scan mode for interface
  * @wpa_s: wpa_supplicant structure for a network interface
  * @ap_scan: AP scan mode
@@ -2265,6 +3008,16 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
        wpa_dbg(wpa_s, MSG_DEBUG, "RX EAPOL from " MACSTR, MAC2STR(src_addr));
        wpa_hexdump(MSG_MSGDUMP, "RX EAPOL", buf, len);
 
+#ifdef CONFIG_PEERKEY
+       if (wpa_s->wpa_state > WPA_ASSOCIATED && wpa_s->current_ssid &&
+           wpa_s->current_ssid->peerkey &&
+           !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE) &&
+           wpa_sm_rx_eapol_peerkey(wpa_s->wpa, src_addr, buf, len) == 1) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "RSN: Processed PeerKey EAPOL-Key");
+               return;
+       }
+#endif /* CONFIG_PEERKEY */
+
        if (wpa_s->wpa_state < WPA_ASSOCIATED ||
            (wpa_s->last_eapol_matches_bssid &&
 #ifdef CONFIG_AP
@@ -2290,7 +3043,7 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
                wpabuf_free(wpa_s->pending_eapol_rx);
                wpa_s->pending_eapol_rx = wpabuf_alloc_copy(buf, len);
                if (wpa_s->pending_eapol_rx) {
-                       os_get_time(&wpa_s->pending_eapol_rx_time);
+                       os_get_reltime(&wpa_s->pending_eapol_rx_time);
                        os_memcpy(wpa_s->pending_eapol_rx_src, src_addr,
                                  ETH_ALEN);
                }
@@ -2370,15 +3123,9 @@ void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
 
 int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s)
 {
-       if (wpa_s->driver->send_eapol) {
-               const u8 *addr = wpa_drv_get_mac_addr(wpa_s);
-               if (addr)
-                       os_memcpy(wpa_s->own_addr, addr, ETH_ALEN);
-       } else if ((!wpa_s->p2p_mgmt ||
-                   !(wpa_s->drv_flags &
-                     WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) &&
-                  !(wpa_s->drv_flags &
-                    WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) {
+       if ((!wpa_s->p2p_mgmt ||
+            !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE)) &&
+           !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_P2P_DEDICATED_INTERFACE)) {
                l2_packet_deinit(wpa_s->l2);
                wpa_s->l2 = l2_packet_init(wpa_s->ifname,
                                           wpa_drv_get_mac_addr(wpa_s),
@@ -2397,6 +3144,8 @@ int wpa_supplicant_update_mac_addr(struct wpa_supplicant *wpa_s)
                return -1;
        }
 
+       wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
+
        return 0;
 }
 
@@ -2444,16 +3193,15 @@ int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s)
 
        wpa_dbg(wpa_s, MSG_DEBUG, "Own MAC address: " MACSTR,
                MAC2STR(wpa_s->own_addr));
+       os_memcpy(wpa_s->perm_addr, wpa_s->own_addr, ETH_ALEN);
        wpa_sm_set_own_addr(wpa_s->wpa, wpa_s->own_addr);
 
        if (wpa_s->bridge_ifname[0]) {
                wpa_dbg(wpa_s, MSG_DEBUG, "Receiving packets from bridge "
                        "interface '%s'", wpa_s->bridge_ifname);
-               wpa_s->l2_br = l2_packet_init(wpa_s->bridge_ifname,
-                                             wpa_s->own_addr,
-                                             ETH_P_EAPOL,
-                                             wpa_supplicant_rx_eapol_bridge,
-                                             wpa_s, 1);
+               wpa_s->l2_br = l2_packet_init_bridge(
+                       wpa_s->bridge_ifname, wpa_s->ifname, wpa_s->own_addr,
+                       ETH_P_EAPOL, wpa_supplicant_rx_eapol_bridge, wpa_s, 1);
                if (wpa_s->l2_br == NULL) {
                        wpa_msg(wpa_s, MSG_ERROR, "Failed to open l2_packet "
                                "connection for the bridge interface '%s'",
@@ -2475,10 +3223,18 @@ int wpa_supplicant_driver_init(struct wpa_supplicant *wpa_s)
        wpa_s->prev_scan_wildcard = 0;
 
        if (wpa_supplicant_enabled_networks(wpa_s)) {
-               if (wpa_supplicant_delayed_sched_scan(wpa_s, interface_count,
+               if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED) {
+                       wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
+                       interface_count = 0;
+               }
+#ifndef ANDROID
+               if (!wpa_s->p2p_mgmt &&
+                   wpa_supplicant_delayed_sched_scan(wpa_s,
+                                                     interface_count % 3,
                                                      100000))
-                       wpa_supplicant_req_scan(wpa_s, interface_count,
+                       wpa_supplicant_req_scan(wpa_s, interface_count % 3,
                                                100000);
+#endif /* ANDROID */
                interface_count++;
        } else
                wpa_supplicant_set_state(wpa_s, WPA_INACTIVE);
@@ -2494,7 +3250,8 @@ static int wpa_supplicant_daemon(const char *pid_file)
 }
 
 
-static struct wpa_supplicant * wpa_supplicant_alloc(void)
+static struct wpa_supplicant *
+wpa_supplicant_alloc(struct wpa_supplicant *parent)
 {
        struct wpa_supplicant *wpa_s;
 
@@ -2504,7 +3261,7 @@ static struct wpa_supplicant * wpa_supplicant_alloc(void)
        wpa_s->scan_req = INITIAL_SCAN_REQ;
        wpa_s->scan_interval = 5;
        wpa_s->new_connection = 1;
-       wpa_s->parent = wpa_s;
+       wpa_s->parent = parent ? parent : wpa_s;
        wpa_s->sched_scanning = 0;
 
        return wpa_s;
@@ -2572,7 +3329,7 @@ static int wpa_disable_max_amsdu(struct wpa_supplicant *wpa_s,
                                 struct ieee80211_ht_capabilities *htcaps_mask,
                                 int disabled)
 {
-       u16 msk;
+       le16 msk;
 
        wpa_msg(wpa_s, MSG_DEBUG, "set_disable_max_amsdu: %d", disabled);
 
@@ -2645,8 +3402,8 @@ static int wpa_set_disable_ht40(struct wpa_supplicant *wpa_s,
                                int disabled)
 {
        /* Masking these out disables HT40 */
-       u16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET |
-                              HT_CAP_INFO_SHORT_GI40MHZ);
+       le16 msk = host_to_le16(HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET |
+                               HT_CAP_INFO_SHORT_GI40MHZ);
 
        wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ht40: %d", disabled);
 
@@ -2667,8 +3424,8 @@ static int wpa_set_disable_sgi(struct wpa_supplicant *wpa_s,
                               int disabled)
 {
        /* Masking these out disables SGI */
-       u16 msk = host_to_le16(HT_CAP_INFO_SHORT_GI20MHZ |
-                              HT_CAP_INFO_SHORT_GI40MHZ);
+       le16 msk = host_to_le16(HT_CAP_INFO_SHORT_GI20MHZ |
+                               HT_CAP_INFO_SHORT_GI40MHZ);
 
        wpa_msg(wpa_s, MSG_DEBUG, "set_disable_sgi: %d", disabled);
 
@@ -2683,6 +3440,27 @@ static int wpa_set_disable_sgi(struct wpa_supplicant *wpa_s,
 }
 
 
+static int wpa_set_disable_ldpc(struct wpa_supplicant *wpa_s,
+                              struct ieee80211_ht_capabilities *htcaps,
+                              struct ieee80211_ht_capabilities *htcaps_mask,
+                              int disabled)
+{
+       /* Masking these out disables LDPC */
+       le16 msk = host_to_le16(HT_CAP_INFO_LDPC_CODING_CAP);
+
+       wpa_msg(wpa_s, MSG_DEBUG, "set_disable_ldpc: %d", disabled);
+
+       if (disabled)
+               htcaps->ht_capabilities_info &= ~msk;
+       else
+               htcaps->ht_capabilities_info |= msk;
+
+       htcaps_mask->ht_capabilities_info |= msk;
+
+       return 0;
+}
+
+
 void wpa_supplicant_apply_ht_overrides(
        struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid,
        struct wpa_driver_associate_params *params)
@@ -2706,6 +3484,13 @@ void wpa_supplicant_apply_ht_overrides(
        wpa_set_ampdu_density(wpa_s, htcaps, htcaps_mask, ssid->ampdu_density);
        wpa_set_disable_ht40(wpa_s, htcaps, htcaps_mask, ssid->disable_ht40);
        wpa_set_disable_sgi(wpa_s, htcaps, htcaps_mask, ssid->disable_sgi);
+       wpa_set_disable_ldpc(wpa_s, htcaps, htcaps_mask, ssid->disable_ldpc);
+
+       if (ssid->ht40_intolerant) {
+               le16 bit = host_to_le16(HT_CAP_INFO_40MHZ_INTOLERANT);
+               htcaps->ht_capabilities_info |= bit;
+               htcaps_mask->ht_capabilities_info |= bit;
+       }
 }
 
 #endif /* CONFIG_HT_OVERRIDES */
@@ -2733,6 +3518,23 @@ void wpa_supplicant_apply_vht_overrides(
        vhtcaps->vht_capabilities_info = ssid->vht_capa;
        vhtcaps_mask->vht_capabilities_info = ssid->vht_capa_mask;
 
+#ifdef CONFIG_HT_OVERRIDES
+       /* if max ampdu is <= 3, we have to make the HT cap the same */
+       if (ssid->vht_capa_mask & VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX) {
+               int max_ampdu;
+
+               max_ampdu = (ssid->vht_capa &
+                            VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX) >>
+                       VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MAX_SHIFT;
+
+               max_ampdu = max_ampdu < 3 ? max_ampdu : 3;
+               wpa_set_ampdu_factor(wpa_s,
+                                    (void *) params->htcaps,
+                                    (void *) params->htcaps_mask,
+                                    max_ampdu);
+       }
+#endif /* CONFIG_HT_OVERRIDES */
+
 #define OVERRIDE_MCS(i)                                                        \
        if (ssid->vht_tx_mcs_nss_ ##i >= 0) {                           \
                vhtcaps_mask->vht_supported_mcs_set.tx_map |=           \
@@ -2767,7 +3569,7 @@ static int pcsc_reader_init(struct wpa_supplicant *wpa_s)
        if (!wpa_s->conf->pcsc_reader)
                return 0;
 
-       wpa_s->scard = scard_init(SCARD_TRY_BOTH, wpa_s->conf->pcsc_reader);
+       wpa_s->scard = scard_init(wpa_s->conf->pcsc_reader);
        if (!wpa_s->scard)
                return 1;
 
@@ -2819,15 +3621,341 @@ int wpas_init_ext_pw(struct wpa_supplicant *wpa_s)
        if (pos)
                *pos++ = '\0';
 
-       wpa_printf(MSG_DEBUG, "EXT PW: Initialize backend '%s'", val);
+       wpa_printf(MSG_DEBUG, "EXT PW: Initialize backend '%s'", val);
+
+       wpa_s->ext_pw = ext_password_init(val, pos);
+       os_free(val);
+       if (wpa_s->ext_pw == NULL) {
+               wpa_printf(MSG_DEBUG, "EXT PW: Failed to initialize backend");
+               return -1;
+       }
+       eapol_sm_set_ext_pw_ctx(wpa_s->eapol, wpa_s->ext_pw);
+
+       return 0;
+}
+
+
+static int wpas_set_wowlan_triggers(struct wpa_supplicant *wpa_s,
+                                   const struct wpa_driver_capa *capa)
+{
+       struct wowlan_triggers *triggers;
+       int ret = 0;
+
+       if (!wpa_s->conf->wowlan_triggers)
+               return 0;
+
+       triggers = wpa_get_wowlan_triggers(wpa_s->conf->wowlan_triggers, capa);
+       if (triggers) {
+               ret = wpa_drv_wowlan(wpa_s, triggers);
+               os_free(triggers);
+       }
+       return ret;
+}
+
+
+static struct wpa_radio * radio_add_interface(struct wpa_supplicant *wpa_s,
+                                             const char *rn)
+{
+       struct wpa_supplicant *iface = wpa_s->global->ifaces;
+       struct wpa_radio *radio;
+
+       while (rn && iface) {
+               radio = iface->radio;
+               if (radio && os_strcmp(rn, radio->name) == 0) {
+                       wpa_printf(MSG_DEBUG, "Add interface %s to existing radio %s",
+                                  wpa_s->ifname, rn);
+                       dl_list_add(&radio->ifaces, &wpa_s->radio_list);
+                       return radio;
+               }
+
+               iface = iface->next;
+       }
+
+       wpa_printf(MSG_DEBUG, "Add interface %s to a new radio %s",
+                  wpa_s->ifname, rn ? rn : "N/A");
+       radio = os_zalloc(sizeof(*radio));
+       if (radio == NULL)
+               return NULL;
+
+       if (rn)
+               os_strlcpy(radio->name, rn, sizeof(radio->name));
+       dl_list_init(&radio->ifaces);
+       dl_list_init(&radio->work);
+       dl_list_add(&radio->ifaces, &wpa_s->radio_list);
+
+       return radio;
+}
+
+
+static void radio_work_free(struct wpa_radio_work *work)
+{
+       if (work->wpa_s->scan_work == work) {
+               /* This should not really happen. */
+               wpa_dbg(work->wpa_s, MSG_INFO, "Freeing radio work '%s'@%p (started=%d) that is marked as scan_work",
+                       work->type, work, work->started);
+               work->wpa_s->scan_work = NULL;
+       }
+
+#ifdef CONFIG_P2P
+       if (work->wpa_s->p2p_scan_work == work) {
+               /* This should not really happen. */
+               wpa_dbg(work->wpa_s, MSG_INFO, "Freeing radio work '%s'@%p (started=%d) that is marked as p2p_scan_work",
+                       work->type, work, work->started);
+               work->wpa_s->p2p_scan_work = NULL;
+       }
+#endif /* CONFIG_P2P */
+
+       dl_list_del(&work->list);
+       os_free(work);
+}
+
+
+static void radio_start_next_work(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_radio *radio = eloop_ctx;
+       struct wpa_radio_work *work;
+       struct os_reltime now, diff;
+       struct wpa_supplicant *wpa_s;
+
+       work = dl_list_first(&radio->work, struct wpa_radio_work, list);
+       if (work == NULL)
+               return;
+
+       if (work->started)
+               return; /* already started and still in progress */
+
+       wpa_s = dl_list_first(&radio->ifaces, struct wpa_supplicant,
+                             radio_list);
+       if (wpa_s && wpa_s->radio->external_scan_running) {
+               wpa_printf(MSG_DEBUG, "Delay radio work start until externally triggered scan completes");
+               return;
+       }
+
+       os_get_reltime(&now);
+       os_reltime_sub(&now, &work->time, &diff);
+       wpa_dbg(work->wpa_s, MSG_DEBUG, "Starting radio work '%s'@%p after %ld.%06ld second wait",
+               work->type, work, diff.sec, diff.usec);
+       work->started = 1;
+       work->time = now;
+       work->cb(work, 0);
+}
+
+
+/*
+ * This function removes both started and pending radio works running on
+ * the provided interface's radio.
+ * Prior to the removal of the radio work, its callback (cb) is called with
+ * deinit set to be 1. Each work's callback is responsible for clearing its
+ * internal data and restoring to a correct state.
+ * @wpa_s: wpa_supplicant data
+ * @type: type of works to be removed
+ * @remove_all: 1 to remove all the works on this radio, 0 to remove only
+ * this interface's works.
+ */
+void radio_remove_works(struct wpa_supplicant *wpa_s,
+                       const char *type, int remove_all)
+{
+       struct wpa_radio_work *work, *tmp;
+       struct wpa_radio *radio = wpa_s->radio;
+
+       dl_list_for_each_safe(work, tmp, &radio->work, struct wpa_radio_work,
+                             list) {
+               if (type && os_strcmp(type, work->type) != 0)
+                       continue;
+
+               /* skip other ifaces' works */
+               if (!remove_all && work->wpa_s != wpa_s)
+                       continue;
+
+               wpa_dbg(wpa_s, MSG_DEBUG, "Remove radio work '%s'@%p%s",
+                       work->type, work, work->started ? " (started)" : "");
+               work->cb(work, 1);
+               radio_work_free(work);
+       }
+
+       /* in case we removed the started work */
+       radio_work_check_next(wpa_s);
+}
+
+
+static void radio_remove_interface(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_radio *radio = wpa_s->radio;
+
+       if (!radio)
+               return;
+
+       wpa_printf(MSG_DEBUG, "Remove interface %s from radio %s",
+                  wpa_s->ifname, radio->name);
+       dl_list_del(&wpa_s->radio_list);
+       radio_remove_works(wpa_s, NULL, 0);
+       wpa_s->radio = NULL;
+       if (!dl_list_empty(&radio->ifaces))
+               return; /* Interfaces remain for this radio */
+
+       wpa_printf(MSG_DEBUG, "Remove radio %s", radio->name);
+       eloop_cancel_timeout(radio_start_next_work, radio, NULL);
+       os_free(radio);
+}
+
+
+void radio_work_check_next(struct wpa_supplicant *wpa_s)
+{
+       struct wpa_radio *radio = wpa_s->radio;
+
+       if (dl_list_empty(&radio->work))
+               return;
+       if (wpa_s->ext_work_in_progress) {
+               wpa_printf(MSG_DEBUG,
+                          "External radio work in progress - delay start of pending item");
+               return;
+       }
+       eloop_cancel_timeout(radio_start_next_work, radio, NULL);
+       eloop_register_timeout(0, 0, radio_start_next_work, radio, NULL);
+}
+
+
+/**
+ * radio_add_work - Add a radio work item
+ * @wpa_s: Pointer to wpa_supplicant data
+ * @freq: Frequency of the offchannel operation in MHz or 0
+ * @type: Unique identifier for each type of work
+ * @next: Force as the next work to be executed
+ * @cb: Callback function for indicating when radio is available
+ * @ctx: Context pointer for the work (work->ctx in cb())
+ * Returns: 0 on success, -1 on failure
+ *
+ * This function is used to request time for an operation that requires
+ * exclusive radio control. Once the radio is available, the registered callback
+ * function will be called. radio_work_done() must be called once the exclusive
+ * radio operation has been completed, so that the radio is freed for other
+ * operations. The special case of deinit=1 is used to free the context data
+ * during interface removal. That does not allow the callback function to start
+ * the radio operation, i.e., it must free any resources allocated for the radio
+ * work and return.
+ *
+ * The @freq parameter can be used to indicate a single channel on which the
+ * offchannel operation will occur. This may allow multiple radio work
+ * operations to be performed in parallel if they apply for the same channel.
+ * Setting this to 0 indicates that the work item may use multiple channels or
+ * requires exclusive control of the radio.
+ */
+int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
+                  const char *type, int next,
+                  void (*cb)(struct wpa_radio_work *work, int deinit),
+                  void *ctx)
+{
+       struct wpa_radio_work *work;
+       int was_empty;
+
+       work = os_zalloc(sizeof(*work));
+       if (work == NULL)
+               return -1;
+       wpa_dbg(wpa_s, MSG_DEBUG, "Add radio work '%s'@%p", type, work);
+       os_get_reltime(&work->time);
+       work->freq = freq;
+       work->type = type;
+       work->wpa_s = wpa_s;
+       work->cb = cb;
+       work->ctx = ctx;
+
+       was_empty = dl_list_empty(&wpa_s->radio->work);
+       if (next)
+               dl_list_add(&wpa_s->radio->work, &work->list);
+       else
+               dl_list_add_tail(&wpa_s->radio->work, &work->list);
+       if (was_empty) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "First radio work item in the queue - schedule start immediately");
+               radio_work_check_next(wpa_s);
+       }
+
+       return 0;
+}
+
+
+/**
+ * radio_work_done - Indicate that a radio work item has been completed
+ * @work: Completed work
+ *
+ * This function is called once the callback function registered with
+ * radio_add_work() has completed its work.
+ */
+void radio_work_done(struct wpa_radio_work *work)
+{
+       struct wpa_supplicant *wpa_s = work->wpa_s;
+       struct os_reltime now, diff;
+       unsigned int started = work->started;
+
+       os_get_reltime(&now);
+       os_reltime_sub(&now, &work->time, &diff);
+       wpa_dbg(wpa_s, MSG_DEBUG, "Radio work '%s'@%p %s in %ld.%06ld seconds",
+               work->type, work, started ? "done" : "canceled",
+               diff.sec, diff.usec);
+       radio_work_free(work);
+       if (started)
+               radio_work_check_next(wpa_s);
+}
+
+
+struct wpa_radio_work *
+radio_work_pending(struct wpa_supplicant *wpa_s, const char *type)
+{
+       struct wpa_radio_work *work;
+       struct wpa_radio *radio = wpa_s->radio;
+
+       dl_list_for_each(work, &radio->work, struct wpa_radio_work, list) {
+               if (work->wpa_s == wpa_s && os_strcmp(work->type, type) == 0)
+                       return work;
+       }
+
+       return NULL;
+}
+
+
+static int wpas_init_driver(struct wpa_supplicant *wpa_s,
+                           struct wpa_interface *iface)
+{
+       const char *ifname, *driver, *rn;
+
+       driver = iface->driver;
+next_driver:
+       if (wpa_supplicant_set_driver(wpa_s, driver) < 0)
+               return -1;
+
+       wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname);
+       if (wpa_s->drv_priv == NULL) {
+               const char *pos;
+               pos = driver ? os_strchr(driver, ',') : NULL;
+               if (pos) {
+                       wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
+                               "driver interface - try next driver wrapper");
+                       driver = pos + 1;
+                       goto next_driver;
+               }
+               wpa_msg(wpa_s, MSG_ERROR, "Failed to initialize driver "
+                       "interface");
+               return -1;
+       }
+       if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) {
+               wpa_msg(wpa_s, MSG_ERROR, "Driver interface rejected "
+                       "driver_param '%s'", wpa_s->conf->driver_param);
+               return -1;
+       }
+
+       ifname = wpa_drv_get_ifname(wpa_s);
+       if (ifname && os_strcmp(ifname, wpa_s->ifname) != 0) {
+               wpa_dbg(wpa_s, MSG_DEBUG, "Driver interface replaced "
+                       "interface name with '%s'", ifname);
+               os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
+       }
 
-       wpa_s->ext_pw = ext_password_init(val, pos);
-       os_free(val);
-       if (wpa_s->ext_pw == NULL) {
-               wpa_printf(MSG_DEBUG, "EXT PW: Failed to initialize backend");
+       rn = wpa_driver_get_radio_name(wpa_s);
+       if (rn && rn[0] == '\0')
+               rn = NULL;
+
+       wpa_s->radio = radio_add_interface(wpa_s, rn);
+       if (wpa_s->radio == NULL)
                return -1;
-       }
-       eapol_sm_set_ext_pw_ctx(wpa_s->eapol, wpa_s->ext_pw);
 
        return 0;
 }
@@ -2836,8 +3964,8 @@ int wpas_init_ext_pw(struct wpa_supplicant *wpa_s)
 static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
                                     struct wpa_interface *iface)
 {
-       const char *ifname, *driver;
        struct wpa_driver_capa capa;
+       int capa_res;
 
        wpa_printf(MSG_DEBUG, "Initializing interface '%s' conf '%s' driver "
                   "'%s' ctrl_interface '%s' bridge '%s'", iface->ifname,
@@ -2928,37 +4056,8 @@ static int wpa_supplicant_init_iface(struct wpa_supplicant *wpa_s,
         * L2 receive handler so that association events are processed before
         * EAPOL-Key packets if both become available for the same select()
         * call. */
-       driver = iface->driver;
-next_driver:
-       if (wpa_supplicant_set_driver(wpa_s, driver) < 0)
-               return -1;
-
-       wpa_s->drv_priv = wpa_drv_init(wpa_s, wpa_s->ifname);
-       if (wpa_s->drv_priv == NULL) {
-               const char *pos;
-               pos = driver ? os_strchr(driver, ',') : NULL;
-               if (pos) {
-                       wpa_dbg(wpa_s, MSG_DEBUG, "Failed to initialize "
-                               "driver interface - try next driver wrapper");
-                       driver = pos + 1;
-                       goto next_driver;
-               }
-               wpa_msg(wpa_s, MSG_ERROR, "Failed to initialize driver "
-                       "interface");
-               return -1;
-       }
-       if (wpa_drv_set_param(wpa_s, wpa_s->conf->driver_param) < 0) {
-               wpa_msg(wpa_s, MSG_ERROR, "Driver interface rejected "
-                       "driver_param '%s'", wpa_s->conf->driver_param);
+       if (wpas_init_driver(wpa_s, iface) < 0)
                return -1;
-       }
-
-       ifname = wpa_drv_get_ifname(wpa_s);
-       if (ifname && os_strcmp(ifname, wpa_s->ifname) != 0) {
-               wpa_dbg(wpa_s, MSG_DEBUG, "Driver interface replaced "
-                       "interface name with '%s'", ifname);
-               os_strlcpy(wpa_s->ifname, ifname, sizeof(wpa_s->ifname));
-       }
 
        if (wpa_supplicant_init_wpa(wpa_s) < 0)
                return -1;
@@ -2995,11 +4094,31 @@ next_driver:
        wpa_s->hw.modes = wpa_drv_get_hw_feature_data(wpa_s,
                                                      &wpa_s->hw.num_modes,
                                                      &wpa_s->hw.flags);
+       if (wpa_s->hw.modes) {
+               u16 i;
+
+               for (i = 0; i < wpa_s->hw.num_modes; i++) {
+                       if (wpa_s->hw.modes[i].vht_capab) {
+                               wpa_s->hw_capab = CAPAB_VHT;
+                               break;
+                       }
+
+                       if (wpa_s->hw.modes[i].ht_capab &
+                           HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)
+                               wpa_s->hw_capab = CAPAB_HT40;
+                       else if (wpa_s->hw.modes[i].ht_capab &&
+                                wpa_s->hw_capab == CAPAB_NO_HT_VHT)
+                               wpa_s->hw_capab = CAPAB_HT;
+               }
+       }
 
-       if (wpa_drv_get_capa(wpa_s, &capa) == 0) {
+       capa_res = wpa_drv_get_capa(wpa_s, &capa);
+       if (capa_res == 0) {
                wpa_s->drv_capa_known = 1;
                wpa_s->drv_flags = capa.flags;
                wpa_s->drv_enc = capa.enc;
+               wpa_s->drv_smps_modes = capa.smps_modes;
+               wpa_s->drv_rrm_flags = capa.rrm_flags;
                wpa_s->probe_resp_offloads = capa.probe_resp_offloads;
                wpa_s->max_scan_ssids = capa.max_scan_ssids;
                wpa_s->max_sched_scan_ssids = capa.max_sched_scan_ssids;
@@ -3010,10 +4129,23 @@ next_driver:
                wpa_s->extended_capa = capa.extended_capa;
                wpa_s->extended_capa_mask = capa.extended_capa_mask;
                wpa_s->extended_capa_len = capa.extended_capa_len;
+               wpa_s->num_multichan_concurrent =
+                       capa.num_multichan_concurrent;
+               wpa_s->wmm_ac_supported = capa.wmm_ac_supported;
+
+               if (capa.mac_addr_rand_scan_supported)
+                       wpa_s->mac_addr_rand_supported |= MAC_ADDR_RAND_SCAN;
+               if (wpa_s->sched_scan_supported &&
+                   capa.mac_addr_rand_sched_scan_supported)
+                       wpa_s->mac_addr_rand_supported |=
+                               (MAC_ADDR_RAND_SCHED_SCAN | MAC_ADDR_RAND_PNO);
        }
        if (wpa_s->max_remain_on_chan == 0)
                wpa_s->max_remain_on_chan = 1000;
 
+#ifdef TIZEN_WLAN_BOARD_SPRD
+       wpa_s->drv_flags &= ~WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE;
+#endif /* TIZEN_WLAN_BOARD_SPRD */
        /*
         * Only take p2p_mgmt parameters when P2P Device is supported.
         * Doing it here as it determines whether l2_packet_init() will be done
@@ -3024,6 +4156,9 @@ next_driver:
        else
                iface->p2p_mgmt = 1;
 
+       if (wpa_s->num_multichan_concurrent == 0)
+               wpa_s->num_multichan_concurrent = 1;
+
        if (wpa_supplicant_driver_init(wpa_s) < 0)
                return -1;
 
@@ -3068,20 +4203,32 @@ next_driver:
                return -1;
        }
 
-#ifdef CONFIG_P2P
+#ifdef TIZEN_WLAN_BOARD_SPRD
+       if (iface->p2p_mgmt && strncmp(wpa_s->ifname, "wlan", 4) &&
+                       wpas_p2p_init(wpa_s->global, wpa_s) < 0) {
+#else /* TIZEN_WLAN_BOARD_SPRD */
        if (iface->p2p_mgmt && wpas_p2p_init(wpa_s->global, wpa_s) < 0) {
+#endif /* TIZEN_WLAN_BOARD_SPRD */
                wpa_msg(wpa_s, MSG_ERROR, "Failed to init P2P");
                return -1;
        }
-#endif /* CONFIG_P2P */
 
        if (wpa_bss_init(wpa_s) < 0)
                return -1;
 
+       /*
+        * Set Wake-on-WLAN triggers, if configured.
+        * Note: We don't restore/remove the triggers on shutdown (it doesn't
+        * have effect anyway when the interface is down).
+        */
+       if (capa_res == 0 && wpas_set_wowlan_triggers(wpa_s, &capa) < 0)
+               return -1;
+
 #ifdef CONFIG_EAP_PROXY
 {
        size_t len;
-       wpa_s->mnc_len = eap_proxy_get_imsi(wpa_s->imsi, &len);
+       wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol, wpa_s->imsi,
+                                                    &len);
        if (wpa_s->mnc_len > 0) {
                wpa_s->imsi[len] = '\0';
                wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)",
@@ -3098,6 +4245,8 @@ next_driver:
        if (wpas_init_ext_pw(wpa_s) < 0)
                return -1;
 
+       wpas_rrm_reset(wpa_s);
+
        return 0;
 }
 
@@ -3105,6 +4254,26 @@ next_driver:
 static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s,
                                        int notify, int terminate)
 {
+       struct wpa_global *global = wpa_s->global;
+       struct wpa_supplicant *iface, *prev;
+
+       if (wpa_s == wpa_s->parent)
+               wpas_p2p_group_remove(wpa_s, "*");
+
+       iface = global->ifaces;
+       while (iface) {
+               if (iface == wpa_s || iface->parent != wpa_s) {
+                       iface = iface->next;
+                       continue;
+               }
+               wpa_printf(MSG_DEBUG,
+                          "Remove remaining child interface %s from parent %s",
+                          iface->ifname, wpa_s->ifname);
+               prev = iface;
+               iface = iface->next;
+               wpa_supplicant_remove_iface(global, prev, terminate);
+       }
+
        wpa_s->disconnected = 1;
        if (wpa_s->drv_priv) {
                wpa_supplicant_deauthenticate(wpa_s,
@@ -3115,14 +4284,10 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s,
        }
 
        wpa_supplicant_cleanup(wpa_s);
+       wpas_p2p_deinit_iface(wpa_s);
 
-#ifdef CONFIG_P2P
-       if (wpa_s == wpa_s->global->p2p_init_wpa_s && wpa_s->global->p2p) {
-               wpa_dbg(wpa_s, MSG_DEBUG, "P2P: Disable P2P since removing "
-                       "the management interface is being removed");
-               wpas_p2p_deinit_global(wpa_s->global);
-       }
-#endif /* CONFIG_P2P */
+       wpas_ctrl_radio_work_flush(wpa_s);
+       radio_remove_interface(wpa_s);
 
        if (wpa_s->drv_priv)
                wpa_drv_deinit(wpa_s);
@@ -3138,6 +4303,13 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s,
                wpa_s->ctrl_iface = NULL;
        }
 
+#ifdef CONFIG_MESH
+       if (wpa_s->ifmsh) {
+               wpa_supplicant_mesh_iface_deinit(wpa_s, wpa_s->ifmsh);
+               wpa_s->ifmsh = NULL;
+       }
+#endif /* CONFIG_MESH */
+
        if (wpa_s->conf != NULL) {
                wpa_config_free(wpa_s->conf);
                wpa_s->conf = NULL;
@@ -3151,6 +4323,7 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s,
  * wpa_supplicant_add_iface - Add a new network interface
  * @global: Pointer to global data from wpa_supplicant_init()
  * @iface: Interface configuration options
+ * @parent: Parent interface or %NULL to assign new interface as parent
  * Returns: Pointer to the created interface or %NULL on failure
  *
  * This function is used to add new network interfaces for %wpa_supplicant.
@@ -3160,7 +4333,8 @@ static void wpa_supplicant_deinit_iface(struct wpa_supplicant *wpa_s,
  * e.g., when a hotplug network adapter is inserted.
  */
 struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
-                                                struct wpa_interface *iface)
+                                                struct wpa_interface *iface,
+                                                struct wpa_supplicant *parent)
 {
        struct wpa_supplicant *wpa_s;
        struct wpa_interface t_iface;
@@ -3169,7 +4343,7 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
        if (global == NULL || iface == NULL)
                return NULL;
 
-       wpa_s = wpa_supplicant_alloc();
+       wpa_s = wpa_supplicant_alloc(parent);
        if (wpa_s == NULL)
                return NULL;
 
@@ -3197,14 +4371,16 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
                return NULL;
        }
 
-       /* Notify the control interfaces about new iface */
-       if (wpas_notify_iface_added(wpa_s)) {
-               wpa_supplicant_deinit_iface(wpa_s, 1, 0);
-               return NULL;
-       }
+       if (iface->p2p_mgmt == 0) {
+               /* Notify the control interfaces about new iface */
+               if (wpas_notify_iface_added(wpa_s)) {
+                       wpa_supplicant_deinit_iface(wpa_s, 1, 0);
+                       return NULL;
+               }
 
-       for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
-               wpas_notify_network_added(wpa_s, ssid);
+               for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next)
+                       wpas_notify_network_added(wpa_s, ssid);
+       }
 
        wpa_s->next = global->ifaces;
        global->ifaces = wpa_s;
@@ -3212,6 +4388,16 @@ struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
        wpa_dbg(wpa_s, MSG_DEBUG, "Added interface %s", wpa_s->ifname);
        wpa_supplicant_set_state(wpa_s, WPA_DISCONNECTED);
 
+#ifdef CONFIG_P2P
+       if (wpa_s->global->p2p == NULL &&
+           (wpa_s->drv_flags & WPA_DRIVER_FLAGS_DEDICATED_P2P_DEVICE) &&
+           wpas_p2p_add_p2pdev_interface(wpa_s, iface->conf_p2p_dev) < 0) {
+               wpa_printf(MSG_INFO,
+                          "P2P: Failed to enable P2P Device interface");
+               /* Try to continue without. P2P will be disabled. */
+       }
+#endif /* CONFIG_P2P */
+
        return wpa_s;
 }
 
@@ -3232,6 +4418,10 @@ int wpa_supplicant_remove_iface(struct wpa_global *global,
                                int terminate)
 {
        struct wpa_supplicant *prev;
+#ifdef CONFIG_MESH
+       unsigned int mesh_if_created = wpa_s->mesh_if_created;
+       char *ifname = NULL;
+#endif /* CONFIG_MESH */
 
        /* Remove interface from the global list of interfaces */
        prev = global->ifaces;
@@ -3247,12 +4437,30 @@ int wpa_supplicant_remove_iface(struct wpa_global *global,
 
        wpa_dbg(wpa_s, MSG_DEBUG, "Removing interface %s", wpa_s->ifname);
 
+#ifdef CONFIG_MESH
+       if (mesh_if_created) {
+               ifname = os_strdup(wpa_s->ifname);
+               if (ifname == NULL) {
+                       wpa_dbg(wpa_s, MSG_ERROR,
+                               "mesh: Failed to malloc ifname");
+                       return -1;
+               }
+       }
+#endif /* CONFIG_MESH */
+
        if (global->p2p_group_formation == wpa_s)
                global->p2p_group_formation = NULL;
        if (global->p2p_invite_group == wpa_s)
                global->p2p_invite_group = NULL;
        wpa_supplicant_deinit_iface(wpa_s, 1, terminate);
 
+#ifdef CONFIG_MESH
+       if (mesh_if_created) {
+               wpa_drv_if_remove(global->ifaces, WPA_IF_MESH, ifname);
+               os_free(ifname);
+       }
+#endif /* CONFIG_MESH */
+
        return 0;
 }
 
@@ -3337,7 +4545,10 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
        wpa_msg_register_ifname_cb(wpa_supplicant_msg_ifname_cb);
 #endif /* CONFIG_NO_WPA_MSG */
 
-       wpa_debug_open_file(params->wpa_debug_file_path);
+       if (params->wpa_debug_file_path)
+               wpa_debug_open_file(params->wpa_debug_file_path);
+       else
+               wpa_debug_setup_stdout();
        if (params->wpa_debug_syslog)
                wpa_debug_open_syslog();
        if (params->wpa_debug_tracing) {
@@ -3415,7 +4626,7 @@ struct wpa_global * wpa_supplicant_init(struct wpa_params *params)
                wpa_supplicant_deinit(global);
                return NULL;
        }
-       global->drv_priv = os_zalloc(global->drv_count * sizeof(void *));
+       global->drv_priv = os_calloc(global->drv_count, sizeof(void *));
        if (global->drv_priv == NULL) {
                wpa_supplicant_deinit(global);
                return NULL;
@@ -3517,7 +4728,9 @@ void wpa_supplicant_deinit(struct wpa_global *global)
        os_free(global->params.override_driver);
        os_free(global->params.override_ctrl_interface);
 
-       os_free(global->p2p_disallow_freq);
+       os_free(global->p2p_disallow_freq.range);
+       os_free(global->p2p_go_avoid_freq.range);
+       os_free(global->add_psk);
 
        os_free(global);
        wpa_debug_close_syslog();
@@ -3546,16 +4759,12 @@ void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s)
 #ifdef CONFIG_WPS
        wpas_wps_update_config(wpa_s);
 #endif /* CONFIG_WPS */
-
-#ifdef CONFIG_P2P
        wpas_p2p_update_config(wpa_s);
-#endif /* CONFIG_P2P */
-
        wpa_s->conf->changed_parameters = 0;
 }
 
 
-static void add_freq(int *freqs, int *num_freqs, int freq)
+void add_freq(int *freqs, int *num_freqs, int freq)
 {
        int i;
 
@@ -3576,7 +4785,7 @@ static int * get_bss_freqs_in_ess(struct wpa_supplicant *wpa_s)
        int *freqs;
        int num_freqs = 0;
 
-       freqs = os_zalloc(sizeof(int) * (max_freqs + 1));
+       freqs = os_calloc(max_freqs + 1, sizeof(int));
        if (freqs == NULL)
                return NULL;
 
@@ -3609,16 +4818,24 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid)
        int count;
        int *freqs = NULL;
 
+       wpas_connect_work_done(wpa_s);
+
        /*
         * Remove possible authentication timeout since the connection failed.
         */
        eloop_cancel_timeout(wpa_supplicant_timeout, wpa_s, NULL);
 
+       /*
+        * There is no point in blacklisting the AP if this event is
+        * generated based on local request to disconnect.
+        */
+       if (wpa_s->own_disconnect_req) {
+               wpa_s->own_disconnect_req = 0;
+               wpa_dbg(wpa_s, MSG_DEBUG,
+                       "Ignore connection failure due to local request to disconnect");
+               return;
+       }
        if (wpa_s->disconnected) {
-               /*
-                * There is no point in blacklisting the AP if this event is
-                * generated based on local request to disconnect.
-                */
                wpa_dbg(wpa_s, MSG_DEBUG, "Ignore connection failure "
                        "indication since interface has been put into "
                        "disconnected state");
@@ -3665,7 +4882,7 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid)
        if (count > 3 && wpa_s->current_ssid) {
                wpa_printf(MSG_DEBUG, "Continuous association failures - "
                           "consider temporary network disabling");
-               wpas_auth_failed(wpa_s);
+               wpas_auth_failed(wpa_s, "CONN_FAILED");
        }
 
        switch (count) {
@@ -3695,8 +4912,6 @@ void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid)
         */
        wpa_supplicant_req_scan(wpa_s, timeout / 1000,
                                1000 * (timeout % 1000));
-
-       wpas_p2p_continue_after_scan(wpa_s);
 }
 
 
@@ -3730,7 +4945,7 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
                        wpa_s->reassociate = 1;
                break;
        case WPA_CTRL_REQ_EAP_PASSWORD:
-               os_free(eap->password);
+               bin_clear_free(eap->password, eap->password_len);
                eap->password = (u8 *) os_strdup(value);
                eap->password_len = os_strlen(value);
                eap->pending_req_password = 0;
@@ -3738,7 +4953,7 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
                        wpa_s->reassociate = 1;
                break;
        case WPA_CTRL_REQ_EAP_NEW_PASSWORD:
-               os_free(eap->new_password);
+               bin_clear_free(eap->new_password, eap->new_password_len);
                eap->new_password = (u8 *) os_strdup(value);
                eap->new_password_len = os_strlen(value);
                eap->pending_req_new_password = 0;
@@ -3746,14 +4961,14 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
                        wpa_s->reassociate = 1;
                break;
        case WPA_CTRL_REQ_EAP_PIN:
-               os_free(eap->pin);
+               str_clear_free(eap->pin);
                eap->pin = os_strdup(value);
                eap->pending_req_pin = 0;
                if (ssid == wpa_s->current_ssid)
                        wpa_s->reassociate = 1;
                break;
        case WPA_CTRL_REQ_EAP_OTP:
-               os_free(eap->otp);
+               bin_clear_free(eap->otp, eap->otp_len);
                eap->otp = (u8 *) os_strdup(value);
                eap->otp_len = os_strlen(value);
                os_free(eap->pending_req_otp);
@@ -3761,12 +4976,16 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
                eap->pending_req_otp_len = 0;
                break;
        case WPA_CTRL_REQ_EAP_PASSPHRASE:
-               os_free(eap->private_key_passwd);
-               eap->private_key_passwd = (u8 *) os_strdup(value);
+               str_clear_free(eap->private_key_passwd);
+               eap->private_key_passwd = os_strdup(value);
                eap->pending_req_passphrase = 0;
                if (ssid == wpa_s->current_ssid)
                        wpa_s->reassociate = 1;
                break;
+       case WPA_CTRL_REQ_SIM:
+               str_clear_free(eap->external_sim_resp);
+               eap->external_sim_resp = os_strdup(value);
+               break;
        default:
                wpa_printf(MSG_DEBUG, "CTRL_IFACE: Unknown field '%s'", field);
                return -1;
@@ -3786,13 +5005,16 @@ int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
        int i;
        unsigned int drv_enc;
 
+       if (wpa_s->p2p_mgmt)
+               return 1; /* no normal network profiles on p2p_mgmt interface */
+
        if (ssid == NULL)
                return 1;
-#ifdef CONCURRENT_MODE
+
        if (ssid->disabled)
                return 1;
-#endif
-       if (wpa_s && wpa_s->drv_capa_known)
+
+       if (wpa_s->drv_capa_known)
                drv_enc = wpa_s->drv_enc;
        else
                drv_enc = (unsigned int) -1;
@@ -3811,13 +5033,37 @@ int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
        }
 
        if (wpa_key_mgmt_wpa_psk(ssid->key_mgmt) && !ssid->psk_set &&
-           !ssid->ext_psk)
+           (!ssid->passphrase || ssid->ssid_len != 0) && !ssid->ext_psk)
                return 1;
 
        return 0;
 }
 
 
+int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+#ifdef CONFIG_IEEE80211W
+       if (ssid == NULL || ssid->ieee80211w == MGMT_FRAME_PROTECTION_DEFAULT) {
+               if (wpa_s->conf->pmf == MGMT_FRAME_PROTECTION_OPTIONAL &&
+                   !(wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_BIP)) {
+                       /*
+                        * Driver does not support BIP -- ignore pmf=1 default
+                        * since the connection with PMF would fail and the
+                        * configuration does not require PMF to be enabled.
+                        */
+                       return NO_MGMT_FRAME_PROTECTION;
+               }
+
+               return wpa_s->conf->pmf;
+       }
+
+       return ssid->ieee80211w;
+#else /* CONFIG_IEEE80211W */
+       return NO_MGMT_FRAME_PROTECTION;
+#endif /* CONFIG_IEEE80211W */
+}
+
+
 int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s)
 {
        if (wpa_s->global->conc_pref == WPA_CONC_PREF_P2P)
@@ -3828,11 +5074,11 @@ int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s)
 }
 
 
-void wpas_auth_failed(struct wpa_supplicant *wpa_s)
+void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason)
 {
        struct wpa_ssid *ssid = wpa_s->current_ssid;
        int dur;
-       struct os_time now;
+       struct os_reltime now;
 
        if (ssid == NULL) {
                wpa_printf(MSG_DEBUG, "Authentication failure but no known "
@@ -3844,29 +5090,47 @@ void wpas_auth_failed(struct wpa_supplicant *wpa_s)
                return;
 
        ssid->auth_failures++;
+
+#ifdef CONFIG_P2P
+       if (ssid->p2p_group &&
+           (wpa_s->p2p_in_provisioning || wpa_s->show_group_started)) {
+               /*
+                * Skip the wait time since there is a short timeout on the
+                * connection to a P2P group.
+                */
+               return;
+       }
+#endif /* CONFIG_P2P */
+
        if (ssid->auth_failures > 50)
                dur = 300;
-       else if (ssid->auth_failures > 20)
-               dur = 120;
        else if (ssid->auth_failures > 10)
-               dur = 60;
+               dur = 120;
        else if (ssid->auth_failures > 5)
+               dur = 90;
+       else if (ssid->auth_failures > 3)
+               dur = 60;
+       else if (ssid->auth_failures > 2)
                dur = 30;
        else if (ssid->auth_failures > 1)
                dur = 20;
        else
                dur = 10;
 
-       os_get_time(&now);
+       if (ssid->auth_failures > 1 &&
+           wpa_key_mgmt_wpa_ieee8021x(ssid->key_mgmt))
+               dur += os_random() % (ssid->auth_failures * 10);
+
+       os_get_reltime(&now);
        if (now.sec + dur <= ssid->disabled_until.sec)
                return;
 
        ssid->disabled_until.sec = now.sec + dur;
 
        wpa_msg(wpa_s, MSG_INFO, WPA_EVENT_TEMP_DISABLED
-               "id=%d ssid=\"%s\" auth_failures=%u duration=%d",
+               "id=%d ssid=\"%s\" auth_failures=%u duration=%d reason=%s",
                ssid->id, wpa_ssid_txt(ssid->ssid, ssid->ssid_len),
-               ssid->auth_failures, dur);
+               ssid->auth_failures, dur, reason);
 }
 
 
@@ -3935,6 +5199,7 @@ int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid,
 void wpas_request_connection(struct wpa_supplicant *wpa_s)
 {
        wpa_s->normal_scans = 0;
+       wpa_s->scan_req = NORMAL_SCAN_REQ;
        wpa_supplicant_reinit_autoscan(wpa_s);
        wpa_s->extra_blacklist_count = 0;
        wpa_s->disconnected = 0;
@@ -3942,43 +5207,367 @@ void wpas_request_connection(struct wpa_supplicant *wpa_s)
 
        if (wpa_supplicant_fast_associate(wpa_s) != 1)
                wpa_supplicant_req_scan(wpa_s, 0, 0);
+       else
+               wpa_s->reattach = 0;
 }
 
 
-/**
- * wpas_wpa_is_in_progress - Check whether a connection is in progress
- * @wpa_s: Pointer to wpa_supplicant data
- *
- * This function is to check if the wpa state is in beginning of the connection
- * during 4-way handshake or group key handshake with WPA on any shared
- * interface.
+void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title,
+                   struct wpa_used_freq_data *freqs_data,
+                   unsigned int len)
+{
+       unsigned int i;
+
+       wpa_dbg(wpa_s, MSG_DEBUG, "Shared frequencies (len=%u): %s",
+               len, title);
+       for (i = 0; i < len; i++) {
+               struct wpa_used_freq_data *cur = &freqs_data[i];
+               wpa_dbg(wpa_s, MSG_DEBUG, "freq[%u]: %d, flags=0x%X",
+                       i, cur->freq, cur->flags);
+       }
+}
+
+
+/*
+ * Find the operating frequencies of any of the virtual interfaces that
+ * are using the same radio as the current interface, and in addition, get
+ * information about the interface types that are using the frequency.
  */
-int wpas_wpa_is_in_progress(struct wpa_supplicant *wpa_s)
+int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s,
+                               struct wpa_used_freq_data *freqs_data,
+                               unsigned int len)
 {
-       const char *rn, *rn2;
        struct wpa_supplicant *ifs;
+       u8 bssid[ETH_ALEN];
+       int freq;
+       unsigned int idx = 0, i;
 
-       if (!wpa_s->driver->get_radio_name)
-                return 0;
+       wpa_dbg(wpa_s, MSG_DEBUG,
+               "Determining shared radio frequencies (max len %u)", len);
+       os_memset(freqs_data, 0, sizeof(struct wpa_used_freq_data) * len);
 
-       rn = wpa_s->driver->get_radio_name(wpa_s->drv_priv);
-       if (rn == NULL || rn[0] == '\0')
-               return 0;
+       dl_list_for_each(ifs, &wpa_s->radio->ifaces, struct wpa_supplicant,
+                        radio_list) {
+               if (idx == len)
+                       break;
 
-       for (ifs = wpa_s->global->ifaces; ifs; ifs = ifs->next) {
-               if (ifs == wpa_s || !ifs->driver->get_radio_name)
+               if (ifs->current_ssid == NULL || ifs->assoc_freq == 0)
                        continue;
 
-               rn2 = ifs->driver->get_radio_name(ifs->drv_priv);
-               if (!rn2 || os_strcmp(rn, rn2) != 0)
+               if (ifs->current_ssid->mode == WPAS_MODE_AP ||
+                   ifs->current_ssid->mode == WPAS_MODE_P2P_GO)
+                       freq = ifs->current_ssid->frequency;
+               else if (wpa_drv_get_bssid(ifs, bssid) == 0)
+                       freq = ifs->assoc_freq;
+               else
                        continue;
-               if (ifs->wpa_state >= WPA_AUTHENTICATING &&
-                   ifs->wpa_state != WPA_COMPLETED) {
-                       wpa_dbg(wpa_s, MSG_DEBUG, "Connection is in progress "
-                               "on interface %s - defer scan", ifs->ifname);
-                       return 1;
+
+               /* Hold only distinct freqs */
+               for (i = 0; i < idx; i++)
+                       if (freqs_data[i].freq == freq)
+                               break;
+
+               if (i == idx)
+                       freqs_data[idx++].freq = freq;
+
+               if (ifs->current_ssid->mode == WPAS_MODE_INFRA) {
+                       freqs_data[i].flags = ifs->current_ssid->p2p_group ?
+                               WPA_FREQ_USED_BY_P2P_CLIENT :
+                               WPA_FREQ_USED_BY_INFRA_STATION;
                }
        }
 
+       dump_freq_data(wpa_s, "completed iteration", freqs_data, idx);
+       return idx;
+}
+
+
+/*
+ * Find the operating frequencies of any of the virtual interfaces that
+ * are using the same radio as the current interface.
+ */
+int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
+                          int *freq_array, unsigned int len)
+{
+       struct wpa_used_freq_data *freqs_data;
+       int num, i;
+
+       os_memset(freq_array, 0, sizeof(int) * len);
+
+       freqs_data = os_calloc(len, sizeof(struct wpa_used_freq_data));
+       if (!freqs_data)
+               return -1;
+
+       num = get_shared_radio_freqs_data(wpa_s, freqs_data, len);
+       for (i = 0; i < num; i++)
+               freq_array[i] = freqs_data[i].freq;
+
+       os_free(freqs_data);
+
+       return num;
+}
+
+
+static void wpas_rrm_neighbor_rep_timeout_handler(void *data, void *user_ctx)
+{
+       struct rrm_data *rrm = data;
+
+       if (!rrm->notify_neighbor_rep) {
+               wpa_printf(MSG_ERROR,
+                          "RRM: Unexpected neighbor report timeout");
+               return;
+       }
+
+       wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report - NONE");
+       rrm->notify_neighbor_rep(rrm->neighbor_rep_cb_ctx, NULL);
+
+       rrm->notify_neighbor_rep = NULL;
+       rrm->neighbor_rep_cb_ctx = NULL;
+}
+
+
+/*
+ * wpas_rrm_reset - Clear and reset all RRM data in wpa_supplicant
+ * @wpa_s: Pointer to wpa_supplicant
+ */
+void wpas_rrm_reset(struct wpa_supplicant *wpa_s)
+{
+       wpa_s->rrm.rrm_used = 0;
+
+       eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
+                            NULL);
+       if (wpa_s->rrm.notify_neighbor_rep)
+               wpas_rrm_neighbor_rep_timeout_handler(&wpa_s->rrm, NULL);
+       wpa_s->rrm.next_neighbor_rep_token = 1;
+}
+
+
+/*
+ * wpas_rrm_process_neighbor_rep - Handle incoming neighbor report
+ * @wpa_s: Pointer to wpa_supplicant
+ * @report: Neighbor report buffer, prefixed by a 1-byte dialog token
+ * @report_len: Length of neighbor report buffer
+ */
+void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s,
+                                  const u8 *report, size_t report_len)
+{
+       struct wpabuf *neighbor_rep;
+
+       wpa_hexdump(MSG_DEBUG, "RRM: New Neighbor Report", report, report_len);
+       if (report_len < 1)
+               return;
+
+       if (report[0] != wpa_s->rrm.next_neighbor_rep_token - 1) {
+               wpa_printf(MSG_DEBUG,
+                          "RRM: Discarding neighbor report with token %d (expected %d)",
+                          report[0], wpa_s->rrm.next_neighbor_rep_token - 1);
+               return;
+       }
+
+       eloop_cancel_timeout(wpas_rrm_neighbor_rep_timeout_handler, &wpa_s->rrm,
+                            NULL);
+
+       if (!wpa_s->rrm.notify_neighbor_rep) {
+               wpa_printf(MSG_ERROR, "RRM: Unexpected neighbor report");
+               return;
+       }
+
+       /* skipping the first byte, which is only an id (dialog token) */
+       neighbor_rep = wpabuf_alloc(report_len - 1);
+       if (neighbor_rep == NULL)
+               return;
+       wpabuf_put_data(neighbor_rep, report + 1, report_len - 1);
+       wpa_printf(MSG_DEBUG, "RRM: Notifying neighbor report (token = %d)",
+                  report[0]);
+       wpa_s->rrm.notify_neighbor_rep(wpa_s->rrm.neighbor_rep_cb_ctx,
+                                      neighbor_rep);
+       wpa_s->rrm.notify_neighbor_rep = NULL;
+       wpa_s->rrm.neighbor_rep_cb_ctx = NULL;
+}
+
+
+#if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS)
+/* Workaround different, undefined for Windows, error codes used here */
+#define ENOTCONN -1
+#define EOPNOTSUPP -1
+#define ECANCELED -1
+#endif
+
+/**
+ * wpas_rrm_send_neighbor_rep_request - Request a neighbor report from our AP
+ * @wpa_s: Pointer to wpa_supplicant
+ * @ssid: if not null, this is sent in the request. Otherwise, no SSID IE
+ *       is sent in the request.
+ * @cb: Callback function to be called once the requested report arrives, or
+ *     timed out after RRM_NEIGHBOR_REPORT_TIMEOUT seconds.
+ *     In the former case, 'neighbor_rep' is a newly allocated wpabuf, and it's
+ *     the requester's responsibility to free it.
+ *     In the latter case NULL will be sent in 'neighbor_rep'.
+ * @cb_ctx: Context value to send the callback function
+ * Returns: 0 in case of success, negative error code otherwise
+ *
+ * In case there is a previous request which has not been answered yet, the
+ * new request fails. The caller may retry after RRM_NEIGHBOR_REPORT_TIMEOUT.
+ * Request must contain a callback function.
+ */
+int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
+                                      const struct wpa_ssid *ssid,
+                                      void (*cb)(void *ctx,
+                                                 struct wpabuf *neighbor_rep),
+                                      void *cb_ctx)
+{
+       struct wpabuf *buf;
+       const u8 *rrm_ie;
+
+       if (wpa_s->wpa_state != WPA_COMPLETED || wpa_s->current_ssid == NULL) {
+               wpa_printf(MSG_DEBUG, "RRM: No connection, no RRM.");
+               return -ENOTCONN;
+       }
+
+       if (!wpa_s->rrm.rrm_used) {
+               wpa_printf(MSG_DEBUG, "RRM: No RRM in current connection.");
+               return -EOPNOTSUPP;
+       }
+
+       rrm_ie = wpa_bss_get_ie(wpa_s->current_bss,
+                               WLAN_EID_RRM_ENABLED_CAPABILITIES);
+       if (!rrm_ie || !(wpa_s->current_bss->caps & IEEE80211_CAP_RRM) ||
+           !(rrm_ie[2] & WLAN_RRM_CAPS_NEIGHBOR_REPORT)) {
+               wpa_printf(MSG_DEBUG,
+                          "RRM: No network support for Neighbor Report.");
+               return -EOPNOTSUPP;
+       }
+
+       if (!cb) {
+               wpa_printf(MSG_DEBUG,
+                          "RRM: Neighbor Report request must provide a callback.");
+               return -EINVAL;
+       }
+
+       /* Refuse if there's a live request */
+       if (wpa_s->rrm.notify_neighbor_rep) {
+               wpa_printf(MSG_DEBUG,
+                          "RRM: Currently handling previous Neighbor Report.");
+               return -EBUSY;
+       }
+
+       /* 3 = action category + action code + dialog token */
+       buf = wpabuf_alloc(3 + (ssid ? 2 + ssid->ssid_len : 0));
+       if (buf == NULL) {
+               wpa_printf(MSG_DEBUG,
+                          "RRM: Failed to allocate Neighbor Report Request");
+               return -ENOMEM;
+       }
+
+       wpa_printf(MSG_DEBUG, "RRM: Neighbor report request (for %s), token=%d",
+                  (ssid ? wpa_ssid_txt(ssid->ssid, ssid->ssid_len) : ""),
+                  wpa_s->rrm.next_neighbor_rep_token);
+
+       wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+       wpabuf_put_u8(buf, WLAN_RRM_NEIGHBOR_REPORT_REQUEST);
+       wpabuf_put_u8(buf, wpa_s->rrm.next_neighbor_rep_token);
+       if (ssid) {
+               wpabuf_put_u8(buf, WLAN_EID_SSID);
+               wpabuf_put_u8(buf, ssid->ssid_len);
+               wpabuf_put_data(buf, ssid->ssid, ssid->ssid_len);
+       }
+
+       wpa_s->rrm.next_neighbor_rep_token++;
+
+       if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, wpa_s->bssid,
+                               wpa_s->own_addr, wpa_s->bssid,
+                               wpabuf_head(buf), wpabuf_len(buf), 0) < 0) {
+               wpa_printf(MSG_DEBUG,
+                          "RRM: Failed to send Neighbor Report Request");
+               wpabuf_free(buf);
+               return -ECANCELED;
+       }
+
+       wpa_s->rrm.neighbor_rep_cb_ctx = cb_ctx;
+       wpa_s->rrm.notify_neighbor_rep = cb;
+       eloop_register_timeout(RRM_NEIGHBOR_REPORT_TIMEOUT, 0,
+                              wpas_rrm_neighbor_rep_timeout_handler,
+                              &wpa_s->rrm, NULL);
+
+       wpabuf_free(buf);
        return 0;
 }
+
+
+void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
+                                             const u8 *src,
+                                             const u8 *frame, size_t len,
+                                             int rssi)
+{
+       struct wpabuf *buf;
+       const struct rrm_link_measurement_request *req;
+       struct rrm_link_measurement_report report;
+
+       if (wpa_s->wpa_state != WPA_COMPLETED) {
+               wpa_printf(MSG_INFO,
+                          "RRM: Ignoring link measurement request. Not associated");
+               return;
+       }
+
+       if (!wpa_s->rrm.rrm_used) {
+               wpa_printf(MSG_INFO,
+                          "RRM: Ignoring link measurement request. Not RRM network");
+               return;
+       }
+
+       if (!(wpa_s->drv_rrm_flags & WPA_DRIVER_FLAGS_TX_POWER_INSERTION)) {
+               wpa_printf(MSG_INFO,
+                          "RRM: Measurement report failed. TX power insertion not supported");
+               return;
+       }
+
+       req = (const struct rrm_link_measurement_request *) frame;
+       if (len < sizeof(*req)) {
+               wpa_printf(MSG_INFO,
+                          "RRM: Link measurement report failed. Request too short");
+               return;
+       }
+
+       os_memset(&report, 0, sizeof(report));
+       report.tpc.eid = WLAN_EID_TPC_REPORT;
+       report.tpc.len = 2;
+       report.rsni = 255; /* 255 indicates that RSNI is not available */
+       report.dialog_token = req->dialog_token;
+
+       /*
+        * It's possible to estimate RCPI based on RSSI in dBm. This
+        * calculation will not reflect the correct value for high rates,
+        * but it's good enough for Action frames which are transmitted
+        * with up to 24 Mbps rates.
+        */
+       if (!rssi)
+               report.rcpi = 255; /* not available */
+       else if (rssi < -110)
+               report.rcpi = 0;
+       else if (rssi > 0)
+               report.rcpi = 220;
+       else
+               report.rcpi = (rssi + 110) * 2;
+
+       /* action_category + action_code */
+       buf = wpabuf_alloc(2 + sizeof(report));
+       if (buf == NULL) {
+               wpa_printf(MSG_ERROR,
+                          "RRM: Link measurement report failed. Buffer allocation failed");
+               return;
+       }
+
+       wpabuf_put_u8(buf, WLAN_ACTION_RADIO_MEASUREMENT);
+       wpabuf_put_u8(buf, WLAN_RRM_LINK_MEASUREMENT_REPORT);
+       wpabuf_put_data(buf, &report, sizeof(report));
+       wpa_hexdump(MSG_DEBUG, "RRM: Link measurement report:",
+                   wpabuf_head(buf), wpabuf_len(buf));
+
+       if (wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0, src,
+                               wpa_s->own_addr, wpa_s->bssid,
+                               wpabuf_head(buf), wpabuf_len(buf), 0)) {
+               wpa_printf(MSG_ERROR,
+                          "RRM: Link measurement report failed. Send action failed");
+       }
+       wpabuf_free(buf);
+}
index d73d371..10b459d 100644 (file)
@@ -21,7 +21,7 @@
 # wpa_cli/wpa_gui to be able to store the configuration changes permanently.
 # Please note that overwriting configuration file will remove the comments from
 # it.
-#update_config=1
+update_config=0
 
 # global configuration (shared by all network blocks)
 #
@@ -72,7 +72,7 @@
 # DACL (which will reject all connections). See README-Windows.txt for more
 # information about SDDL string format.
 #
-ctrl_interface=/var/run/wpa_supplicant
+#ctrl_interface=/var/run/wpa_supplicant
 
 # IEEE 802.1X/EAPOL version
 # wpa_supplicant is implemented based on IEEE Std 802.1X-2004 which defines
@@ -81,6 +81,8 @@ ctrl_interface=/var/run/wpa_supplicant
 # to make wpa_supplicant interoperate with these APs, the version number is set
 # to 1 by default. This configuration value can be used to set it to the new
 # version (2).
+# Note: When using MACsec, eapol_version shall be set to 3, which is
+# defined in IEEE Std 802.1X-2010.
 eapol_version=1
 
 # AP scanning/selection
@@ -97,6 +99,8 @@ eapol_version=1
 #    non-WPA drivers when using IEEE 802.1X mode; do not try to associate with
 #    APs (i.e., external program needs to control association). This mode must
 #    also be used when using wired Ethernet drivers.
+#    Note: macsec_qca driver is one type of Ethernet driver which implements
+#    macsec feature.
 # 2: like 0, but associate with APs using security policy and SSID (but not
 #    BSSID); this can be used, e.g., with ndiswrapper and NDIS drivers to
 #    enable operation with hidden SSIDs and optimized roaming; in this mode,
@@ -110,11 +114,35 @@ eapol_version=1
 # networks are found, a new IBSS or AP mode network is created.
 ap_scan=1
 
+# MPM residency
+# By default, wpa_supplicant implements the mesh peering manager (MPM) for an
+# open mesh. However, if the driver can implement the MPM, you may set this to
+# 0 to use the driver version. When AMPE is enabled, the wpa_supplicant MPM is
+# always used.
+# 0: MPM lives in the driver
+# 1: wpa_supplicant provides an MPM which handles peering (default)
+#user_mpm=1
+
+# Maximum number of peer links (0-255; default: 99)
+# Maximum number of mesh peering currently maintained by the STA.
+#max_peer_links=99
+
+# Timeout in seconds to detect STA inactivity (default: 300 seconds)
+#
+# This timeout value is used in mesh STA to clean up inactive stations.
+#mesh_max_inactivity=300
+
+# cert_in_cb - Whether to include a peer certificate dump in events
+# This controls whether peer certificates for authentication server and
+# its certificate chain are included in EAP peer certificate events. This is
+# enabled by default.
+#cert_in_cb=1
+
 # EAP fast re-authentication
 # By default, fast re-authentication is enabled for all EAP methods that
 # support it. This variable can be used to disable fast re-authentication.
 # Normally, there is no need to disable this.
-fast_reauth=1
+fast_reauth=0
 
 # OpenSSL Engine support
 # These options can be used to load OpenSSL engines.
@@ -128,6 +156,16 @@ fast_reauth=1
 # configure the path to the pkcs11 module required by the pkcs11 engine
 #pkcs11_module_path=/usr/lib/pkcs11/opensc-pkcs11.so
 
+# OpenSSL cipher string
+#
+# This is an OpenSSL specific configuration option for configuring the default
+# ciphers. If not set, "DEFAULT:!EXP:!LOW" is used as the default.
+# See https://www.openssl.org/docs/apps/ciphers.html for OpenSSL documentation
+# on cipher suite configuration. This is applicable only if wpa_supplicant is
+# built to use OpenSSL.
+#openssl_ciphers=DEFAULT:!EXP:!LOW
+
+
 # Dynamic EAP methods
 # If EAP methods were built dynamically as shared object files, they need to be
 # loaded here before being used in the network blocks. By default, EAP methods
@@ -139,7 +177,7 @@ fast_reauth=1
 # This field can be used to configure arbitrary driver interace parameters. The
 # format is specific to the selected driver interface. This field is not used
 # in most cases.
-#driver_param="field=value"
+#driver_param=use_p2p_group_interface=1
 
 # Country code
 # The ISO/IEC alpha2 country code for the country in which this device is
@@ -161,7 +199,7 @@ fast_reauth=1
 
 # Device Name
 # User-friendly description of device; up to 32 octets encoded in UTF-8
-#device_name=Wireless Client
+device_name=Tizen
 
 # Manufacturer
 # The manufacturer of the device (up to 64 ASCII characters)
@@ -190,7 +228,7 @@ fast_reauth=1
 #   1-0050F204-2 (Computer / Server)
 #   5-0050F204-1 (Storage / NAS)
 #   6-0050F204-1 (Network Infrastructure / AP)
-#device_type=1-0050F204-1
+device_type=10-0050F204-5
 
 # OS Version
 # 4-octet operating system version number (hex string)
@@ -204,7 +242,7 @@ fast_reauth=1
 # For WSC 1.0:
 #config_methods=label display push_button keypad
 # For WSC 2.0:
-#config_methods=label virtual_display virtual_push_button keypad
+config_methods=keypad,virtual_push_button,physical_display
 
 # Credential processing
 #   0 = process received credentials internally (default)
@@ -241,14 +279,14 @@ fast_reauth=1
 # This is an optional set of parameters for automatic scanning
 # within an interface in following format:
 #autoscan=<autoscan module name>:<module parameters>
-# autoscan is like bgscan but on disconnected or inactive state.
-# For instance, on exponential module parameters would be <base>:<limit>
+# autoscan is like bgscan but on disconnected or inactive state.
+# For instance, on exponential module parameters would be <base>:<limit>
 #autoscan=exponential:3:300
 # Which means a delay between scans on a base exponential of 3,
-# up to the limit of 300 seconds (3, 9, 27 ... 300)
-# For periodic module, parameters would be <fixed interval>
+# up to the limit of 300 seconds (3, 9, 27 ... 300)
+# For periodic module, parameters would be <fixed interval>
 #autoscan=periodic:30
-# So a delay of 30 seconds will be applied between each scan
+# So a delay of 30 seconds will be applied between each scan
 
 # filter_ssids - SSID-based scan result filtering
 # 0 = do not filter scan results (default)
@@ -265,6 +303,19 @@ fast_reauth=1
 # inactive stations.
 #p2p_go_max_inactivity=300
 
+# Passphrase length (8..63) for P2P GO
+#
+# This parameter controls the length of the random passphrase that is
+# generated at the GO. Default: 8.
+#p2p_passphrase_len=8
+
+# Extra delay between concurrent P2P search iterations
+#
+# This value adds extra delay in milliseconds between concurrent search
+# iterations to make p2p_find friendlier to concurrent operations by avoiding
+# it from taking 100% of radio resources. The default value is 500 ms.
+#p2p_search_delay=500
+
 # Opportunistic Key Caching (also known as Proactive Key Caching) default
 # This parameter can be used to set the default behavior for the
 # proactive_key_caching parameter. By default, OKC is disabled unless enabled
@@ -315,10 +366,29 @@ fast_reauth=1
 # 1:  Scan current operating frequency if another VIF on the same radio
 #     is already associated.
 
+# MAC address policy default
+# 0 = use permanent MAC address
+# 1 = use random MAC address for each ESS connection
+# 2 = like 1, but maintain OUI (with local admin bit set)
+#
+# By default, permanent MAC address is used unless policy is changed by
+# the per-network mac_addr parameter. Global mac_addr=1 can be used to
+# change this default behavior.
+#mac_addr=0
+
+# Lifetime of random MAC address in seconds (default: 60)
+#rand_addr_lifetime=60
+
+# MAC address policy for pre-association operations (scanning, ANQP)
+# 0 = use permanent MAC address
+# 1 = use random MAC address
+# 2 = like 1, but maintain OUI (with local admin bit set)
+#preassoc_mac_addr=0
+
 # Interworking (IEEE 802.11u)
 
 # Enable Interworking
-# interworking=1
+interworking=0
 
 # Homogenous ESS identifier
 # If this is set, scans will be used to request response only from BSSes
@@ -332,7 +402,8 @@ fast_reauth=1
 # 1 = perform Interworking network selection if one or more
 #     credentials have been configured and scan did not find a
 #     matching network block
-#auto_interworking=0
+auto_interworking=0
+wowlan_triggers=any
 
 # credential block
 #
@@ -342,6 +413,8 @@ fast_reauth=1
 #
 # credential fields:
 #
+# temporary: Whether this credential is temporary and not to be saved
+#
 # priority: Priority group
 #      By default, all networks and credentials get the same priority group
 #      (0). This field can be used to give higher priority for credentials
@@ -399,9 +472,11 @@ fast_reauth=1
 # milenage: Milenage parameters for SIM/USIM simulator in <Ki>:<OPc>:<SQN>
 #      format
 #
-# domain: Home service provider FQDN
+# domain: Home service provider FQDN(s)
 #      This is used to compare against the Domain Name List to figure out
-#      whether the AP is operated by the Home SP.
+#      whether the AP is operated by the Home SP. Multiple domain entries can
+#      be used to configure alternative FQDNs that will be considered home
+#      networks.
 #
 # roaming_consortium: Roaming Consortium OI
 #      If roaming_consortium_len is non-zero, this field contains the
@@ -428,6 +503,59 @@ fast_reauth=1
 #      matching with the network. Multiple entries can be used to specify more
 #      than one SSID.
 #
+# roaming_partner: Roaming partner information
+#      This optional field can be used to configure preferences between roaming
+#      partners. The field is a string in following format:
+#      <FQDN>,<0/1 exact match>,<priority>,<* or country code>
+#      (non-exact match means any subdomain matches the entry; priority is in
+#      0..255 range with 0 being the highest priority)
+#
+# update_identifier: PPS MO ID
+#      (Hotspot 2.0 PerProviderSubscription/UpdateIdentifier)
+#
+# provisioning_sp: FQDN of the SP that provisioned the credential
+#      This optional field can be used to keep track of the SP that provisioned
+#      the credential to find the PPS MO (./Wi-Fi/<provisioning_sp>).
+#
+# Minimum backhaul threshold (PPS/<X+>/Policy/MinBackhauldThreshold/*)
+#      These fields can be used to specify minimum download/upload backhaul
+#      bandwidth that is preferred for the credential. This constraint is
+#      ignored if the AP does not advertise WAN Metrics information or if the
+#      limit would prevent any connection. Values are in kilobits per second.
+# min_dl_bandwidth_home
+# min_ul_bandwidth_home
+# min_dl_bandwidth_roaming
+# min_ul_bandwidth_roaming
+#
+# max_bss_load: Maximum BSS Load Channel Utilization (1..255)
+#      (PPS/<X+>/Policy/MaximumBSSLoadValue)
+#      This value is used as the maximum channel utilization for network
+#      selection purposes for home networks. If the AP does not advertise
+#      BSS Load or if the limit would prevent any connection, this constraint
+#      will be ignored.
+#
+# req_conn_capab: Required connection capability
+#      (PPS/<X+>/Policy/RequiredProtoPortTuple)
+#      This value is used to configure set of required protocol/port pairs that
+#      a roaming network shall support (include explicitly in Connection
+#      Capability ANQP element). This constraint is ignored if the AP does not
+#      advertise Connection Capability or if this constraint would prevent any
+#      network connection. This policy is not used in home networks.
+#      Format: <protocol>[:<comma-separated list of ports]
+#      Multiple entries can be used to list multiple requirements.
+#      For example, number of common TCP protocols:
+#      req_conn_capab=6,22,80,443
+#      For example, IPSec/IKE:
+#      req_conn_capab=17:500
+#      req_conn_capab=50
+#
+# ocsp: Whether to use/require OCSP to check server certificate
+#      0 = do not use OCSP stapling (TLS certificate status extension)
+#      1 = try to use OCSP stapling, but not require response
+#      2 = require valid OCSP stapling response
+#
+# sim_num: Identifier for which SIM to use in multi-SIM devices
+#
 # for example:
 #
 #cred={
@@ -455,7 +583,7 @@ fast_reauth=1
 #}
 
 # Hotspot 2.0
-# hs20=1
+hs20=0
 
 # network block
 #
@@ -504,9 +632,10 @@ fast_reauth=1
 # 0 = infrastructure (Managed) mode, i.e., associate with an AP (default)
 # 1 = IBSS (ad-hoc, peer-to-peer)
 # 2 = AP (access point)
-# Note: IBSS can only be used with key_mgmt NONE (plaintext and static WEP)
-# and key_mgmt=WPA-NONE (fixed group key TKIP/CCMP). WPA-None requires
-# following network block options:
+# Note: IBSS can only be used with key_mgmt NONE (plaintext and static WEP) and
+# WPA-PSK (with proto=RSN). In addition, key_mgmt=WPA-NONE (fixed group key
+# TKIP/CCMP) is available for backwards compatibility, but its use is
+# deprecated. WPA-None requires following network block options:
 # proto=WPA, key_mgmt=WPA-NONE, pairwise=NONE, group=TKIP (or CCMP, but not
 # both), and psk must also be set.
 #
@@ -547,6 +676,12 @@ fast_reauth=1
 # bgscan="learn:<short bgscan interval in seconds>:<signal strength threshold>:
 # <long interval>[:<database file name>]"
 # bgscan="learn:30:-45:300:/etc/wpa_supplicant/network1.bgscan"
+# Explicitly disable bgscan by setting
+# bgscan=""
+#
+# This option can also be set outside of all network blocks for the bgscan
+# parameter to apply for all the networks that have no specific bgscan
+# parameter.
 #
 # proto: list of accepted protocols
 # WPA = WPA/IEEE 802.11i/D3.0
@@ -611,8 +746,16 @@ fast_reauth=1
 # bit0 (1): require dynamically generated unicast WEP key
 # bit1 (2): require dynamically generated broadcast WEP key
 #      (3 = require both keys; default)
-# Note: When using wired authentication, eapol_flags must be set to 0 for the
-# authentication to be completed successfully.
+# Note: When using wired authentication (including macsec_qca driver),
+# eapol_flags must be set to 0 for the authentication to be completed
+# successfully.
+#
+# macsec_policy: IEEE 802.1X/MACsec options
+# This determines how sessions are secured with MACsec. It is currently
+# applicable only when using the macsec_qca driver interface.
+# 0: MACsec not in use (default)
+# 1: MACsec enabled - Should secure, accept key server's advice to
+#    determine whether to use a secure session or not.
 #
 # mixed_cell: This option can be used to configure whether so called mixed
 # cells, i.e., networks that use both plaintext and encryption in the same
@@ -734,6 +877,10 @@ fast_reauth=1
 #      sertificate is only accepted if it contains this string in the subject.
 #      The subject string is in following format:
 #      /C=US/ST=CA/L=San Francisco/CN=Test AS/emailAddress=as@example.com
+#      Note: Since this is a substring match, this cannot be used securily to
+#      do a suffix match against a possible domain name in the CN entry. For
+#      such a use case, domain_suffix_match or domain_match should be used
+#      instead.
 # altsubject_match: Semicolon separated string of entries to be matched against
 #      the alternative subject name of the authentication server certificate.
 #      If this string is set, the server sertificate is only accepted if it
@@ -742,6 +889,30 @@ fast_reauth=1
 #      Example: EMAIL:server@example.com
 #      Example: DNS:server.example.com;DNS:server2.example.com
 #      Following types are supported: EMAIL, DNS, URI
+# domain_suffix_match: Constraint for server domain name. If set, this FQDN is
+#      used as a suffix match requirement for the AAAserver certificate in
+#      SubjectAltName dNSName element(s). If a matching dNSName is found, this
+#      constraint is met. If no dNSName values are present, this constraint is
+#      matched against SubjectName CN using same suffix match comparison.
+#
+#      Suffix match here means that the host/domain name is compared one label
+#      at a time starting from the top-level domain and all the labels in
+#      domain_suffix_match shall be included in the certificate. The
+#      certificate may include additional sub-level labels in addition to the
+#      required labels.
+#
+#      For example, domain_suffix_match=example.com would match
+#      test.example.com but would not match test-example.com.
+# domain_match: Constraint for server domain name
+#      If set, this FQDN is used as a full match requirement for the
+#      server certificate in SubjectAltName dNSName element(s). If a
+#      matching dNSName is found, this constraint is met. If no dNSName
+#      values are present, this constraint is matched against SubjectName CN
+#      using same full match comparison. This behavior is similar to
+#      domain_suffix_match, but has the requirement of a full match, i.e.,
+#      no subdomains or wildcard matches are allowed. Case-insensitive
+#      comparison is used, so "Example.com" matches "example.com", but would
+#      not match "test.Example.com".
 # phase1: Phase1 (outer authentication, i.e., TLS tunnel) parameters
 #      (string with field-value pairs, e.g., "peapver=0" or
 #      "peapver=1 peaplabel=1")
@@ -770,9 +941,20 @@ fast_reauth=1
 #       * 2 = require cryptobinding
 #      EAP-WSC (WPS) uses following options: pin=<Device Password> or
 #      pbc=1.
+#
+#      For wired IEEE 802.1X authentication, "allow_canned_success=1" can be
+#      used to configure a mode that allows EAP-Success (and EAP-Failure)
+#      without going through authentication step. Some switches use such
+#      sequence when forcing the port to be authorized/unauthorized or as a
+#      fallback option if the authentication server is unreachable. By default,
+#      wpa_supplicant discards such frames to protect against potential attacks
+#      by rogue devices, but this option can be used to disable that protection
+#      for cases where the server/authenticator does not need to be
+#      authenticated.
 # phase2: Phase2 (inner authentication with TLS tunnel) parameters
 #      (string with field-value pairs, e.g., "auth=MSCHAPV2" for EAP-PEAP or
-#      "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS)
+#      "autheap=MSCHAPV2 autheap=MD5" for EAP-TTLS). "mschapv2_retry=0" can be
+#      used to disable MSCHAPv2 password retry in authentication failure cases.
 #
 # TLS-based methods can use the following parameters to control TLS behavior
 # (these are normally in the phase1 parameter, but can be used also in the
@@ -791,6 +973,10 @@ fast_reauth=1
 #      EAP workarounds are disabled with eap_workarounds=0.
 #      For EAP-FAST, this must be set to 0 (or left unconfigured for the
 #      default value to be used automatically).
+# tls_disable_tlsv1_1=1 - disable use of TLSv1.1 (a workaround for AAA servers
+#      that have issues interoperating with updated TLS version)
+# tls_disable_tlsv1_2=1 - disable use of TLSv1.2 (a workaround for AAA servers
+#      that have issues interoperating with updated TLS version)
 #
 # Following certificate/private key fields are used in inner Phase2
 # authentication when using EAP-TTLS or EAP-PEAP.
@@ -804,9 +990,12 @@ fast_reauth=1
 # private_key2_passwd: Password for private key file
 # dh_file2: File path to DH/DSA parameters file (in PEM format)
 # subject_match2: Substring to be matched against the subject of the
-#      authentication server certificate.
-# altsubject_match2: Substring to be matched against the alternative subject
-#      name of the authentication server certificate.
+#      authentication server certificate. See subject_match for more details.
+# altsubject_match2: Semicolon separated string of entries to be matched
+#      against the alternative subject name of the authentication server
+#      certificate. See altsubject_match documentation for more details.
+# domain_suffix_match2: Constraint for server domain name. See
+#      domain_suffix_match for more details.
 #
 # fragment_size: Maximum EAP fragment size in bytes (default 1398).
 #      This value limits the fragment size for EAP methods that support
@@ -820,6 +1009,12 @@ fast_reauth=1
 #      1 = try to use OCSP stapling, but not require response
 #      2 = require valid OCSP stapling response
 #
+# openssl_ciphers: OpenSSL specific cipher configuration
+#      This can be used to override the global openssl_ciphers configuration
+#      parameter (see above).
+#
+# erp: Whether EAP Re-authentication Protocol (ERP) is enabled
+#
 # EAP-FAST variables:
 # pac_file: File path for the PAC entries. wpa_supplicant will need to be able
 #      to create this file and write updates to it when PAC is being
@@ -869,6 +1064,12 @@ fast_reauth=1
 # Beacon interval (default: 100 TU)
 #beacon_int=100
 
+# MAC address policy
+# 0 = use permanent MAC address
+# 1 = use random MAC address for each ESS connection
+# 2 = like 1, but maintain OUI (with local admin bit set)
+#mac_addr=0
+
 # disable_ht: Whether HT (802.11n) should be disabled.
 # 0 = HT enabled (if AP supports it)
 # 1 = HT disabled
@@ -881,6 +1082,14 @@ fast_reauth=1
 # 0 = SGI enabled (if AP supports it)
 # 1 = SGI disabled
 #
+# disable_ldpc: Whether LDPC should be disabled.
+# 0 = LDPC enabled (if AP supports it)
+# 1 = LDPC disabled
+#
+# ht40_intolerant: Whether 40 MHz intolerant should be indicated.
+# 0 = 40 MHz tolerant (default)
+# 1 = 40 MHz intolerant
+#
 # ht_mcs:  Configure allowed MCS rates.
 #  Parsed as an array of bytes, in base-16 (ascii-hex)
 # ht_mcs=""                                   // Use all available (default)
@@ -892,6 +1101,9 @@ fast_reauth=1
 # 0  = Enable MAX-AMSDU if hardware supports it.
 # 1  = Disable AMSDU
 #
+# ampdu_factor: Maximum A-MPDU Length Exponent
+# Value: 0-3, see 7.3.2.56.3 in IEEE Std 802.11n-2009.
+#
 # ampdu_density:  Allow overriding AMPDU density configuration.
 #  Treated as hint by the kernel.
 # -1 = Do not make any changes.
@@ -914,339 +1126,3 @@ fast_reauth=1
 # Example blocks:
 
 # Simple case: WPA-PSK, PSK as an ASCII passphrase, allow all valid ciphers
-network={
-       ssid="simple"
-       psk="very secret passphrase"
-       priority=5
-}
-
-# Same as previous, but request SSID-specific scanning (for APs that reject
-# broadcast SSID)
-network={
-       ssid="second ssid"
-       scan_ssid=1
-       psk="very secret passphrase"
-       priority=2
-}
-
-# Only WPA-PSK is used. Any valid cipher combination is accepted.
-network={
-       ssid="example"
-       proto=WPA
-       key_mgmt=WPA-PSK
-       pairwise=CCMP TKIP
-       group=CCMP TKIP WEP104 WEP40
-       psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb
-       priority=2
-}
-
-# WPA-Personal(PSK) with TKIP and enforcement for frequent PTK rekeying
-network={
-       ssid="example"
-       proto=WPA
-       key_mgmt=WPA-PSK
-       pairwise=TKIP
-       group=TKIP
-       psk="not so secure passphrase"
-       wpa_ptk_rekey=600
-}
-
-# Only WPA-EAP is used. Both CCMP and TKIP is accepted. An AP that used WEP104
-# or WEP40 as the group cipher will not be accepted.
-network={
-       ssid="example"
-       proto=RSN
-       key_mgmt=WPA-EAP
-       pairwise=CCMP TKIP
-       group=CCMP TKIP
-       eap=TLS
-       identity="user@example.com"
-       ca_cert="/etc/cert/ca.pem"
-       client_cert="/etc/cert/user.pem"
-       private_key="/etc/cert/user.prv"
-       private_key_passwd="password"
-       priority=1
-}
-
-# EAP-PEAP/MSCHAPv2 configuration for RADIUS servers that use the new peaplabel
-# (e.g., Radiator)
-network={
-       ssid="example"
-       key_mgmt=WPA-EAP
-       eap=PEAP
-       identity="user@example.com"
-       password="foobar"
-       ca_cert="/etc/cert/ca.pem"
-       phase1="peaplabel=1"
-       phase2="auth=MSCHAPV2"
-       priority=10
-}
-
-# EAP-TTLS/EAP-MD5-Challenge configuration with anonymous identity for the
-# unencrypted use. Real identity is sent only within an encrypted TLS tunnel.
-network={
-       ssid="example"
-       key_mgmt=WPA-EAP
-       eap=TTLS
-       identity="user@example.com"
-       anonymous_identity="anonymous@example.com"
-       password="foobar"
-       ca_cert="/etc/cert/ca.pem"
-       priority=2
-}
-
-# EAP-TTLS/MSCHAPv2 configuration with anonymous identity for the unencrypted
-# use. Real identity is sent only within an encrypted TLS tunnel.
-network={
-       ssid="example"
-       key_mgmt=WPA-EAP
-       eap=TTLS
-       identity="user@example.com"
-       anonymous_identity="anonymous@example.com"
-       password="foobar"
-       ca_cert="/etc/cert/ca.pem"
-       phase2="auth=MSCHAPV2"
-}
-
-# WPA-EAP, EAP-TTLS with different CA certificate used for outer and inner
-# authentication.
-network={
-       ssid="example"
-       key_mgmt=WPA-EAP
-       eap=TTLS
-       # Phase1 / outer authentication
-       anonymous_identity="anonymous@example.com"
-       ca_cert="/etc/cert/ca.pem"
-       # Phase 2 / inner authentication
-       phase2="autheap=TLS"
-       ca_cert2="/etc/cert/ca2.pem"
-       client_cert2="/etc/cer/user.pem"
-       private_key2="/etc/cer/user.prv"
-       private_key2_passwd="password"
-       priority=2
-}
-
-# Both WPA-PSK and WPA-EAP is accepted. Only CCMP is accepted as pairwise and
-# group cipher.
-network={
-       ssid="example"
-       bssid=00:11:22:33:44:55
-       proto=WPA RSN
-       key_mgmt=WPA-PSK WPA-EAP
-       pairwise=CCMP
-       group=CCMP
-       psk=06b4be19da289f475aa46a33cb793029d4ab3db7a23ee92382eb0106c72ac7bb
-}
-
-# Special characters in SSID, so use hex string. Default to WPA-PSK, WPA-EAP
-# and all valid ciphers.
-network={
-       ssid=00010203
-       psk=000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
-}
-
-
-# EAP-SIM with a GSM SIM or USIM
-network={
-       ssid="eap-sim-test"
-       key_mgmt=WPA-EAP
-       eap=SIM
-       pin="1234"
-       pcsc=""
-}
-
-
-# EAP-PSK
-network={
-       ssid="eap-psk-test"
-       key_mgmt=WPA-EAP
-       eap=PSK
-       anonymous_identity="eap_psk_user"
-       password=06b4be19da289f475aa46a33cb793029
-       identity="eap_psk_user@example.com"
-}
-
-
-# IEEE 802.1X/EAPOL with dynamically generated WEP keys (i.e., no WPA) using
-# EAP-TLS for authentication and key generation; require both unicast and
-# broadcast WEP keys.
-network={
-       ssid="1x-test"
-       key_mgmt=IEEE8021X
-       eap=TLS
-       identity="user@example.com"
-       ca_cert="/etc/cert/ca.pem"
-       client_cert="/etc/cert/user.pem"
-       private_key="/etc/cert/user.prv"
-       private_key_passwd="password"
-       eapol_flags=3
-}
-
-
-# LEAP with dynamic WEP keys
-network={
-       ssid="leap-example"
-       key_mgmt=IEEE8021X
-       eap=LEAP
-       identity="user"
-       password="foobar"
-}
-
-# EAP-IKEv2 using shared secrets for both server and peer authentication
-network={
-       ssid="ikev2-example"
-       key_mgmt=WPA-EAP
-       eap=IKEV2
-       identity="user"
-       password="foobar"
-}
-
-# EAP-FAST with WPA (WPA or WPA2)
-network={
-       ssid="eap-fast-test"
-       key_mgmt=WPA-EAP
-       eap=FAST
-       anonymous_identity="FAST-000102030405"
-       identity="username"
-       password="password"
-       phase1="fast_provisioning=1"
-       pac_file="/etc/wpa_supplicant.eap-fast-pac"
-}
-
-network={
-       ssid="eap-fast-test"
-       key_mgmt=WPA-EAP
-       eap=FAST
-       anonymous_identity="FAST-000102030405"
-       identity="username"
-       password="password"
-       phase1="fast_provisioning=1"
-       pac_file="blob://eap-fast-pac"
-}
-
-# Plaintext connection (no WPA, no IEEE 802.1X)
-network={
-       ssid="plaintext-test"
-       key_mgmt=NONE
-}
-
-
-# Shared WEP key connection (no WPA, no IEEE 802.1X)
-network={
-       ssid="static-wep-test"
-       key_mgmt=NONE
-       wep_key0="abcde"
-       wep_key1=0102030405
-       wep_key2="1234567890123"
-       wep_tx_keyidx=0
-       priority=5
-}
-
-
-# Shared WEP key connection (no WPA, no IEEE 802.1X) using Shared Key
-# IEEE 802.11 authentication
-network={
-       ssid="static-wep-test2"
-       key_mgmt=NONE
-       wep_key0="abcde"
-       wep_key1=0102030405
-       wep_key2="1234567890123"
-       wep_tx_keyidx=0
-       priority=5
-       auth_alg=SHARED
-}
-
-
-# IBSS/ad-hoc network with WPA-None/TKIP.
-network={
-       ssid="test adhoc"
-       mode=1
-       frequency=2412
-       proto=WPA
-       key_mgmt=WPA-NONE
-       pairwise=NONE
-       group=TKIP
-       psk="secret passphrase"
-}
-
-
-# Catch all example that allows more or less all configuration modes
-network={
-       ssid="example"
-       scan_ssid=1
-       key_mgmt=WPA-EAP WPA-PSK IEEE8021X NONE
-       pairwise=CCMP TKIP
-       group=CCMP TKIP WEP104 WEP40
-       psk="very secret passphrase"
-       eap=TTLS PEAP TLS
-       identity="user@example.com"
-       password="foobar"
-       ca_cert="/etc/cert/ca.pem"
-       client_cert="/etc/cert/user.pem"
-       private_key="/etc/cert/user.prv"
-       private_key_passwd="password"
-       phase1="peaplabel=0"
-}
-
-# Example of EAP-TLS with smartcard (openssl engine)
-network={
-       ssid="example"
-       key_mgmt=WPA-EAP
-       eap=TLS
-       proto=RSN
-       pairwise=CCMP TKIP
-       group=CCMP TKIP
-       identity="user@example.com"
-       ca_cert="/etc/cert/ca.pem"
-       client_cert="/etc/cert/user.pem"
-
-       engine=1
-
-       # The engine configured here must be available. Look at
-       # OpenSSL engine support in the global section.
-       # The key available through the engine must be the private key
-       # matching the client certificate configured above.
-
-       # use the opensc engine
-       #engine_id="opensc"
-       #key_id="45"
-
-       # use the pkcs11 engine
-       engine_id="pkcs11"
-       key_id="id_45"
-
-       # Optional PIN configuration; this can be left out and PIN will be
-       # asked through the control interface
-       pin="1234"
-}
-
-# Example configuration showing how to use an inlined blob as a CA certificate
-# data instead of using external file
-network={
-       ssid="example"
-       key_mgmt=WPA-EAP
-       eap=TTLS
-       identity="user@example.com"
-       anonymous_identity="anonymous@example.com"
-       password="foobar"
-       ca_cert="blob://exampleblob"
-       priority=20
-}
-
-blob-base64-exampleblob={
-SGVsbG8gV29ybGQhCg==
-}
-
-
-# Wildcard match for SSID (plaintext APs only). This example select any
-# open AP regardless of its SSID.
-network={
-       key_mgmt=NONE
-}
-
-
-# Example config file that will only scan on channel 36.
-freq_list=5180
-network={
-       key_mgmt=NONE
-}
index 659bc7b..26ff216 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant - Internal definitions
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
 #include "utils/list.h"
 #include "common/defs.h"
 #include "common/sae.h"
+#include "common/wpa_ctrl.h"
+#include "wps/wps_defs.h"
 #include "config_ssid.h"
+#include "wmm_ac.h"
 
 extern const char *wpa_supplicant_version;
 extern const char *wpa_supplicant_license;
@@ -63,6 +66,17 @@ struct wpa_interface {
         */
        const char *confanother;
 
+#ifdef CONFIG_P2P
+       /**
+        * conf_p2p_dev - Configuration file used to hold the
+        * P2P Device configuration parameters.
+        *
+        * This can also be %NULL. In such a case, if a P2P Device dedicated
+        * interfaces is created, the main configuration file will be used.
+        */
+       const char *conf_p2p_dev;
+#endif /* CONFIG_P2P */
+
        /**
         * ctrl_interface - Control interface parameter
         *
@@ -227,12 +241,6 @@ struct p2p_srv_upnp {
        char *service;
 };
 
-struct wpa_freq_range {
-       unsigned int min;
-       unsigned int max;
-};
-
-
 /**
  * struct wpa_global - Internal, global data for all %wpa_supplicant interfaces
  *
@@ -252,26 +260,91 @@ struct wpa_global {
        struct wpa_supplicant *p2p_group_formation;
        struct wpa_supplicant *p2p_invite_group;
        u8 p2p_dev_addr[ETH_ALEN];
+       struct os_reltime p2p_go_wait_client;
        struct dl_list p2p_srv_bonjour; /* struct p2p_srv_bonjour */
        struct dl_list p2p_srv_upnp; /* struct p2p_srv_upnp */
        int p2p_disabled;
        int cross_connection;
-       struct wpa_freq_range *p2p_disallow_freq;
-       unsigned int num_p2p_disallow_freq;
+       struct wpa_freq_range_list p2p_disallow_freq;
+       struct wpa_freq_range_list p2p_go_avoid_freq;
        enum wpa_conc_pref {
                WPA_CONC_PREF_NOT_SET,
                WPA_CONC_PREF_STA,
                WPA_CONC_PREF_P2P
        } conc_pref;
-       unsigned int p2p_cb_on_scan_complete:1;
+       unsigned int p2p_per_sta_psk:1;
+       unsigned int p2p_fail_on_wps_complete:1;
+       unsigned int p2p_24ghz_social_channels:1;
+       unsigned int pending_p2ps_group:1;
+       unsigned int pending_group_iface_for_p2ps:1;
 
 #ifdef CONFIG_WIFI_DISPLAY
        int wifi_display;
 #define MAX_WFD_SUBELEMS 10
        struct wpabuf *wfd_subelem[MAX_WFD_SUBELEMS];
 #endif /* CONFIG_WIFI_DISPLAY */
+
+       struct psk_list_entry *add_psk; /* From group formation */
+};
+
+
+/**
+ * struct wpa_radio - Internal data for per-radio information
+ *
+ * This structure is used to share data about configured interfaces
+ * (struct wpa_supplicant) that share the same physical radio, e.g., to allow
+ * better coordination of offchannel operations.
+ */
+struct wpa_radio {
+       char name[16]; /* from driver_ops get_radio_name() or empty if not
+                       * available */
+       unsigned int external_scan_running:1;
+       struct dl_list ifaces; /* struct wpa_supplicant::radio_list entries */
+       struct dl_list work; /* struct wpa_radio_work::list entries */
+};
+
+/**
+ * struct wpa_radio_work - Radio work item
+ */
+struct wpa_radio_work {
+       struct dl_list list;
+       unsigned int freq; /* known frequency (MHz) or 0 for multiple/unknown */
+       const char *type;
+       struct wpa_supplicant *wpa_s;
+       void (*cb)(struct wpa_radio_work *work, int deinit);
+       void *ctx;
+       unsigned int started:1;
+       struct os_reltime time;
 };
 
+int radio_add_work(struct wpa_supplicant *wpa_s, unsigned int freq,
+                  const char *type, int next,
+                  void (*cb)(struct wpa_radio_work *work, int deinit),
+                  void *ctx);
+void radio_work_done(struct wpa_radio_work *work);
+void radio_remove_works(struct wpa_supplicant *wpa_s,
+                       const char *type, int remove_all);
+void radio_work_check_next(struct wpa_supplicant *wpa_s);
+struct wpa_radio_work *
+radio_work_pending(struct wpa_supplicant *wpa_s, const char *type);
+
+struct wpa_connect_work {
+       unsigned int sme:1;
+       unsigned int bss_removed:1;
+       struct wpa_bss *bss;
+       struct wpa_ssid *ssid;
+};
+
+int wpas_valid_bss_ssid(struct wpa_supplicant *wpa_s, struct wpa_bss *test_bss,
+                       struct wpa_ssid *test_ssid);
+void wpas_connect_work_free(struct wpa_connect_work *cwork);
+void wpas_connect_work_done(struct wpa_supplicant *wpa_s);
+
+struct wpa_external_work {
+       unsigned int id;
+       char type[100];
+       unsigned int timeout;
+};
 
 /**
  * offchannel_send_action_result - Result of offchannel send Action frame
@@ -292,7 +365,7 @@ struct wps_ap_info {
                WPS_AP_SEL_REG_OUR
        } type;
        unsigned int tries;
-       struct os_time last_attempt;
+       struct os_reltime last_attempt;
 };
 
 struct wpa_ssid_value {
@@ -300,6 +373,44 @@ struct wpa_ssid_value {
        size_t ssid_len;
 };
 
+#define WPA_FREQ_USED_BY_INFRA_STATION BIT(0)
+#define WPA_FREQ_USED_BY_P2P_CLIENT BIT(1)
+
+struct wpa_used_freq_data {
+       int freq;
+       unsigned int flags;
+};
+
+#define RRM_NEIGHBOR_REPORT_TIMEOUT 1 /* 1 second for AP to send a report */
+
+/*
+ * struct rrm_data - Data used for managing RRM features
+ */
+struct rrm_data {
+       /* rrm_used - indication regarding the current connection */
+       unsigned int rrm_used:1;
+
+       /*
+        * notify_neighbor_rep - Callback for notifying report requester
+        */
+       void (*notify_neighbor_rep)(void *ctx, struct wpabuf *neighbor_rep);
+
+       /*
+        * neighbor_rep_cb_ctx - Callback context
+        * Received in the callback registration, and sent to the callback
+        * function as a parameter.
+        */
+       void *neighbor_rep_cb_ctx;
+
+       /* next_neighbor_rep_token - Next request's dialog token */
+       u8 next_neighbor_rep_token;
+};
+
+enum wpa_supplicant_test_failure {
+       WPAS_TEST_FAILURE_NONE,
+       WPAS_TEST_FAILURE_SCAN_TRIGGER,
+};
+
 /**
  * struct wpa_supplicant - Internal data for wpa_supplicant interface
  *
@@ -310,11 +421,14 @@ struct wpa_ssid_value {
  */
 struct wpa_supplicant {
        struct wpa_global *global;
+       struct wpa_radio *radio; /* shared radio context */
+       struct dl_list radio_list; /* list head: struct wpa_radio::ifaces */
        struct wpa_supplicant *parent;
        struct wpa_supplicant *next;
        struct l2_packet_data *l2;
        struct l2_packet_data *l2_br;
        unsigned char own_addr[ETH_ALEN];
+       unsigned char perm_addr[ETH_ALEN];
        char ifname[100];
 #ifdef CONFIG_CTRL_IFACE_DBUS
        char *dbus_path;
@@ -330,16 +444,19 @@ struct wpa_supplicant {
 
        char *confname;
        char *confanother;
+
        struct wpa_config *conf;
        int countermeasures;
-       os_time_t last_michael_mic_error;
+       struct os_reltime last_michael_mic_error;
        u8 bssid[ETH_ALEN];
        u8 pending_bssid[ETH_ALEN]; /* If wpa_state == WPA_ASSOCIATING, this
                                     * field contains the target BSSID. */
        int reassociate; /* reassociation requested */
+       int reassoc_same_bss; /* reassociating to the same bss */
        int disconnected; /* all connections disabled; i.e., do no reassociate
                           * before this has been cleared */
        struct wpa_ssid *current_ssid;
+       struct wpa_ssid *last_ssid;
        struct wpa_bss *current_bss;
        int ap_ies_from_associnfo;
        unsigned int assoc_freq;
@@ -364,6 +481,9 @@ struct wpa_supplicant {
 
        enum { WPA_SETBAND_AUTO, WPA_SETBAND_5G, WPA_SETBAND_2G } setband;
 
+       /* Preferred network for the next connection attempt */
+       struct wpa_ssid *next_ssid;
+
        /* previous scan was wildcard when interleaving between
         * wildcard scans and specific SSID scan when max_ssids=1 */
        int prev_scan_wildcard;
@@ -396,8 +516,7 @@ struct wpa_supplicant {
        struct wpa_bss **last_scan_res;
        unsigned int last_scan_res_used;
        unsigned int last_scan_res_size;
-       int last_scan_full;
-       struct os_time last_scan;
+       struct os_reltime last_scan;
 
        struct wpa_driver_ops *driver;
        int interface_removed; /* whether the network interface has been
@@ -408,12 +527,10 @@ struct wpa_supplicant {
        struct ctrl_iface_priv *ctrl_iface;
 
        enum wpa_states wpa_state;
+       struct wpa_radio_work *scan_work;
        int scanning;
        int sched_scanning;
        int new_connection;
-#if defined(TIZEN_EXT)
-       int reassociated_connection;
-#endif /* TIZEN_EXT */
 
        int eapol_received; /* number of EAPOL packets received after the
                             * previous association event */
@@ -424,7 +541,8 @@ struct wpa_supplicant {
 
        unsigned char last_eapol_src[ETH_ALEN];
 
-       int keys_cleared;
+       unsigned int keys_cleared; /* bitfield of key indexes that the driver is
+                                   * known not to be configured with a key */
 
        struct wpa_blacklist *blacklist;
 
@@ -469,17 +587,32 @@ struct wpa_supplicant {
                 * to be run.
                 */
                MANUAL_SCAN_REQ
-       } scan_req;
-       struct os_time scan_trigger_time;
+       } scan_req, last_scan_req;
+       enum wpa_states scan_prev_wpa_state;
+       struct os_reltime scan_trigger_time, scan_start_time;
        int scan_runs; /* number of scan runs since WPS was started */
        int *next_scan_freqs;
+       int *manual_scan_freqs;
+       int *manual_sched_scan_freqs;
+       unsigned int manual_scan_passive:1;
+       unsigned int manual_scan_use_id:1;
+       unsigned int manual_scan_only_new:1;
+       unsigned int own_scan_requested:1;
+       unsigned int own_scan_running:1;
+       unsigned int clear_driver_scan_cache:1;
+       unsigned int manual_scan_id;
        int scan_interval; /* time in sec between scans to find suitable AP */
        int normal_scans; /* normal scans run before sched_scan */
        int scan_for_connection; /* whether the scan request was triggered for
                                  * finding a connection */
+#define MAX_SCAN_ID 16
+       int scan_id[MAX_SCAN_ID];
+       unsigned int scan_id_count;
 
-       unsigned int drv_flags;
+       u64 drv_flags;
        unsigned int drv_enc;
+       unsigned int drv_smps_modes;
+       unsigned int drv_rrm_flags;
 
        /*
         * A bitmap of supported protocols for probe response offload. See
@@ -505,12 +638,19 @@ struct wpa_supplicant {
        struct wps_context *wps;
        int wps_success; /* WPS success event received */
        struct wps_er *wps_er;
+       unsigned int wps_run;
        int blacklist_cleared;
 
        struct wpabuf *pending_eapol_rx;
-       struct os_time pending_eapol_rx_time;
+       struct os_reltime pending_eapol_rx_time;
        u8 pending_eapol_rx_src[ETH_ALEN];
        unsigned int last_eapol_matches_bssid:1;
+       unsigned int eap_expected_failure:1;
+       unsigned int reattach:1; /* reassociation to the same BSS requested */
+       unsigned int mac_addr_changed:1;
+
+       struct os_reltime last_mac_addr_change;
+       int last_mac_addr_style;
 
        struct ibss_rsn *ibss_rsn;
 
@@ -542,7 +682,11 @@ struct wpa_supplicant {
                u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN *
                                        * sa_query_count octets of pending
                                        * SA Query transaction identifiers */
-               struct os_time sa_query_start;
+               struct os_reltime sa_query_start;
+               struct os_reltime last_unprot_disconnect;
+               enum { HT_SEC_CHAN_UNKNOWN,
+                      HT_SEC_CHAN_ABOVE,
+                      HT_SEC_CHAN_BELOW } ht_sec_chan;
                u8 sched_obss_scan;
                u16 obss_scan_int;
                u16 bss_max_idle_period;
@@ -550,6 +694,7 @@ struct wpa_supplicant {
                struct sae_data sae;
                struct wpabuf *sae_token;
                int sae_group_index;
+               unsigned int sae_pmksa_caching:1;
 #endif /* CONFIG_SAE */
        } sme;
 #endif /* CONFIG_SME */
@@ -561,6 +706,15 @@ struct wpa_supplicant {
        void *ap_configured_cb_data;
 #endif /* CONFIG_AP */
 
+       struct hostapd_iface *ifmsh;
+#ifdef CONFIG_MESH
+       struct mesh_rsn *mesh_rsn;
+       int mesh_if_idx;
+       unsigned int mesh_if_created:1;
+       unsigned int mesh_ht_enabled:1;
+       int mesh_auth_block_duration; /* sec */
+#endif /* CONFIG_MESH */
+
        unsigned int off_channel_freq;
        struct wpabuf *pending_action_tx;
        u8 pending_action_src[ETH_ALEN];
@@ -569,6 +723,7 @@ struct wpa_supplicant {
        unsigned int pending_action_freq;
        int pending_action_no_cck;
        int pending_action_without_roc;
+       unsigned int pending_action_tx_done:1;
        void (*pending_action_tx_status_cb)(struct wpa_supplicant *wpa_s,
                                            unsigned int freq, const u8 *dst,
                                            const u8 *src, const u8 *bssid,
@@ -581,6 +736,7 @@ struct wpa_supplicant {
        int p2p_mgmt;
 
 #ifdef CONFIG_P2P
+       struct wpa_supplicant *p2p_dev;
        struct p2p_go_neg_results *go_params;
        int create_p2p_iface;
        u8 pending_interface_addr[ETH_ALEN];
@@ -602,6 +758,8 @@ struct wpa_supplicant {
        u8 p2p_auth_invite[ETH_ALEN];
        int p2p_sd_over_ctrl_iface;
        int p2p_in_provisioning;
+       int p2p_in_invitation;
+       int p2p_invite_go_freq;
        int pending_invite_ssid_id;
        int show_group_started;
        u8 go_dev_addr[ETH_ALEN];
@@ -609,12 +767,14 @@ struct wpa_supplicant {
        u8 pending_join_iface_addr[ETH_ALEN];
        u8 pending_join_dev_addr[ETH_ALEN];
        int pending_join_wps_method;
+       u8 p2p_join_ssid[32];
+       size_t p2p_join_ssid_len;
        int p2p_join_scan_count;
        int auto_pd_scan_retry;
        int force_long_sd;
        u16 pending_pd_config_methods;
        enum {
-               NORMAL_PD, AUTO_PD_GO_NEG, AUTO_PD_JOIN
+               NORMAL_PD, AUTO_PD_GO_NEG, AUTO_PD_JOIN, AUTO_PD_ASP
        } pending_pd_use;
 
        /*
@@ -638,19 +798,42 @@ struct wpa_supplicant {
         */
        char cross_connect_uplink[100];
 
-       unsigned int sta_scan_pending:1;
        unsigned int p2p_auto_join:1;
        unsigned int p2p_auto_pd:1;
        unsigned int p2p_persistent_group:1;
        unsigned int p2p_fallback_to_go_neg:1;
        unsigned int p2p_pd_before_go_neg:1;
        unsigned int p2p_go_ht40:1;
+       unsigned int p2p_go_vht:1;
        unsigned int user_initiated_pd:1;
+       unsigned int p2p_go_group_formation_completed:1;
+       unsigned int group_formation_reported:1;
+       unsigned int waiting_presence_resp;
+       int p2p_first_connection_timeout;
+       unsigned int p2p_nfc_tag_enabled:1;
+       unsigned int p2p_peer_oob_pk_hash_known:1;
+       unsigned int p2p_disable_ip_addr_req:1;
+       unsigned int p2ps_join_addr_valid:1;
        int p2p_persistent_go_freq;
        int p2p_persistent_id;
        int p2p_go_intent;
        int p2p_connect_freq;
-       struct os_time p2p_auto_started;
+       struct os_reltime p2p_auto_started;
+       struct wpa_ssid *p2p_last_4way_hs_fail;
+       struct wpa_radio_work *p2p_scan_work;
+       struct wpa_radio_work *p2p_listen_work;
+       struct wpa_radio_work *p2p_send_action_work;
+
+       u16 p2p_oob_dev_pw_id; /* OOB Device Password Id for group formation */
+       struct wpabuf *p2p_oob_dev_pw; /* OOB Device Password for group
+                                       * formation */
+       u8 p2p_peer_oob_pubkey_hash[WPS_OOB_PUBKEY_HASH_LEN];
+       u8 p2p_ip_addr_info[3 * 4];
+
+       /* group common frequencies */
+       int *p2p_group_common_freqs;
+       unsigned int p2p_group_common_freqs_num;
+       u8 p2ps_join_addr[ETH_ALEN];
 #endif /* CONFIG_P2P */
 
        struct wpa_ssid *bgscan_ssid;
@@ -670,7 +853,6 @@ struct wpa_supplicant {
        int after_wps;
        int known_wps_freq;
        unsigned int wps_freq;
-       u16 wps_ap_channel;
        int wps_fragment_size;
        int auto_reconnect_disabled;
 
@@ -686,8 +868,18 @@ struct wpa_supplicant {
        unsigned int network_select:1;
        unsigned int auto_select:1;
        unsigned int auto_network_select:1;
+       unsigned int interworking_fast_assoc_tried:1;
        unsigned int fetch_all_anqp:1;
+       unsigned int fetch_osu_info:1;
+       unsigned int fetch_osu_waiting_scan:1;
+       unsigned int fetch_osu_icon_in_progress:1;
        struct wpa_bss *interworking_gas_bss;
+       unsigned int osu_icon_id;
+       struct osu_provider *osu_prov;
+       size_t osu_prov_count;
+       struct os_reltime osu_icon_fetch_start;
+       unsigned int num_osu_scans;
+       unsigned int num_prov_found;
 #endif /* CONFIG_INTERWORKING */
        unsigned int drv_capa_known;
 
@@ -696,19 +888,48 @@ struct wpa_supplicant {
                u16 num_modes;
                u16 flags;
        } hw;
+       enum local_hw_capab {
+               CAPAB_NO_HT_VHT,
+               CAPAB_HT,
+               CAPAB_HT40,
+               CAPAB_VHT,
+       } hw_capab;
+#ifdef CONFIG_MACSEC
+       struct ieee802_1x_kay *kay;
+#endif /* CONFIG_MACSEC */
 
        int pno;
+       int pno_sched_pending;
 
        /* WLAN_REASON_* reason codes. Negative if locally generated. */
        int disconnect_reason;
 
        struct ext_password_data *ext_pw;
 
-       struct wpabuf *last_gas_resp;
-       u8 last_gas_addr[ETH_ALEN];
-       u8 last_gas_dialog_token;
+       struct wpabuf *last_gas_resp, *prev_gas_resp;
+       u8 last_gas_addr[ETH_ALEN], prev_gas_addr[ETH_ALEN];
+       u8 last_gas_dialog_token, prev_gas_dialog_token;
 
        unsigned int no_keep_alive:1;
+       unsigned int ext_mgmt_frame_handling:1;
+       unsigned int ext_eapol_frame_io:1;
+       unsigned int wmm_ac_supported:1;
+       unsigned int ext_work_in_progress:1;
+       unsigned int own_disconnect_req:1;
+
+#define MAC_ADDR_RAND_SCAN       BIT(0)
+#define MAC_ADDR_RAND_SCHED_SCAN BIT(1)
+#define MAC_ADDR_RAND_PNO        BIT(2)
+#define MAC_ADDR_RAND_ALL        (MAC_ADDR_RAND_SCAN | \
+                                 MAC_ADDR_RAND_SCHED_SCAN | \
+                                 MAC_ADDR_RAND_PNO)
+       unsigned int mac_addr_rand_supported;
+       unsigned int mac_addr_rand_enable;
+
+       /* MAC Address followed by mask (2 * ETH_ALEN) */
+       u8 *mac_addr_scan;
+       u8 *mac_addr_sched_scan;
+       u8 *mac_addr_pno;
 
 #ifdef CONFIG_WNM
        u8 wnm_dialog_token;
@@ -716,15 +937,38 @@ struct wpa_supplicant {
        u8 wnm_num_neighbor_report;
        u8 wnm_mode;
        u16 wnm_dissoc_timer;
-       u8 wnm_validity_interval;
        u8 wnm_bss_termination_duration[12];
        struct neighbor_report *wnm_neighbor_report_elements;
+       struct os_reltime wnm_cand_valid_until;
+       u8 wnm_cand_from_bss[ETH_ALEN];
 #endif /* CONFIG_WNM */
 
 #ifdef CONFIG_TESTING_GET_GTK
        u8 last_gtk[32];
        size_t last_gtk_len;
 #endif /* CONFIG_TESTING_GET_GTK */
+
+       unsigned int num_multichan_concurrent;
+       struct wpa_radio_work *connect_work;
+
+       unsigned int ext_work_id;
+
+       struct wpabuf *vendor_elem[NUM_VENDOR_ELEM_FRAMES];
+
+#ifdef CONFIG_TESTING_OPTIONS
+       struct l2_packet_data *l2_test;
+       unsigned int extra_roc_dur;
+       enum wpa_supplicant_test_failure test_failure;
+#endif /* CONFIG_TESTING_OPTIONS */
+
+       struct wmm_ac_assoc_data *wmm_ac_assoc_info;
+       struct wmm_tspec_element *tspecs[WMM_AC_NUM][TS_DIR_IDX_COUNT];
+       struct wmm_ac_addts_request *addts_request;
+       u8 wmm_ac_last_dialog_token;
+       struct wmm_tspec_element *last_tspecs;
+       u8 last_tspecs_count;
+
+       struct rrm_data rrm;
 };
 
 
@@ -765,10 +1009,6 @@ const char * wpa_supplicant_get_eap_mode(struct wpa_supplicant *wpa_s);
 void wpa_supplicant_cancel_auth_timeout(struct wpa_supplicant *wpa_s);
 void wpa_supplicant_deauthenticate(struct wpa_supplicant *wpa_s,
                                   int reason_code);
-#ifdef TIZEN_EXT
-void wpa_supplicant_disassociate(struct wpa_supplicant *wpa_s,
-                                int reason_code);
-#endif /* TIZEN_EXT */
 
 void wpa_supplicant_enable_network(struct wpa_supplicant *wpa_s,
                                   struct wpa_ssid *ssid);
@@ -776,6 +1016,9 @@ void wpa_supplicant_disable_network(struct wpa_supplicant *wpa_s,
                                    struct wpa_ssid *ssid);
 void wpa_supplicant_select_network(struct wpa_supplicant *wpa_s,
                                   struct wpa_ssid *ssid);
+int wpas_set_pkcs11_engine_and_module_path(struct wpa_supplicant *wpa_s,
+                                          const char *pkcs11_engine_path,
+                                          const char *pkcs11_module_path);
 int wpa_supplicant_set_ap_scan(struct wpa_supplicant *wpa_s,
                               int ap_scan);
 int wpa_supplicant_set_bss_expiration_age(struct wpa_supplicant *wpa_s,
@@ -792,7 +1035,8 @@ void free_hw_features(struct wpa_supplicant *wpa_s);
 void wpa_show_license(void);
 
 struct wpa_supplicant * wpa_supplicant_add_iface(struct wpa_global *global,
-                                                struct wpa_interface *iface);
+                                                struct wpa_interface *iface,
+                                                struct wpa_supplicant *parent);
 int wpa_supplicant_remove_iface(struct wpa_global *global,
                                struct wpa_supplicant *wpa_s,
                                int terminate);
@@ -807,21 +1051,35 @@ int wpa_supplicant_scard_init(struct wpa_supplicant *wpa_s,
 void wpa_supplicant_terminate_proc(struct wpa_global *global);
 void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr,
                             const u8 *buf, size_t len);
-enum wpa_key_mgmt key_mgmt2driver(int key_mgmt);
 void wpa_supplicant_update_config(struct wpa_supplicant *wpa_s);
 void wpa_supplicant_clear_status(struct wpa_supplicant *wpa_s);
 void wpas_connection_failed(struct wpa_supplicant *wpa_s, const u8 *bssid);
 int wpas_driver_bss_selection(struct wpa_supplicant *wpa_s);
 int wpas_is_p2p_prioritized(struct wpa_supplicant *wpa_s);
-void wpas_auth_failed(struct wpa_supplicant *wpa_s);
+void wpas_auth_failed(struct wpa_supplicant *wpa_s, char *reason);
 void wpas_clear_temp_disabled(struct wpa_supplicant *wpa_s,
                              struct wpa_ssid *ssid, int clear_failures);
 int disallowed_bssid(struct wpa_supplicant *wpa_s, const u8 *bssid);
 int disallowed_ssid(struct wpa_supplicant *wpa_s, const u8 *ssid,
                    size_t ssid_len);
 void wpas_request_connection(struct wpa_supplicant *wpa_s);
-int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf);
-int wpas_wpa_is_in_progress(struct wpa_supplicant *wpa_s);
+int wpas_build_ext_capab(struct wpa_supplicant *wpa_s, u8 *buf, size_t buflen);
+int wpas_update_random_addr(struct wpa_supplicant *wpa_s, int style);
+int wpas_update_random_addr_disassoc(struct wpa_supplicant *wpa_s);
+void add_freq(int *freqs, int *num_freqs, int freq);
+
+void wpas_rrm_reset(struct wpa_supplicant *wpa_s);
+void wpas_rrm_process_neighbor_rep(struct wpa_supplicant *wpa_s,
+                                  const u8 *report, size_t report_len);
+int wpas_rrm_send_neighbor_rep_request(struct wpa_supplicant *wpa_s,
+                                      const struct wpa_ssid *ssid,
+                                      void (*cb)(void *ctx,
+                                                 struct wpabuf *neighbor_rep),
+                                      void *cb_ctx);
+void wpas_rrm_handle_link_measurement_request(struct wpa_supplicant *wpa_s,
+                                             const u8 *src,
+                                             const u8 *frame, size_t len,
+                                             int rssi);
 
 /**
  * wpa_supplicant_ctrl_iface_ctrl_rsp_handle - Handle a control response
@@ -838,6 +1096,10 @@ int wpa_supplicant_ctrl_iface_ctrl_rsp_handle(struct wpa_supplicant *wpa_s,
                                              const char *field,
                                              const char *value);
 
+void ibss_mesh_setup_freq(struct wpa_supplicant *wpa_s,
+                         const struct wpa_ssid *ssid,
+                         struct hostapd_freq_params *freq);
+
 /* events.c */
 void wpa_supplicant_mark_disassoc(struct wpa_supplicant *wpa_s);
 int wpa_supplicant_connect(struct wpa_supplicant *wpa_s,
@@ -864,7 +1126,18 @@ static inline int network_is_persistent_group(struct wpa_ssid *ssid)
 }
 
 int wpas_network_disabled(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
+int wpas_get_ssid_pmf(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid);
 
 int wpas_init_ext_pw(struct wpa_supplicant *wpa_s);
 
+void dump_freq_data(struct wpa_supplicant *wpa_s, const char *title,
+                   struct wpa_used_freq_data *freqs_data,
+                   unsigned int len);
+
+int get_shared_radio_freqs_data(struct wpa_supplicant *wpa_s,
+                               struct wpa_used_freq_data *freqs_data,
+                               unsigned int len);
+int get_shared_radio_freqs(struct wpa_supplicant *wpa_s,
+                          int *freq_array, unsigned int len);
+
 #endif /* WPA_SUPPLICANT_I_H */
index a08eb33..f3f2a64 100644 (file)
@@ -1,6 +1,6 @@
 ##### wpa_supplicant configuration file template #####
 update_config=1
-ctrl_interface=wlan0
 eapol_version=1
 ap_scan=1
 fast_reauth=1
+pmf=1
index 61a42bd..1bb82ba 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant - Glue code to setup EAPOL and RSN modules
- * Copyright (c) 2003-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -26,6 +26,7 @@
 #include "bss.h"
 #include "scan.h"
 #include "notify.h"
+#include "wpas_kay.h"
 
 
 #ifndef CONFIG_NO_CONFIG_BLOBS
@@ -95,11 +96,26 @@ static u8 * wpa_alloc_eapol(const struct wpa_supplicant *wpa_s, u8 type,
 static int wpa_ether_send(struct wpa_supplicant *wpa_s, const u8 *dest,
                          u16 proto, const u8 *buf, size_t len)
 {
+#ifdef CONFIG_TESTING_OPTIONS
+       if (wpa_s->ext_eapol_frame_io && proto == ETH_P_EAPOL) {
+               size_t hex_len = 2 * len + 1;
+               char *hex = os_malloc(hex_len);
+
+               if (hex == NULL)
+                       return -1;
+               wpa_snprintf_hex(hex, hex_len, buf, len);
+               wpa_msg(wpa_s, MSG_INFO, "EAPOL-TX " MACSTR " %s",
+                       MAC2STR(dest), hex);
+               os_free(hex);
+               return 0;
+       }
+#endif /* CONFIG_TESTING_OPTIONS */
+
        if (wpa_s->l2) {
                return l2_packet_send(wpa_s->l2, dest, proto, buf, len);
        }
 
-       return wpa_drv_send_eapol(wpa_s, dest, proto, buf, len);
+       return -1;
 }
 #endif /* IEEE8021X_EAPOL || !CONFIG_NO_WPA */
 
@@ -141,11 +157,29 @@ static int wpa_supplicant_eapol_send(void *ctx, int type, const u8 *buf,
 
        if (pmksa_cache_get_current(wpa_s->wpa) &&
            type == IEEE802_1X_TYPE_EAPOL_START) {
-               /* Trying to use PMKSA caching - do not send EAPOL-Start frames
-                * since they will trigger full EAPOL authentication. */
-               wpa_printf(MSG_DEBUG, "RSN: PMKSA caching - do not send "
-                          "EAPOL-Start");
-               return -1;
+               /*
+                * We were trying to use PMKSA caching and sending EAPOL-Start
+                * would abort that and trigger full EAPOL authentication.
+                * However, we've already waited for the AP/Authenticator to
+                * start 4-way handshake or EAP authentication, and apparently
+                * it has not done so since the startWhen timer has reached zero
+                * to get the state machine sending EAPOL-Start. This is not
+                * really supposed to happen, but an interoperability issue with
+                * a deployed AP has been identified where the connection fails
+                * due to that AP failing to operate correctly if PMKID is
+                * included in the Association Request frame. To work around
+                * this, assume PMKSA caching failed and try to initiate full
+                * EAP authentication.
+                */
+               if (!wpa_s->current_ssid ||
+                   wpa_s->current_ssid->eap_workaround) {
+                       wpa_printf(MSG_DEBUG,
+                                  "RSN: Timeout on waiting for the AP to initiate 4-way handshake for PMKSA caching or EAP authentication - try to force it to start EAP authentication");
+               } else {
+                       wpa_printf(MSG_DEBUG,
+                                  "RSN: PMKSA caching - do not send EAPOL-Start");
+                       return -1;
+               }
        }
 
        if (is_zero_ether_addr(wpa_s->bssid)) {
@@ -216,29 +250,50 @@ static void wpa_supplicant_aborted_cached(void *ctx)
 }
 
 
-static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol, int success,
+static const char * result_str(enum eapol_supp_result result)
+{
+       switch (result) {
+       case EAPOL_SUPP_RESULT_FAILURE:
+               return "FAILURE";
+       case EAPOL_SUPP_RESULT_SUCCESS:
+               return "SUCCESS";
+       case EAPOL_SUPP_RESULT_EXPECTED_FAILURE:
+               return "EXPECTED_FAILURE";
+       }
+       return "?";
+}
+
+
+static void wpa_supplicant_eapol_cb(struct eapol_sm *eapol,
+                                   enum eapol_supp_result result,
                                    void *ctx)
 {
        struct wpa_supplicant *wpa_s = ctx;
        int res, pmk_len;
        u8 pmk[PMK_LEN];
 
-       wpa_printf(MSG_DEBUG, "EAPOL authentication completed %ssuccessfully",
-                  success ? "" : "un");
+       wpa_printf(MSG_DEBUG, "EAPOL authentication completed - result=%s",
+                  result_str(result));
 
        if (wpas_wps_eapol_cb(wpa_s) > 0)
                return;
 
-       if (!success) {
+       wpa_s->eap_expected_failure = result ==
+               EAPOL_SUPP_RESULT_EXPECTED_FAILURE;
+
+       if (result != EAPOL_SUPP_RESULT_SUCCESS) {
                /*
                 * Make sure we do not get stuck here waiting for long EAPOL
                 * timeout if the AP does not disconnect in case of
                 * authentication failure.
                 */
                wpa_supplicant_req_auth_timeout(wpa_s, 2, 0);
+       } else {
+               ieee802_1x_notify_create_actor(wpa_s, wpa_s->last_eapol_src);
        }
 
-       if (!success || !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE))
+       if (result != EAPOL_SUPP_RESULT_SUCCESS ||
+           !(wpa_s->drv_flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE))
                return;
 
        if (!wpa_key_mgmt_wpa_ieee8021x(wpa_s->key_mgmt))
@@ -488,7 +543,44 @@ static int wpa_supplicant_send_ft_action(void *ctx, u8 action,
                                         const u8 *ies, size_t ies_len)
 {
        struct wpa_supplicant *wpa_s = ctx;
-       return wpa_drv_send_ft_action(wpa_s, action, target_ap, ies, ies_len);
+       int ret;
+       u8 *data, *pos;
+       size_t data_len;
+
+       if (action != 1) {
+               wpa_printf(MSG_ERROR, "Unsupported send_ft_action action %d",
+                          action);
+               return -1;
+       }
+
+       /*
+        * Action frame payload:
+        * Category[1] = 6 (Fast BSS Transition)
+        * Action[1] = 1 (Fast BSS Transition Request)
+        * STA Address
+        * Target AP Address
+        * FT IEs
+        */
+
+       data_len = 2 + 2 * ETH_ALEN + ies_len;
+       data = os_malloc(data_len);
+       if (data == NULL)
+               return -1;
+       pos = data;
+       *pos++ = 0x06; /* FT Action category */
+       *pos++ = action;
+       os_memcpy(pos, wpa_s->own_addr, ETH_ALEN);
+       pos += ETH_ALEN;
+       os_memcpy(pos, target_ap, ETH_ALEN);
+       pos += ETH_ALEN;
+       os_memcpy(pos, ies, ies_len);
+
+       ret = wpa_drv_send_action(wpa_s, wpa_s->assoc_freq, 0,
+                                 wpa_s->bssid, wpa_s->own_addr, wpa_s->bssid,
+                                 data, data_len, 0);
+       os_free(data);
+
+       return ret;
 }
 
 
@@ -517,12 +609,14 @@ static int wpa_supplicant_mark_authenticated(void *ctx, const u8 *target_ap)
 #ifdef CONFIG_TDLS
 
 static int wpa_supplicant_tdls_get_capa(void *ctx, int *tdls_supported,
-                                       int *tdls_ext_setup)
+                                       int *tdls_ext_setup,
+                                       int *tdls_chan_switch)
 {
        struct wpa_supplicant *wpa_s = ctx;
 
        *tdls_supported = 0;
        *tdls_ext_setup = 0;
+       *tdls_chan_switch = 0;
 
        if (!wpa_s->drv_capa_known)
                return -1;
@@ -533,18 +627,23 @@ static int wpa_supplicant_tdls_get_capa(void *ctx, int *tdls_supported,
        if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_EXTERNAL_SETUP)
                *tdls_ext_setup = 1;
 
+       if (wpa_s->drv_flags & WPA_DRIVER_FLAGS_TDLS_CHANNEL_SWITCH)
+               *tdls_chan_switch = 1;
+
        return 0;
 }
 
 
 static int wpa_supplicant_send_tdls_mgmt(void *ctx, const u8 *dst,
                                         u8 action_code, u8 dialog_token,
-                                        u16 status_code, const u8 *buf,
+                                        u16 status_code, u32 peer_capab,
+                                        int initiator, const u8 *buf,
                                         size_t len)
 {
        struct wpa_supplicant *wpa_s = ctx;
        return wpa_drv_send_tdls_mgmt(wpa_s, dst, action_code, dialog_token,
-                                     status_code, buf, len);
+                                     status_code, peer_capab, initiator, buf,
+                                     len);
 }
 
 
@@ -560,7 +659,9 @@ static int wpa_supplicant_tdls_peer_addset(
        const u8 *supp_rates, size_t supp_rates_len,
        const struct ieee80211_ht_capabilities *ht_capab,
        const struct ieee80211_vht_capabilities *vht_capab,
-       u8 qosinfo, const u8 *ext_capab, size_t ext_capab_len)
+       u8 qosinfo, int wmm, const u8 *ext_capab, size_t ext_capab_len,
+       const u8 *supp_channels, size_t supp_channels_len,
+       const u8 *supp_oper_classes, size_t supp_oper_classes_len)
 {
        struct wpa_supplicant *wpa_s = ctx;
        struct hostapd_sta_add_params params;
@@ -573,10 +674,10 @@ static int wpa_supplicant_tdls_peer_addset(
        params.flags = WPA_STA_TDLS_PEER | WPA_STA_AUTHORIZED;
 
        /*
-        * TDLS Setup frames do not contain WMM IEs, hence need to depend on
-        * qosinfo to check if the peer is WMM capable.
+        * Don't rely only on qosinfo for WMM capability. It may be 0 even when
+        * present. Allow the WMM IE to also indicate QoS support.
         */
-       if (qosinfo)
+       if (wmm || qosinfo)
                params.flags |= WPA_STA_WMM;
 
        params.ht_capabilities = ht_capab;
@@ -588,10 +689,33 @@ static int wpa_supplicant_tdls_peer_addset(
        params.set = !add;
        params.ext_capab = ext_capab;
        params.ext_capab_len = ext_capab_len;
+       params.supp_channels = supp_channels;
+       params.supp_channels_len = supp_channels_len;
+       params.supp_oper_classes = supp_oper_classes;
+       params.supp_oper_classes_len = supp_oper_classes_len;
 
        return wpa_drv_sta_add(wpa_s, &params);
 }
 
+
+static int wpa_supplicant_tdls_enable_channel_switch(
+       void *ctx, const u8 *addr, u8 oper_class,
+       const struct hostapd_freq_params *params)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       return wpa_drv_tdls_enable_channel_switch(wpa_s, addr, oper_class,
+                                                 params);
+}
+
+
+static int wpa_supplicant_tdls_disable_channel_switch(void *ctx, const u8 *addr)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       return wpa_drv_tdls_disable_channel_switch(wpa_s, addr);
+}
+
 #endif /* CONFIG_TDLS */
 
 #endif /* CONFIG_NO_WPA */
@@ -611,6 +735,8 @@ enum wpa_ctrl_req_type wpa_supplicant_ctrl_req_from_string(const char *field)
                return WPA_CTRL_REQ_EAP_OTP;
        else if (os_strcmp(field, "PASSPHRASE") == 0)
                return WPA_CTRL_REQ_EAP_PASSPHRASE;
+       else if (os_strcmp(field, "SIM") == 0)
+               return WPA_CTRL_REQ_SIM;
        return WPA_CTRL_REQ_UNKNOWN;
 }
 
@@ -647,6 +773,9 @@ const char * wpa_supplicant_ctrl_req_to_string(enum wpa_ctrl_req_type field,
                *txt = "Private key passphrase";
                ret = "PASSPHRASE";
                break;
+       case WPA_CTRL_REQ_SIM:
+               ret = "SIM";
+               break;
        default:
                break;
        }
@@ -695,7 +824,7 @@ static void wpa_supplicant_eap_param_needed(void *ctx,
        len = os_snprintf(buf, buflen,
                          WPA_CTRL_REQ "%s-%d:%s needed for SSID ",
                          field_name, ssid->id, txt);
-       if (len < 0 || (size_t) len >= buflen) {
+       if (os_snprintf_error(buflen, len)) {
                os_free(buf);
                return;
        }
@@ -713,6 +842,25 @@ static void wpa_supplicant_eap_param_needed(void *ctx,
 #endif /* CONFIG_CTRL_IFACE || !CONFIG_NO_STDOUT_DEBUG */
 
 
+#ifdef CONFIG_EAP_PROXY
+static void wpa_supplicant_eap_proxy_cb(void *ctx)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+       size_t len;
+
+       wpa_s->mnc_len = eapol_sm_get_eap_proxy_imsi(wpa_s->eapol,
+                                                    wpa_s->imsi, &len);
+       if (wpa_s->mnc_len > 0) {
+               wpa_s->imsi[len] = '\0';
+               wpa_printf(MSG_DEBUG, "eap_proxy: IMSI %s (MNC length %d)",
+                          wpa_s->imsi, wpa_s->mnc_len);
+       } else {
+               wpa_printf(MSG_DEBUG, "eap_proxy: IMSI not available");
+       }
+}
+#endif /* CONFIG_EAP_PROXY */
+
+
 static void wpa_supplicant_port_cb(void *ctx, int authorized)
 {
        struct wpa_supplicant *wpa_s = ctx;
@@ -731,12 +879,14 @@ static void wpa_supplicant_port_cb(void *ctx, int authorized)
 
 
 static void wpa_supplicant_cert_cb(void *ctx, int depth, const char *subject,
+                                  const char *altsubject[], int num_altsubject,
                                   const char *cert_hash,
                                   const struct wpabuf *cert)
 {
        struct wpa_supplicant *wpa_s = ctx;
 
-       wpas_notify_certification(wpa_s, depth, subject, cert_hash, cert);
+       wpas_notify_certification(wpa_s, depth, subject, altsubject,
+                                 num_altsubject, cert_hash, cert);
 }
 
 
@@ -813,11 +963,16 @@ int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s)
        ctx->opensc_engine_path = wpa_s->conf->opensc_engine_path;
        ctx->pkcs11_engine_path = wpa_s->conf->pkcs11_engine_path;
        ctx->pkcs11_module_path = wpa_s->conf->pkcs11_module_path;
+       ctx->openssl_ciphers = wpa_s->conf->openssl_ciphers;
        ctx->wps = wpa_s->wps;
        ctx->eap_param_needed = wpa_supplicant_eap_param_needed;
+#ifdef CONFIG_EAP_PROXY
+       ctx->eap_proxy_cb = wpa_supplicant_eap_proxy_cb;
+#endif /* CONFIG_EAP_PROXY */
        ctx->port_cb = wpa_supplicant_port_cb;
        ctx->cb = wpa_supplicant_eapol_cb;
        ctx->cert_cb = wpa_supplicant_cert_cb;
+       ctx->cert_in_cb = wpa_s->conf->cert_in_cb;
        ctx->status_cb = wpa_supplicant_status_cb;
        ctx->set_anon_id = wpa_supplicant_set_anon_id;
        ctx->cb_ctx = wpa_s;
@@ -835,17 +990,31 @@ int wpa_supplicant_init_eapol(struct wpa_supplicant *wpa_s)
 
 
 #ifndef CONFIG_NO_WPA
-static void wpa_supplicant_set_rekey_offload(void *ctx, const u8 *kek,
-                                            const u8 *kck,
+static void wpa_supplicant_set_rekey_offload(void *ctx,
+                                            const u8 *kek, size_t kek_len,
+                                            const u8 *kck, size_t kck_len,
                                             const u8 *replay_ctr)
 {
        struct wpa_supplicant *wpa_s = ctx;
 
-       wpa_drv_set_rekey_info(wpa_s, kek, kck, replay_ctr);
+       wpa_drv_set_rekey_info(wpa_s, kek, kek_len, kck, kck_len, replay_ctr);
 }
 #endif /* CONFIG_NO_WPA */
 
 
+static int wpa_supplicant_key_mgmt_set_pmk(void *ctx, const u8 *pmk,
+                                          size_t pmk_len)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       if (wpa_s->conf->key_mgmt_offload)
+               return wpa_drv_set_key(wpa_s, WPA_ALG_PMK, NULL, 0, 0,
+                                      NULL, 0, pmk, pmk_len);
+       else
+               return 0;
+}
+
+
 int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
 {
 #ifndef CONFIG_NO_WPA
@@ -885,13 +1054,19 @@ int wpa_supplicant_init_wpa(struct wpa_supplicant *wpa_s)
        ctx->send_tdls_mgmt = wpa_supplicant_send_tdls_mgmt;
        ctx->tdls_oper = wpa_supplicant_tdls_oper;
        ctx->tdls_peer_addset = wpa_supplicant_tdls_peer_addset;
+       ctx->tdls_enable_channel_switch =
+               wpa_supplicant_tdls_enable_channel_switch;
+       ctx->tdls_disable_channel_switch =
+               wpa_supplicant_tdls_disable_channel_switch;
 #endif /* CONFIG_TDLS */
        ctx->set_rekey_offload = wpa_supplicant_set_rekey_offload;
+       ctx->key_mgmt_set_pmk = wpa_supplicant_key_mgmt_set_pmk;
 
        wpa_s->wpa = wpa_sm_init(ctx);
        if (wpa_s->wpa == NULL) {
                wpa_printf(MSG_ERROR, "Failed to initialize WPA state "
                           "machine");
+               os_free(ctx);
                return -1;
        }
 #endif /* CONFIG_NO_WPA */
@@ -918,6 +1093,22 @@ void wpa_supplicant_rsn_supp_set_config(struct wpa_supplicant *wpa_s,
                conf.ssid = ssid->ssid;
                conf.ssid_len = ssid->ssid_len;
                conf.wpa_ptk_rekey = ssid->wpa_ptk_rekey;
+#ifdef CONFIG_P2P
+               if (ssid->p2p_group && wpa_s->current_bss &&
+                   !wpa_s->p2p_disable_ip_addr_req) {
+                       struct wpabuf *p2p;
+                       p2p = wpa_bss_get_vendor_ie_multi(wpa_s->current_bss,
+                                                         P2P_IE_VENDOR_TYPE);
+                       if (p2p) {
+                               u8 group_capab;
+                               group_capab = p2p_get_group_capab(p2p);
+                               if (group_capab &
+                                   P2P_GROUP_CAPAB_IP_ADDR_ALLOCATION)
+                                       conf.p2p = 1;
+                               wpabuf_free(p2p);
+                       }
+               }
+#endif /* CONFIG_P2P */
        }
        wpa_sm_set_config(wpa_s->wpa, ssid ? &conf : NULL);
 }
diff --git a/wpa_supplicant/wpas_kay.c b/wpa_supplicant/wpas_kay.c
new file mode 100644 (file)
index 0000000..354decf
--- /dev/null
@@ -0,0 +1,378 @@
+/*
+ * IEEE 802.1X-2010 KaY Interface
+ * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+#include <openssl/ssl.h>
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "eap_peer/eap.h"
+#include "eap_peer/eap_i.h"
+#include "eapol_supp/eapol_supp_sm.h"
+#include "pae/ieee802_1x_key.h"
+#include "pae/ieee802_1x_kay.h"
+#include "wpa_supplicant_i.h"
+#include "config.h"
+#include "config_ssid.h"
+#include "driver_i.h"
+#include "wpas_kay.h"
+
+
+#define DEFAULT_KEY_LEN                16
+/* secure Connectivity Association Key Name (CKN) */
+#define DEFAULT_CKN_LEN                16
+
+
+static int wpas_macsec_init(void *priv, struct macsec_init_params *params)
+{
+       return wpa_drv_macsec_init(priv, params);
+}
+
+
+static int wpas_macsec_deinit(void *priv)
+{
+       return wpa_drv_macsec_deinit(priv);
+}
+
+
+static int wpas_enable_protect_frames(void *wpa_s, Boolean enabled)
+{
+       return wpa_drv_enable_protect_frames(wpa_s, enabled);
+}
+
+
+static int wpas_set_replay_protect(void *wpa_s, Boolean enabled, u32 window)
+{
+       return wpa_drv_set_replay_protect(wpa_s, enabled, window);
+}
+
+
+static int wpas_set_current_cipher_suite(void *wpa_s, const u8 *cs,
+                                        size_t cs_len)
+{
+       return wpa_drv_set_current_cipher_suite(wpa_s, cs, cs_len);
+}
+
+
+static int wpas_enable_controlled_port(void *wpa_s, Boolean enabled)
+{
+       return wpa_drv_enable_controlled_port(wpa_s, enabled);
+}
+
+
+static int wpas_get_receive_lowest_pn(void *wpa_s, u32 channel,
+                                     u8 an, u32 *lowest_pn)
+{
+       return wpa_drv_get_receive_lowest_pn(wpa_s, channel, an, lowest_pn);
+}
+
+
+static int wpas_get_transmit_next_pn(void *wpa_s, u32 channel,
+                                     u8 an, u32 *next_pn)
+{
+       return wpa_drv_get_transmit_next_pn(wpa_s, channel, an, next_pn);
+}
+
+
+static int wpas_set_transmit_next_pn(void *wpa_s, u32 channel,
+                                     u8 an, u32 next_pn)
+{
+       return wpa_drv_set_transmit_next_pn(wpa_s, channel, an, next_pn);
+}
+
+
+static int wpas_get_available_receive_sc(void *wpa_s, u32 *channel)
+{
+       return wpa_drv_get_available_receive_sc(wpa_s, channel);
+}
+
+
+static unsigned int conf_offset_val(enum confidentiality_offset co)
+{
+       switch (co) {
+       case CONFIDENTIALITY_OFFSET_30:
+               return 30;
+               break;
+       case CONFIDENTIALITY_OFFSET_50:
+               return 50;
+       default:
+               return 0;
+       }
+}
+
+
+static int wpas_create_receive_sc(void *wpa_s, u32 channel,
+                                 struct ieee802_1x_mka_sci *sci,
+                                 enum validate_frames vf,
+                                 enum confidentiality_offset co)
+{
+       return wpa_drv_create_receive_sc(wpa_s, channel, sci->addr, sci->port,
+                                        conf_offset_val(co), vf);
+}
+
+
+static int wpas_delete_receive_sc(void *wpa_s, u32 channel)
+{
+       return wpa_drv_delete_receive_sc(wpa_s, channel);
+}
+
+
+static int wpas_create_receive_sa(void *wpa_s, u32 channel, u8 an,
+                                 u32 lowest_pn, const u8 *sak)
+{
+       return wpa_drv_create_receive_sa(wpa_s, channel, an, lowest_pn, sak);
+}
+
+
+static int wpas_enable_receive_sa(void *wpa_s, u32 channel, u8 an)
+{
+       return wpa_drv_enable_receive_sa(wpa_s, channel, an);
+}
+
+
+static int wpas_disable_receive_sa(void *wpa_s, u32 channel, u8 an)
+{
+       return wpa_drv_disable_receive_sa(wpa_s, channel, an);
+}
+
+
+static int wpas_get_available_transmit_sc(void *wpa_s, u32 *channel)
+{
+       return wpa_drv_get_available_transmit_sc(wpa_s, channel);
+}
+
+
+static int
+wpas_create_transmit_sc(void *wpa_s, u32 channel,
+                       const struct ieee802_1x_mka_sci *sci,
+                       enum confidentiality_offset co)
+{
+       return wpa_drv_create_transmit_sc(wpa_s, channel, sci->addr, sci->port,
+                                         conf_offset_val(co));
+}
+
+
+static int wpas_delete_transmit_sc(void *wpa_s, u32 channel)
+{
+       return wpa_drv_delete_transmit_sc(wpa_s, channel);
+}
+
+
+static int wpas_create_transmit_sa(void *wpa_s, u32 channel, u8 an,
+                                  u32 next_pn, Boolean confidentiality,
+                                  const u8 *sak)
+{
+       return wpa_drv_create_transmit_sa(wpa_s, channel, an, next_pn,
+                                         confidentiality, sak);
+}
+
+
+static int wpas_enable_transmit_sa(void *wpa_s, u32 channel, u8 an)
+{
+       return wpa_drv_enable_transmit_sa(wpa_s, channel, an);
+}
+
+
+static int wpas_disable_transmit_sa(void *wpa_s, u32 channel, u8 an)
+{
+       return wpa_drv_disable_transmit_sa(wpa_s, channel, an);
+}
+
+
+int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s, struct wpa_ssid *ssid)
+{
+       struct ieee802_1x_kay_ctx *kay_ctx;
+       struct ieee802_1x_kay *res = NULL;
+       enum macsec_policy policy;
+
+       ieee802_1x_dealloc_kay_sm(wpa_s);
+
+       if (!ssid || ssid->macsec_policy == 0)
+               return 0;
+
+       policy = ssid->macsec_policy == 1 ? SHOULD_SECURE : DO_NOT_SECURE;
+
+       kay_ctx = os_zalloc(sizeof(*kay_ctx));
+       if (!kay_ctx)
+               return -1;
+
+       kay_ctx->ctx = wpa_s;
+
+       kay_ctx->macsec_init = wpas_macsec_init;
+       kay_ctx->macsec_deinit = wpas_macsec_deinit;
+       kay_ctx->enable_protect_frames = wpas_enable_protect_frames;
+       kay_ctx->set_replay_protect = wpas_set_replay_protect;
+       kay_ctx->set_current_cipher_suite = wpas_set_current_cipher_suite;
+       kay_ctx->enable_controlled_port = wpas_enable_controlled_port;
+       kay_ctx->get_receive_lowest_pn = wpas_get_receive_lowest_pn;
+       kay_ctx->get_transmit_next_pn = wpas_get_transmit_next_pn;
+       kay_ctx->set_transmit_next_pn = wpas_set_transmit_next_pn;
+       kay_ctx->get_available_receive_sc = wpas_get_available_receive_sc;
+       kay_ctx->create_receive_sc = wpas_create_receive_sc;
+       kay_ctx->delete_receive_sc = wpas_delete_receive_sc;
+       kay_ctx->create_receive_sa = wpas_create_receive_sa;
+       kay_ctx->enable_receive_sa = wpas_enable_receive_sa;
+       kay_ctx->disable_receive_sa = wpas_disable_receive_sa;
+       kay_ctx->get_available_transmit_sc = wpas_get_available_transmit_sc;
+       kay_ctx->create_transmit_sc = wpas_create_transmit_sc;
+       kay_ctx->delete_transmit_sc = wpas_delete_transmit_sc;
+       kay_ctx->create_transmit_sa = wpas_create_transmit_sa;
+       kay_ctx->enable_transmit_sa = wpas_enable_transmit_sa;
+       kay_ctx->disable_transmit_sa = wpas_disable_transmit_sa;
+
+       res = ieee802_1x_kay_init(kay_ctx, policy, wpa_s->ifname,
+                                 wpa_s->own_addr);
+       if (res == NULL) {
+               os_free(kay_ctx);
+               return -1;
+       }
+
+       wpa_s->kay = res;
+
+       return 0;
+}
+
+
+void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s)
+{
+       if (!wpa_s->kay)
+               return;
+
+       ieee802_1x_kay_deinit(wpa_s->kay);
+       wpa_s->kay = NULL;
+}
+
+
+static int ieee802_1x_auth_get_session_id(struct wpa_supplicant *wpa_s,
+                                         const u8 *addr, u8 *sid, size_t *len)
+{
+       const u8 *session_id;
+       size_t id_len, need_len;
+
+       session_id = eapol_sm_get_session_id(wpa_s->eapol, &id_len);
+       if (session_id == NULL) {
+               wpa_printf(MSG_DEBUG,
+                          "Failed to get SessionID from EAPOL state machines");
+               return -1;
+       }
+
+       need_len = 1 + 2 * SSL3_RANDOM_SIZE;
+       if (need_len > id_len) {
+               wpa_printf(MSG_DEBUG, "EAP Session-Id not long enough");
+               return -1;
+       }
+
+       os_memcpy(sid, session_id, need_len);
+       *len = need_len;
+
+       return 0;
+}
+
+
+static int ieee802_1x_auth_get_msk(struct wpa_supplicant *wpa_s, const u8 *addr,
+                                  u8 *msk, size_t *len)
+{
+       u8 key[EAP_MSK_LEN];
+       size_t keylen;
+       struct eapol_sm *sm;
+       int res;
+
+       sm = wpa_s->eapol;
+       if (sm == NULL)
+               return -1;
+
+       keylen = EAP_MSK_LEN;
+       res = eapol_sm_get_key(sm, key, keylen);
+       if (res) {
+               wpa_printf(MSG_DEBUG,
+                          "Failed to get MSK from EAPOL state machines");
+               return -1;
+       }
+
+       if (keylen > *len)
+               keylen = *len;
+       os_memcpy(msk, key, keylen);
+       *len = keylen;
+
+       return 0;
+}
+
+
+void * ieee802_1x_notify_create_actor(struct wpa_supplicant *wpa_s,
+                                     const u8 *peer_addr)
+{
+       u8 *sid;
+       size_t sid_len = 128;
+       struct mka_key_name *ckn;
+       struct mka_key *cak;
+       struct mka_key *msk;
+       void *res = NULL;
+
+       if (!wpa_s->kay || wpa_s->kay->policy == DO_NOT_SECURE)
+               return NULL;
+
+       wpa_printf(MSG_DEBUG,
+                  "IEEE 802.1X: External notification - Create MKA for "
+                  MACSTR, MAC2STR(peer_addr));
+
+       msk = os_zalloc(sizeof(*msk));
+       sid = os_zalloc(sid_len);
+       ckn = os_zalloc(sizeof(*ckn));
+       cak = os_zalloc(sizeof(*cak));
+       if (!msk || !sid || !ckn || !cak)
+               goto fail;
+
+       msk->len = DEFAULT_KEY_LEN;
+       if (ieee802_1x_auth_get_msk(wpa_s, wpa_s->bssid, msk->key, &msk->len)) {
+               wpa_printf(MSG_ERROR, "IEEE 802.1X: Could not get MSK");
+               goto fail;
+       }
+
+       if (ieee802_1x_auth_get_session_id(wpa_s, wpa_s->bssid, sid, &sid_len))
+       {
+               wpa_printf(MSG_ERROR,
+                          "IEEE 802.1X: Could not get EAP Session Id");
+               goto fail;
+       }
+
+       /* Derive CAK from MSK */
+       cak->len = DEFAULT_KEY_LEN;
+       if (ieee802_1x_cak_128bits_aes_cmac(msk->key, wpa_s->own_addr,
+                                           peer_addr, cak->key)) {
+               wpa_printf(MSG_ERROR,
+                          "IEEE 802.1X: Deriving CAK failed");
+               goto fail;
+       }
+       wpa_hexdump_key(MSG_DEBUG, "Derived CAK", cak->key, cak->len);
+
+       /* Derive CKN from MSK */
+       ckn->len = DEFAULT_CKN_LEN;
+       if (ieee802_1x_ckn_128bits_aes_cmac(msk->key, wpa_s->own_addr,
+                                           peer_addr, sid, sid_len,
+                                           ckn->name)) {
+               wpa_printf(MSG_ERROR,
+                          "IEEE 802.1X: Deriving CKN failed");
+               goto fail;
+       }
+       wpa_hexdump(MSG_DEBUG, "Derived CKN", ckn->name, ckn->len);
+
+       res = ieee802_1x_kay_create_mka(wpa_s->kay, ckn, cak, 0,
+                                       EAP_EXCHANGE, FALSE);
+
+fail:
+       if (msk) {
+               os_memset(msk, 0, sizeof(*msk));
+               os_free(msk);
+       }
+       os_free(sid);
+       os_free(ckn);
+       if (cak) {
+               os_memset(cak, 0, sizeof(*cak));
+               os_free(cak);
+       }
+
+       return res;
+}
diff --git a/wpa_supplicant/wpas_kay.h b/wpa_supplicant/wpas_kay.h
new file mode 100644 (file)
index 0000000..b7236d0
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * IEEE 802.1X-2010 KaY Interface
+ * Copyright (c) 2013-2014, Qualcomm Atheros, Inc.
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef WPAS_KAY_H
+#define WPAS_KAY_H
+
+#ifdef CONFIG_MACSEC
+
+int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s,
+                           struct wpa_ssid *ssid);
+void * ieee802_1x_notify_create_actor(struct wpa_supplicant *wpa_s,
+                                     const u8 *peer_addr);
+void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s);
+
+#else /* CONFIG_MACSEC */
+
+static inline int ieee802_1x_alloc_kay_sm(struct wpa_supplicant *wpa_s,
+                                         struct wpa_ssid *ssid)
+{
+       return 0;
+}
+
+static inline void *
+ieee802_1x_notify_create_actor(struct wpa_supplicant *wpa_s,
+                              const u8 *peer_addr)
+{
+       return NULL;
+}
+
+static inline void ieee802_1x_dealloc_kay_sm(struct wpa_supplicant *wpa_s)
+{
+}
+
+#endif /* CONFIG_MACSEC */
+
+#endif /* WPAS_KAY_H */
diff --git a/wpa_supplicant/wpas_module_tests.c b/wpa_supplicant/wpas_module_tests.c
new file mode 100644 (file)
index 0000000..6af1678
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * wpa_supplicant 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.
+ */
+
+#include "utils/includes.h"
+
+#include "utils/common.h"
+#include "wpa_supplicant_i.h"
+#include "blacklist.h"
+
+
+static int wpas_blacklist_module_tests(void)
+{
+       struct wpa_supplicant wpa_s;
+       int ret = -1;
+
+       os_memset(&wpa_s, 0, sizeof(wpa_s));
+
+       wpa_blacklist_clear(&wpa_s);
+
+       if (wpa_blacklist_get(NULL, NULL) != NULL ||
+           wpa_blacklist_get(NULL, (u8 *) "123456") != NULL ||
+           wpa_blacklist_get(&wpa_s, NULL) != NULL ||
+           wpa_blacklist_get(&wpa_s, (u8 *) "123456") != NULL)
+               goto fail;
+
+       if (wpa_blacklist_add(NULL, NULL) == 0 ||
+           wpa_blacklist_add(NULL, (u8 *) "123456") == 0 ||
+           wpa_blacklist_add(&wpa_s, NULL) == 0)
+               goto fail;
+
+       if (wpa_blacklist_del(NULL, NULL) == 0 ||
+           wpa_blacklist_del(NULL, (u8 *) "123456") == 0 ||
+           wpa_blacklist_del(&wpa_s, NULL) == 0 ||
+           wpa_blacklist_del(&wpa_s, (u8 *) "123456") == 0)
+               goto fail;
+
+       if (wpa_blacklist_add(&wpa_s, (u8 *) "111111") < 0 ||
+           wpa_blacklist_add(&wpa_s, (u8 *) "111111") < 0 ||
+           wpa_blacklist_add(&wpa_s, (u8 *) "222222") < 0 ||
+           wpa_blacklist_add(&wpa_s, (u8 *) "333333") < 0 ||
+           wpa_blacklist_add(&wpa_s, (u8 *) "444444") < 0 ||
+           wpa_blacklist_del(&wpa_s, (u8 *) "333333") < 0 ||
+           wpa_blacklist_del(&wpa_s, (u8 *) "xxxxxx") == 0 ||
+           wpa_blacklist_get(&wpa_s, (u8 *) "xxxxxx") != NULL ||
+           wpa_blacklist_get(&wpa_s, (u8 *) "111111") == NULL ||
+           wpa_blacklist_get(&wpa_s, (u8 *) "222222") == NULL ||
+           wpa_blacklist_get(&wpa_s, (u8 *) "444444") == NULL ||
+           wpa_blacklist_del(&wpa_s, (u8 *) "111111") < 0 ||
+           wpa_blacklist_del(&wpa_s, (u8 *) "222222") < 0 ||
+           wpa_blacklist_del(&wpa_s, (u8 *) "444444") < 0 ||
+           wpa_blacklist_add(&wpa_s, (u8 *) "111111") < 0 ||
+           wpa_blacklist_add(&wpa_s, (u8 *) "222222") < 0 ||
+           wpa_blacklist_add(&wpa_s, (u8 *) "333333") < 0)
+               goto fail;
+
+       ret = 0;
+fail:
+       wpa_blacklist_clear(&wpa_s);
+
+       if (ret)
+               wpa_printf(MSG_ERROR, "blacklist module test failure");
+
+       return ret;
+}
+
+
+int wpas_module_tests(void)
+{
+       int ret = 0;
+
+       wpa_printf(MSG_INFO, "wpa_supplicant module tests");
+
+       if (wpas_blacklist_module_tests() < 0)
+               ret = -1;
+
+#ifdef CONFIG_WPS
+       {
+               int wps_module_tests(void);
+               if (wps_module_tests() < 0)
+                       ret = -1;
+       }
+#endif /* CONFIG_WPS */
+
+       {
+               int utils_module_tests(void);
+               if (utils_module_tests() < 0)
+                       ret = -1;
+       }
+
+       {
+               int common_module_tests(void);
+               if (common_module_tests() < 0)
+                       ret = -1;
+       }
+
+       {
+               int crypto_module_tests(void);
+               if (crypto_module_tests() < 0)
+                       ret = -1;
+       }
+
+       return ret;
+}
index d73e023..eabe986 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * wpa_supplicant / WPS integration
- * Copyright (c) 2008-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2008-2014, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -52,8 +52,30 @@ static void wpas_wps_clear_ap_info(struct wpa_supplicant *wpa_s)
 }
 
 
+static void wpas_wps_assoc_with_cred(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       int use_fast_assoc = timeout_ctx != NULL;
+
+       wpa_printf(MSG_DEBUG, "WPS: Continuing association after eapol_cb");
+       if (!use_fast_assoc ||
+           wpa_supplicant_fast_associate(wpa_s) != 1)
+               wpa_supplicant_req_scan(wpa_s, 0, 0);
+}
+
+
+static void wpas_wps_assoc_with_cred_cancel(struct wpa_supplicant *wpa_s)
+{
+       eloop_cancel_timeout(wpas_wps_assoc_with_cred, wpa_s, (void *) 0);
+       eloop_cancel_timeout(wpas_wps_assoc_with_cred, wpa_s, (void *) 1);
+}
+
+
 int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
 {
+       if (wpas_p2p_wps_eapol_cb(wpa_s) > 0)
+               return 1;
+
        if (!wpa_s->wps_success &&
            wpa_s->current_ssid &&
            eap_is_wps_pin_enrollee(&wpa_s->current_ssid->eap)) {
@@ -91,6 +113,7 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
                wpa_printf(MSG_DEBUG, "WPS: Network configuration replaced - "
                           "try to associate with the received credential "
                           "(freq=%u)", freq);
+               wpa_s->own_disconnect_req = 1;
                wpa_supplicant_deauthenticate(wpa_s,
                                              WLAN_REASON_DEAUTH_LEAVING);
                if (disabled) {
@@ -119,9 +142,18 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
                        wpabuf_free(wps);
                }
 
-               if (!use_fast_assoc ||
-                   wpa_supplicant_fast_associate(wpa_s) != 1)
-                       wpa_supplicant_req_scan(wpa_s, 0, 0);
+               /*
+                * Complete the next step from an eloop timeout to allow pending
+                * driver events related to the disconnection to be processed
+                * first. This makes it less likely for disconnection event to
+                * cause problems with the following connection.
+                */
+               wpa_printf(MSG_DEBUG, "WPS: Continue association from timeout");
+               wpas_wps_assoc_with_cred_cancel(wpa_s);
+               eloop_register_timeout(0, 10000,
+                                      wpas_wps_assoc_with_cred, wpa_s,
+                                      use_fast_assoc ? (void *) 1 :
+                                      (void *) 0);
                return 1;
        }
 
@@ -129,6 +161,7 @@ int wpas_wps_eapol_cb(struct wpa_supplicant *wpa_s)
                wpa_printf(MSG_DEBUG, "WPS: Registration completed - waiting "
                           "for external credential processing");
                wpas_clear_wps(wpa_s);
+               wpa_s->own_disconnect_req = 1;
                wpa_supplicant_deauthenticate(wpa_s,
                                              WLAN_REASON_DEAUTH_LEAVING);
                return 1;
@@ -255,39 +288,61 @@ static void wpas_wps_remove_dup_network(struct wpa_supplicant *wpa_s,
                /* compare security parameters */
                if (ssid->auth_alg != new_ssid->auth_alg ||
                    ssid->key_mgmt != new_ssid->key_mgmt ||
-                   ssid->proto != new_ssid->proto ||
-                   ssid->pairwise_cipher != new_ssid->pairwise_cipher ||
-                   ssid->group_cipher != new_ssid->group_cipher)
+                   (ssid->group_cipher != new_ssid->group_cipher &&
+                    !(ssid->group_cipher & new_ssid->group_cipher &
+                      WPA_CIPHER_CCMP)))
                        continue;
 
-               if (ssid->passphrase && new_ssid->passphrase) {
-                       if (os_strlen(ssid->passphrase) !=
-                           os_strlen(new_ssid->passphrase))
-                               continue;
-                       if (os_strcmp(ssid->passphrase, new_ssid->passphrase) !=
-                           0)
+               /*
+                * Some existing WPS APs will send two creds in case they are
+                * configured for mixed mode operation (WPA+WPA2 and TKIP+CCMP).
+                * Try to merge these two creds if they are received in the same
+                * M8 message.
+                */
+               if (ssid->wps_run && ssid->wps_run == new_ssid->wps_run &&
+                   wpa_key_mgmt_wpa_psk(ssid->key_mgmt)) {
+                       if (new_ssid->passphrase && ssid->passphrase &&
+                           os_strcmp(new_ssid->passphrase, ssid->passphrase) !=
+                           0) {
+                               wpa_printf(MSG_DEBUG,
+                                          "WPS: M8 Creds with different passphrase - do not merge");
                                continue;
-               } else if (ssid->passphrase || new_ssid->passphrase)
-                       continue;
-
-               if ((ssid->psk_set || new_ssid->psk_set) &&
-                   os_memcmp(ssid->psk, new_ssid->psk, sizeof(ssid->psk)) != 0)
-                       continue;
+                       }
 
-               if (ssid->auth_alg == WPA_ALG_WEP) {
-                       if (ssid->wep_tx_keyidx != new_ssid->wep_tx_keyidx)
+                       if (new_ssid->psk_set &&
+                           (!ssid->psk_set ||
+                            os_memcmp(new_ssid->psk, ssid->psk, 32) != 0)) {
+                               wpa_printf(MSG_DEBUG,
+                                          "WPS: M8 Creds with different PSK - do not merge");
                                continue;
-                       if (os_memcmp(ssid->wep_key, new_ssid->wep_key,
-                                     sizeof(ssid->wep_key)))
+                       }
+
+                       if ((new_ssid->passphrase && !ssid->passphrase) ||
+                           (!new_ssid->passphrase && ssid->passphrase)) {
+                               wpa_printf(MSG_DEBUG,
+                                          "WPS: M8 Creds with different passphrase/PSK type - do not merge");
                                continue;
-                       if (os_memcmp(ssid->wep_key_len, new_ssid->wep_key_len,
-                                     sizeof(ssid->wep_key_len)))
+                       }
+
+                       wpa_printf(MSG_DEBUG,
+                                  "WPS: Workaround - merge likely WPA/WPA2-mixed mode creds in same M8 message");
+                       new_ssid->proto |= ssid->proto;
+                       new_ssid->pairwise_cipher |= ssid->pairwise_cipher;
+               } else {
+                       /*
+                        * proto and pairwise_cipher difference matter for
+                        * non-mixed-mode creds.
+                        */
+                       if (ssid->proto != new_ssid->proto ||
+                           ssid->pairwise_cipher != new_ssid->pairwise_cipher)
                                continue;
                }
 
                /* Remove the duplicated older network entry. */
                wpa_printf(MSG_DEBUG, "Remove duplicate network %d", ssid->id);
                wpas_notify_network_removed(wpa_s, ssid);
+               if (wpa_s->current_ssid == ssid)
+                       wpa_s->current_ssid = NULL;
                wpa_config_remove_network(wpa_s->conf, ssid->id);
        }
 }
@@ -298,7 +353,6 @@ static int wpa_supplicant_wps_cred(void *ctx,
 {
        struct wpa_supplicant *wpa_s = ctx;
        struct wpa_ssid *ssid = wpa_s->current_ssid;
-       u8 key_idx = 0;
        u16 auth_type;
 #ifdef CONFIG_WPS_REG_DISABLE_OPEN
        int registrar = 0;
@@ -344,7 +398,6 @@ static int wpa_supplicant_wps_cred(void *ctx,
        }
 
        if (auth_type != WPS_AUTH_OPEN &&
-           auth_type != WPS_AUTH_SHARED &&
            auth_type != WPS_AUTH_WPAPSK &&
            auth_type != WPS_AUTH_WPA2PSK) {
                wpa_printf(MSG_DEBUG, "WPS: Ignored credentials for "
@@ -392,10 +445,22 @@ static int wpa_supplicant_wps_cred(void *ctx,
                ssid = wpa_config_add_network(wpa_s->conf);
                if (ssid == NULL)
                        return -1;
+               if (wpa_s->current_ssid) {
+                       /*
+                        * Should the GO issue multiple credentials for some
+                        * reason, each credential should be marked as a
+                        * temporary P2P group similarly to the one that gets
+                        * marked as such based on the pre-configured values
+                        * used for the WPS network block.
+                        */
+                       ssid->p2p_group = wpa_s->current_ssid->p2p_group;
+                       ssid->temporary = wpa_s->current_ssid->temporary;
+               }
                wpas_notify_network_added(wpa_s, ssid);
        }
 
        wpa_config_set_network_defaults(ssid);
+       ssid->wps_run = wpa_s->wps_run;
 
        os_free(ssid->ssid);
        ssid->ssid = os_malloc(cred->ssid_len);
@@ -407,43 +472,16 @@ static int wpa_supplicant_wps_cred(void *ctx,
        switch (cred->encr_type) {
        case WPS_ENCR_NONE:
                break;
-       case WPS_ENCR_WEP:
-               if (cred->key_len <= 0)
-                       break;
-               if (cred->key_len != 5 && cred->key_len != 13 &&
-                   cred->key_len != 10 && cred->key_len != 26) {
-                       wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key length "
-                                  "%lu", (unsigned long) cred->key_len);
-                       return -1;
-               }
-               if (cred->key_idx > NUM_WEP_KEYS) {
-                       wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key index %d",
-                                  cred->key_idx);
-                       return -1;
-               }
-               if (cred->key_idx)
-                       key_idx = cred->key_idx - 1;
-               if (cred->key_len == 10 || cred->key_len == 26) {
-                       if (hexstr2bin((char *) cred->key,
-                                      ssid->wep_key[key_idx],
-                                      cred->key_len / 2) < 0) {
-                               wpa_printf(MSG_ERROR, "WPS: Invalid WEP Key "
-                                          "%d", key_idx);
-                               return -1;
-                       }
-                       ssid->wep_key_len[key_idx] = cred->key_len / 2;
-               } else {
-                       os_memcpy(ssid->wep_key[key_idx], cred->key,
-                                 cred->key_len);
-                       ssid->wep_key_len[key_idx] = cred->key_len;
-               }
-               ssid->wep_tx_keyidx = key_idx;
-               break;
        case WPS_ENCR_TKIP:
                ssid->pairwise_cipher = WPA_CIPHER_TKIP;
                break;
        case WPS_ENCR_AES:
                ssid->pairwise_cipher = WPA_CIPHER_CCMP;
+               if (wpa_s->drv_capa_known &&
+                   (wpa_s->drv_enc & WPA_DRIVER_CAPA_ENC_GCMP)) {
+                       ssid->pairwise_cipher |= WPA_CIPHER_GCMP;
+                       ssid->group_cipher |= WPA_CIPHER_GCMP;
+               }
                break;
        }
 
@@ -463,11 +501,6 @@ static int wpa_supplicant_wps_cred(void *ctx,
                }
 #endif /* CONFIG_WPS_REG_DISABLE_OPEN */
                break;
-       case WPS_AUTH_SHARED:
-               ssid->auth_alg = WPA_AUTH_ALG_SHARED;
-               ssid->key_mgmt = WPA_KEY_MGMT_NONE;
-               ssid->proto = 0;
-               break;
        case WPS_AUTH_WPAPSK:
                ssid->auth_alg = WPA_AUTH_ALG_OPEN;
                ssid->key_mgmt = WPA_KEY_MGMT_PSK;
@@ -509,9 +542,6 @@ static int wpa_supplicant_wps_cred(void *ctx,
 
        wpas_wps_security_workaround(wpa_s, ssid, cred);
 
-       if (cred->ap_channel)
-               wpa_s->wps_ap_channel = cred->ap_channel;
-
        wpas_wps_remove_dup_network(wpa_s, ssid);
 
 #ifndef CONFIG_NO_CONFIG_WRITE
@@ -533,15 +563,6 @@ static int wpa_supplicant_wps_cred(void *ctx,
 }
 
 
-#ifdef CONFIG_P2P
-static void wpas_wps_pbc_overlap_cb(void *eloop_ctx, void *timeout_ctx)
-{
-       struct wpa_supplicant *wpa_s = eloop_ctx;
-       wpas_p2p_notif_pbc_overlap(wpa_s);
-}
-#endif /* CONFIG_P2P */
-
-
 static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s,
                                         struct wps_event_m2d *m2d)
 {
@@ -560,18 +581,20 @@ static void wpa_supplicant_wps_event_m2d(struct wpa_supplicant *wpa_s,
                 * Notify P2P from eloop timeout to avoid issues with the
                 * interface getting removed while processing a message.
                 */
-               eloop_register_timeout(0, 0, wpas_wps_pbc_overlap_cb, wpa_s,
+               eloop_register_timeout(0, 0, wpas_p2p_pbc_overlap_cb, wpa_s,
                                       NULL);
        }
 #endif /* CONFIG_P2P */
 }
 
 
-static const char * wps_event_fail_reason[NUM_WPS_EI_VALUES] = {
-       "No Error", /* WPS_EI_NO_ERROR */
-       "TKIP Only Prohibited", /* WPS_EI_SECURITY_TKIP_ONLY_PROHIBITED */
-       "WEP Prohibited" /* WPS_EI_SECURITY_WEP_PROHIBITED */
-};
+static void wpas_wps_clear_timeout(void *eloop_ctx, void *timeout_ctx)
+{
+       struct wpa_supplicant *wpa_s = eloop_ctx;
+       wpa_printf(MSG_DEBUG, "WPS: Clear WPS network from timeout");
+       wpas_clear_wps(wpa_s);
+}
+
 
 static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s,
                                          struct wps_event_fail *fail)
@@ -581,13 +604,13 @@ static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s,
                wpa_msg(wpa_s, MSG_INFO,
                        WPS_EVENT_FAIL "msg=%d config_error=%d reason=%d (%s)",
                        fail->msg, fail->config_error, fail->error_indication,
-                       wps_event_fail_reason[fail->error_indication]);
+                       wps_ei_str(fail->error_indication));
                if (wpa_s->parent && wpa_s->parent != wpa_s)
                        wpa_msg(wpa_s->parent, MSG_INFO, WPS_EVENT_FAIL
                                "msg=%d config_error=%d reason=%d (%s)",
                                fail->msg, fail->config_error,
                                fail->error_indication,
-                               wps_event_fail_reason[fail->error_indication]);
+                               wps_ei_str(fail->error_indication));
        } else {
                wpa_msg(wpa_s, MSG_INFO,
                        WPS_EVENT_FAIL "msg=%d config_error=%d",
@@ -597,11 +620,16 @@ static void wpa_supplicant_wps_event_fail(struct wpa_supplicant *wpa_s,
                                "msg=%d config_error=%d",
                                fail->msg, fail->config_error);
        }
-       wpas_clear_wps(wpa_s);
+
+       /*
+        * Need to allow WPS processing to complete, e.g., by sending WSC_NACK.
+        */
+       wpa_printf(MSG_DEBUG, "WPS: Register timeout to clear WPS network");
+       eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL);
+       eloop_register_timeout(0, 100000, wpas_wps_clear_timeout, wpa_s, NULL);
+
        wpas_notify_wps_event_fail(wpa_s, fail);
-#ifdef CONFIG_P2P
        wpas_p2p_wps_failed(wpa_s, fail);
-#endif /* CONFIG_P2P */
 }
 
 
@@ -660,9 +688,7 @@ static void wpa_supplicant_wps_event_success(struct wpa_supplicant *wpa_s)
        eloop_register_timeout(10, 0, wpas_wps_reenable_networks_cb, wpa_s,
                               NULL);
 
-#ifdef CONFIG_P2P
        wpas_p2p_wps_success(wpa_s, wpa_s->bssid, 0);
-#endif /* CONFIG_P2P */
 }
 
 
@@ -813,6 +839,12 @@ static void wpa_supplicant_wps_event(void *ctx, enum wps_event event,
                break;
        case WPS_EV_PBC_TIMEOUT:
                break;
+       case WPS_EV_PBC_ACTIVE:
+               wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_ACTIVE);
+               break;
+       case WPS_EV_PBC_DISABLE:
+               wpa_msg(wpa_s, MSG_INFO, WPS_EVENT_DISABLE);
+               break;
        case WPS_EV_ER_AP_ADD:
                wpa_supplicant_wps_event_er_ap_add(wpa_s, &data->ap);
                break;
@@ -841,6 +873,17 @@ static void wpa_supplicant_wps_event(void *ctx, enum wps_event event,
 }
 
 
+static int wpa_supplicant_wps_rf_band(void *ctx)
+{
+       struct wpa_supplicant *wpa_s = ctx;
+
+       if (!wpa_s->current_ssid || !wpa_s->assoc_freq)
+               return 0;
+
+       return (wpa_s->assoc_freq > 2484) ? WPS_RF_50GHZ : WPS_RF_24GHZ;
+}
+
+
 enum wps_request_type wpas_wps_get_req_type(struct wpa_ssid *ssid)
 {
        if (eap_is_wps_pbc_enrollee(&ssid->eap) ||
@@ -856,21 +899,25 @@ static void wpas_clear_wps(struct wpa_supplicant *wpa_s)
        int id;
        struct wpa_ssid *ssid, *remove_ssid = NULL, *prev_current;
 
+       wpa_s->after_wps = 0;
+       wpa_s->known_wps_freq = 0;
+
        prev_current = wpa_s->current_ssid;
 
        /* Enable the networks disabled during wpas_wps_reassoc */
        wpas_wps_reenable_networks(wpa_s);
 
        eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
+       eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL);
 
        /* Remove any existing WPS network from configuration */
        ssid = wpa_s->conf->ssid;
        while (ssid) {
                if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
                        if (ssid == wpa_s->current_ssid) {
-                               wpa_s->current_ssid = NULL;
-                               if (ssid != NULL)
-                                       wpas_notify_network_changed(wpa_s);
+                               wpa_s->own_disconnect_req = 1;
+                               wpa_supplicant_deauthenticate(
+                                       wpa_s, WLAN_REASON_DEAUTH_LEAVING);
                        }
                        id = ssid->id;
                        remove_ssid = ssid;
@@ -902,7 +949,8 @@ static void wpas_wps_timeout(void *eloop_ctx, void *timeout_ctx)
 
 
 static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
-                                             int registrar, const u8 *bssid)
+                                             int registrar, const u8 *dev_addr,
+                                             const u8 *bssid)
 {
        struct wpa_ssid *ssid;
 
@@ -922,6 +970,11 @@ static struct wpa_ssid * wpas_wps_add_network(struct wpa_supplicant *wpa_s,
                return NULL;
        }
 
+#ifdef CONFIG_P2P
+       if (dev_addr)
+               os_memcpy(ssid->go_p2p_dev_addr, dev_addr, ETH_ALEN);
+#endif /* CONFIG_P2P */
+
        if (bssid) {
 #ifndef CONFIG_P2P
                struct wpa_bss *bss;
@@ -972,9 +1025,11 @@ static void wpas_wps_temp_disable(struct wpa_supplicant *wpa_s,
 {
        struct wpa_ssid *ssid;
 
-       if (wpa_s->current_ssid)
+       if (wpa_s->current_ssid) {
+               wpa_s->own_disconnect_req = 1;
                wpa_supplicant_deauthenticate(
                        wpa_s, WLAN_REASON_DEAUTH_LEAVING);
+       }
 
        /* Mark all other networks disabled and trigger reassociation */
        ssid = wpa_s->conf->ssid;
@@ -1002,13 +1057,20 @@ static void wpas_wps_temp_disable(struct wpa_supplicant *wpa_s,
 
 
 static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
-                            struct wpa_ssid *selected, const u8 *bssid)
+                            struct wpa_ssid *selected, const u8 *bssid,
+                            int freq)
 {
        struct wpa_bss *bss;
 
+       wpa_s->wps_run++;
+       if (wpa_s->wps_run == 0)
+               wpa_s->wps_run++;
        wpa_s->after_wps = 0;
        wpa_s->known_wps_freq = 0;
-       if (bssid) {
+       if (freq) {
+               wpa_s->after_wps = 5;
+               wpa_s->wps_freq = freq;
+       } else if (bssid) {
                bss = wpa_bss_get_bssid_latest(wpa_s, bssid);
                if (bss && bss->freq > 0) {
                        wpa_s->known_wps_freq = 1;
@@ -1024,6 +1086,8 @@ static void wpas_wps_reassoc(struct wpa_supplicant *wpa_s,
        wpa_s->normal_scans = 0;
        wpa_s->wps_success = 0;
        wpa_s->blacklist_cleared = 0;
+
+       wpa_supplicant_cancel_sched_scan(wpa_s);
        wpa_supplicant_req_scan(wpa_s, 0, 0);
 }
 
@@ -1032,8 +1096,16 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
                       int p2p_group)
 {
        struct wpa_ssid *ssid;
+
+#ifdef CONFIG_AP
+       if (wpa_s->ap_iface) {
+               wpa_printf(MSG_DEBUG,
+                          "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled");
+               return -1;
+       }
+#endif /* CONFIG_AP */
        wpas_clear_wps(wpa_s);
-       ssid = wpas_wps_add_network(wpa_s, 0, bssid);
+       ssid = wpas_wps_add_network(wpa_s, 0, NULL, bssid);
        if (ssid == NULL)
                return -1;
        ssid->temporary = 1;
@@ -1056,24 +1128,54 @@ int wpas_wps_start_pbc(struct wpa_supplicant *wpa_s, const u8 *bssid,
                ssid->eap.fragment_size = wpa_s->wps_fragment_size;
        eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
                               wpa_s, NULL);
-       wpas_wps_reassoc(wpa_s, ssid, bssid);
+       wpas_wps_reassoc(wpa_s, ssid, bssid, 0);
        return 0;
 }
 
 
-int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
-                      const char *pin, int p2p_group, u16 dev_pw_id)
+static int wpas_wps_start_dev_pw(struct wpa_supplicant *wpa_s,
+                                const u8 *dev_addr, const u8 *bssid,
+                                const char *pin, int p2p_group, u16 dev_pw_id,
+                                const u8 *peer_pubkey_hash,
+                                const u8 *ssid_val, size_t ssid_len, int freq)
 {
        struct wpa_ssid *ssid;
-       char val[128];
+       char val[128 + 2 * WPS_OOB_PUBKEY_HASH_LEN];
        unsigned int rpin = 0;
+       char hash[2 * WPS_OOB_PUBKEY_HASH_LEN + 10];
 
+#ifdef CONFIG_AP
+       if (wpa_s->ap_iface) {
+               wpa_printf(MSG_DEBUG,
+                          "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled");
+               return -1;
+       }
+#endif /* CONFIG_AP */
        wpas_clear_wps(wpa_s);
-       ssid = wpas_wps_add_network(wpa_s, 0, bssid);
-       if (ssid == NULL)
+       if (bssid && is_zero_ether_addr(bssid))
+               bssid = NULL;
+       ssid = wpas_wps_add_network(wpa_s, 0, dev_addr, bssid);
+       if (ssid == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Could not add network");
                return -1;
+       }
        ssid->temporary = 1;
        ssid->p2p_group = p2p_group;
+       if (ssid_val) {
+               ssid->ssid = os_malloc(ssid_len);
+               if (ssid->ssid) {
+                       os_memcpy(ssid->ssid, ssid_val, ssid_len);
+                       ssid->ssid_len = ssid_len;
+               }
+       }
+       if (peer_pubkey_hash) {
+               os_memcpy(hash, " pkhash=", 8);
+               wpa_snprintf_hex_uppercase(hash + 8, sizeof(hash) - 8,
+                                          peer_pubkey_hash,
+                                          WPS_OOB_PUBKEY_HASH_LEN);
+       } else {
+               hash[0] = '\0';
+       }
 #ifdef CONFIG_P2P
        if (p2p_group && wpa_s->go_params && wpa_s->go_params->ssid_len) {
                ssid->ssid = os_zalloc(wpa_s->go_params->ssid_len + 1);
@@ -1087,25 +1189,38 @@ int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
        }
 #endif /* CONFIG_P2P */
        if (pin)
-               os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u\"",
-                           pin, dev_pw_id);
-       else {
+               os_snprintf(val, sizeof(val), "\"pin=%s dev_pw_id=%u%s\"",
+                           pin, dev_pw_id, hash);
+       else if (pin == NULL && dev_pw_id == DEV_PW_NFC_CONNECTION_HANDOVER) {
+               os_snprintf(val, sizeof(val), "\"dev_pw_id=%u%s\"",
+                           dev_pw_id, hash);
+       } else {
                rpin = wps_generate_pin();
-               os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u\"",
-                           rpin, dev_pw_id);
+               os_snprintf(val, sizeof(val), "\"pin=%08d dev_pw_id=%u%s\"",
+                           rpin, dev_pw_id, hash);
        }
-       if (wpa_config_set(ssid, "phase1", val, 0) < 0)
+       if (wpa_config_set(ssid, "phase1", val, 0) < 0) {
+               wpa_printf(MSG_DEBUG, "WPS: Failed to set phase1 '%s'", val);
                return -1;
+       }
        if (wpa_s->wps_fragment_size)
                ssid->eap.fragment_size = wpa_s->wps_fragment_size;
        eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
                               wpa_s, NULL);
        wpa_s->wps_ap_iter = 1;
-       wpas_wps_reassoc(wpa_s, ssid, bssid);
+       wpas_wps_reassoc(wpa_s, ssid, bssid, freq);
        return rpin;
 }
 
 
+int wpas_wps_start_pin(struct wpa_supplicant *wpa_s, const u8 *bssid,
+                      const char *pin, int p2p_group, u16 dev_pw_id)
+{
+       return wpas_wps_start_dev_pw(wpa_s, NULL, bssid, pin, p2p_group,
+                                    dev_pw_id, NULL, NULL, 0, 0);
+}
+
+
 /* Cancel the wps pbc/pin requests */
 int wpas_wps_cancel(struct wpa_supplicant *wpa_s)
 {
@@ -1124,14 +1239,20 @@ int wpas_wps_cancel(struct wpa_supplicant *wpa_s)
        } else if (wpa_s->wpa_state >= WPA_ASSOCIATED) {
                wpa_printf(MSG_DEBUG, "WPS: Cancel operation - "
                           "deauthenticate");
+               wpa_s->own_disconnect_req = 1;
                wpa_supplicant_deauthenticate(wpa_s,
                                              WLAN_REASON_DEAUTH_LEAVING);
                wpas_clear_wps(wpa_s);
        } else {
                wpas_wps_reenable_networks(wpa_s);
                wpas_wps_clear_ap_info(wpa_s);
+               if (eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL) >
+                   0)
+                       wpas_clear_wps(wpa_s);
        }
 
+       wpa_s->after_wps = 0;
+
        return 0;
 }
 
@@ -1144,17 +1265,24 @@ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
        char *pos, *end;
        int res;
 
+#ifdef CONFIG_AP
+       if (wpa_s->ap_iface) {
+               wpa_printf(MSG_DEBUG,
+                          "WPS: Reject request to start Registrar(as station) operation while AP mode is enabled");
+               return -1;
+       }
+#endif /* CONFIG_AP */
        if (!pin)
                return -1;
        wpas_clear_wps(wpa_s);
-       ssid = wpas_wps_add_network(wpa_s, 1, bssid);
+       ssid = wpas_wps_add_network(wpa_s, 1, NULL, bssid);
        if (ssid == NULL)
                return -1;
        ssid->temporary = 1;
        pos = val;
        end = pos + sizeof(val);
        res = os_snprintf(pos, end - pos, "\"pin=%s", pin);
-       if (res < 0 || res >= end - pos)
+       if (os_snprintf_error(end - pos, res))
                return -1;
        pos += res;
        if (settings) {
@@ -1162,12 +1290,12 @@ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
                                  "new_encr=%s new_key=%s",
                                  settings->ssid_hex, settings->auth,
                                  settings->encr, settings->key_hex);
-               if (res < 0 || res >= end - pos)
+               if (os_snprintf_error(end - pos, res))
                        return -1;
                pos += res;
        }
        res = os_snprintf(pos, end - pos, "\"");
-       if (res < 0 || res >= end - pos)
+       if (os_snprintf_error(end - pos, res))
                return -1;
        if (wpa_config_set(ssid, "phase1", val, 0) < 0)
                return -1;
@@ -1175,16 +1303,25 @@ int wpas_wps_start_reg(struct wpa_supplicant *wpa_s, const u8 *bssid,
                ssid->eap.fragment_size = wpa_s->wps_fragment_size;
        eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wpas_wps_timeout,
                               wpa_s, NULL);
-       wpas_wps_reassoc(wpa_s, ssid, bssid);
+       wpas_wps_reassoc(wpa_s, ssid, bssid, 0);
        return 0;
 }
 
 
-static int wpas_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk,
+static int wpas_wps_new_psk_cb(void *ctx, const u8 *mac_addr,
+                              const u8 *p2p_dev_addr, const u8 *psk,
                               size_t psk_len)
 {
-       wpa_printf(MSG_DEBUG, "WPS: Received new WPA/WPA2-PSK from WPS for "
-                  "STA " MACSTR, MAC2STR(mac_addr));
+       if (is_zero_ether_addr(p2p_dev_addr)) {
+               wpa_printf(MSG_DEBUG,
+                          "Received new WPA/WPA2-PSK from WPS for STA " MACSTR,
+                          MAC2STR(mac_addr));
+       } else {
+               wpa_printf(MSG_DEBUG,
+                          "Received new WPA/WPA2-PSK from WPS for STA " MACSTR
+                          " P2P Device Addr " MACSTR,
+                          MAC2STR(mac_addr), MAC2STR(p2p_dev_addr));
+       }
        wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len);
 
        /* TODO */
@@ -1209,7 +1346,7 @@ static void wpas_wps_pin_needed_cb(void *ctx, const u8 *uuid_e,
                          dev->model_number, dev->serial_number,
                          wps_dev_type_bin2str(dev->pri_dev_type, devtype,
                                               sizeof(devtype)));
-       if (len > 0 && len < (int) sizeof(txt))
+       if (!os_snprintf_error(sizeof(txt), len))
                wpa_printf(MSG_INFO, "%s", txt);
 }
 
@@ -1233,7 +1370,6 @@ static void wpas_wps_set_sel_reg_cb(void *ctx, int sel_reg, u16 dev_passwd_id,
 
 static u16 wps_fix_config_methods(u16 config_methods)
 {
-#ifdef CONFIG_WPS2
        if ((config_methods &
             (WPS_CONFIG_DISPLAY | WPS_CONFIG_VIRT_DISPLAY |
              WPS_CONFIG_PHY_DISPLAY)) == WPS_CONFIG_DISPLAY) {
@@ -1248,7 +1384,6 @@ static u16 wps_fix_config_methods(u16 config_methods)
                           "virtual_push_button for WPS 2.0 compliance");
                config_methods |= WPS_CONFIG_VIRT_PUSHBUTTON;
        }
-#endif /* CONFIG_WPS2 */
 
        return config_methods;
 }
@@ -1257,7 +1392,9 @@ static u16 wps_fix_config_methods(u16 config_methods)
 static void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s,
                              struct wps_context *wps)
 {
-       wpa_printf(MSG_DEBUG, "WPS: Set UUID for interface %s", wpa_s->ifname);
+       char buf[50];
+       const char *src;
+
        if (is_nil_uuid(wpa_s->conf->uuid)) {
                struct wpa_supplicant *first;
                first = wpa_s->global->ifaces;
@@ -1268,18 +1405,18 @@ static void wpas_wps_set_uuid(struct wpa_supplicant *wpa_s,
                                os_memcpy(wps->uuid,
                                          wpa_s->global->ifaces->wps->uuid,
                                          WPS_UUID_LEN);
-                       wpa_hexdump(MSG_DEBUG, "WPS: UUID from the first "
-                                   "interface", wps->uuid, WPS_UUID_LEN);
+                       src = "from the first interface";
                } else {
                        uuid_gen_mac_addr(wpa_s->own_addr, wps->uuid);
-                       wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC "
-                                   "address", wps->uuid, WPS_UUID_LEN);
+                       src = "based on MAC address";
                }
        } else {
                os_memcpy(wps->uuid, wpa_s->conf->uuid, WPS_UUID_LEN);
-               wpa_hexdump(MSG_DEBUG, "WPS: UUID based on configuration",
-                           wps->uuid, WPS_UUID_LEN);
+               src = "based on configuration";
        }
+
+       uuid_bin2str(wps->uuid, buf, sizeof(buf));
+       wpa_dbg(wpa_s, MSG_DEBUG, "WPS: UUID %s: %s", src, buf);
 }
 
 
@@ -1313,6 +1450,7 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s)
 
        wps->cred_cb = wpa_supplicant_wps_cred;
        wps->event_cb = wpa_supplicant_wps_event;
+       wps->rf_band_cb = wpa_supplicant_wps_rf_band;
        wps->cb_ctx = wpa_s;
 
        wps->dev.device_name = wpa_s->conf->device_name;
@@ -1383,18 +1521,39 @@ int wpas_wps_init(struct wpa_supplicant *wpa_s)
 }
 
 
+#ifdef CONFIG_WPS_ER
+static void wpas_wps_nfc_clear(struct wps_context *wps)
+{
+       wps->ap_nfc_dev_pw_id = 0;
+       wpabuf_free(wps->ap_nfc_dh_pubkey);
+       wps->ap_nfc_dh_pubkey = NULL;
+       wpabuf_free(wps->ap_nfc_dh_privkey);
+       wps->ap_nfc_dh_privkey = NULL;
+       wpabuf_free(wps->ap_nfc_dev_pw);
+       wps->ap_nfc_dev_pw = NULL;
+}
+#endif /* CONFIG_WPS_ER */
+
+
 void wpas_wps_deinit(struct wpa_supplicant *wpa_s)
 {
+       wpas_wps_assoc_with_cred_cancel(wpa_s);
        eloop_cancel_timeout(wpas_wps_timeout, wpa_s, NULL);
+       eloop_cancel_timeout(wpas_wps_clear_timeout, wpa_s, NULL);
        eloop_cancel_timeout(wpas_wps_reenable_networks_cb, wpa_s, NULL);
        wpas_wps_clear_ap_info(wpa_s);
 
+#ifdef CONFIG_P2P
+       eloop_cancel_timeout(wpas_p2p_pbc_overlap_cb, wpa_s, NULL);
+#endif /* CONFIG_P2P */
+
        if (wpa_s->wps == NULL)
                return;
 
 #ifdef CONFIG_WPS_ER
        wps_er_deinit(wpa_s->wps_er, NULL, NULL);
        wpa_s->wps_er = NULL;
+       wpas_wps_nfc_clear(wpa_s->wps);
 #endif /* CONFIG_WPS_ER */
 
        wps_registrar_deinit(wpa_s->wps->registrar);
@@ -1575,6 +1734,10 @@ int wpas_wps_scan_pbc_overlap(struct wpa_supplicant *wpa_s,
                uuid = wps_get_uuid_e(ie);
                wpa_hexdump(MSG_DEBUG, "WPS: UUID of the other BSS",
                            uuid, UUID_LEN);
+               if (os_memcmp(selected->bssid, bss->bssid, ETH_ALEN) == 0) {
+                       wpabuf_free(ie);
+                       continue;
+               }
                if (sel_uuid == NULL || uuid == NULL ||
                    os_memcmp(sel_uuid, uuid, UUID_LEN) != 0) {
                        ret = 1; /* PBC overlap */
@@ -1678,13 +1841,12 @@ int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter)
 }
 
 
-int wpas_wps_er_stop(struct wpa_supplicant *wpa_s)
+void wpas_wps_er_stop(struct wpa_supplicant *wpa_s)
 {
 #ifdef CONFIG_WPS_ER
        wps_er_deinit(wpa_s->wps_er, NULL, NULL);
        wpa_s->wps_er = NULL;
 #endif /* CONFIG_WPS_ER */
-       return 0;
 }
 
 
@@ -1785,6 +1947,7 @@ int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid,
        u8 addr[ETH_ALEN], *use_addr = NULL;
        struct wpa_ssid *ssid;
        struct wps_credential cred;
+       int ret;
 
        if (uuid_str2bin(uuid, u) == 0)
                use_uuid = u;
@@ -1798,7 +1961,9 @@ int wpas_wps_er_set_config(struct wpa_supplicant *wpa_s, const char *uuid,
 
        if (wpas_wps_network_to_cred(ssid, &cred) < 0)
                return -1;
-       return wps_er_set_config(wpa_s->wps_er, use_uuid, use_addr, &cred);
+       ret = wps_er_set_config(wpa_s->wps_er, use_uuid, use_addr, &cred);
+       os_memset(&cred, 0, sizeof(cred));
+       return ret;
 }
 
 
@@ -1844,8 +2009,10 @@ int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid,
 
        if (os_strcmp(settings->encr, "NONE") == 0)
                cred.encr_type = WPS_ENCR_NONE;
+#ifdef CONFIG_TESTING_OPTIONS
        else if (os_strcmp(settings->encr, "WEP") == 0)
                cred.encr_type = WPS_ENCR_WEP;
+#endif /* CONFIG_TESTING_OPTIONS */
        else if (os_strcmp(settings->encr, "TKIP") == 0)
                cred.encr_type = WPS_ENCR_TKIP;
        else if (os_strcmp(settings->encr, "CCMP") == 0)
@@ -1916,19 +2083,6 @@ int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s)
 }
 
 
-int wpas_wps_in_progress(struct wpa_supplicant *wpa_s)
-{
-       struct wpa_ssid *ssid;
-
-       for (ssid = wpa_s->conf->ssid; ssid; ssid = ssid->next) {
-               if (!ssid->disabled && ssid->key_mgmt == WPA_KEY_MGMT_WPS)
-                       return 1;
-       }
-
-       return 0;
-}
-
-
 void wpas_wps_update_config(struct wpa_supplicant *wpa_s)
 {
        struct wps_context *wps = wpa_s->wps;
@@ -2054,15 +2208,32 @@ struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef)
 }
 
 
-int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid)
+int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *go_dev_addr,
+                      const u8 *bssid,
+                      const struct wpabuf *dev_pw, u16 dev_pw_id,
+                      int p2p_group, const u8 *peer_pubkey_hash,
+                      const u8 *ssid, size_t ssid_len, int freq)
 {
        struct wps_context *wps = wpa_s->wps;
        char pw[32 * 2 + 1];
 
+       if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && dev_pw == NULL) {
+               dev_pw = wpa_s->conf->wps_nfc_dev_pw;
+               dev_pw_id = wpa_s->conf->wps_nfc_dev_pw_id;
+       }
+
        if (wpa_s->conf->wps_nfc_dh_pubkey == NULL ||
-           wpa_s->conf->wps_nfc_dh_privkey == NULL ||
-           wpa_s->conf->wps_nfc_dev_pw == NULL)
+           wpa_s->conf->wps_nfc_dh_privkey == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Missing DH params - "
+                          "cannot start NFC-triggered connection");
+               return -1;
+       }
+
+       if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER && dev_pw == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: Missing Device Password (id=%u) - "
+                          "cannot start NFC-triggered connection", dev_pw_id);
                return -1;
+       }
 
        dh5_free(wps->dh_ctx);
        wpabuf_free(wps->dh_pubkey);
@@ -2075,6 +2246,7 @@ int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid)
                wps->dh_pubkey = NULL;
                wpabuf_free(wps->dh_privkey);
                wps->dh_privkey = NULL;
+               wpa_printf(MSG_DEBUG, "WPS: Failed to get DH priv/pub key");
                return -1;
        }
        wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, wps->dh_pubkey);
@@ -2083,22 +2255,25 @@ int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid)
                wps->dh_pubkey = NULL;
                wpabuf_free(wps->dh_privkey);
                wps->dh_privkey = NULL;
+               wpa_printf(MSG_DEBUG, "WPS: Failed to initialize DH context");
                return -1;
        }
 
-       wpa_snprintf_hex_uppercase(pw, sizeof(pw),
-                                  wpabuf_head(wpa_s->conf->wps_nfc_dev_pw),
-                                  wpabuf_len(wpa_s->conf->wps_nfc_dev_pw));
-       return wpas_wps_start_pin(wpa_s, bssid, pw, 0,
-                                 wpa_s->conf->wps_nfc_dev_pw_id);
+       if (dev_pw) {
+               wpa_snprintf_hex_uppercase(pw, sizeof(pw),
+                                          wpabuf_head(dev_pw),
+                                          wpabuf_len(dev_pw));
+       }
+       return wpas_wps_start_dev_pw(wpa_s, go_dev_addr, bssid,
+                                    dev_pw ? pw : NULL,
+                                    p2p_group, dev_pw_id, peer_pubkey_hash,
+                                    ssid, ssid_len, freq);
 }
 
 
 static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s,
                             struct wps_parse_attr *attr)
 {
-       wpa_s->wps_ap_channel = 0;
-
        /*
         * Disable existing networks temporarily to allow the newly learned
         * credential to be preferred. Enable the temporarily disabled networks
@@ -2114,12 +2289,8 @@ static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s,
        if (wpa_s->wpa_state == WPA_INTERFACE_DISABLED)
                return 0;
 
-       wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network "
-                  "based on the received credential added");
-       wpa_s->normal_scans = 0;
-       wpa_supplicant_reinit_autoscan(wpa_s);
-       if (wpa_s->wps_ap_channel) {
-               u16 chan = wpa_s->wps_ap_channel;
+       if (attr->ap_channel) {
+               u16 chan = WPA_GET_BE16(attr->ap_channel);
                int freq = 0;
 
                if (chan >= 1 && chan <= 13)
@@ -2130,14 +2301,21 @@ static int wpas_wps_use_cred(struct wpa_supplicant *wpa_s,
                        freq = 5000 + 5 * chan;
 
                if (freq) {
-                       wpa_printf(MSG_DEBUG, "WPS: Credential indicated "
-                                  "AP channel %u -> %u MHz", chan, freq);
+                       wpa_printf(MSG_DEBUG, "WPS: Credential container indicated AP channel %u -> %u MHz",
+                                  chan, freq);
                        wpa_s->after_wps = 5;
                        wpa_s->wps_freq = freq;
                }
        }
+
+       wpa_printf(MSG_DEBUG, "WPS: Request reconnection with new network "
+                  "based on the received credential added");
+       wpa_s->normal_scans = 0;
+       wpa_supplicant_reinit_autoscan(wpa_s);
        wpa_s->disconnected = 0;
        wpa_s->reassociate = 1;
+
+       wpa_supplicant_cancel_sched_scan(wpa_s);
        wpa_supplicant_req_scan(wpa_s, 0, 0);
 
        return 0;
@@ -2181,7 +2359,7 @@ static int wpas_wps_nfc_tag_process(struct wpa_supplicant *wpa_s,
 
 
 int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
-                         const struct wpabuf *data)
+                         const struct wpabuf *data, int forced_freq)
 {
        const struct wpabuf *wps = data;
        struct wpabuf *tmp = NULL;
@@ -2194,6 +2372,15 @@ int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
                /* Assume this contains full NDEF record */
                tmp = ndef_parse_wifi(data);
                if (tmp == NULL) {
+#ifdef CONFIG_P2P
+                       tmp = ndef_parse_p2p(data);
+                       if (tmp) {
+                               ret = wpas_p2p_nfc_tag_process(wpa_s, tmp,
+                                                              forced_freq);
+                               wpabuf_free(tmp);
+                               return ret;
+                       }
+#endif /* CONFIG_P2P */
                        wpa_printf(MSG_DEBUG, "WPS: Could not parse NDEF");
                        return -1;
                }
@@ -2206,24 +2393,45 @@ int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
 }
 
 
-struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s, int cr)
+struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s,
+                                         int ndef)
 {
-       if (cr)
-               return ndef_build_wifi_hc(1);
-       return ndef_build_wifi_hr();
+       struct wpabuf *ret;
+
+       if (wpa_s->conf->wps_nfc_dh_pubkey == NULL &&
+           wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+                          &wpa_s->conf->wps_nfc_dh_privkey) < 0)
+               return NULL;
+
+       ret = wps_build_nfc_handover_req(wpa_s->wps,
+                                        wpa_s->conf->wps_nfc_dh_pubkey);
+
+       if (ndef && ret) {
+               struct wpabuf *tmp;
+               tmp = ndef_build_wifi(ret);
+               wpabuf_free(ret);
+               if (tmp == NULL)
+                       return NULL;
+               ret = tmp;
+       }
+
+       return ret;
 }
 
 
 #ifdef CONFIG_WPS_NFC
-struct wpabuf * wpas_wps_er_nfc_handover_sel(struct wpa_supplicant *wpa_s,
-                                            int ndef, const char *uuid)
+
+static struct wpabuf *
+wpas_wps_er_nfc_handover_sel(struct wpa_supplicant *wpa_s, int ndef,
+                            const char *uuid)
 {
 #ifdef CONFIG_WPS_ER
        struct wpabuf *ret;
        u8 u[UUID_LEN], *use_uuid = NULL;
        u8 addr[ETH_ALEN], *use_addr = NULL;
+       struct wps_context *wps = wpa_s->wps;
 
-       if (!wpa_s->wps_er)
+       if (wps == NULL)
                return NULL;
 
        if (uuid == NULL)
@@ -2235,11 +2443,23 @@ struct wpabuf * wpas_wps_er_nfc_handover_sel(struct wpa_supplicant *wpa_s,
        else
                return NULL;
 
-       /*
-        * Handover Select carrier record for WPS uses the same format as
-        * configuration token.
-        */
-       ret = wps_er_nfc_config_token(wpa_s->wps_er, use_uuid, use_addr);
+       if (wpa_s->conf->wps_nfc_dh_pubkey == NULL) {
+               if (wps_nfc_gen_dh(&wpa_s->conf->wps_nfc_dh_pubkey,
+                                  &wpa_s->conf->wps_nfc_dh_privkey) < 0)
+                       return NULL;
+       }
+
+       wpas_wps_nfc_clear(wps);
+       wps->ap_nfc_dev_pw_id = DEV_PW_NFC_CONNECTION_HANDOVER;
+       wps->ap_nfc_dh_pubkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_pubkey);
+       wps->ap_nfc_dh_privkey = wpabuf_dup(wpa_s->conf->wps_nfc_dh_privkey);
+       if (!wps->ap_nfc_dh_pubkey || !wps->ap_nfc_dh_privkey) {
+               wpas_wps_nfc_clear(wps);
+               return NULL;
+       }
+
+       ret = wps_er_nfc_handover_sel(wpa_s->wps_er, wpa_s->wps, use_uuid,
+                                     use_addr, wpa_s->conf->wps_nfc_dh_pubkey);
        if (ndef && ret) {
                struct wpabuf *tmp;
                tmp = ndef_build_wifi(ret);
@@ -2270,29 +2490,125 @@ struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
 }
 
 
-int wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s,
-                                const struct wpabuf *data)
-{
-       /* TODO */
-       return -1;
-}
-
-
-int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
-                                const struct wpabuf *data)
+static int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
+                                       const struct wpabuf *data)
 {
        struct wpabuf *wps;
-       int ret;
+       int ret = -1;
+       u16 wsc_len;
+       const u8 *pos;
+       struct wpabuf msg;
+       struct wps_parse_attr attr;
+       u16 dev_pw_id;
+       const u8 *bssid = NULL;
+       int freq = 0;
 
        wps = ndef_parse_wifi(data);
        if (wps == NULL)
                return -1;
        wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
                   "payload from NFC connection handover");
-       wpa_hexdump_buf_key(MSG_DEBUG, "WPS: NFC payload", wps);
-       ret = wpas_wps_nfc_tag_process(wpa_s, wps);
-       wpabuf_free(wps);
+       wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
+       if (wpabuf_len(wps) < 2) {
+               wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Select "
+                          "Message");
+               goto out;
+       }
+       pos = wpabuf_head(wps);
+       wsc_len = WPA_GET_BE16(pos);
+       if (wsc_len > wpabuf_len(wps) - 2) {
+               wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) "
+                          "in Wi-Fi Handover Select Message", wsc_len);
+               goto out;
+       }
+       pos += 2;
+
+       wpa_hexdump(MSG_DEBUG,
+                   "WPS: WSC attributes in Wi-Fi Handover Select Message",
+                   pos, wsc_len);
+       if (wsc_len < wpabuf_len(wps) - 2) {
+               wpa_hexdump(MSG_DEBUG,
+                           "WPS: Ignore extra data after WSC attributes",
+                           pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len);
+       }
+
+       wpabuf_set(&msg, pos, wsc_len);
+       ret = wps_parse_msg(&msg, &attr);
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in "
+                          "Wi-Fi Handover Select Message");
+               goto out;
+       }
+
+       if (attr.oob_dev_password == NULL ||
+           attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+               wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password "
+                          "included in Wi-Fi Handover Select Message");
+               ret = -1;
+               goto out;
+       }
+
+       if (attr.ssid == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No SSID included in Wi-Fi Handover "
+                          "Select Message");
+               ret = -1;
+               goto out;
+       }
+
+       wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", attr.ssid, attr.ssid_len);
+
+       if (attr.mac_addr) {
+               bssid = attr.mac_addr;
+               wpa_printf(MSG_DEBUG, "WPS: MAC Address (BSSID): " MACSTR,
+                          MAC2STR(bssid));
+       }
+
+       if (attr.rf_bands)
+               wpa_printf(MSG_DEBUG, "WPS: RF Bands: %d", *attr.rf_bands);
+
+       if (attr.ap_channel) {
+               u16 chan = WPA_GET_BE16(attr.ap_channel);
 
+               wpa_printf(MSG_DEBUG, "WPS: AP Channel: %d", chan);
+
+               if (chan >= 1 && chan <= 13 &&
+                   (attr.rf_bands == NULL || *attr.rf_bands & WPS_RF_24GHZ))
+                       freq = 2407 + 5 * chan;
+               else if (chan == 14 &&
+                        (attr.rf_bands == NULL ||
+                         *attr.rf_bands & WPS_RF_24GHZ))
+                       freq = 2484;
+               else if (chan >= 30 &&
+                        (attr.rf_bands == NULL ||
+                         *attr.rf_bands & WPS_RF_50GHZ))
+                       freq = 5000 + 5 * chan;
+
+               if (freq) {
+                       wpa_printf(MSG_DEBUG,
+                                  "WPS: AP indicated channel %u -> %u MHz",
+                                  chan, freq);
+               }
+       }
+
+       wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password",
+                   attr.oob_dev_password, attr.oob_dev_password_len);
+       dev_pw_id = WPA_GET_BE16(attr.oob_dev_password +
+                                WPS_OOB_PUBKEY_HASH_LEN);
+       if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) {
+               wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID "
+                          "%u in Wi-Fi Handover Select Message", dev_pw_id);
+               ret = -1;
+               goto out;
+       }
+       wpa_hexdump(MSG_DEBUG, "WPS: AP Public Key hash",
+                   attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
+
+       ret = wpas_wps_start_nfc(wpa_s, NULL, bssid, NULL, dev_pw_id, 0,
+                                attr.oob_dev_password,
+                                attr.ssid, attr.ssid_len, freq);
+
+out:
+       wpabuf_free(wps);
        return ret;
 }
 
@@ -2307,15 +2623,107 @@ int wpas_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
        return wpas_wps_nfc_rx_handover_sel(wpa_s, sel);
 }
 
-#endif /* CONFIG_WPS_NFC */
 
+int wpas_er_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+                                   const struct wpabuf *req,
+                                   const struct wpabuf *sel)
+{
+       struct wpabuf *wps;
+       int ret = -1;
+       u16 wsc_len;
+       const u8 *pos;
+       struct wpabuf msg;
+       struct wps_parse_attr attr;
+       u16 dev_pw_id;
+
+       /*
+        * Enrollee/station is always initiator of the NFC connection handover,
+        * so use the request message here to find Enrollee public key hash.
+        */
+       wps = ndef_parse_wifi(req);
+       if (wps == NULL)
+               return -1;
+       wpa_printf(MSG_DEBUG, "WPS: Received application/vnd.wfa.wsc "
+                  "payload from NFC connection handover");
+       wpa_hexdump_buf(MSG_DEBUG, "WPS: NFC payload", wps);
+       if (wpabuf_len(wps) < 2) {
+               wpa_printf(MSG_DEBUG, "WPS: Too short Wi-Fi Handover Request "
+                          "Message");
+               goto out;
+       }
+       pos = wpabuf_head(wps);
+       wsc_len = WPA_GET_BE16(pos);
+       if (wsc_len > wpabuf_len(wps) - 2) {
+               wpa_printf(MSG_DEBUG, "WPS: Invalid WSC attribute length (%u) "
+                          "in rt Wi-Fi Handover Request Message", wsc_len);
+               goto out;
+       }
+       pos += 2;
+
+       wpa_hexdump(MSG_DEBUG,
+                   "WPS: WSC attributes in Wi-Fi Handover Request Message",
+                   pos, wsc_len);
+       if (wsc_len < wpabuf_len(wps) - 2) {
+               wpa_hexdump(MSG_DEBUG,
+                           "WPS: Ignore extra data after WSC attributes",
+                           pos + wsc_len, wpabuf_len(wps) - 2 - wsc_len);
+       }
+
+       wpabuf_set(&msg, pos, wsc_len);
+       ret = wps_parse_msg(&msg, &attr);
+       if (ret < 0) {
+               wpa_printf(MSG_DEBUG, "WPS: Could not parse WSC attributes in "
+                          "Wi-Fi Handover Request Message");
+               goto out;
+       }
+
+       if (attr.oob_dev_password == NULL ||
+           attr.oob_dev_password_len < WPS_OOB_PUBKEY_HASH_LEN + 2) {
+               wpa_printf(MSG_DEBUG, "WPS: No Out-of-Band Device Password "
+                          "included in Wi-Fi Handover Request Message");
+               ret = -1;
+               goto out;
+       }
+
+       if (attr.uuid_e == NULL) {
+               wpa_printf(MSG_DEBUG, "WPS: No UUID-E included in Wi-Fi "
+                          "Handover Request Message");
+               ret = -1;
+               goto out;
+       }
+
+       wpa_hexdump(MSG_DEBUG, "WPS: UUID-E", attr.uuid_e, WPS_UUID_LEN);
+
+       wpa_hexdump(MSG_DEBUG, "WPS: Out-of-Band Device Password",
+                   attr.oob_dev_password, attr.oob_dev_password_len);
+       dev_pw_id = WPA_GET_BE16(attr.oob_dev_password +
+                                WPS_OOB_PUBKEY_HASH_LEN);
+       if (dev_pw_id != DEV_PW_NFC_CONNECTION_HANDOVER) {
+               wpa_printf(MSG_DEBUG, "WPS: Unexpected OOB Device Password ID "
+                          "%u in Wi-Fi Handover Request Message", dev_pw_id);
+               ret = -1;
+               goto out;
+       }
+       wpa_hexdump(MSG_DEBUG, "WPS: Enrollee Public Key hash",
+                   attr.oob_dev_password, WPS_OOB_PUBKEY_HASH_LEN);
+
+       ret = wps_registrar_add_nfc_pw_token(wpa_s->wps->registrar,
+                                            attr.oob_dev_password,
+                                            DEV_PW_NFC_CONNECTION_HANDOVER,
+                                            NULL, 0, 1);
+
+out:
+       wpabuf_free(wps);
+       return ret;
+}
+
+#endif /* CONFIG_WPS_NFC */
 
-extern int wpa_debug_level;
 
 static void wpas_wps_dump_ap_info(struct wpa_supplicant *wpa_s)
 {
        size_t i;
-       struct os_time now;
+       struct os_reltime now;
 
        if (wpa_debug_level > MSG_DEBUG)
                return;
@@ -2323,7 +2731,7 @@ static void wpas_wps_dump_ap_info(struct wpa_supplicant *wpa_s)
        if (wpa_s->wps_ap == NULL)
                return;
 
-       os_get_time(&now);
+       os_get_reltime(&now);
 
        for (i = 0; i < wpa_s->num_wps_ap; i++) {
                struct wps_ap_info *ap = &wpa_s->wps_ap[i];
@@ -2427,11 +2835,14 @@ void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
 void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid)
 {
        struct wps_ap_info *ap;
+
+       wpa_s->after_wps = 0;
+
        if (!wpa_s->wps_ap_iter)
                return;
        ap = wpas_wps_get_ap_info(wpa_s, bssid);
        if (ap == NULL)
                return;
        ap->tries++;
-       os_get_time(&ap->last_attempt);
+       os_get_reltime(&ap->last_attempt);
 }
index 2a212ca..683bd50 100644 (file)
@@ -47,7 +47,7 @@ int wpas_wps_searching(struct wpa_supplicant *wpa_s);
 int wpas_wps_scan_result_text(const u8 *ies, size_t ies_len, char *pos,
                              char *end);
 int wpas_wps_er_start(struct wpa_supplicant *wpa_s, const char *filter);
-int wpas_wps_er_stop(struct wpa_supplicant *wpa_s);
+void wpas_wps_er_stop(struct wpa_supplicant *wpa_s);
 int wpas_wps_er_add_pin(struct wpa_supplicant *wpa_s, const u8 *addr,
                        const char *uuid, const char *pin);
 int wpas_wps_er_pbc(struct wpa_supplicant *wpa_s, const char *uuid);
@@ -60,24 +60,27 @@ int wpas_wps_er_config(struct wpa_supplicant *wpa_s, const char *uuid,
 struct wpabuf * wpas_wps_er_nfc_config_token(struct wpa_supplicant *wpa_s,
                                             int ndef, const char *uuid);
 int wpas_wps_terminate_pending(struct wpa_supplicant *wpa_s);
-int wpas_wps_in_progress(struct wpa_supplicant *wpa_s);
 void wpas_wps_update_config(struct wpa_supplicant *wpa_s);
 struct wpabuf * wpas_wps_nfc_config_token(struct wpa_supplicant *wpa_s,
                                          int ndef, const char *id_str);
 struct wpabuf * wpas_wps_nfc_token(struct wpa_supplicant *wpa_s, int ndef);
-int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *bssid);
+int wpas_wps_start_nfc(struct wpa_supplicant *wpa_s, const u8 *dev_addr,
+                      const u8 *bssid,
+                      const struct wpabuf *dev_pw, u16 dev_pw_id,
+                      int p2p_group, const u8 *peer_pubkey_hash,
+                      const u8 *ssid, size_t ssid_len, int freq);
 int wpas_wps_nfc_tag_read(struct wpa_supplicant *wpa_s,
-                         const struct wpabuf *data);
-struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s, int cr);
+                         const struct wpabuf *data, int forced_freq);
+struct wpabuf * wpas_wps_nfc_handover_req(struct wpa_supplicant *wpa_s,
+                                         int ndef);
 struct wpabuf * wpas_wps_nfc_handover_sel(struct wpa_supplicant *wpa_s,
                                          int ndef, int cr, const char *uuid);
-int wpas_wps_nfc_rx_handover_req(struct wpa_supplicant *wpa_s,
-                                const struct wpabuf *data);
-int wpas_wps_nfc_rx_handover_sel(struct wpa_supplicant *wpa_s,
-                                const struct wpabuf *data);
 int wpas_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
                                 const struct wpabuf *req,
                                 const struct wpabuf *sel);
+int wpas_er_wps_nfc_report_handover(struct wpa_supplicant *wpa_s,
+                                   const struct wpabuf *req,
+                                   const struct wpabuf *sel);
 void wpas_wps_update_ap_info(struct wpa_supplicant *wpa_s,
                             struct wpa_scan_results *scan_res);
 void wpas_wps_notify_assoc(struct wpa_supplicant *wpa_s, const u8 *bssid);
index 678695f..2f57d74 100644 (file)
@@ -22,7 +22,12 @@ class Ctrl:
         self.local = "/tmp/wpa_ctrl_" + str(os.getpid()) + '-' + str(counter)
         counter += 1
         self.s.bind(self.local)
-        self.s.connect(self.dest)
+        try:
+            self.s.connect(self.dest)
+        except Exception, e:
+            self.s.close()
+            os.unlink(self.local)
+            raise
         self.started = True
 
     def __del__(self):
@@ -30,15 +35,20 @@ class Ctrl:
 
     def close(self):
         if self.attached:
-            self.detach()
+            try:
+                self.detach()
+            except Exception, e:
+                # Need to ignore this allow the socket to be closed
+                self.attached = False
+                pass
         if self.started:
             self.s.close()
             os.unlink(self.local)
             self.started = False
 
-    def request(self, cmd):
+    def request(self, cmd, timeout=10):
         self.s.send(cmd)
-        [r, w, e] = select.select([self.s], [], [], 10)
+        [r, w, e] = select.select([self.s], [], [], timeout)
         if r:
             return self.s.recv(4096)
         raise Exception("Timeout on waiting response")
@@ -48,19 +58,23 @@ class Ctrl:
             return None
         res = self.request("ATTACH")
         if "OK" in res:
+            self.attached = True
             return None
         raise Exception("ATTACH failed")
 
     def detach(self):
         if not self.attached:
             return None
+        while self.pending():
+            ev = self.recv()
         res = self.request("DETACH")
-        if "OK" in res:
+        if "FAIL" not in res:
+            self.attached = False
             return None
         raise Exception("DETACH failed")
 
-    def pending(self):
-        [r, w, e] = select.select([self.s], [], [], 0)
+    def pending(self, timeout=0):
+        [r, w, e] = select.select([self.s], [], [], timeout)
         if r:
             return True
         return False